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 <hoan...@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 <rohit.ya...@shapeblue.com> Co-authored-by: Rohit Yadav <rohit.ya...@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>