Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ui/public/locales/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@
"label.available": "Verfügbar",
"label.back": "Zurück",
"label.backup": "Backup",
"label.backups": "Backup",
"label.backup.attach.restore": "Backup-Volume wiederherstellen und anhängen",
"label.backup.offering.assign": "VM zum Backup-Angebot zuordnen",
"label.backup.offering.remove": "VM vom Backup-Angebot entfernen",
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/el_GR.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@
"label.availableprocessors": "Διαθέσιμοι πυρήνες επεξεργαστή",
"label.back": "Πίσω",
"label.backup": "Αντίγραφα ασφαλείας",
"label.backups": "Αντίγραφα ασφαλείας",
"label.backup.attach.restore": "Επαναφορά και επισύναψη τόμου αντιγράφου ασφαλείας",
"label.backup.offering.assign": "Αντιστοίχιση εικονικής μηχανής με προσφορά δημιουργίας αντιγράφων ασφαλείας",
"label.backup.offering.remove": "Κατάργηση εικονικής μηχανής από την προσφορά δημιουργίας αντιγράφων ασφαλείας",
Expand Down
8 changes: 7 additions & 1 deletion ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
"label.add.acl.rule": "Add ACL rule",
"label.add.acl": "Add ACL",
"label.add.affinity.group": "Add new Affinity Group",
"label.add.backup.schedule": "Add Backup Schedule",
"label.add.baremetal.dhcp.device": "Add bare metal DHCP device",
"label.add.bgp.peer": "Add BGP Peer",
"label.add.bigswitchbcf.device": "Add BigSwitch BCF Controller",
Expand Down Expand Up @@ -442,14 +443,17 @@
"label.availablevirtualmachinecount": "Available Instances",
"label.back": "Back",
"label.back.login": "Back to login",
"label.backup": "Backups",
"label.backup": "Backup",
"label.backups": "Backups",
"label.backup.attach.restore": "Restore and attach backup volume",
"label.backup.configure.schedule": "Configure Backup Schedule",
"label.backup.offering.assign": "Assign Instance to backup offering",
"label.backup.offering.remove": "Remove Instance from backup offering",
"label.backup.offerings": "Backup Offerings",
"label.backup.offering.assign.failed": "Failed to assign Backup Offering",
"label.backup.repository": "Backup Repository",
"label.backup.restore": "Restore Instance backup",
"label.backup.schedule.create.failed": "Failed to create Backup Schedule",
"label.backuplimit": "Backup Limits",
"label.backup.storage": "Backup Storage",
"label.backupstoragelimit": "Backup Storage Limits (GiB)",
Expand Down Expand Up @@ -2189,6 +2193,7 @@
"label.select.all": "Select all",
"label.select.columns": "Select columns",
"label.select.a.zone": "Select a Zone",
"label.select.backup.offering": "Select Backup Offering",
"label.select.deployment.infrastructure": "Select deployment infrastructure",
"label.select.guest.os.type": "Please select the guest OS type",
"label.select.network": "Select Network",
Expand Down Expand Up @@ -3062,6 +3067,7 @@
"message.backup.attach.restore": "Please confirm that you want to restore and attach the volume from the backup?",
"message.backup.create": "Are you sure you want to create an Instance backup?",
"message.backup.offering.remove": "Are you sure you want to remove Instance from backup offering and delete the backup chain?",
"message.backup.provision.instance": "Select a backup offering to assign to the Instance. You can also add one or more backup schedules to automate backups for this Instance. Assigning a backup offering and schedules helps protect your data by enabling automated and scheduled backups.",
"message.backup.restore": "Please confirm that you want to restore the Instance backup?",
"message.cancel.shutdown": "Please confirm that you would like to cancel the shutdown on this Management Server. It will resume accepting any new Async Jobs.",
"message.cancel.maintenance": "Please confirm that you would like to cancel the maintenance on this Management Server. It will resume accepting any new Async Jobs.",
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@
"label.available": "Disponible",
"label.back": "Volver",
"label.backup": "Respaldos",
"label.backups": "Respaldos",
"label.backup.attach.restore": "Restaurar y conectar un Volumen de Respaldo",
"label.backup.offering.assign": "Asignar instancia a una oferta de respaldo",
"label.backup.offering.remove": "remover instancia de una oferta de respaldo",
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/ja_JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@
"label.available.public.ips": "使用できるパブリックIPアドレス",
"label.back": "戻る",
"label.backup": "バックアップ",
"label.backups": "バックアップ",
"label.backup.attach.restore": "復元とバックアップボリュームをアタッチ",
"label.backup.offering.assign": "VMをバックアップオファリングに割り当て",
"label.backup.offering.remove": "VMバックアップオファリングから削除",
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/ko_KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
"label.available": "\uc0ac\uc6a9 \uac00\ub2a5",
"label.back": "\ub4a4\ub85c",
"label.backup": "\ubc31\uc5c5",
"label.backups": "\ubc31\uc5c5",
"label.backup.attach.restore": "\ubc31\uc5c5 \ubcfc\ub968 \ubcf5\uc6d0 \ubc0f \uc5f0\uacb0",
"label.backup.offering.assign": "\uac00\uc0c1\uba38\uc2e0\uc5d0 \ubc31\uc5c5 \uc624\ud37c\ub9c1 \ud560\ub2f9",
"label.backup.offering.remove": "\uac00\uc0c1\uba38\uc2e0\uc5d0 \ubc31\uc5c5 \uc624\ud37c\ub9c1 \uc81c\uac70",
Expand Down
3 changes: 2 additions & 1 deletion ui/public/locales/pt_BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,8 @@
"label.availability": "Disponibilidade",
"label.available": "Dispon\u00edvel",
"label.back": "Voltar",
"label.backup": "Backups",
"label.backup": "Backup",
"label.backups": "Backups",
"label.backup.attach.restore": "Restaurar e anexar volume de backup",
"label.backup.offering.assign": "Atribuir VM a oferta de backup",
"label.backup.offering.remove": "Remover VM de oferta de backup",
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/te.json
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@
"label.back": "వెనుకకు",
"label.back.login": "తిరిగి లాగిన్‌కి",
"label.backup": "బ్యాకప్‌లు",
"label.backups": "బ్యాకప్‌లు",
"label.backup.attach.restore": "బ్యాకప్ వాల్యూమ్‌ను పునరుద్ధరించండి మరియు అటాచ్ చేయండి",
"label.backup.configure.schedule": "బ్యాకప్ షెడ్యూల్‌ను కాన్ఫిగర్ చేయండి",
"label.backup.offering.assign": "బ్యాకప్ సమర్పణకు ఉదాహరణను కేటాయించండి",
Expand Down
1 change: 1 addition & 0 deletions ui/public/locales/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@

"label.back": "\u540E\u9000",
"label.backup": "\u5907\u4EFD",
"label.backups": "\u5907\u4EFD",
"label.backup.attach.restore": "\u6062\u590D\u5E76\u8FDE\u63A5\u5907\u4EFD\u5377",
"label.backup.offering.assign": "\u5C06\u865A\u62DF\u673A\u5206\u914D\u7ED9\u5907\u4EFD\u4EA7\u54C1",
"label.backup.offering.remove": "\u4ECE\u5907\u4EFD\u4EA7\u54C1\u4E2D\u5220\u9664\u865A\u62DF\u673A",
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/widgets/InfiniteScrollSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
</template>
<a-select-option v-for="option in options" :key="option.id" :value="option[optionValueKey]">
<span>
<span v-if="showIcon">
<span v-if="showIcon && option.showicon !== false">
<resource-icon v-if="option.icon && option.icon.base64image" :image="option.icon.base64image" size="1x" style="margin-right: 5px"/>
<render-icon v-else :icon="defaultIcon" style="margin-right: 5px" />
</span>
Expand Down
2 changes: 1 addition & 1 deletion ui/src/config/section/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ export default {
},
{
name: 'backup',
title: 'label.backup',
title: 'label.backups',
icon: 'cloud-upload-outlined',
permission: ['listBackups'],
params: { listvmdetails: 'true' },
Expand Down
4 changes: 3 additions & 1 deletion ui/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ import {
dialogUtilPlugin,
cpuArchitectureUtilPlugin,
imagesUtilPlugin,
extensionsUtilPlugin
extensionsUtilPlugin,
backupUtilPlugin
} from './utils/plugins'
import { VueAxios } from './utils/request'
import directives from './utils/directives'
Expand All @@ -63,6 +64,7 @@ vueApp.use(dialogUtilPlugin)
vueApp.use(cpuArchitectureUtilPlugin)
vueApp.use(imagesUtilPlugin)
vueApp.use(extensionsUtilPlugin)
vueApp.use(backupUtilPlugin)
vueApp.use(extensions)
vueApp.use(directives)

Expand Down
11 changes: 11 additions & 0 deletions ui/src/utils/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,14 @@ export const extensionsUtilPlugin = {
}
}
}

export const backupUtilPlugin = {
install (app) {
app.config.globalProperties.$isBackupProviderSupportsQuiesceVm = function (provider) {
if (!provider && typeof provider !== 'string') {
return false
}
return ['nas'].includes(provider.toLowerCase())
}
}
}
4 changes: 1 addition & 3 deletions ui/src/views/compute/BackupScheduleWizard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
<a-tab-pane :tab="$t('label.schedule')" key="1">
<FormSchedule
:loading="loading"
:resource="resource"
:dataSource="dataSource"/>
:resource="resource"/>
</a-tab-pane>
<a-tab-pane :tab="$t('label.scheduled.backups')" key="2">
<BackupSchedule
:loading="loading"
:resource="resource"
:dataSource="dataSource" />
</a-tab-pane>
</a-tabs>
Expand Down
143 changes: 141 additions & 2 deletions ui/src/views/compute/DeployVM.vue
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,25 @@
</div>
</template>
</a-step>
<a-step
v-if="isUserAllowedBackupOperations"
:title="$t('label.backup')"
:status="zoneSelected ? 'process' : 'wait'">
<template #description>
<div v-if="zoneSelected" style="margin-top: 15px">
<deploy-instance-backup-selection
:zoneId="zoneId"
v-model:backupOfferingId="form.backupofferingid"
:backupSchedules="backupSchedules"
@change-backup-offering="onChangeBackupOffering"
@add-backup-schedule="onAddBackupSchedule"
@delete-backup-schedule="backupSchedules = backupSchedules.filter(schedule => schedule.id !== $event.id)" />
<a-form-item class="form-item-hidden">
<a-input v-model:value="form.backupofferingid" />
</a-form-item>
</div>
</template>
</a-step>
<a-step
:title="$t('label.advanced.mode')"
:status="zoneSelected ? 'process' : 'wait'">
Expand Down Expand Up @@ -930,6 +949,7 @@ import SecurityGroupSelection from '@views/compute/wizard/SecurityGroupSelection
import TooltipLabel from '@/components/widgets/TooltipLabel'
import InstanceNicsNetworkSelectListView from '@/components/view/InstanceNicsNetworkSelectListView'
import DetailsInput from '@/components/widgets/DetailsInput'
import DeployInstanceBackupSelection from '@views/compute/wizard/DeployInstanceBackupSelection'

export default {
name: 'Wizard',
Expand All @@ -955,7 +975,8 @@ export default {
SecurityGroupSelection,
TooltipLabel,
InstanceNicsNetworkSelectListView,
DetailsInput
DetailsInput,
DeployInstanceBackupSelection
},
props: {
visible: {
Expand Down Expand Up @@ -1135,7 +1156,9 @@ export default {
opts: []
},
externalDetailsEnabled: false,
selectedExtensionId: null
selectedExtensionId: null,
selectedBackupOffering: null,
backupSchedules: []
}
},
computed: {
Expand Down Expand Up @@ -1515,6 +1538,10 @@ export default {
},
isTemplateHypervisorExternal () {
return !!this.template && this.template.hypervisor === 'External'
},
isUserAllowedBackupOperations () {
return Boolean('listBackupOfferings' in this.$store.getters.apis) &&
Boolean('assignVirtualMachineToBackupOffering' in this.$store.getters.apis)
}
},
watch: {
Expand Down Expand Up @@ -1672,6 +1699,13 @@ export default {
if (this.leaseduration < 1) {
this.vm.leaseduration = undefined
}

delete this.vm.backupofferingid
delete this.vm.backupofferingname
if (this.form.backupofferingid && this.selectedBackupOffering) {
this.vm.backupofferingid = this.selectedBackupOffering.id
this.vm.backupofferingname = this.selectedBackupOffering.name
}
}
}
},
Expand Down Expand Up @@ -2507,6 +2541,7 @@ export default {
duration: 0
})
}
this.performPostDeployBackupActions(vm)
eventBus.emit('vm-refresh-data')
},
loadingMessage: `${title} ${this.$t('label.in.progress')}`,
Expand Down Expand Up @@ -3004,6 +3039,7 @@ export default {
this.resetTemplatesList()
this.resetIsosList()
this.imageType = this.queryIsoId ? 'isoid' : 'templateid'
this.form.backupofferingid = undefined
this.fetchZoneOptions()
},
onSelectPodId (value) {
Expand Down Expand Up @@ -3395,6 +3431,109 @@ export default {
return
}
this.form.externaldetails = undefined
},
onChangeBackupOffering (val) {
if (!val || !val.id) {
this.selectedBackupOffering = null
this.backupSchedules = []
return
}
this.selectedBackupOffering = val
if (this.backupSchedules && this.backupSchedules.length > 0 && !this.$isBackupProviderSupportsQuiesceVm(val.provider)) {
this.backupSchedules = this.backupSchedules.filter(item => !item.quiescevm)
}
},
onAddBackupSchedule (schedule) {
if (!schedule) {
return
}
// This is in accordance with the API behavior that only one schedule per intervaltype is allowed
const existingIndex = this.backupSchedules.findIndex(item => item.intervaltype === schedule.intervaltype)
if (existingIndex !== -1) {
this.backupSchedules.splice(existingIndex, 1, schedule)
return
}
this.backupSchedules.push(schedule)
},
async performPostDeployBackupActions (vm) {
if (!this.isUserAllowedBackupOperations) {
return
}
const assigned = await this.assignVirtualMachineToBackupOfferingIfNeeded(vm)
if (assigned) {
await this.createVirtualMachineBackupSchedulesIfNeeded(vm)
}
},
assignVirtualMachineToBackupOfferingIfNeeded (vm) {
if (!this.form.backupofferingid || !vm || !vm.id) {
return Promise.resolve(false)
}
const params = {
virtualmachineid: vm.id,
backupofferingid: this.form.backupofferingid
}
return new Promise((resolve, reject) => {
postAPI('assignVirtualMachineToBackupOffering', params).then(json => {
const jobId = json.assignvirtualmachinetobackupofferingresponse?.jobid
if (!jobId) {
resolve(false)
return
}
this.$pollJob({
jobId,
loadingMessage: `${this.$t('label.backup.offering.assign')} ${this.$t('label.in.progress')}`,
successMethod: () => {
resolve(true)
},
errorMethod: (result) => {
this.$notification.error({
message: this.$t('label.backup.offering.assign.failed'),
description: result?.jobresult?.errortext || this.$t('error.fetching.async.job.result')
})
resolve(false)
},
catchMessage: this.$t('error.fetching.async.job.result')
})
}).catch(error => {
this.$notification.error({
message: this.$t('label.backup.offering.assign.failed'),
description: error.message || error
})
resolve(false)
})
})
},
createVirtualMachineBackupSchedulesIfNeeded (vm) {
if (!vm || !vm.id || !this.backupSchedules) {
return Promise.resolve()
}
const promises = (this.backupSchedules || []).map(item =>
this.createVirtualMachineBackupSchedule(vm, item)
)
return Promise.all(promises)
},
createVirtualMachineBackupSchedule (vm, item) {
const params = {
virtualmachineid: vm.id,
intervaltype: item.intervaltype,
maxbackups: item.maxbackups,
timezone: item.timezone,
schedule: item.schedule
}
if (item.quiescevm) {
params.quiescevm = item.quiescevm
}
return new Promise((resolve, reject) => {
postAPI('createBackupSchedule', params).then(response => {
resolve(response)
}).catch(error => {
this.$notification.error({
message: this.$t('label.backup.schedule.create.failed'),
description: error.message || error
})
reject(error)
})
})
}
}
}
Expand Down
Loading
Loading