From 1312ec2b8bf9c60cac3e9c2573eb86b67bbb1a8d Mon Sep 17 00:00:00 2001
From: Hoang Nguyen
Date: Wed, 22 Apr 2020 16:13:12 +0700
Subject: [PATCH] compute: Fix VM Deployment Wizard Issues (#273)
Fixes #254
Pod, cluster, host should be hidden for normal and domain admin user
Listing of all items are not done by passing account/domainid, or zoneid (for example listing of networks, template, isos for zone; keypair, affiinity groups etc for account/domain)
Add a way to filter templates by featured, community, shared, mine?
Multiple listing of templates for the same zone? (multiple radios button)
Add button to add network that can open the add network popup or add a router-link?
User data not properly base64 encoded (check encoding?)
Add support for processing min/max cpu/ram based on type of compute offering selected and custom disk offering
Signed-off-by: Rohit Yadav
Co-authored-by: Hoang Nguyen
Co-authored-by: Abhishek Kumar
Co-authored-by: Rohit Yadav
---
ui/src/components/header/ProjectMenu.vue | 4 +-
ui/src/locales/en.json | 9 +-
ui/src/views/AutogenView.vue | 2 +
ui/src/views/compute/DeployVM.vue | 256 +++++++++++++---
.../compute/wizard/AffinityGroupSelection.vue | 15 +
.../wizard/ComputeOfferingSelection.vue | 169 +++++++++++
.../views/compute/wizard/ComputeSelection.vue | 278 ++++++++++++------
.../compute/wizard/DiskOfferingSelection.vue | 37 ++-
.../compute/wizard/DiskSizeSelection.vue | 47 ++-
.../compute/wizard/NetworkConfiguration.vue | 8 +-
.../views/compute/wizard/NetworkSelection.vue | 54 +++-
.../compute/wizard/SshKeyPairSelection.vue | 31 +-
.../compute/wizard/TemplateIsoRadioGroup.vue | 24 +-
.../compute/wizard/TemplateIsoSelection.vue | 144 ++++++++-
14 files changed, 902 insertions(+), 176 deletions(-)
create mode 100644 ui/src/views/compute/wizard/ComputeOfferingSelection.vue
diff --git a/ui/src/components/header/ProjectMenu.vue b/ui/src/components/header/ProjectMenu.vue
index 2c922d18a44..f3ea38bb9f1 100644
--- a/ui/src/components/header/ProjectMenu.vue
+++ b/ui/src/components/header/ProjectMenu.vue
@@ -67,9 +67,9 @@ export default {
if (json && json.listprojectsresponse && json.listprojectsresponse.project) {
this.projects.push(...json.listprojectsresponse.project)
}
- const currentProject = Vue.ls.get(CURRENT_PROJECT)
+ const currentProject = Vue.ls.get(CURRENT_PROJECT) || {}
for (var project of this.projects) {
- if (project.id === currentProject.id) {
+ if (project && currentProject && project.id === currentProject.id) {
this.setSelectedProject(project)
break
}
diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json
index fad21462280..285f1823c25 100644
--- a/ui/src/locales/en.json
+++ b/ui/src/locales/en.json
@@ -1151,6 +1151,8 @@
"message.required.traffic.type": "Error in configuration! All required traffic types should be added and with multiple physical networks each network should have a label.",
"message.desc.primary.storage": "Each cluster must contain one or more primary storage servers, and we will add the first one now. Primary storage contains the disk volumes for all the VMs running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor.",
"message.desc.secondary.storage": "Each zone must have at least one NFS or secondary storage server, and we will add the first one now. Secondary storage stores VM templates, ISO images, and VM disk volume snapshots. This server must be available to all hosts in the zone.
Provide the IP address and exported path.",
+"message.error.required.input": "Please enter input",
+"message.error.invalid.range": "Please enter values from {min} to {max}",
"label.name": "Name",
"label.ipv4.dns1": "IPv4 DNS1",
"label.ipv4.dns2": "IPv4 DNS2",
@@ -1266,5 +1268,10 @@
"label.launch.zone": "Launch Zone",
"label.done": "Done",
"label.fix.errors": "Fix errors",
-"error.something.went.wrong.please.correct.the.following": "Something went wrong; please correct the following"
+"error.something.went.wrong.please.correct.the.following": "Something went wrong; please correct the following",
+"filter": "Filter",
+"featured": "Featured",
+"community": "Community",
+"selfexecutable": "Self",
+"sharedexecutable": "Shared"
}
diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue
index 47b10af0430..07f471fcc9f 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -396,6 +396,8 @@ export default {
if (this.searchQuery !== '') {
if (this.apiName === 'listRoles') {
params.name = this.searchQuery
+ } else if (this.apiName === 'quotaEmailTemplateList') {
+ params.templatetype = this.searchQuery
} else {
params.keyword = this.searchQuery
}
diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue
index f1a4c9eb7d2..d7b7eee90dd 100644
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@ -44,21 +44,27 @@
:loading="loading.zones"
>
-
+
-
+
-
+
@@ -113,8 +121,22 @@
:items="options.isos"
:selected="tabKey"
:loading="loading.isos"
+ :preFillContent="dataPreFill"
@update-template-iso="updateFieldValue"
>
+
+ this.hypervisor = value"
+ >
+
+
@@ -134,13 +156,42 @@
:status="zoneSelected ? 'process' : 'wait'">
-
updateComputeOffering($event)"
@handle-search-filter="($event) => handleSearchFilter('serviceOfferings', $event)"
- >
+ >
+
+
+
+
+
+
+
+
+
+
+
+
@@ -153,12 +204,14 @@
:items="options.diskOfferings"
:value="diskOffering ? diskOffering.id : ''"
:loading="loading.diskOfferings"
+ :preFillContent="dataPreFill"
@select-disk-offering-item="($event) => updateDiskOffering($event)"
@handle-search-filter="($event) => handleSearchFilter('diskOfferings', $event)"
>
@@ -175,6 +228,7 @@
:items="options.affinityGroups"
:value="affinityGroupIds"
:loading="loading.affinityGroups"
+ :preFillContent="dataPreFill"
@select-affinity-group-item="($event) => updateAffinityGroups($event)"
@handle-search-filter="($event) => handleSearchFilter('affinityGroups', $event)"
>
@@ -191,12 +245,14 @@
:value="networkOfferingIds"
:loading="loading.networks"
:zoneId="zoneId"
+ :preFillContent="dataPreFill"
@select-network-item="($event) => updateNetworks($event)"
@handle-search-filter="($event) => handleSearchFilter('networks', $event)"
>
updateNetworkConfig($event)"
@select-default-network-item="($event) => updateDefaultNetworks($event)"
>
@@ -212,6 +268,7 @@
:items="options.sshKeyPairs"
:value="sshKeyPair ? sshKeyPair.name : ''"
:loading="loading.sshKeyPairs"
+ :preFillContent="dataPreFill"
@select-ssh-key-pair-item="($event) => updateSshKeyPairs($event)"
@handle-search-filter="($event) => handleSearchFilter('sshKeyPairs', $event)"
/>
@@ -261,6 +318,7 @@ 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 DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
@@ -281,11 +339,16 @@ export default {
DiskSizeSelection,
DiskOfferingSelection,
InfoCard,
+ ComputeOfferingSelection,
ComputeSelection
},
props: {
visible: {
type: Boolean
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
mixins: [mixin, mixinDevice],
@@ -297,6 +360,7 @@ export default {
options: {
templates: [],
isos: [],
+ hypervisors: [],
serviceOfferings: [],
diskOfferings: [],
zones: [],
@@ -313,6 +377,7 @@ export default {
deploy: false,
templates: false,
isos: false,
+ hypervisors: false,
serviceOfferings: false,
diskOfferings: false,
affinityGroups: false,
@@ -324,9 +389,10 @@ export default {
hosts: false,
groups: false
},
- instanceConfig: [],
+ instanceConfig: {},
template: {},
iso: {},
+ hypervisor: '',
serviceOffering: {},
diskOffering: {},
affinityGroups: [],
@@ -368,10 +434,14 @@ export default {
tab: this.$t('ISOs')
}
],
- tabKey: 'templateid'
+ tabKey: 'templateid',
+ dataPreFill: {}
}
},
computed: {
+ isNormalAndDomainUser () {
+ return ['DomainAdmin', 'User'].includes(this.$store.getters.userInfo.roletype)
+ },
diskSize () {
const rootDiskSize = _.get(this.instanceConfig, 'rootdisksize', 0)
const customDiskSize = _.get(this.instanceConfig, 'size', 0)
@@ -411,23 +481,33 @@ export default {
}
},
zones: {
- list: 'listZones'
+ list: 'listZones',
+ isLoad: true,
+ field: 'zoneid'
+ },
+ hypervisors: {
+ list: 'listHypervisors',
+ options: {
+ zoneid: _.get(this.zone, 'id')
+ },
+ field: 'hypervisor'
},
affinityGroups: {
list: 'listAffinityGroups',
options: {
page: 1,
pageSize: 10,
- keyword: undefined
+ keyword: undefined,
+ listall: false
}
},
sshKeyPairs: {
list: 'listSSHKeyPairs',
options: {
- zoneid: _.get(this.zone, 'id'),
page: 1,
pageSize: 10,
- keyword: undefined
+ keyword: undefined,
+ listall: false
}
},
networks: {
@@ -436,6 +516,8 @@ export default {
zoneid: _.get(this.zone, 'id'),
canusefordeploy: true,
projectid: store.getters.project.id,
+ domainid: store.getters.project.id ? null : store.getters.userInfo.domainid,
+ account: store.getters.project.id ? null : store.getters.userInfo.account,
page: 1,
pageSize: 10,
keyword: undefined
@@ -443,26 +525,37 @@ export default {
},
pods: {
list: 'listPods',
+ isLoad: !this.isNormalAndDomainUser,
options: {
zoneid: _.get(this.zone, 'id')
- }
+ },
+ field: 'podid'
},
clusters: {
list: 'listClusters',
+ isLoad: !this.isNormalAndDomainUser,
options: {
zoneid: _.get(this.zone, 'id')
- }
+ },
+ field: 'clusterid'
},
hosts: {
list: 'listHosts',
+ isLoad: !this.isNormalAndDomainUser,
options: {
zoneid: _.get(this.zone, 'id'),
state: 'Up',
type: 'Routing'
- }
+ },
+ field: 'hostid'
},
groups: {
- list: 'listInstanceGroups'
+ list: 'listInstanceGroups',
+ options: {
+ listall: false
+ },
+ isLoad: true,
+ field: 'group'
}
}
},
@@ -477,6 +570,14 @@ export default {
}
})
},
+ hypervisorSelectOptions () {
+ return this.options.hypervisors.map((hypervisor) => {
+ return {
+ label: hypervisor.name,
+ value: hypervisor.name
+ }
+ })
+ },
podSelectOptions () {
return this.options.pods.map((pod) => {
return {
@@ -527,6 +628,8 @@ export default {
instanceConfig (instanceConfig) {
this.template = _.find(this.options.templates, (option) => option.id === instanceConfig.templateid)
this.iso = _.find(this.options.isos, (option) => option.id === instanceConfig.isoid)
+ var hypervisorItem = _.find(this.options.hypervisors, (option) => option.name === instanceConfig.hypervisor)
+ this.hypervisor = hypervisorItem ? hypervisorItem.name : null
this.serviceOffering = _.find(this.options.serviceOfferings, (option) => option.id === instanceConfig.computeofferingid)
this.diskOffering = _.find(this.options.diskOfferings, (option) => option.id === instanceConfig.diskofferingid)
this.zone = _.find(this.options.zones, (option) => option.id === instanceConfig.zoneid)
@@ -551,6 +654,9 @@ export default {
this.vm.templatename = this.iso.displaytext
this.vm.ostypeid = this.iso.ostypeid
this.vm.ostypename = this.iso.ostypename
+ if (this.hypervisor) {
+ this.vm.hypervisor = this.hypervisor
+ }
}
if (this.serviceOffering) {
@@ -572,7 +678,7 @@ export default {
}
}
},
- beforeCreate () {
+ created () {
this.form = this.$form.createForm(this, {
onValuesChange: (props, fields) => {
if (fields.isoid) {
@@ -584,36 +690,63 @@ export default {
this.form.setFieldsValue({ isoid: null })
}
this.instanceConfig = { ...this.form.getFieldsValue(), ...fields }
- this.vm = this.instanceConfig
+ this.vm = Object.assign({}, this.instanceConfig)
}
})
this.form.getFieldDecorator('computeofferingid', { initialValue: undefined, preserve: true })
this.form.getFieldDecorator('diskofferingid', { initialValue: undefined, preserve: true })
this.form.getFieldDecorator('affinitygroupids', { initialValue: [], preserve: true })
- this.form.getFieldDecorator('isoid', { initialValue: undefined, preserve: true })
this.form.getFieldDecorator('networkids', { initialValue: [], preserve: true })
this.form.getFieldDecorator('keypair', { initialValue: undefined, preserve: true })
- this.apiParams = {}
- this.apiDeployVirtualMachine = this.$store.getters.apis.deployVirtualMachine || {}
- this.apiDeployVirtualMachine.params.forEach(param => {
- this.apiParams[param.name] = param
- })
+ this.form.getFieldDecorator('cpunumber', { initialValue: undefined, preserve: true })
+ this.form.getFieldDecorator('cpuSpeed', { initialValue: undefined, preserve: true })
+ this.form.getFieldDecorator('memory', { initialValue: undefined, preserve: true })
},
- created () {
+ mounted () {
+ this.dataPreFill = this.preFillContent && Object.keys(this.preFillContent).length > 0 ? this.preFillContent : {}
this.fetchData()
},
+ provide () {
+ return {
+ vmFetchTemplates: this.fetchAllTemplates,
+ vmFetchIsos: this.fetchAllIsos,
+ vmFetchNetworks: this.fetchNetwork
+ }
+ },
methods: {
+ fillValue (field) {
+ this.form.getFieldDecorator([field], { initialValue: this.dataPreFill[field] })
+ },
fetchData () {
- this.fetchOptions(this.params.zones, 'zones')
- this.fetchOptions(this.params.pods, 'pods')
- this.fetchOptions(this.params.clusters, 'clusters')
- this.fetchOptions(this.params.hosts, 'hosts')
- this.fetchOptions(this.params.groups, 'groups')
+ if (this.dataPreFill.zoneid) {
+ this.fetchDataByZone(this.dataPreFill.zoneid)
+ } else {
+ _.each(this.params, (param, name) => {
+ if (param.isLoad) {
+ this.fetchOptions(param, name)
+ }
+ })
+ }
+
this.fetchKeyboard()
Vue.nextTick().then(() => {
+ ['name', 'keyboard', 'userdata'].forEach(this.fillValue)
this.instanceConfig = this.form.getFieldsValue() // ToDo: maybe initialize with some other defaults
})
},
+ async fetchDataByZone (zoneId) {
+ this.fillValue('zoneid')
+ this.options.zones = await this.fetchZones()
+ this.zoneId = zoneId
+ this.zoneSelected = true
+ this.tabKey = 'templateid'
+ await _.each(this.params, (param, name) => {
+ if (!('isLoad' in param) || param.isLoad) {
+ this.fetchOptions(param, name, ['zones'])
+ }
+ })
+ await this.fetchAllTemplates()
+ },
fetchKeyboard () {
const keyboardType = []
keyboardType.push({
@@ -643,6 +776,10 @@ export default {
this.$set(this.options, 'keyboards', keyboardType)
},
+ fetchNetwork () {
+ const param = this.params.networks
+ this.fetchOptions(param, 'networks')
+ },
resetData () {
this.vm = {}
this.zoneSelected = false
@@ -654,13 +791,13 @@ export default {
this.tabKey = 'templateid'
this.form.setFieldsValue({
templateid: value,
- isoid: undefined
+ isoid: null
})
} else if (name === 'isoid') {
this.tabKey = 'isoid'
this.form.setFieldsValue({
isoid: value,
- templateid: undefined
+ templateid: null
})
} else {
this.form.setFieldsValue({
@@ -729,8 +866,8 @@ export default {
deployVmData.hostid = values.hostid
deployVmData.group = values.group
deployVmData.keyboard = values.keyboard
- if (values.keyboard && values.keyboard.length > 0) {
- deployVmData.userdata = encodeURIComponent(btoa(this.sanitizeReverse(values.keyboard)))
+ if (values.userdata && values.userdata.length > 0) {
+ deployVmData.userdata = encodeURIComponent(btoa(this.sanitizeReverse(values.userdata)))
}
// step 2: select template/iso
if (this.tabKey === 'templateid') {
@@ -741,15 +878,29 @@ export default {
if (values.rootdisksize && values.rootdisksize > 0) {
deployVmData.rootdisksize = values.rootdisksize
}
+ if (values.hypervisor && values.hypervisor.length > 0) {
+ deployVmData.hypervisor = values.hypervisor
+ }
// step 3: select service offering
deployVmData.serviceofferingid = values.computeofferingid
+ if (values.cpunumber || values.cpuspeed || values.memory) {
+ if (values.cpunumber) {
+ deployVmData['details[0].cpuNumber'] = values.cpunumber
+ }
+ if (values.cpuspeed) {
+ deployVmData['details[0].cpuSpeed'] = values.cpuspeed
+ }
+ if (values.memory) {
+ deployVmData['details[0].memory'] = values.memory
+ }
+ }
// step 4: select disk offering
deployVmData.diskofferingid = values.diskofferingid
if (values.size) {
deployVmData.size = values.size
}
// step 5: select an affinity group
- deployVmData.affinitygroupids = values.affinitygroupids.join(',')
+ 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++) {
@@ -804,6 +955,20 @@ export default {
})
})
},
+ fetchZones () {
+ return new Promise((resolve) => {
+ this.loading.zones = true
+ const param = this.params.zones
+ api(param.list, { listall: true }).then(json => {
+ const zones = json.listzonesresponse.zone || []
+ resolve(zones)
+ }).catch(function (error) {
+ console.log(error.stack)
+ }).finally(() => {
+ this.loading.zones = false
+ })
+ })
+ },
fetchOptions (param, name, exclude) {
if (exclude && exclude.length > 0) {
if (exclude.includes(name)) {
@@ -814,7 +979,9 @@ export default {
param.loading = true
param.opts = []
const options = param.options || {}
- options.listall = true
+ if (!('listall' in options)) {
+ options.listall = true
+ }
api(param.list, options).then((response) => {
param.loading = false
_.map(response, (responseItem, responseKey) => {
@@ -833,6 +1000,9 @@ export default {
param.opts = response
this.options[name] = response
this.$forceUpdate()
+ if (param.field) {
+ this.fillValue(param.field)
+ }
})
})
}).catch(function (error) {
@@ -869,11 +1039,14 @@ export default {
})
})
},
- fetchAllTemplates () {
+ fetchAllTemplates (filterKey) {
const promises = []
this.options.templates = []
this.loading.templates = true
this.templateFilter.forEach((filter) => {
+ if (filterKey && filterKey !== filter) {
+ return true
+ }
promises.push(this.fetchTemplates(filter))
})
Promise.all(promises).then(response => {
@@ -888,11 +1061,14 @@ export default {
this.loading.templates = false
})
},
- fetchAllIsos () {
+ fetchAllIsos (filterKey) {
const promises = []
this.options.isos = []
this.loading.isos = true
this.isoFilter.forEach((filter) => {
+ if (filterKey && filterKey !== filter) {
+ return true
+ }
promises.push(this.fetchIsos(filter))
})
Promise.all(promises).then(response => {
@@ -908,7 +1084,9 @@ export default {
})
},
onSelectZoneId (value) {
+ this.dataPreFill = {}
this.zoneId = value
+ this.zone = _.find(this.options.zones, (option) => option.id === value)
this.zoneSelected = true
this.form.setFieldsValue({
clusterid: undefined,
@@ -919,7 +1097,9 @@ export default {
})
this.tabKey = 'templateid'
_.each(this.params, (param, name) => {
- this.fetchOptions(param, name, ['zones', 'groups'])
+ if (!('isLoad' in param) || param.isLoad) {
+ this.fetchOptions(param, name, ['zones', 'groups'])
+ }
})
this.fetchAllTemplates()
},
diff --git a/ui/src/views/compute/wizard/AffinityGroupSelection.vue b/ui/src/views/compute/wizard/AffinityGroupSelection.vue
index be14d6f2f01..9c25355002b 100644
--- a/ui/src/views/compute/wizard/AffinityGroupSelection.vue
+++ b/ui/src/views/compute/wizard/AffinityGroupSelection.vue
@@ -53,6 +53,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -96,6 +100,17 @@ export default {
if (newValue && !_.isEqual(newValue, oldValue)) {
this.selectedRowKeys = newValue
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.affinitygroupids) {
+ this.selectedRowKeys = this.preFillContent.affinitygroupids
+ this.$emit('select-affinity-group-item', this.preFillContent.affinitygroupids)
+ } else {
+ this.selectedRowKeys = []
+ this.$emit('select-affinity-group-item', null)
+ }
+ }
}
},
methods: {
diff --git a/ui/src/views/compute/wizard/ComputeOfferingSelection.vue b/ui/src/views/compute/wizard/ComputeOfferingSelection.vue
new file mode 100644
index 00000000000..4403ee49476
--- /dev/null
+++ b/ui/src/views/compute/wizard/ComputeOfferingSelection.vue
@@ -0,0 +1,169 @@
+// 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.
+
+
+
+
+
+ {{ $t('cpu') }}
+ {{ $t('memory') }}
+
+
+
+
+
+
+
diff --git a/ui/src/views/compute/wizard/ComputeSelection.vue b/ui/src/views/compute/wizard/ComputeSelection.vue
index e44d17ad9da..68ae44219c5 100644
--- a/ui/src/views/compute/wizard/ComputeSelection.vue
+++ b/ui/src/views/compute/wizard/ComputeSelection.vue
@@ -16,122 +16,228 @@
// under the License.
-
-
-
- {{ $t('cpu') }}
- {{ $t('memory') }}
-
-
+
+
+
+
+
+
+
+ updateComputeCpuNumber($event)"
+ />
+
+
+ updateComputeCpuNumber($event)"
+ />
+
+
+
+
+
+
+ updateComputeCpuSpeed($event)"
+ />
+
+
+
+
+
+
+ updateComputeMemory($event)"
+ />
+
+
+ updateComputeMemory($event)"
+ />
+
+
+
+
+
+
+
-
-
diff --git a/ui/src/views/compute/wizard/DiskOfferingSelection.vue b/ui/src/views/compute/wizard/DiskOfferingSelection.vue
index 33d94ac9e09..78b06efac2e 100644
--- a/ui/src/views/compute/wizard/DiskOfferingSelection.vue
+++ b/ui/src/views/compute/wizard/DiskOfferingSelection.vue
@@ -64,6 +64,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -93,15 +97,7 @@ export default {
}
},
created () {
- this.dataItems = []
- this.dataItems.push({
- id: '0',
- name: this.$t('noselect'),
- diskSize: undefined,
- miniops: undefined,
- maxiops: undefined,
- isCustomized: undefined
- })
+ this.initDataItem()
},
computed: {
options () {
@@ -139,11 +135,34 @@ export default {
},
items (newData, oldData) {
if (newData && newData.length > 0) {
+ this.initDataItem()
this.dataItems = this.dataItems.concat(newData)
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.diskofferingid) {
+ this.selectedRowKeys = [this.preFillContent.diskofferingid]
+ this.$emit('select-disk-offering-item', this.preFillContent.diskofferingid)
+ } else {
+ this.selectedRowKeys = ['0']
+ this.$emit('select-disk-offering-item', '0')
+ }
+ }
}
},
methods: {
+ initDataItem () {
+ this.dataItems = []
+ this.dataItems.push({
+ id: '0',
+ name: this.$t('noselect'),
+ diskSize: undefined,
+ miniops: undefined,
+ maxiops: undefined,
+ isCustomized: undefined
+ })
+ },
onSelectRow (value) {
this.selectedRowKeys = value
this.$emit('select-disk-offering-item', value[0])
diff --git a/ui/src/views/compute/wizard/DiskSizeSelection.vue b/ui/src/views/compute/wizard/DiskSizeSelection.vue
index bec025a47b4..684cb18d47c 100644
--- a/ui/src/views/compute/wizard/DiskSizeSelection.vue
+++ b/ui/src/views/compute/wizard/DiskSizeSelection.vue
@@ -16,22 +16,26 @@
// under the License.
-
-
-
+
+
+
updateDickSize($event)"
+ @change="($event) => updateDiskSize($event)"
/>
-
-
+
+
+ updateDiskSize($event)"
+ />
+ GB
+
@@ -44,6 +48,10 @@ export default {
inputDecorator: {
type: String,
default: ''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -51,14 +59,27 @@ export default {
inputValue: 0
}
},
+ mounted () {
+ this.fillValue()
+ },
methods: {
- updateDickSize (value) {
+ fillValue () {
+ if (this.inputDecorator === 'rootdisksize') {
+ this.inputValue = this.preFillContent.rootdisksize ? this.preFillContent.rootdisksize : 0
+ } else if (this.inputDecorator === 'size') {
+ this.inputValue = this.preFillContent.size ? this.preFillContent.size : 0
+ }
+ this.$emit('update-disk-size', this.inputDecorator, this.inputValue)
+ },
+ updateDiskSize (value) {
this.$emit('update-disk-size', this.inputDecorator, value)
}
}
}
-
diff --git a/ui/src/views/compute/wizard/NetworkConfiguration.vue b/ui/src/views/compute/wizard/NetworkConfiguration.vue
index c53ed19f844..af205680353 100644
--- a/ui/src/views/compute/wizard/NetworkConfiguration.vue
+++ b/ui/src/views/compute/wizard/NetworkConfiguration.vue
@@ -28,13 +28,13 @@
updateNetworkData('ipAddress', record.id, $event.target.value)" />
updateNetworkData('macAddress', record.id, $event.target.value)" />
@@ -51,6 +51,10 @@ export default {
value: {
type: String,
default: ''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
diff --git a/ui/src/views/compute/wizard/NetworkSelection.vue b/ui/src/views/compute/wizard/NetworkSelection.vue
index 21151f21183..74678b00109 100644
--- a/ui/src/views/compute/wizard/NetworkSelection.vue
+++ b/ui/src/views/compute/wizard/NetworkSelection.vue
@@ -22,6 +22,13 @@
placeholder="Search"
v-model="filter"
@search="handleSearch" />
+
+
+ {{ $t('addNewNetworks') }}
+
+
''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -77,7 +92,11 @@ export default {
filter: '',
selectedRowKeys: [],
vpcs: [],
- filteredInfo: null
+ filteredInfo: null,
+ networkOffering: {
+ loading: false,
+ opts: []
+ }
}
},
computed: {
@@ -147,8 +166,22 @@ export default {
if (newValue && !_.isEqual(newValue, oldValue)) {
this.selectedRowKeys = newValue
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.networkids) {
+ this.selectedRowKeys = this.preFillContent.networkids
+ this.$emit('select-network-item', this.preFillContent.networkids)
+ } else {
+ this.selectedRowKeys = []
+ this.$emit('select-network-item', null)
+ }
+ }
}
},
+ beforeCreate () {
+ this.form = this.$form.createForm(this)
+ },
created () {
api('listVPCs', {
projectid: store.getters.project.id
@@ -156,6 +189,7 @@ export default {
this.vpcs = _.get(response, 'listvpcsresponse.vpc')
})
},
+ inject: ['vmFetchNetworks'],
methods: {
getDetails (network) {
return [
@@ -178,6 +212,24 @@ export default {
this.options.page = pagination.current
this.options.pageSize = pagination.pageSize
this.$emit('handle-search-filter', this.options)
+ },
+ listNetworkOfferings () {
+ return new Promise((resolve, reject) => {
+ const args = {}
+ args.forvpc = false
+ args.zoneid = this.zoneId
+ args.guestiptype = 'Isolated'
+ args.supportedServices = 'SourceNat'
+ args.specifyvlan = false
+ args.state = 'Enabled'
+
+ api('listNetworkOfferings', args).then(json => {
+ const listNetworkOfferings = json.listnetworkofferingsresponse.networkoffering || []
+ resolve(listNetworkOfferings)
+ }).catch(error => {
+ resolve(error)
+ })
+ })
}
}
}
diff --git a/ui/src/views/compute/wizard/SshKeyPairSelection.vue b/ui/src/views/compute/wizard/SshKeyPairSelection.vue
index 420f406fca8..f443cfcc9b2 100644
--- a/ui/src/views/compute/wizard/SshKeyPairSelection.vue
+++ b/ui/src/views/compute/wizard/SshKeyPairSelection.vue
@@ -53,6 +53,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -80,12 +84,7 @@ export default {
}
},
created () {
- this.dataItems = []
- this.dataItems.push({
- name: this.$t('noselect'),
- account: '-',
- domain: '-'
- })
+ this.initDataItem()
},
computed: {
options () {
@@ -121,11 +120,31 @@ export default {
},
items (newData, oldData) {
if (newData && newData.length > 0) {
+ this.initDataItem()
this.dataItems = this.dataItems.concat(newData)
}
+ },
+ loading () {
+ if (!this.loading) {
+ if (this.preFillContent.keypair) {
+ this.selectedRowKeys = [this.preFillContent.keypair]
+ this.$emit('select-ssh-key-pair-item', this.preFillContent.keypair)
+ } else {
+ this.selectedRowKeys = [this.$t('noselect')]
+ this.$emit('select-ssh-key-pair-item', this.$t('noselect'))
+ }
+ }
}
},
methods: {
+ initDataItem () {
+ this.dataItems = []
+ this.dataItems.push({
+ name: this.$t('noselect'),
+ account: '-',
+ domain: '-'
+ })
+ },
onSelectRow (value) {
this.selectedRowKeys = value
this.$emit('select-ssh-key-pair-item', value[0])
diff --git a/ui/src/views/compute/wizard/TemplateIsoRadioGroup.vue b/ui/src/views/compute/wizard/TemplateIsoRadioGroup.vue
index 594b2d05af2..c01e753739b 100644
--- a/ui/src/views/compute/wizard/TemplateIsoRadioGroup.vue
+++ b/ui/src/views/compute/wizard/TemplateIsoRadioGroup.vue
@@ -81,6 +81,14 @@ export default {
itemCount: {
type: Number,
default: 0
+ },
+ osType: {
+ type: String,
+ default: ''
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -90,16 +98,14 @@ export default {
pageSize: 10
}
},
- created () {
- this.value = this.selected
- this.$emit('emit-update-template-iso', this.inputDecorator, this.value)
- },
- watch: {
- inputDecorator (value) {
- if (value === 'templateid') {
- this.value = this.selected
- }
+ mounted () {
+ if (this.inputDecorator === 'templateid') {
+ this.value = !this.preFillContent.templateid ? this.selected : this.preFillContent.templateid
+ } else {
+ this.value = !this.preFillContent.isoid ? this.selected : this.preFillContent.isoid
}
+
+ this.$emit('emit-update-template-iso', this.inputDecorator, this.value)
},
computed: {
pagination () {
diff --git a/ui/src/views/compute/wizard/TemplateIsoSelection.vue b/ui/src/views/compute/wizard/TemplateIsoSelection.vue
index b26f893275d..7edef3c0ace 100644
--- a/ui/src/views/compute/wizard/TemplateIsoSelection.vue
+++ b/ui/src/views/compute/wizard/TemplateIsoSelection.vue
@@ -17,26 +17,74 @@
-
+
+
+
+
+
+
+
+ {{ $t(opt.name) }}
+
+
+
+
+
+
+
+
+
+ :defaultActiveKey="Object.keys(dataSource)[0]"
+ tabPosition="top"
+ v-model="osType"
+ @change="changeOsName">
@@ -72,6 +120,10 @@ export default {
loading: {
type: Boolean,
default: false
+ },
+ preFillContent: {
+ type: Object,
+ default: () => {}
}
},
data () {
@@ -80,7 +132,22 @@ export default {
filteredItems: this.items,
checkedValue: '',
dataSource: {},
- itemCount: {}
+ itemCount: {},
+ visibleFilter: false,
+ filterOpts: [{
+ id: 'featured',
+ name: 'featured'
+ }, {
+ id: 'community',
+ name: 'community'
+ }, {
+ id: 'selfexecutable',
+ name: 'selfexecutable'
+ }, {
+ id: 'sharedexecutable',
+ name: 'sharedexecutable'
+ }],
+ osType: ''
}
},
watch: {
@@ -92,6 +159,7 @@ export default {
this.checkedValue = items[0].id
}
this.dataSource = this.mappingDataSource()
+ this.osType = Object.keys(this.dataSource)[0]
},
inputDecorator (newValue, oldValue) {
if (newValue !== oldValue) {
@@ -99,6 +167,10 @@ export default {
}
}
},
+ beforeCreate () {
+ this.form = this.$form.createForm(this)
+ },
+ inject: ['vmFetchTemplates', 'vmFetchIsos'],
methods: {
mappingDataSource () {
let mappedItems = {}
@@ -124,6 +196,7 @@ export default {
return mappedItems
},
updateTemplateIso (name, id) {
+ this.checkedValue = id
this.$emit('update-template-iso', name, id)
},
filterDataSource (strQuery) {
@@ -164,6 +237,31 @@ export default {
}
return arrResult
+ },
+ handleSubmit (e) {
+ e.preventDefault()
+ this.form.validateFields((err, values) => {
+ if (err) {
+ return
+ }
+ if (this.inputDecorator === 'template') {
+ this.vmFetchTemplates(values.filter)
+ } else {
+ this.vmFetchIsos(values.filter)
+ }
+ })
+ },
+ onClear () {
+ const field = { filter: undefined }
+ this.form.setFieldsValue(field)
+ if (this.inputDecorator === 'template') {
+ this.vmFetchTemplates()
+ } else {
+ this.vmFetchIsos()
+ }
+ },
+ changeOsName (value) {
+ this.osType = value
}
}
}
@@ -188,4 +286,32 @@ export default {
/deep/.ant-tabs-nav-scroll {
min-height: 45px;
}
+
+ .filter-group {
+ /deep/.ant-input-group-addon {
+ padding: 0 5px;
+ }
+
+ &-button {
+ background: inherit;
+ border: 0;
+ padding: 0;
+ }
+
+ &-button {
+ position: relative;
+ display: block;
+ min-height: 25px;
+
+ &-clear {
+ position: absolute;
+ left: 0;
+ }
+
+ &-search {
+ position: absolute;
+ right: 0;
+ }
+ }
+ }