mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
forward Merge branch '4.19' into main
This commit is contained in:
commit
3baa45bc2a
@ -44,6 +44,10 @@ public interface PrimaryDataStoreDriver extends DataStoreDriver {
|
||||
|
||||
void revokeAccess(DataObject dataObject, Host host, DataStore dataStore);
|
||||
|
||||
default boolean requiresAccessForMigration(DataObject dataObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* intended for managed storage (cloud.storage_pool.managed = true)
|
||||
* if not managed, return volume.getSize()
|
||||
|
||||
@ -57,6 +57,8 @@ public interface VolumeService {
|
||||
|
||||
void revokeAccess(DataObject dataObject, Host host, DataStore dataStore);
|
||||
|
||||
boolean requiresAccessForMigration(DataObject dataObject, DataStore dataStore);
|
||||
|
||||
/**
|
||||
* Creates the volume based on the given criteria
|
||||
*
|
||||
|
||||
@ -1230,8 +1230,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
||||
DataStore dataStore = dataStoreMgr.getDataStore(volumeForVm.getPoolId(), DataStoreRole.Primary);
|
||||
PrimaryDataStore primaryDataStore = (PrimaryDataStore)dataStore;
|
||||
|
||||
// This might impact other managed storages, grant access for PowerFlex storage pool only
|
||||
if (primaryDataStore.isManaged() && primaryDataStore.getPoolType() == Storage.StoragePoolType.PowerFlex) {
|
||||
// This might impact other managed storages, enable requires access for migration in relevant datastore driver (currently enabled for PowerFlex storage pool only)
|
||||
if (primaryDataStore.isManaged() && volService.requiresAccessForMigration(volumeInfo, dataStore)) {
|
||||
volService.revokeAccess(volumeInfo, host, dataStore);
|
||||
}
|
||||
}
|
||||
@ -1509,8 +1509,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
||||
disk.setDetails(getDetails(volumeInfo, dataStore));
|
||||
|
||||
PrimaryDataStore primaryDataStore = (PrimaryDataStore)dataStore;
|
||||
// This might impact other managed storages, grant access for PowerFlex storage pool only
|
||||
if (primaryDataStore.isManaged() && primaryDataStore.getPoolType() == Storage.StoragePoolType.PowerFlex) {
|
||||
// This might impact other managed storages, enable requires access for migration in relevant datastore driver (currently enabled for PowerFlex storage pool only)
|
||||
if (primaryDataStore.isManaged() && volService.requiresAccessForMigration(volumeInfo, dataStore)) {
|
||||
volService.grantAccess(volFactory.getVolume(vol.getId()), dest.getHost(), dataStore);
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
-- VIEW `cloud`.`domain_view`;
|
||||
|
||||
DROP VIEW IF EXISTS `cloud`.`domain_view`;
|
||||
|
||||
CREATE VIEW `cloud`.`domain_view` AS
|
||||
select
|
||||
`domain`.`id` AS `id`,
|
||||
|
||||
@ -268,6 +268,19 @@ public class VolumeServiceImpl implements VolumeService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresAccessForMigration(DataObject dataObject, DataStore dataStore) {
|
||||
DataStoreDriver dataStoreDriver = dataStore != null ? dataStore.getDriver() : null;
|
||||
if (dataStoreDriver == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dataStoreDriver instanceof PrimaryDataStoreDriver) {
|
||||
return ((PrimaryDataStoreDriver)dataStoreDriver).requiresAccessForMigration(dataObject);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncCallFuture<VolumeApiResult> createVolumeAsync(VolumeInfo volume, DataStore dataStore) {
|
||||
AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
|
||||
|
||||
@ -21,14 +21,14 @@ function usage() {
|
||||
Usage: ./deploy-cloudstack-secret [OPTIONS]...
|
||||
To deploy the keys needed for the cloudstack kubernetes provider.
|
||||
Arguments:
|
||||
-u, --url string ID of the cluster
|
||||
-u, --url string URL of the CloudStack API
|
||||
-k, --key string API Key
|
||||
-s, --secret string Secret Key
|
||||
-p, --project string Project ID
|
||||
Other arguments:
|
||||
-h, --help Display this help message and exit
|
||||
Examples:
|
||||
./deploy-cloudstack-secret -u http://localhost:8080 -k abcd -s efgh
|
||||
./deploy-cloudstack-secret -u http://10.10.10.10:8080/client/api -k abcd -s efgh
|
||||
USAGE
|
||||
exit 0
|
||||
}
|
||||
|
||||
@ -304,6 +304,11 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresAccessForMigration(DataObject dataObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUsedBytes(StoragePool storagePool) {
|
||||
long usedSpaceBytes = 0;
|
||||
|
||||
2
ui/public/config.json
vendored
2
ui/public/config.json
vendored
@ -14,6 +14,8 @@
|
||||
"logo": "assets/logo.svg",
|
||||
"minilogo": "assets/mini-logo.svg",
|
||||
"banner": "assets/banner.svg",
|
||||
"loginPageTitle": "CloudStack",
|
||||
"loginPageFavicon": "assets/logo.svg",
|
||||
"error": {
|
||||
"403": "assets/403.png",
|
||||
"404": "assets/404.png",
|
||||
|
||||
12
ui/public/index.html
vendored
12
ui/public/index.html
vendored
@ -22,8 +22,8 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>cloud.ico">
|
||||
<title>Apache CloudStack</title>
|
||||
<link id="favicon" rel="icon" href="<%= BASE_URL %>cloud.ico">
|
||||
<title id="title" >Apache CloudStack</title>
|
||||
<style>
|
||||
.loader {
|
||||
border: 16px solid #F3F3F3;
|
||||
@ -54,4 +54,12 @@
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
fetch('./config.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
document.getElementById("favicon").setAttribute("href", data.loginPageFavicon);
|
||||
document.getElementById("title").innerHTML = data.loginPageTitle;
|
||||
}).catch((err) => {});
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@ -580,6 +580,7 @@
|
||||
"label.creating.iprange": "Creating IP ranges",
|
||||
"label.credit": "Credit",
|
||||
"label.cron": "Cron expression",
|
||||
"label.cron.mode": "Cron mode",
|
||||
"label.crosszones": "Cross zones",
|
||||
"label.currency": "Currency",
|
||||
"label.current": "Current",
|
||||
|
||||
@ -22,8 +22,9 @@
|
||||
style="width: 100%; margin-bottom: 10px"
|
||||
@click="showAddModal"
|
||||
:loading="loading"
|
||||
:disabled="!('createVMSchedule' in $store.getters.apis)">
|
||||
<template #icon><plus-outlined/></template> {{ $t('label.schedule.add') }}
|
||||
:disabled="!('createVMSchedule' in $store.getters.apis)"
|
||||
>
|
||||
<template #icon><plus-outlined /></template> {{ $t('label.schedule.add') }}
|
||||
</a-button>
|
||||
<list-view
|
||||
:loading="tabLoading"
|
||||
@ -35,7 +36,8 @@
|
||||
@update-selected-columns="updateSelectedColumns"
|
||||
@update-vm-schedule="updateVMSchedule"
|
||||
@remove-vm-schedule="removeVMSchedule"
|
||||
@refresh="this.fetchData"/>
|
||||
@refresh="this.fetchData"
|
||||
/>
|
||||
<a-pagination
|
||||
class="row-element"
|
||||
style="margin-top: 10px"
|
||||
@ -43,12 +45,13 @@
|
||||
:current="page"
|
||||
:pageSize="pageSize"
|
||||
:total="totalCount"
|
||||
:showTotal="total => `${$t('label.showing')} ${Math.min(total, 1+((page-1)*pageSize))}-${Math.min(page*pageSize, total)} ${$t('label.of')} ${total} ${$t('label.items')}`"
|
||||
:showTotal="total => `${$t('label.showing')} ${Math.min(total, 1 + ((page - 1) * pageSize))}-${Math.min(page * pageSize, total)} ${$t('label.of')} ${total} ${$t('label.items')}`"
|
||||
:pageSizeOptions="pageSizeOptions"
|
||||
@change="changePage"
|
||||
@showSizeChange="changePage"
|
||||
showSizeChanger
|
||||
showQuickJumper>
|
||||
showQuickJumper
|
||||
>
|
||||
<template #buildOptionText="props">
|
||||
<span>{{ props.value }} / {{ $t('label.page') }}</span>
|
||||
</template>
|
||||
@ -60,66 +63,68 @@
|
||||
:title="$t('label.schedule')"
|
||||
:maskClosable="false"
|
||||
:closable="true"
|
||||
:footer="null"
|
||||
@cancel="closeModal">
|
||||
@cancel="closeModal"
|
||||
@ok="submitForm"
|
||||
>
|
||||
<a-form
|
||||
layout="vertical"
|
||||
:ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
@finish="submitForm"
|
||||
v-ctrl-enter="submitForm">
|
||||
<a-form-item name="description" ref="description">
|
||||
v-ctrl-enter="submitForm"
|
||||
>
|
||||
<a-form-item
|
||||
name="description"
|
||||
ref="description"
|
||||
:wrapperCol="{ span: 24 }"
|
||||
>
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.description')" :tooltip="apiParams.description.description"/>
|
||||
<tooltip-label
|
||||
:title="$t('label.description')"
|
||||
:tooltip="apiParams.description.description"
|
||||
/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.description"
|
||||
v-focus="true" />
|
||||
v-focus="true"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item name="action" ref="action">
|
||||
<a-form-item
|
||||
name="action"
|
||||
ref="action"
|
||||
:wrapperCol="{ span: 24 }"
|
||||
>
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.action')" :tooltip="apiParams.action.description"/>
|
||||
<tooltip-label
|
||||
:title="$t('label.action')"
|
||||
:tooltip="apiParams.action.description"
|
||||
/>
|
||||
</template>
|
||||
<a-radio-group
|
||||
v-model:value="form.action"
|
||||
button-style="solid"
|
||||
:disabled="isEdit">
|
||||
<a-radio-button v-for="action in actions" :key="action.id" :value="action.value">
|
||||
:disabled="isEdit"
|
||||
>
|
||||
<a-radio-button
|
||||
v-for="action in actions"
|
||||
:key="action.id"
|
||||
:value="action.value"
|
||||
>
|
||||
{{ $t(action.label) }}
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item name="schedule" ref="schedule">
|
||||
<a-form-item
|
||||
name="timezone"
|
||||
ref="timezone"
|
||||
:wrapperCol="{ span: 24 }"
|
||||
>
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.schedule')" :tooltip="apiParams.schedule.description"/>
|
||||
</template>
|
||||
<label>{{ $t('label.advanced.mode') }}</label>
|
||||
<a-switch
|
||||
v-model:checked="form.useCronFormat"
|
||||
>
|
||||
</a-switch>
|
||||
<br/>
|
||||
<span v-if="!form.useCronFormat">
|
||||
<cron-ant
|
||||
v-model="form.schedule"
|
||||
:periods="periods"
|
||||
:button-props="{ type: 'primary', size: 'small', disabled: form.useCronFormat }"
|
||||
@error="error=$event"/>
|
||||
</span>
|
||||
<span v-if="form.useCronFormat">
|
||||
<label>{{ generateHumanReadableSchedule(form.schedule) }}</label>
|
||||
<br/>
|
||||
</span>
|
||||
<a-input
|
||||
:addonBefore="$t('label.cron')"
|
||||
v-model:value="form.schedule"
|
||||
:disabled="!form.useCronFormat"
|
||||
v-focus="true" />
|
||||
</a-form-item>
|
||||
<a-form-item name="timezone" ref="timezone">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.timezone')" :tooltip="apiParams.timezone.description"/>
|
||||
<tooltip-label
|
||||
:title="$t('label.timezone')"
|
||||
:tooltip="apiParams.timezone.description"
|
||||
/>
|
||||
</template>
|
||||
<a-select
|
||||
showSearch
|
||||
@ -128,54 +133,119 @@
|
||||
:filterOption="(input, option) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="fetching">
|
||||
<a-select-option v-for="opt in timeZoneMap" :key="opt.id" :label="opt.name || opt.description">
|
||||
:loading="fetching"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="opt in timeZoneMap"
|
||||
:key="opt.id"
|
||||
:label="opt.name || opt.description"
|
||||
>
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item name="startDate" ref="startDate">
|
||||
<a-row justify="space-between">
|
||||
<a-col>
|
||||
<a-form-item
|
||||
name="startDate"
|
||||
ref="startDate"
|
||||
>
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.start.date.and.time')"
|
||||
:tooltip="apiParams.startdate.description"
|
||||
/>
|
||||
</template>
|
||||
<a-date-picker
|
||||
v-model:value="form.startDate"
|
||||
show-time
|
||||
:locale="this.$i18n.locale"
|
||||
:placeholder="$t('message.select.start.date.and.time')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<a-form-item
|
||||
name="endDate"
|
||||
ref="endDate"
|
||||
>
|
||||
<template #label>
|
||||
<tooltip-label
|
||||
:title="$t('label.end.date.and.time')"
|
||||
:tooltip="apiParams.enddate.description"
|
||||
/>
|
||||
</template>
|
||||
<a-date-picker
|
||||
v-model:value="form.endDate"
|
||||
show-time
|
||||
:locale="this.$i18n.locale"
|
||||
:placeholder="$t('message.select.end.date.and.time')"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item
|
||||
name="schedule"
|
||||
ref="schedule"
|
||||
:wrapperCol="{ span: 24 }"
|
||||
>
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.start.date.and.time')" :tooltip="apiParams.startdate.description"/>
|
||||
<tooltip-label
|
||||
:title="$t('label.schedule')"
|
||||
:tooltip="apiParams.schedule.description"
|
||||
/>
|
||||
</template>
|
||||
<a-date-picker
|
||||
v-model:value="form.startDate"
|
||||
show-time
|
||||
:locale="this.$i18n.locale"
|
||||
:placeholder="$t('message.select.start.date.and.time')"/>
|
||||
<a-row
|
||||
style="margin-bottom: 15px; text-align: center;"
|
||||
justify="space-around"
|
||||
align="middle"
|
||||
>
|
||||
<cron-ant
|
||||
v-if="!form.useCronFormat"
|
||||
v-model="form.schedule"
|
||||
:periods="periods"
|
||||
:button-props="{ type: 'primary', size: 'small', disabled: form.useCronFormat }"
|
||||
@error="error = $event"
|
||||
/>
|
||||
<label
|
||||
v-if="form.useCronFormat">
|
||||
{{ generateHumanReadableSchedule(form.schedule) }}
|
||||
</label>
|
||||
</a-row>
|
||||
<a-row
|
||||
justify="space-between"
|
||||
align="middle"
|
||||
>
|
||||
<a-col>
|
||||
<label>{{ $t('label.cron.mode') }}</label>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<a-switch v-model:checked="form.useCronFormat">
|
||||
</a-switch>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-input
|
||||
:addonBefore="$t('label.cron')"
|
||||
v-model:value="form.schedule"
|
||||
:disabled="!form.useCronFormat"
|
||||
v-focus="true"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
<a-form-item name="endDate" ref="endDate">
|
||||
<a-form-item
|
||||
name="enabled"
|
||||
ref="enabled"
|
||||
:wrapperCol="{ span: 24}"
|
||||
>
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.end.date.and.time')" :tooltip="apiParams.enddate.description"/>
|
||||
<tooltip-label
|
||||
:title="$t('label.enabled')"
|
||||
:tooltip="apiParams.enabled.description"
|
||||
/>
|
||||
</template>
|
||||
<a-date-picker
|
||||
v-model:value="form.endDate"
|
||||
show-time
|
||||
:locale="this.$i18n.locale"
|
||||
:placeholder="$t('message.select.end.date.and.time')"/>
|
||||
<a-switch v-model:checked="form.enabled" />
|
||||
</a-form-item>
|
||||
<a-form-item name="enabled" ref="enabled">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.enabled')" :tooltip="apiParams.enabled.description"/>
|
||||
</template>
|
||||
<a-switch
|
||||
v-model:checked="form.enabled">
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
<div :span="24" class="action-button">
|
||||
<a-button
|
||||
:loading="loading"
|
||||
@click="closeModal">
|
||||
{{ $t('label.cancel') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
:loading="loading"
|
||||
ref="submit"
|
||||
type="primary"
|
||||
htmlType="submit">
|
||||
{{ $t('label.ok') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
@ -191,7 +261,12 @@ import { mixinForm } from '@/utils/mixin'
|
||||
import { timeZone } from '@/utils/timezone'
|
||||
import debounce from 'lodash/debounce'
|
||||
import cronstrue from 'cronstrue/i18n'
|
||||
import moment from 'moment-timezone'
|
||||
import dayjs from 'dayjs'
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
import timezone from 'dayjs/plugin/timezone'
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
export default {
|
||||
name: 'InstanceSchedules',
|
||||
@ -323,8 +398,8 @@ export default {
|
||||
this.isEdit = true
|
||||
Object.assign(this.form, schedule)
|
||||
// Some weird issue when we directly pass in the moment with tz object
|
||||
this.form.startDate = moment(moment(schedule.startdate).tz(schedule.timezone).format(this.pattern))
|
||||
this.form.endDate = schedule.enddate ? moment(moment(schedule.enddate).tz(schedule.timezone).format(this.pattern)) : ''
|
||||
this.form.startDate = dayjs(schedule.startdate).tz(schedule.timezone)
|
||||
this.form.endDate = schedule.enddate ? dayjs(dayjs(schedule.enddate).tz(schedule.timezone)) : null
|
||||
this.showAddModal()
|
||||
},
|
||||
showAddModal () {
|
||||
@ -431,7 +506,7 @@ export default {
|
||||
this.updateColumns()
|
||||
},
|
||||
generateHumanReadableSchedule (schedule) {
|
||||
return cronstrue.toString(schedule, { locale: this.$i18n.locale, throwExceptionOnParseError: false })
|
||||
return cronstrue.toString(schedule, { locale: this.$i18n.locale, throwExceptionOnParseError: false, verbose: true })
|
||||
},
|
||||
updateColumns () {
|
||||
this.columns = []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user