mirror of https://github.com/apache/cloudstack.git
compute: VM deployment wizard fixes (#307)
- Fix reactive changes on zone selection - Template filter on left side of search box - Allow group name - Ability to add network while deploying VM - Show password if VM deployment returns password Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
7d447e6806
commit
b8a22f27ae
|
|
@ -344,6 +344,13 @@
|
|||
<span v-if="index + 1 < resource.affinitygroup.length">, </span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.templateid">
|
||||
<div class="resource-detail-item__label">{{ $t('templatename') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<a-icon type="picture" />
|
||||
<router-link :to="{ path: '/template/' + resource.templateid }">{{ resource.templatename || resource.templateid }} </router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.serviceofferingname && resource.serviceofferingid">
|
||||
<div class="resource-detail-item__label">{{ $t('serviceofferingname') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
|
|
@ -352,13 +359,6 @@
|
|||
<span v-else>{{ resource.serviceofferingname || resource.serviceofferingid }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.templateid">
|
||||
<div class="resource-detail-item__label">{{ $t('templatename') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<a-icon type="picture" />
|
||||
<router-link :to="{ path: '/template/' + resource.templateid }">{{ resource.templatename || resource.templateid }} </router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.diskofferingname && resource.diskofferingid">
|
||||
<div class="resource-detail-item__label">{{ $t('diskoffering') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
|
|
|
|||
|
|
@ -481,6 +481,7 @@
|
|||
"label.add.ldap.list.users": "List LDAP users",
|
||||
"label.add.list.name":"ACL List Name",
|
||||
"label.add.netScaler.device": "Add Netscaler device",
|
||||
"label.add.network":"Add Network",
|
||||
"label.add.network.offering": "Add network offering",
|
||||
"label.add.new.gateway": "Add new gateway",
|
||||
"label.add.new.tier": "Add new tier",
|
||||
|
|
@ -1308,5 +1309,7 @@
|
|||
"writeback": "Write-back disk caching",
|
||||
"writethrough": "Write-through",
|
||||
"none": "None",
|
||||
"maxcpunumber": "Max CPU Cores"
|
||||
"maxcpunumber": "Max CPU Cores",
|
||||
"message.template.iso": "Please select a template or ISO to continue",
|
||||
"label.launch.vm": "Launch Virtual Machine"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
}"
|
||||
:loading="zoneLoading"
|
||||
:placeholder="apiParams.zoneid.description"
|
||||
@change="val => { this.handleZoneChanged(this.zones[val]) }">
|
||||
@change="val => { this.handleZoneChange(this.zones[val]) }">
|
||||
<a-select-option v-for="(opt, optIndex) in this.zones" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
|
|
|
|||
|
|
@ -72,11 +72,7 @@
|
|||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('group')">
|
||||
<a-select
|
||||
v-decorator="['group']"
|
||||
:options="groupsSelectOptions"
|
||||
:loading="loading.groups"
|
||||
></a-select>
|
||||
<a-input v-decorator="['group']" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('keyboard')">
|
||||
<a-select
|
||||
|
|
@ -122,8 +118,7 @@
|
|||
:selected="tabKey"
|
||||
:loading="loading.isos"
|
||||
:preFillContent="dataPreFill"
|
||||
@update-template-iso="updateFieldValue"
|
||||
></template-iso-selection>
|
||||
@update-template-iso="updateFieldValue" />
|
||||
<a-form-item :label="this.$t('hypervisor')">
|
||||
<a-select
|
||||
v-decorator="['hypervisor', {
|
||||
|
|
@ -133,9 +128,7 @@
|
|||
rules: [{ required: true, message: 'Please select option' }]
|
||||
}]"
|
||||
:options="hypervisorSelectOptions"
|
||||
@change="value => this.hypervisor = value"
|
||||
>
|
||||
</a-select>
|
||||
@change="value => this.hypervisor = value" />
|
||||
</a-form-item>
|
||||
</p>
|
||||
</a-card>
|
||||
|
|
@ -318,8 +311,8 @@ import { mixin, mixinDevice } from '@/utils/mixin.js'
|
|||
import store from '@/store'
|
||||
|
||||
import InfoCard from '@/components/view/InfoCard'
|
||||
import ComputeOfferingSelection from './wizard/ComputeOfferingSelection'
|
||||
import ComputeSelection from './wizard/ComputeSelection'
|
||||
import ComputeOfferingSelection from '@views/compute/wizard/ComputeOfferingSelection'
|
||||
import ComputeSelection from '@views/compute/wizard/ComputeSelection'
|
||||
import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
|
||||
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
|
||||
import TemplateIsoSelection from '@views/compute/wizard/TemplateIsoSelection'
|
||||
|
|
@ -424,6 +417,7 @@ export default {
|
|||
initDataConfig: {},
|
||||
defaultNetwork: '',
|
||||
networkConfig: [],
|
||||
dataNetworkCreated: [],
|
||||
tabList: [
|
||||
{
|
||||
key: 'templateid',
|
||||
|
|
@ -548,14 +542,6 @@ export default {
|
|||
type: 'Routing'
|
||||
},
|
||||
field: 'hostid'
|
||||
},
|
||||
groups: {
|
||||
list: 'listInstanceGroups',
|
||||
options: {
|
||||
listall: false
|
||||
},
|
||||
isLoad: true,
|
||||
field: 'group'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -609,14 +595,6 @@ export default {
|
|||
value: keyboard.id
|
||||
}
|
||||
})
|
||||
},
|
||||
groupsSelectOptions () {
|
||||
return this.options.groups.map((group) => {
|
||||
return {
|
||||
label: group.name,
|
||||
value: group.id
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -854,10 +832,23 @@ export default {
|
|||
handleSubmit (e) {
|
||||
console.log('wizard submit')
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
this.form.validateFields(async (err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!values.templateid && !values.isoid) {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: this.$t('message.template.iso')
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.loading.deploy = true
|
||||
|
||||
let networkIds = []
|
||||
|
||||
const deployVmData = {}
|
||||
// step 1 : select zone
|
||||
deployVmData.zoneid = values.zoneid
|
||||
|
|
@ -902,15 +893,30 @@ export default {
|
|||
// step 5: select an affinity group
|
||||
deployVmData.affinitygroupids = (values.affinitygroupids || []).join(',')
|
||||
// step 6: select network
|
||||
if (values.networkids && values.networkids.length > 0) {
|
||||
for (let i = 0; i < values.networkids.length; i++) {
|
||||
deployVmData['iptonetworklist[' + i + '].networkid'] = values.networkids[i]
|
||||
if (this.networkConfig.length > 0) {
|
||||
const networkConfig = this.networkConfig.filter((item) => item.key === values.networkids[i])
|
||||
if (networkConfig && networkConfig.length > 0) {
|
||||
deployVmData['iptonetworklist[' + i + '].ip'] = networkConfig[0].ipAddress ? networkConfig[0].ipAddress : undefined
|
||||
deployVmData['iptonetworklist[' + i + '].mac'] = networkConfig[0].macAddress ? networkConfig[0].macAddress : undefined
|
||||
const arrNetwork = []
|
||||
networkIds = values.networkids
|
||||
if (networkIds.length > 0) {
|
||||
for (let i = 0; i < networkIds.length; i++) {
|
||||
if (networkIds[i] === this.defaultNetwork) {
|
||||
const ipToNetwork = {
|
||||
networkid: this.defaultNetwork
|
||||
}
|
||||
arrNetwork.unshift(ipToNetwork)
|
||||
} else {
|
||||
const ipToNetwork = {
|
||||
networkid: networkIds[i]
|
||||
}
|
||||
arrNetwork.push(ipToNetwork)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let j = 0; j < arrNetwork.length; j++) {
|
||||
deployVmData['iptonetworklist[' + j + '].networkid'] = arrNetwork[j].networkid
|
||||
if (this.networkConfig.length > 0) {
|
||||
const networkConfig = this.networkConfig.filter((item) => item.key === arrNetwork[j].networkid)
|
||||
if (networkConfig && networkConfig.length > 0) {
|
||||
deployVmData['iptonetworklist[' + j + '].ip'] = networkConfig[0].ipAddress ? networkConfig[0].ipAddress : undefined
|
||||
deployVmData['iptonetworklist[' + j + '].mac'] = networkConfig[0].macAddress ? networkConfig[0].macAddress : undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -918,31 +924,34 @@ export default {
|
|||
deployVmData.keypair = values.keypair
|
||||
deployVmData.name = values.name
|
||||
deployVmData.displayname = values.name
|
||||
const title = this.$t('Launch Virtual Machine')
|
||||
const description = deployVmData.name ? deployVmData.name : values.zoneid
|
||||
this.loading.deploy = true
|
||||
const title = this.$t('label.launch.vm')
|
||||
const description = values.name || ''
|
||||
const password = this.$t('password')
|
||||
api('deployVirtualMachine', deployVmData).then(response => {
|
||||
const jobId = response.deployvirtualmachineresponse.jobid
|
||||
if (jobId) {
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
successMethod: result => {
|
||||
let successDescription = ''
|
||||
if (result.jobresult.virtualmachine.name) {
|
||||
successDescription = result.jobresult.virtualmachine.name
|
||||
} else {
|
||||
successDescription = result.jobresult.virtualmachine.id
|
||||
const vm = result.jobresult.virtualmachine
|
||||
const name = vm.displayname || vm.name || vm.id
|
||||
if (vm.password) {
|
||||
this.$notification.success({
|
||||
message: password + ' for ' + name,
|
||||
description: vm.password,
|
||||
duration: 0
|
||||
})
|
||||
}
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: title,
|
||||
jobid: jobId,
|
||||
description: successDescription,
|
||||
status: 'progress'
|
||||
})
|
||||
},
|
||||
loadingMessage: `${title} in progress for ${description}`,
|
||||
loadingMessage: `${title} in progress`,
|
||||
catchMessage: 'Error encountered while fetching async job result'
|
||||
})
|
||||
this.$store.dispatch('AddAsyncJob', {
|
||||
title: title,
|
||||
jobid: jobId,
|
||||
description: description,
|
||||
status: 'progress'
|
||||
})
|
||||
}
|
||||
this.$router.back()
|
||||
}).catch(error => {
|
||||
|
|
@ -1036,12 +1045,12 @@ export default {
|
|||
})
|
||||
})
|
||||
},
|
||||
fetchAllTemplates (filterKey) {
|
||||
fetchAllTemplates (filterKeys) {
|
||||
const promises = []
|
||||
this.options.templates = []
|
||||
this.loading.templates = true
|
||||
this.templateFilter.forEach((filter) => {
|
||||
if (filterKey && filterKey !== filter) {
|
||||
if (filterKeys && !filterKeys.includes(filter)) {
|
||||
return true
|
||||
}
|
||||
promises.push(this.fetchTemplates(filter))
|
||||
|
|
@ -1095,7 +1104,7 @@ export default {
|
|||
this.tabKey = 'templateid'
|
||||
_.each(this.params, (param, name) => {
|
||||
if (!('isLoad' in param) || param.isLoad) {
|
||||
this.fetchOptions(param, name, ['zones', 'groups'])
|
||||
this.fetchOptions(param, name, ['zones'])
|
||||
}
|
||||
})
|
||||
this.fetchAllTemplates()
|
||||
|
|
|
|||
|
|
@ -137,8 +137,10 @@ export default {
|
|||
this.selectedRowKeys = [this.preFillContent.computeofferingid]
|
||||
this.$emit('select-compute-item', this.preFillContent.computeofferingid)
|
||||
} else {
|
||||
this.selectedRowKeys = []
|
||||
this.$emit('select-compute-item', null)
|
||||
if (this.computeItems && this.computeItems.length > 0) {
|
||||
this.selectedRowKeys = [this.computeItems[0].id]
|
||||
this.$emit('select-compute-item', this.computeItems[0].id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="dataItems"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:pagination="false"
|
||||
:rowSelection="rowSelection"
|
||||
:rowKey="record => record.id"
|
||||
size="middle"
|
||||
|
|
@ -64,7 +64,7 @@ export default {
|
|||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('defaultNetwork'),
|
||||
width: '40%'
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'ip',
|
||||
|
|
@ -88,8 +88,10 @@ export default {
|
|||
},
|
||||
created () {
|
||||
this.dataItems = this.items
|
||||
this.selectedRowKeys = [this.dataItems[0].id]
|
||||
this.$emit('select-default-network-item', this.selectedRowKeys)
|
||||
if (this.dataItems.length > 0) {
|
||||
this.selectedRowKeys = [this.dataItems[0].id]
|
||||
this.$emit('select-default-network-item', this.dataItems[0].id)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rowSelection () {
|
||||
|
|
@ -112,6 +114,7 @@ export default {
|
|||
const keyEx = this.dataItems.filter((item) => this.selectedRowKeys.includes(item.id))
|
||||
if (!keyEx || keyEx.length === 0) {
|
||||
this.selectedRowKeys = [this.dataItems[0].id]
|
||||
this.$emit('select-default-network-item', this.dataItems[0].id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +125,8 @@ export default {
|
|||
this.$emit('select-default-network-item', value[0])
|
||||
},
|
||||
updateNetworkData (name, key, value) {
|
||||
if (this.networks.length === 0) {
|
||||
const index = this.networks.findIndex(item => item.key === key)
|
||||
if (index === -1) {
|
||||
const networkItem = {}
|
||||
networkItem.key = key
|
||||
networkItem[name] = value
|
||||
|
|
@ -137,6 +141,15 @@ export default {
|
|||
}
|
||||
})
|
||||
this.$emit('update-network-config', this.networks)
|
||||
},
|
||||
removeItem (id) {
|
||||
this.dataItems = this.dataItems.filter(item => item.id !== id)
|
||||
if (this.selectedRowKeys.includes(id)) {
|
||||
if (this.dataItems && this.dataItems.length > 0) {
|
||||
this.selectedRowKeys = [this.dataItems[0].id]
|
||||
this.$emit('select-default-network-item', this.dataItems[0].id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,17 +18,13 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-input-search
|
||||
style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
|
||||
style="width: 25vw; float: right; margin-bottom: 10px; z-index: 8"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="handleSearch" />
|
||||
<a-tooltip
|
||||
arrowPointAtCenter
|
||||
placement="bottomRight">
|
||||
<template slot="title">
|
||||
{{ $t('addNewNetworks') }}
|
||||
</template>
|
||||
</a-tooltip>
|
||||
<a-button type="primary" @click="showCreateForm = true" style="float: right; margin-right: 5px; z-index: 8">
|
||||
{{ $t('label.add.network') }}
|
||||
</a-button>
|
||||
<a-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
|
@ -55,6 +51,20 @@
|
|||
</a-list-item>
|
||||
</a-list>
|
||||
</a-table>
|
||||
<a-modal
|
||||
:visible="showCreateForm"
|
||||
:title="$t('label.add.network')"
|
||||
:closable="true"
|
||||
:footer="null"
|
||||
@cancel="showCreateForm = false"
|
||||
centered
|
||||
width="auto">
|
||||
<create-network
|
||||
:resource="{}"
|
||||
@refresh-data="handleSearch"
|
||||
@close-action="showCreateForm = false"
|
||||
/>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -62,9 +72,13 @@
|
|||
import _ from 'lodash'
|
||||
import { api } from '@/api'
|
||||
import store from '@/store'
|
||||
import CreateNetwork from '@/views/network/CreateNetwork'
|
||||
|
||||
export default {
|
||||
name: 'NetworkSelection',
|
||||
components: {
|
||||
CreateNetwork
|
||||
},
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
|
|
@ -96,7 +110,8 @@ export default {
|
|||
networkOffering: {
|
||||
loading: false,
|
||||
opts: []
|
||||
}
|
||||
},
|
||||
showCreateForm: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -173,8 +188,13 @@ export default {
|
|||
this.selectedRowKeys = this.preFillContent.networkids
|
||||
this.$emit('select-network-item', this.preFillContent.networkids)
|
||||
} else {
|
||||
this.selectedRowKeys = []
|
||||
this.$emit('select-network-item', null)
|
||||
if (this.items && this.items.length > 0) {
|
||||
this.selectedRowKeys = [this.items[0].id]
|
||||
this.$emit('select-network-item', this.selectedRowKeys)
|
||||
} else {
|
||||
this.selectedRowKeys = []
|
||||
this.$emit('select-network-item', [])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
<a-form-item :label="$t('filter')">
|
||||
<a-select
|
||||
allowClear
|
||||
mode="multiple"
|
||||
v-decorator="['filter']">
|
||||
<a-select-option
|
||||
v-for="(opt) in filterOpts"
|
||||
|
|
@ -244,21 +245,23 @@ export default {
|
|||
if (err) {
|
||||
return
|
||||
}
|
||||
if (this.inputDecorator === 'template') {
|
||||
this.vmFetchTemplates(values.filter)
|
||||
} else {
|
||||
this.vmFetchIsos(values.filter)
|
||||
}
|
||||
const filtered = values.filter || []
|
||||
this.filter = ''
|
||||
filtered.map(item => {
|
||||
if (this.filter.length === 0) {
|
||||
this.filter += 'is:' + item
|
||||
} else {
|
||||
this.filter += '; is:' + item
|
||||
}
|
||||
})
|
||||
this.filterDataSource(this.filter)
|
||||
})
|
||||
},
|
||||
onClear () {
|
||||
const field = { filter: undefined }
|
||||
this.form.setFieldsValue(field)
|
||||
if (this.inputDecorator === 'template') {
|
||||
this.vmFetchTemplates()
|
||||
} else {
|
||||
this.vmFetchIsos()
|
||||
}
|
||||
this.filter = ''
|
||||
this.filterDataSource('')
|
||||
},
|
||||
changeOsName (value) {
|
||||
this.osType = value
|
||||
|
|
@ -288,8 +291,27 @@ export default {
|
|||
}
|
||||
|
||||
.filter-group {
|
||||
/deep/.ant-input-affix-wrapper {
|
||||
float: right;
|
||||
width: calc(100% - 32px);
|
||||
|
||||
.ant-input {
|
||||
border-radius: 4px;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/.ant-input-group-addon {
|
||||
padding: 0 5px;
|
||||
float: left;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
border-right: 0;
|
||||
border-left: 1px solid #d9d9d9;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
padding: 0 0 0 1px;
|
||||
}
|
||||
|
||||
&-button {
|
||||
|
|
@ -301,7 +323,7 @@ export default {
|
|||
&-button {
|
||||
position: relative;
|
||||
display: block;
|
||||
min-height: 25px;
|
||||
min-height: 30px;
|
||||
|
||||
&-clear {
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
}"
|
||||
:loading="zoneLoading"
|
||||
:placeholder="this.$t('.zoneid')"
|
||||
@change="val => { this.handleZoneChanged(this.zones[val]) }">
|
||||
@change="val => { this.handleZoneChange(this.zones[val]) }">
|
||||
<a-select-option v-for="(opt, optIndex) in this.zones" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
}"
|
||||
:loading="zoneLoading"
|
||||
:placeholder="this.$t('zoneid')"
|
||||
@change="val => { this.handleZoneChanged(this.zones[val]) }">
|
||||
@change="val => { this.handleZoneChange(this.zones[val]) }">
|
||||
<a-select-option v-for="(opt, optIndex) in this.zones" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
}"
|
||||
:loading="zoneLoading"
|
||||
:placeholder="this.$t('zoneid')"
|
||||
@change="val => { this.handleZoneChanged(this.zones[val]) }">
|
||||
@change="val => { this.handleZoneChange(this.zones[val]) }">
|
||||
<a-select-option v-for="(opt, optIndex) in this.zones" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
}"
|
||||
:loading="zoneLoading"
|
||||
:placeholder="this.$t('physicalnetworkid')"
|
||||
@change="val => { this.handleZoneChanged(this.formPhysicalNetworks[val]) }">
|
||||
@change="val => { this.handleZoneChange(this.formPhysicalNetworks[val]) }">
|
||||
<a-select-option v-for="(opt, optIndex) in this.formPhysicalNetworks" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
|
|
|
|||
Loading…
Reference in New Issue