blob: 24fc87fe7e68f66adbd09f4c02f34e07365579a1 [file] [log] [blame]
// 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>&nbsp;{{ algorithm }}
</a-row>
<br/>
<a-row>
<a-col>
<a-input-number
v-model:value="maxMigrations"
:addonBefore="$t('label.max.migrations')"
:min="1"
:step="1"
/>
&nbsp;&nbsp;
</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>