| // 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> |
| <a-form class="form" v-ctrl-enter="handleSubmit"> |
| <p v-html="getMessage()"></p> |
| |
| <div v-if="loading" class="loading"> |
| <loading-outlined style="color: #1890ff;" /> |
| </div> |
| |
| <a-alert v-if="fixedOfferingKvm" type="error" show-icon> |
| <template #message><span style="margin-bottom: 5px" v-html="$t('message.error.fixed.offering.kvm')" /></template> |
| </a-alert> |
| |
| <compute-offering-selection |
| :compute-items="offerings" |
| :loading="loading" |
| :rowCount="total" |
| size="small" |
| @select-compute-item="($event) => updateComputeOffering($event)" |
| @handle-search-filter="($event) => fetchData($event)" /> |
| |
| <compute-selection |
| v-if="selectedOffering && (selectedOffering.iscustomized || selectedOffering.iscustomizediops)" |
| :cpu-number-input-decorator="cpuNumberKey" |
| :cpu-speed-input-decorator="cpuSpeedKey" |
| :memory-input-decorator="memoryKey" |
| :computeOfferingId="selectedOffering.id" |
| :isConstrained="'serviceofferingdetails' in selectedOffering" |
| :minCpu="getMinCpu()" |
| :maxCpu="'serviceofferingdetails' in selectedOffering ? selectedOffering.serviceofferingdetails.maxcpunumber*1 : Number.MAX_SAFE_INTEGER" |
| :cpuSpeed="getCPUSpeed()" |
| :minMemory="getMinMemory()" |
| :maxMemory="'serviceofferingdetails' in selectedOffering ? selectedOffering.serviceofferingdetails.maxmemory*1 : Number.MAX_SAFE_INTEGER" |
| :isCustomized="selectedOffering.iscustomized" |
| :isCustomizedIOps="'iscustomizediops' in selectedOffering && selectedOffering.iscustomizediops" |
| @update-compute-cpunumber="updateFieldValue" |
| @update-compute-cpuspeed="updateFieldValue" |
| @update-compute-memory="updateFieldValue" /> |
| |
| <disk-size-selection |
| v-if="selectedDiskOffering && (selectedDiskOffering.iscustomized || selectedDiskOffering.iscustomizediops)" |
| :inputDecorator="rootDiskSizeKey" |
| :minDiskSize="minDiskSize" |
| :rootDiskSelected="selectedDiskOffering" |
| :isCustomized="selectedDiskOffering.iscustomized" |
| @handler-error="handlerError" |
| @update-disk-size="updateFieldValue" |
| @update-root-disk-iops-value="updateIOPSValue"/> |
| |
| <a-form-item :label="$t('label.automigrate.volume')"> |
| <template #label> |
| <tooltip-label :title="$t('label.automigrate.volume')" :tooltip="apiParams.automigrate.description"/> |
| </template> |
| <a-switch |
| v-model:checked="autoMigrate" |
| @change="val => { autoMigrate = val }"/> |
| </a-form-item> |
| |
| <div :span="24" class="action-button"> |
| <a-button @click="closeAction">{{ $t('label.cancel') }}</a-button> |
| <a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button> |
| </div> |
| </a-form> |
| </template> |
| |
| <script> |
| import { api } from '@/api' |
| import ComputeOfferingSelection from '@views/compute/wizard/ComputeOfferingSelection' |
| import ComputeSelection from '@views/compute/wizard/ComputeSelection' |
| import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection' |
| import TooltipLabel from '@/components/widgets/TooltipLabel' |
| |
| export default { |
| name: 'ScaleVM', |
| components: { |
| ComputeOfferingSelection, |
| ComputeSelection, |
| DiskSizeSelection, |
| TooltipLabel |
| }, |
| props: { |
| resource: { |
| type: Object, |
| required: true |
| } |
| }, |
| inject: ['parentFetchData'], |
| data () { |
| return { |
| offeringsMap: {}, |
| offerings: [], |
| selectedOffering: {}, |
| selectedDiskOffering: {}, |
| autoMigrate: true, |
| total: 0, |
| params: { id: this.resource.id }, |
| loading: false, |
| cpuNumberKey: 'details[0].cpuNumber', |
| cpuSpeedKey: 'details[0].cpuSpeed', |
| memoryKey: 'details[0].memory', |
| rootDiskSizeKey: 'details[0].rootdisksize', |
| minIopsKey: 'details[0].minIops', |
| maxIopsKey: 'details[0].maxIops', |
| fixedOfferingKvm: false, |
| minDiskSize: 0 |
| } |
| }, |
| beforeCreate () { |
| this.apiParams = this.$getApiParams('scaleVirtualMachine') |
| }, |
| created () { |
| this.fetchData({ |
| keyword: '', |
| pageSize: 10, |
| page: 1 |
| }) |
| }, |
| methods: { |
| fetchData (options) { |
| this.loading = true |
| this.total = 0 |
| this.offerings = [] |
| this.offeringsMap = [] |
| api('listServiceOfferings', { |
| virtualmachineid: this.resource.id, |
| keyword: options.keyword, |
| page: options.page, |
| pageSize: options.pageSize, |
| details: 'min', |
| response: 'json' |
| }).then(response => { |
| this.total = response.listserviceofferingsresponse.count |
| if (this.total === 0) { |
| return |
| } |
| this.offerings = response.listserviceofferingsresponse.serviceoffering || [] |
| if (this.resource.state === 'Running' && this.resource.hypervisor === 'KVM') { |
| this.offerings = this.offerings.filter(offering => offering.id === this.resource.serviceofferingid) |
| this.currentOffer = this.offerings[0] |
| if (this.currentOffer === undefined) { |
| this.fixedOfferingKvm = true |
| } |
| } |
| this.offerings.map(i => { this.offeringsMap[i.id] = i }) |
| }).finally(() => { |
| this.loading = false |
| }) |
| }, |
| getMinCpu () { |
| // We can only scale up while a VM is running |
| if (this.resource.state === 'Running') { |
| return this.resource.cpunumber |
| } |
| return this.selectedOffering?.serviceofferingdetails?.mincpunumber * 1 || 1 |
| }, |
| getMinMemory () { |
| // We can only scale up while a VM is running |
| if (this.resource.state === 'Running') { |
| return this.resource.memory |
| } |
| return this.selectedOffering?.serviceofferingdetails?.minmemory * 1 || 32 |
| }, |
| getCPUSpeed () { |
| // We can only scale up while a VM is running |
| if (this.resource.state === 'Running') { |
| return this.resource.cpuspeed |
| } |
| this.getMinDiskSize() |
| return this.selectedOffering?.serviceofferingdetails?.cpuspeed * 1 || 1 |
| }, |
| getTemplate () { |
| return new Promise((resolve, reject) => { |
| api('listTemplates', { |
| templatefilter: 'all', |
| id: this.resource.templateid |
| }).then(response => { |
| var template = response?.listtemplatesresponse?.template?.[0] || null |
| resolve(template) |
| }).catch(error => { |
| reject(error) |
| }) |
| }) |
| }, |
| async getMinDiskSize () { |
| const template = await this.getTemplate() |
| this.minDiskSize = Math.ceil(template?.size / (1024 * 1024 * 1024) || 0) |
| }, |
| getMessage () { |
| if (this.resource.hypervisor === 'VMware') { |
| return this.$t('message.read.admin.guide.scaling.up') |
| } |
| return this.$t('message.change.offering.confirm') |
| }, |
| updateIOPSValue (input, value) { |
| console.log(input) |
| const key = input === 'minIops' ? this.minIopsKey : this.maxIopsKey |
| this.params[key] = value |
| }, |
| updateComputeOffering (id) { |
| // Delete custom details |
| delete this.params[this.cpuNumberKey] |
| delete this.params[this.cpuSpeedKey] |
| delete this.params[this.memoryKey] |
| delete this.params[this.rootDiskSizeKey] |
| |
| this.params.serviceofferingid = id |
| this.selectedOffering = this.offeringsMap[id] |
| this.selectedDiskOffering = null |
| if (this.selectedOffering.diskofferingid) { |
| api('listDiskOfferings', { |
| id: this.selectedOffering.diskofferingid |
| }).then(response => { |
| const diskOfferings = response?.listdiskofferingsresponse?.diskoffering || [] |
| if (diskOfferings?.length > 0) { |
| this.selectedDiskOffering = diskOfferings[0] |
| } |
| }).catch(error => { |
| this.$notifyError(error) |
| }) |
| } |
| this.params.automigrate = this.autoMigrate |
| }, |
| updateFieldValue (name, value) { |
| this.params[name] = value |
| }, |
| closeAction () { |
| this.$emit('close-action') |
| }, |
| handlerError (error) { |
| this.error = error |
| }, |
| handleSubmit () { |
| if (this.loading) return |
| this.loading = true |
| |
| if ('cpuspeed' in this.selectedOffering && this.selectedOffering.iscustomized) { |
| delete this.params[this.cpuSpeedKey] |
| } |
| |
| api('scaleVirtualMachine', this.params).then(response => { |
| const jobId = response.scalevirtualmachineresponse.jobid |
| if (jobId) { |
| this.$pollJob({ |
| jobId, |
| title: this.$t('label.scale.vm'), |
| description: this.resource.name, |
| successMethod: result => { |
| this.$notification.success({ |
| message: this.$t('message.success.change.offering') |
| }) |
| }, |
| loadingMessage: this.$t('message.scale.processing'), |
| catchMessage: this.$t('error.fetching.async.job.result') |
| }) |
| } |
| this.$emit('close-action') |
| this.parentFetchData() |
| }).catch(error => { |
| this.$notifyError(error) |
| }).finally(() => { |
| this.loading = false |
| }) |
| } |
| } |
| } |
| </script> |
| |
| <style scoped lang="scss"> |
| |
| .form { |
| width: 90vw; |
| @media (min-width: 700px) { |
| width: 50vw; |
| } |
| } |
| </style> |