| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| <template> | |
| <div class="form"> | |
| <div v-if="!configure"> | |
| <div style="margin-bottom: 10px"> | |
| <a-alert type="warning"> | |
| <template #message> | |
| <div v-html="$t('message.action.create.instance.from.backup')"></div> | |
| </template> | |
| </a-alert> | |
| </div> | |
| <a-form :model="form" layout="vertical"> | |
| <a-form-item :label="$t('label.name.optional')" name="name"> | |
| <a-input v-model:value="form.name" /> | |
| </a-form-item> | |
| <a-form-item v-if="resource.isbackupvmexpunged" name="preserveIpAddresses" style="margin-top: 8px"> | |
| <a-switch v-model:checked="form.preserveIpAddresses" /> | |
| <template #label> | |
| <tooltip-label :title="$t('label.use.backup.ip.address')" :tooltip="$t('label.use.backup.ip.address.tooltip')"/> | |
| </template> | |
| </a-form-item> | |
| <a-form-item name="quickRestore" ref="quickRestore" > | |
| <template #label> | |
| <tooltip-label :title="$t('label.quickrestore')" :tooltip="apiParams.quickrestore?.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.quickRestore" /> | |
| </a-form-item> | |
| </a-form> | |
| <div class="card-footer"> | |
| <a-button @click="closeAction"> | |
| {{ $t('label.cancel') }} | |
| </a-button> | |
| <a-button @click="setConfigure" :loading="loading"> | |
| {{ $t('label.configure.instance') }} | |
| </a-button> | |
| <a-button style="margin-left: 10px" type="primary" ref="submit" :loading="loading" @click="handleSubmit"> | |
| {{ $t('label.ok') }} | |
| </a-button> | |
| </div> | |
| </div> | |
| <div v-else class="form"> | |
| <DeployVMFromBackup | |
| :preFillContent="dataPreFill" | |
| @close-action="closeAction"/> | |
| </div> | |
| </div> | |
| </template> | |
| <script> | |
| import { h } from 'vue' | |
| import { getAPI, postAPI } from '@/api' | |
| import { Button } from 'ant-design-vue' | |
| import TooltipLabel from '@/components/widgets/TooltipLabel' | |
| import eventBus from '@/config/eventBus' | |
| import DeployVMFromBackup from '@/components/view/DeployVMFromBackup' | |
| export default { | |
| name: 'CreateVMFromBackup', | |
| components: { | |
| DeployVMFromBackup, | |
| TooltipLabel | |
| }, | |
| data () { | |
| return { | |
| configure: false, | |
| dataPreFill: {}, | |
| vmdetails: {}, | |
| serviceOffering: {}, | |
| loading: true, | |
| form: { | |
| name: '', | |
| quickRestore: false, | |
| preserveIpAddresses: false | |
| } | |
| } | |
| }, | |
| props: { | |
| resource: { | |
| type: Object, | |
| required: true | |
| } | |
| }, | |
| beforeCreate () { | |
| this.apiParams = this.$getApiParams('createVMFromBackup') | |
| }, | |
| async created () { | |
| await Promise.all([ | |
| this.fetchServiceOffering(), | |
| this.fetchBackupOffering(), | |
| this.fetchBackupArch() | |
| ]) | |
| this.loading = false | |
| }, | |
| methods: { | |
| fetchServiceOffering () { | |
| return getAPI('listServiceOfferings', { | |
| zoneid: this.resource.zoneid, | |
| id: this.resource.vmdetails.serviceofferingid, | |
| listall: true | |
| }).then(response => { | |
| const serviceOfferings = response.listserviceofferingsresponse.serviceoffering || [] | |
| this.serviceOffering = serviceOfferings[0] | |
| }) | |
| }, | |
| fetchBackupOffering () { | |
| return getAPI('listBackupOfferings', { | |
| id: this.resource.backupofferingid, | |
| listall: true | |
| }).then(response => { | |
| const backupOfferings = response.listbackupofferingsresponse.backupoffering || [] | |
| this.backupOffering = backupOfferings[0] | |
| }) | |
| }, | |
| fetchBackupArch () { | |
| const isIso = this.resource.vmdetails.isiso === 'true' | |
| const api = isIso ? 'listIsos' : 'listTemplates' | |
| const responseKey = isIso ? 'listisosresponse' : 'listtemplatesresponse' | |
| const itemKey = isIso ? 'iso' : 'template' | |
| return getAPI(api, { | |
| id: this.resource.vmdetails.templateid, | |
| listall: true, | |
| ...(isIso ? {} : { templatefilter: 'all' }) | |
| }).then(response => { | |
| const items = response?.[responseKey]?.[itemKey] || [] | |
| this.backupArch = items[0]?.arch || 'x86_64' | |
| }).catch(() => { | |
| this.backupArch = 'x86_64' | |
| }) | |
| }, | |
| populatePreFillData () { | |
| this.vmdetails = this.resource.vmdetails | |
| this.dataPreFill.zoneid = this.resource.zoneid | |
| this.dataPreFill.crosszoneinstancecreation = this.backupOffering?.crosszoneinstancecreation || this.backupOffering.provider === 'dummy' | |
| this.dataPreFill.isIso = (this.vmdetails.isiso === 'true') | |
| this.dataPreFill.ostypeid = this.resource.vmdetails.ostypeid | |
| this.dataPreFill.ostypename = this.resource.vmdetails.osname | |
| this.dataPreFill.backupid = this.resource.id | |
| this.dataPreFill.computeofferingid = this.vmdetails.serviceofferingid | |
| this.dataPreFill.templateid = this.vmdetails.templateid | |
| this.dataPreFill.backupArch = this.backupArch | |
| this.dataPreFill.allowtemplateisoselection = true | |
| this.dataPreFill.isoid = this.vmdetails.templateid | |
| this.dataPreFill.allowIpAddressesFetch = this.resource.isbackupvmexpunged | |
| if (this.vmdetails.nics) { | |
| const nics = JSON.parse(this.vmdetails.nics) | |
| this.dataPreFill.networkids = nics.map(nic => nic.networkid) | |
| this.dataPreFill.ipAddresses = nics.map(nic => nic.ipaddress) | |
| this.dataPreFill.macAddresses = nics.map(nic => nic.macaddress) | |
| } | |
| const volumes = JSON.parse(this.resource.volumes) | |
| const disksdetails = volumes.map((volume, index) => ({ | |
| name: volume.path, | |
| type: volume.type, | |
| size: volume.size / (1024 * 1024 * 1024), | |
| diskofferingid: volume.diskOfferingId, | |
| miniops: volume.minIops, | |
| maxiops: volume.maxIops, | |
| deviceid: volume.deviceId | |
| })).filter(volume => volume.type !== 'ROOT') | |
| this.dataPreFill.datadisksdetails = disksdetails.map((disk, index) => ({ | |
| id: index, | |
| ...disk | |
| })) | |
| const rootdisksdetails = volumes.map((volume, index) => ({ | |
| size: volume.size / (1024 * 1024 * 1024), | |
| type: volume.type, | |
| diskofferingid: volume.diskOfferingId | |
| })).filter(volume => volume.type === 'ROOT') | |
| if (this.dataPreFill.isIso) { | |
| this.dataPreFill.diskofferingid = rootdisksdetails[0].diskofferingid | |
| this.dataPreFill.size = rootdisksdetails[0].size | |
| this.dataPreFill.overridediskoffering = false | |
| } else if (this.serviceOffering && this.serviceOffering.diskofferingid !== rootdisksdetails[0].diskofferingid) { | |
| this.dataPreFill.overridediskoffering = true | |
| this.dataPreFill.diskofferingid = rootdisksdetails[0].diskofferingid | |
| this.dataPreFill.size = rootdisksdetails[0].size | |
| } | |
| }, | |
| setConfigure () { | |
| this.populatePreFillData() | |
| this.configure = true | |
| }, | |
| closeAction () { | |
| this.$emit('close-action') | |
| }, | |
| handleSubmit (e) { | |
| console.log('submit') | |
| e.preventDefault() | |
| const args = {} | |
| args.zoneid = this.resource.zoneid | |
| args.backupid = this.resource.id | |
| if (this.form.name) { | |
| args.name = this.form.name | |
| args.displayname = this.form.name | |
| } | |
| args.quickRestore = this.form.quickRestore | |
| if (this.form.preserveIpAddresses) { | |
| args.preserveip = this.form.preserveIpAddresses | |
| } | |
| const title = this.$t('label.create.instance.from.backup') | |
| const description = '' | |
| const password = this.$t('label.password') | |
| postAPI('createVMFromBackup', args, 'GET', null).then(response => { | |
| const jobId = response.deployvirtualmachineresponse.jobid | |
| if (jobId) { | |
| this.$pollJob({ | |
| jobId, | |
| title, | |
| description, | |
| successMethod: result => { | |
| const vm = result.jobresult.virtualmachine | |
| const name = vm.displayname || vm.name || vm.id | |
| if (vm.password) { | |
| this.$notification.success({ | |
| message: password + ` ${this.$t('label.for')} ` + name, | |
| description: vm.password, | |
| btn: () => h( | |
| Button, | |
| { | |
| type: 'primary', | |
| size: 'small', | |
| onClick: () => this.copyToClipboard(vm.password) | |
| }, | |
| () => [this.$t('label.copy.password')] | |
| ), | |
| duration: 0 | |
| }) | |
| } | |
| eventBus.emit('vm-refresh-data') | |
| }, | |
| loadingMessage: `${title} ${this.$t('label.in.progress')}`, | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| action: { | |
| isFetchData: false | |
| } | |
| }) | |
| } | |
| // Sending a refresh in case it hasn't picked up the new VM | |
| new Promise(resolve => setTimeout(resolve, 3000)).then(() => { | |
| eventBus.emit('vm-refresh-data') | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| this.loading.deploy = false | |
| }).finally(() => { | |
| this.form.stayonpage = false | |
| this.loading.deploy = false | |
| }) | |
| this.$emit('close-action') | |
| } | |
| } | |
| } | |
| </script> | |
| <style lang="scss" scoped> | |
| .card-footer { | |
| text-align: right; | |
| margin-top: 2rem; | |
| button + button { | |
| margin-left: 8px; | |
| } | |
| } | |
| .form { | |
| width: 80vw; | |
| @media (min-width: 500px) { | |
| min-width: 400px; | |
| width: 100%; | |
| } | |
| } | |
| </style> |