| // 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-row> |
| {{ $t('message.drs.plan.description') }} |
| </a-row> |
| <a-row> |
| <strong>{{ $t('label.algorithm') }}:</strong> {{ algorithm }} |
| </a-row> |
| <br/> |
| <a-row> |
| <a-col> |
| <a-input-number |
| v-model:value="maxMigrations" |
| :addonBefore="$t('label.max.migrations')" |
| :min="1" |
| :step="1" |
| /> |
| |
| </a-col> |
| <a-col> |
| <a-button |
| type="primary" |
| @click="generateDrsPlan" |
| :loading="loading" |
| :disabled="!('generateClusterDrsPlan' in $store.getters.apis)"> |
| {{ $t('label.drs.generate.plan') }} |
| </a-button> |
| </a-col> |
| </a-row> |
| <br/> |
| <a-table |
| size="small" |
| :columns="drsPlanColumns" |
| :dataSource="drsPlans" |
| :rowKey="item => item.id" |
| :pagination="{hideOnSinglePage: true, showSizeChanger: true}" |
| > |
| <template #expandedRowRender="{ record }"> |
| <a-table |
| size="small" |
| :columns="migrationColumns" |
| :dataSource="record.migrations" |
| :rowKey="(record, index) => index" |
| :pagination="{hideOnSinglePage: true, showSizeChanger: true}" |
| @resizeColumn="resizeColumn"> |
| <template #bodyCell="{ column, text, record }"> |
| <template v-if="column.key === 'vm'"> |
| <router-link :to="{ path: '/vm/' + record.virtualmachineid }"> |
| <desktop-outlined/> {{ record.virtualmachinename }} |
| </router-link> |
| </template> |
| <template v-else-if="column.key === 'sourcehost'"> |
| <router-link :to="{ path: '/host/' + record.sourcehostid }"> |
| <cluster-outlined/> {{ record.sourcehostname }} |
| </router-link> |
| </template> |
| <template v-else-if="column.key === 'destinationhost'"> |
| <router-link :to="{ path: '/host/' + record.destinationhostid }"> |
| <cluster-outlined/> {{ record.destinationhostname }} |
| </router-link> |
| </template> |
| <template v-else> |
| {{ text }} |
| </template> |
| </template> |
| </a-table> |
| <br/> |
| </template> |
| <template #bodyCell="{ column, text }"> |
| <template v-if="column.key === 'successfulMigrations'"> |
| {{ text.migrations.filter(m => m.jobstatus === 'SUCCEEDED').length }} / {{ text.migrations.length }} |
| <!-- {{ text.migrations }} --> |
| </template> |
| <template v-else-if="column.key === 'created'"> |
| {{ $toLocaleDate(text) }} |
| </template> |
| <template v-else-if="column.key === 'eventid'" > |
| <router-link :to="{ path: '/event', query: { startid: text} }" target="_blank"> |
| <schedule-outlined /> {{ $t('label.events') }} |
| </router-link> |
| </template> |
| <template v-else> |
| {{ text }} |
| </template> |
| </template> |
| </a-table> |
| |
| <a-modal |
| width="50%" |
| :visible="showModal" |
| :title="$t('label.drs.plan')" |
| :maskClosable="false" |
| :closable="true" |
| :okButtonProps="{ style: { display: generatedMigrations.length === 0 ? 'none' : null } }" |
| :okText="$t('label.execute')" |
| :cancelText="$t('label.cancel')" |
| @ok="executeDrsPlan" |
| @cancel="closeModal"> |
| <a-table |
| v-if="generatedMigrations.length > 0" |
| size="small" |
| :columns="generatedPlanMigrationColumns" |
| :dataSource="generatedMigrations" |
| :rowKey="(record, index) => index" |
| :pagination="{ showTotal: (total, range) => [range[0], '-', range[1], $t('label.of'), total, $t('label.items')].join(' ') }" |
| @resizeColumn="resizeColumn" > |
| <template #bodyCell="{ column, text, record }"> |
| <template v-if="column.key === 'vm'"> |
| <router-link :to="{ path: '/vm/' + record.virtualmachineid }"> |
| <desktop-outlined/> {{ record.virtualmachinename }} |
| </router-link> |
| </template> |
| <template v-else-if="column.key === 'sourcehost'"> |
| <router-link :to="{ path: '/host/' + record.sourcehostid }"> |
| <cluster-outlined/> {{ record.sourcehostname }} |
| </router-link> |
| </template> |
| <template v-else-if="column.key === 'destinationhost'"> |
| <router-link :to="{ path: '/host/' + record.destinationhostid }"> |
| <cluster-outlined/> {{ record.destinationhostname }} |
| </router-link> |
| </template> |
| <template v-else> |
| {{ text }} |
| </template> |
| </template> |
| </a-table> |
| <a-p v-else> |
| {{ $t('label.drs.no.plan.generated') }} |
| </a-p> |
| |
| </a-modal> |
| |
| </template> |
| |
| <script> |
| |
| import { reactive } from 'vue' |
| import { getAPI, postAPI } from '@/api' |
| |
| export default { |
| name: 'ClusterDrsTab', |
| props: { |
| resource: { |
| type: Object, |
| required: true |
| } |
| }, |
| data () { |
| const generatedPlanMigrationColumns = [ |
| { |
| key: 'vm', |
| title: this.$t('label.vm'), |
| dataIndex: 'vm', |
| ellipsis: true, |
| resizable: true |
| }, |
| { |
| key: 'sourcehost', |
| title: this.$t('label.sourcehost'), |
| dataIndex: 'sourcehost', |
| ellipsis: true, |
| resizable: true |
| }, |
| { |
| key: 'destinationhost', |
| title: this.$t('label.desthost'), |
| dataIndex: 'created', |
| ellipsis: true, |
| resizable: true |
| } |
| ] |
| return { |
| drsPlanColumns: [ |
| { |
| title: this.$t('label.type'), |
| dataIndex: 'type' |
| }, |
| { |
| title: this.$t('label.success.migrations'), |
| key: 'successfulMigrations' |
| }, |
| { |
| title: this.$t('label.status'), |
| dataIndex: 'status' |
| }, |
| { |
| key: 'created', |
| title: this.$t('label.created'), |
| dataIndex: 'created' |
| }, |
| { |
| key: 'eventid', |
| title: this.$t('label.events'), |
| dataIndex: 'eventid' |
| } |
| ], |
| generatedPlanMigrationColumns: generatedPlanMigrationColumns, |
| migrationColumns: generatedPlanMigrationColumns.concat([ |
| { |
| key: 'jobstatus', |
| title: this.$t('label.status'), |
| dataIndex: 'jobstatus' |
| } |
| ]), |
| loading: false, |
| drsPlans: [], |
| algorithm: '', |
| maxMigrations: 0, |
| generatedMigrations: reactive([]), |
| showModal: false |
| } |
| }, |
| watch: { |
| resource: { |
| deep: true, |
| handler (newItem, oldItem) { |
| if (newItem && (!oldItem || (newItem.id !== oldItem.id))) { |
| this.fetchDRSPlans() |
| } |
| } |
| } |
| }, |
| created () { |
| this.fetchDRSPlans() |
| this.fetchDrsConfig() |
| }, |
| methods: { |
| fetchDRSPlans () { |
| if (!this.resource || !this.resource.id) return |
| getAPI('listClusterDrsPlan', { page: 1, pageSize: 500, clusterid: this.resource.id }).then(json => { |
| this.drsPlans = json.listclusterdrsplanresponse.drsPlan |
| }) |
| }, |
| executeDrsPlan () { |
| if (this.generatedMigrations.length === 0) return |
| |
| var params = { id: this.resource.id } |
| |
| for (var i = 0; i < this.generatedMigrations.length; i++) { |
| const mapping = this.generatedMigrations[i] |
| params['migrateto[' + i + '].vm'] = mapping.virtualmachineid |
| params['migrateto[' + i + '].host'] = mapping.destinationhostid |
| } |
| |
| postAPI('executeClusterDrsPlan', params).then(json => { |
| this.$message.success(this.$t('message.drs.plan.executed')) |
| }).catch(error => { |
| console.error(error) |
| this.$message.error(this.$t('message.drs.plan.execution.failed')) |
| }).finally(() => { |
| this.fetchDRSPlans() |
| this.closeModal() |
| }) |
| }, |
| generateDrsPlan () { |
| this.loading = true |
| postAPI('generateClusterDrsPlan', { id: this.resource.id, migrations: this.maxMigrations }).then(json => { |
| this.generatedMigrations = json?.generateclusterdrsplanresponse?.generateclusterdrsplanresponse?.migrations || [] |
| this.loading = false |
| this.showModal = true |
| }) |
| }, |
| fetchDrsConfig () { |
| this.loading = true |
| getAPI('listConfigurations', { clusterid: this.resource.id, name: 'drs.algorithm' }).then(json => { |
| this.algorithm = json.listconfigurationsresponse.configuration[0].value |
| getAPI('listConfigurations', { clusterid: this.resource.id, name: 'drs.max.migrations' }).then(json => { |
| this.maxMigrations = json.listconfigurationsresponse.configuration[0].value |
| this.loading = false |
| }).catch((err) => { |
| console.error(err) |
| this.loading = false |
| }) |
| }).catch((err) => { |
| console.error(err) |
| this.loading = false |
| }) |
| }, |
| closeModal () { |
| this.showModal = false |
| this.generatedMigrations = reactive([]) |
| }, |
| resizeColumn (w, col) { |
| col.width = w |
| } |
| } |
| } |
| </script> |