You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/05/22 21:20:54 UTC
[cloudstack-primate] branch master updated: compute: VM deployment
wizard fixes (#307)
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git
The following commit(s) were added to refs/heads/master by this push:
new c9de1ff compute: VM deployment wizard fixes (#307)
c9de1ff is described below
commit c9de1ff89b145415f0a46deaa03bf41111ac6299
Author: Hoang Nguyen <ho...@unitech.vn>
AuthorDate: Sat May 23 04:20:43 2020 +0700
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 <ro...@shapeblue.com>
Co-authored-by: Rohit Yadav <ro...@shapeblue.com>
---
src/components/view/InfoCard.vue | 14 +--
src/locales/en.json | 5 +-
src/views/compute/CreateKubernetesCluster.vue | 2 +-
src/views/compute/DeployVM.vue | 119 +++++++++++----------
.../compute/wizard/ComputeOfferingSelection.vue | 6 +-
src/views/compute/wizard/NetworkConfiguration.vue | 23 +++-
src/views/compute/wizard/NetworkSelection.vue | 42 ++++++--
src/views/compute/wizard/TemplateIsoSelection.vue | 46 +++++---
src/views/network/CreateIsolatedNetworkForm.vue | 2 +-
src/views/network/CreateL2NetworkForm.vue | 2 +-
src/views/network/CreateSharedNetworkForm.vue | 4 +-
11 files changed, 167 insertions(+), 98 deletions(-)
diff --git a/src/components/view/InfoCard.vue b/src/components/view/InfoCard.vue
index 4e21f89..befda64 100644
--- a/src/components/view/InfoCard.vue
+++ b/src/components/view/InfoCard.vue
@@ -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">
diff --git a/src/locales/en.json b/src/locales/en.json
index 3134b00..0faabc3 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -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"
}
diff --git a/src/views/compute/CreateKubernetesCluster.vue b/src/views/compute/CreateKubernetesCluster.vue
index 15b9237..e306d72 100644
--- a/src/views/compute/CreateKubernetesCluster.vue
+++ b/src/views/compute/CreateKubernetesCluster.vue
@@ -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>
diff --git a/src/views/compute/DeployVM.vue b/src/views/compute/DeployVM.vue
index 4459141..4943cbb 100644
--- a/src/views/compute/DeployVM.vue
+++ b/src/views/compute/DeployVM.vue
@@ -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()
diff --git a/src/views/compute/wizard/ComputeOfferingSelection.vue b/src/views/compute/wizard/ComputeOfferingSelection.vue
index 4403ee4..6f2304d 100644
--- a/src/views/compute/wizard/ComputeOfferingSelection.vue
+++ b/src/views/compute/wizard/ComputeOfferingSelection.vue
@@ -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)
+ }
}
}
}
diff --git a/src/views/compute/wizard/NetworkConfiguration.vue b/src/views/compute/wizard/NetworkConfiguration.vue
index af20568..d2c0a4d 100644
--- a/src/views/compute/wizard/NetworkConfiguration.vue
+++ b/src/views/compute/wizard/NetworkConfiguration.vue
@@ -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)
+ }
+ }
}
}
}
diff --git a/src/views/compute/wizard/NetworkSelection.vue b/src/views/compute/wizard/NetworkSelection.vue
index 74678b0..a80c891 100644
--- a/src/views/compute/wizard/NetworkSelection.vue
+++ b/src/views/compute/wizard/NetworkSelection.vue
@@ -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', [])
+ }
}
}
}
diff --git a/src/views/compute/wizard/TemplateIsoSelection.vue b/src/views/compute/wizard/TemplateIsoSelection.vue
index 7edef3c..bffc9b5 100644
--- a/src/views/compute/wizard/TemplateIsoSelection.vue
+++ b/src/views/compute/wizard/TemplateIsoSelection.vue
@@ -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;
diff --git a/src/views/network/CreateIsolatedNetworkForm.vue b/src/views/network/CreateIsolatedNetworkForm.vue
index 15ebf19..a4c40a6 100644
--- a/src/views/network/CreateIsolatedNetworkForm.vue
+++ b/src/views/network/CreateIsolatedNetworkForm.vue
@@ -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>
diff --git a/src/views/network/CreateL2NetworkForm.vue b/src/views/network/CreateL2NetworkForm.vue
index 4eff20a..0489aa7 100644
--- a/src/views/network/CreateL2NetworkForm.vue
+++ b/src/views/network/CreateL2NetworkForm.vue
@@ -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>
diff --git a/src/views/network/CreateSharedNetworkForm.vue b/src/views/network/CreateSharedNetworkForm.vue
index 2303fee..f47a008 100644
--- a/src/views/network/CreateSharedNetworkForm.vue
+++ b/src/views/network/CreateSharedNetworkForm.vue
@@ -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>