mirror of https://github.com/apache/cloudstack.git
compute: Custom VM migration form (#67)
Custom VM migration form Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
21036bfba4
commit
c5611be865
File diff suppressed because it is too large
Load Diff
|
|
@ -33,14 +33,14 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@antv/data-set": "^0.10.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.25",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.11.2",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.11.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.11.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.26",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.12.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.12.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.0",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.8",
|
||||
"ant-design-vue": "~1.4.8",
|
||||
"ant-design-vue": "~1.4.10",
|
||||
"axios": "^0.19.0",
|
||||
"core-js": "^3.4.7",
|
||||
"core-js": "^3.4.8",
|
||||
"enquire.js": "^2.1.6",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
"md5": "^2.2.1",
|
||||
"moment": "^2.24.0",
|
||||
"node-emoji": "^1.10.0",
|
||||
"npm-check-updates": "^3.2.2",
|
||||
"npm-check-updates": "^4.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"viser-vue": "^2.4.7",
|
||||
"vue": "^2.6.10",
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
"babel-plugin-import": "^1.13.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-html": "^6.0.0",
|
||||
"eslint-plugin-import": "^2.18.2",
|
||||
"eslint-plugin-import": "^2.19.1",
|
||||
"eslint-plugin-node": "^10.0.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
|
|
|
|||
|
|
@ -629,7 +629,7 @@
|
|||
"memorytotalgb": "Total",
|
||||
"memoryused": "Used",
|
||||
"memoryusedgb": "Used",
|
||||
"memused": "Mem Usage",
|
||||
"memused": "Memory Usage",
|
||||
"message.edit.account": "Edit (\"-1\" indicates no limit to the amount of resources create)",
|
||||
"minCPUNumber": "Min CPU Cores",
|
||||
"minInstance": "Min Instances",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
// 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.
|
||||
|
||||
import { api } from '@/api'
|
||||
import { message, notification } from 'ant-design-vue'
|
||||
|
||||
/**
|
||||
* Reusable queryAsyncJobResult method
|
||||
* @param {String} jobId
|
||||
* @param {String} successMessage
|
||||
* @param {Function} successMethod
|
||||
* @param {String} errorMessage
|
||||
* @param {Function} errorMethod
|
||||
* @param {String} loadingMessage
|
||||
* @param {String} catchMessage
|
||||
* @param {Function} catchMethod
|
||||
* @param {Number} loadingDuration
|
||||
*/
|
||||
export const pollActionCompletion = ({
|
||||
jobId, successMessage, successMethod, errorMessage, errorMethod, loadingMessage, catchMessage, catchMethod, loadingDuration = 3
|
||||
}) => {
|
||||
function runApi () {
|
||||
api('queryAsyncJobResult', { jobId }).then(json => {
|
||||
const result = json.queryasyncjobresultresponse
|
||||
|
||||
if (result.jobstatus === 1) {
|
||||
message.success(successMessage || 'Success')
|
||||
successMethod && successMethod()
|
||||
} else if (result.jobstatus === 2) {
|
||||
notification.error({
|
||||
message: errorMessage || 'Error',
|
||||
description: result.jobresult.errortext || 'Error'
|
||||
})
|
||||
errorMethod && errorMethod()
|
||||
} else if (result.jobstatus === 0) {
|
||||
message
|
||||
.loading(loadingMessage, loadingDuration)
|
||||
.then(() => runApi())
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(`${catchMessage} - ${e}`)
|
||||
catchMethod && catchMethod()
|
||||
})
|
||||
}
|
||||
runApi()
|
||||
}
|
||||
|
|
@ -77,6 +77,7 @@
|
|||
:confirmLoading="currentAction.loading"
|
||||
:footer="null"
|
||||
centered
|
||||
width="auto"
|
||||
>
|
||||
<component :is="currentAction.component" :resource="resource" :loading="loading" v-bind="{currentAction}" />
|
||||
</a-modal>
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ export default {
|
|||
loginSuccess (res) {
|
||||
this.$router.push({ name: 'dashboard' })
|
||||
this.$message.success('Login Successful')
|
||||
this.$message.loading('Discoverying Features', 4)
|
||||
},
|
||||
requestFailed (err) {
|
||||
if (err && err.response && err.response.data && err.response.data.loginresponse) {
|
||||
|
|
|
|||
|
|
@ -16,18 +16,68 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
{{ resource }}
|
||||
This needs to implement migrate wizard
|
||||
</div>
|
||||
<a-list :dataSource="hosts" itemLayout="vertical" class="list" :loading="loading">
|
||||
<div slot="header" class="list__header">
|
||||
<a-input-search
|
||||
placeholder="Search"
|
||||
v-model="searchQuery"
|
||||
@search="fetchData" />
|
||||
</div>
|
||||
<a-list-item
|
||||
slot="renderItem"
|
||||
slot-scope="host, index"
|
||||
class="host-item"
|
||||
:class="{ 'host-item--selected' : selectedIndex === index }"
|
||||
>
|
||||
<div class="host-item__row">
|
||||
<div class="host-item__value">
|
||||
<span class="host-item__title">{{ $t('name') }}</span>
|
||||
{{ host.name }}
|
||||
</div>
|
||||
<div class="host-item__value host-item__value--small">
|
||||
<span class="host-item__title">Suitability</span>
|
||||
<a-icon
|
||||
class="host-item__suitability-icon"
|
||||
type="check-circle"
|
||||
theme="twoTone"
|
||||
twoToneColor="#52c41a"
|
||||
v-if="host.suitableformigration" />
|
||||
<a-icon
|
||||
class="host-item__suitability-icon"
|
||||
type="close-circle"
|
||||
theme="twoTone"
|
||||
twoToneColor="#f5222d"
|
||||
v-else />
|
||||
</div>
|
||||
<div class="host-item__value host-item__value--full">
|
||||
<span class="host-item__title">{{ $t('cpuused') }}</span>
|
||||
{{ host.cpuused }}
|
||||
</div>
|
||||
<div class="host-item__value">
|
||||
<span class="host-item__title">{{ $t('memused') }}</span>
|
||||
{{ host.memoryused | byteToGigabyte }} GB
|
||||
</div>
|
||||
<a-radio
|
||||
class="host-item__radio"
|
||||
@click="selectedIndex = index"
|
||||
:checked="selectedIndex === index"
|
||||
:disabled="!host.suitableformigration"></a-radio>
|
||||
</div>
|
||||
</a-list-item>
|
||||
<div slot="footer" class="list__footer">
|
||||
<a-button type="primary" :disabled="selectedIndex === null" @click="submitForm">
|
||||
{{ $t('OK') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</a-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import { pollActionCompletion } from '@/utils/methods'
|
||||
|
||||
export default {
|
||||
name: 'VMMigrateWizard',
|
||||
components: {
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
|
|
@ -36,12 +86,152 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
hosts: [],
|
||||
selectedIndex: null,
|
||||
searchQuery: ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.loading = true
|
||||
api('findHostsForMigration', {
|
||||
virtualmachineid: this.resource.id,
|
||||
keyword: this.searchQuery,
|
||||
page: 1,
|
||||
pagesize: 500
|
||||
}).then(response => {
|
||||
this.hosts = response.findhostsformigrationresponse.host
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
this.$message.error('Failed to load hosts: ' + error)
|
||||
})
|
||||
},
|
||||
submitForm () {
|
||||
this.loading = true
|
||||
api('migrateVirtualMachine', {
|
||||
hostid: this.hosts[this.selectedIndex].id,
|
||||
virtualmachineid: this.resource.id
|
||||
}).then(response => {
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: `Migrating ${this.resource.name}`,
|
||||
jobid: response.migratevirtualmachineresponse.jobid,
|
||||
description: this.resource.name,
|
||||
status: 'progress'
|
||||
})
|
||||
pollActionCompletion({
|
||||
jobId: response.migratevirtualmachineresponse.jobid,
|
||||
successMessage: `Migration completed successfully for ${this.resource.name}`,
|
||||
successMethod: () => {
|
||||
this.$parent.$parent.close()
|
||||
},
|
||||
errorMessage: 'Migration failed',
|
||||
errorMethod: () => {
|
||||
this.$parent.$parent.close()
|
||||
},
|
||||
loadingMessage: `Migration in progress for ${this.resource.name}`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.$parent.$parent.close()
|
||||
}
|
||||
})
|
||||
this.$parent.$parent.close()
|
||||
}).catch(error => {
|
||||
console.error(error)
|
||||
this.$message.error('Failed to migrate host.')
|
||||
})
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
byteToGigabyte: value => {
|
||||
if (!value) return ''
|
||||
value = value / Math.pow(10, 9)
|
||||
return value.toFixed(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
|
||||
.list {
|
||||
max-height: 95vh;
|
||||
width: 95vw;
|
||||
overflow-y: scroll;
|
||||
margin: -24px;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
max-height: 70vh;
|
||||
width: 60vw;
|
||||
}
|
||||
|
||||
&__header,
|
||||
&__footer {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.host-item {
|
||||
padding-right: 20px;
|
||||
padding-bottom: 0;
|
||||
padding-left: 20px;
|
||||
|
||||
&--selected {
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__value {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
flex: 1;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&--small {
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex: none;
|
||||
margin-right: 40px;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&__suitability-icon {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&__radio {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue