mirror of https://github.com/apache/cloudstack.git
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 <rohit.yadav@shapeblue.com> Co-authored-by: Hoang Nguyen <hoangnm@unitech.vn> Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com> Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
51b3e033ca
commit
1312ec2b8b
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.<br/><br/>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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,21 +44,27 @@
|
|||
:loading="loading.zones"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('podId')">
|
||||
<a-form-item
|
||||
v-if="!isNormalAndDomainUser"
|
||||
:label="this.$t('podId')">
|
||||
<a-select
|
||||
v-decorator="['podid']"
|
||||
:options="podSelectOptions"
|
||||
:loading="loading.pods"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('clusterid')">
|
||||
<a-form-item
|
||||
v-if="!isNormalAndDomainUser"
|
||||
:label="this.$t('clusterid')">
|
||||
<a-select
|
||||
v-decorator="['clusterid']"
|
||||
:options="clusterSelectOptions"
|
||||
:loading="loading.clusters"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="this.$t('hostId')">
|
||||
<a-form-item
|
||||
v-if="!isNormalAndDomainUser"
|
||||
:label="this.$t('hostId')">
|
||||
<a-select
|
||||
v-decorator="['hostid']"
|
||||
:options="hostSelectOptions"
|
||||
|
|
@ -101,10 +107,12 @@
|
|||
:items="options.templates"
|
||||
:selected="tabKey"
|
||||
:loading="loading.templates"
|
||||
:preFillContent="dataPreFill"
|
||||
@update-template-iso="updateFieldValue"
|
||||
></template-iso-selection>
|
||||
<disk-size-selection
|
||||
input-decorator="rootdisksize"
|
||||
:preFillContent="dataPreFill"
|
||||
@update-disk-size="updateFieldValue"/>
|
||||
</p>
|
||||
<p v-else>
|
||||
|
|
@ -113,8 +121,22 @@
|
|||
:items="options.isos"
|
||||
:selected="tabKey"
|
||||
:loading="loading.isos"
|
||||
:preFillContent="dataPreFill"
|
||||
@update-template-iso="updateFieldValue"
|
||||
></template-iso-selection>
|
||||
<a-form-item :label="this.$t('hypervisor')">
|
||||
<a-select
|
||||
v-decorator="['hypervisor', {
|
||||
initialValue: hypervisorSelectOptions && hypervisorSelectOptions.length > 0
|
||||
? hypervisorSelectOptions[0].value
|
||||
: null,
|
||||
rules: [{ required: true, message: 'Please select option' }]
|
||||
}]"
|
||||
:options="hypervisorSelectOptions"
|
||||
@change="value => this.hypervisor = value"
|
||||
>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</p>
|
||||
</a-card>
|
||||
<a-form-item class="form-item-hidden">
|
||||
|
|
@ -134,13 +156,42 @@
|
|||
:status="zoneSelected ? 'process' : 'wait'">
|
||||
<template slot="description">
|
||||
<div v-if="zoneSelected">
|
||||
<compute-selection
|
||||
<compute-offering-selection
|
||||
:compute-items="options.serviceOfferings"
|
||||
:value="serviceOffering ? serviceOffering.id : ''"
|
||||
:loading="loading.serviceOfferings"
|
||||
:preFillContent="dataPreFill"
|
||||
@select-compute-item="($event) => updateComputeOffering($event)"
|
||||
@handle-search-filter="($event) => handleSearchFilter('serviceOfferings', $event)"
|
||||
></compute-selection>
|
||||
></compute-offering-selection>
|
||||
<compute-selection
|
||||
v-if="serviceOffering && serviceOffering.iscustomized"
|
||||
cpunumber-input-decorator="cpunumber"
|
||||
cpuspeed-input-decorator="cpuspeed"
|
||||
memory-input-decorator="memory"
|
||||
:preFillContent="dataPreFill"
|
||||
:computeOfferingId="instanceConfig.computeofferingid"
|
||||
:isConstrained="'serviceofferingdetails' in serviceOffering"
|
||||
:minCpu="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.mincpunumber*1 : 1"
|
||||
:maxCpu="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.maxcpunumber*1 : Number.MAX_SAFE_INTEGER"
|
||||
:minMemory="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.minmemory*1 : 1"
|
||||
:maxMemory="'serviceofferingdetails' in serviceOffering ? serviceOffering.serviceofferingdetails.maxmemory*1 : Number.MAX_SAFE_INTEGER"
|
||||
@update-compute-cpunumber="updateFieldValue"
|
||||
@update-compute-cpuspeed="updateFieldValue"
|
||||
@update-compute-memory="updateFieldValue" />
|
||||
<span v-if="serviceOffering && serviceOffering.iscustomized">
|
||||
<a-form-item class="form-item-hidden" >
|
||||
<a-input v-decorator="['cpunumber']"/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
class="form-item-hidden"
|
||||
v-if="serviceOffering && !(serviceOffering.cpuspeed > 0)">
|
||||
<a-input v-decorator="['cpuspeed']"/>
|
||||
</a-form-item>
|
||||
<a-form-item class="form-item-hidden">
|
||||
<a-input v-decorator="['memory']"/>
|
||||
</a-form-item>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</a-step>
|
||||
|
|
@ -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)"
|
||||
></disk-offering-selection>
|
||||
<disk-size-selection
|
||||
v-if="diskOffering && diskOffering.iscustomized"
|
||||
input-decorator="size"
|
||||
:preFillContent="dataPreFill"
|
||||
@update-disk-size="updateFieldValue" />
|
||||
<a-form-item class="form-item-hidden">
|
||||
<a-input v-decorator="['size']"/>
|
||||
|
|
@ -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)"
|
||||
></affinity-group-selection>
|
||||
|
|
@ -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)"
|
||||
></network-selection>
|
||||
<network-configuration
|
||||
v-if="networks.length > 0"
|
||||
:items="networks"
|
||||
:preFillContent="dataPreFill"
|
||||
@update-network-config="($event) => updateNetworkConfig($event)"
|
||||
@select-default-network-item="($event) => updateDefaultNetworks($event)"
|
||||
></network-configuration>
|
||||
|
|
@ -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()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-input-search
|
||||
style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="handleSearch" />
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
:loading="loading"
|
||||
size="middle"
|
||||
@change="handleTableChange"
|
||||
:scroll="{ y: 225 }"
|
||||
>
|
||||
<span slot="cpuTitle"><a-icon type="appstore" /> {{ $t('cpu') }}</span>
|
||||
<span slot="ramTitle"><a-icon type="bulb" /> {{ $t('memory') }}</span>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ComputeOfferingSelection',
|
||||
props: {
|
||||
computeItems: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
preFillContent: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filter: '',
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('serviceOfferingId'),
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'cpu',
|
||||
slots: { title: 'cpuTitle' },
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'ram',
|
||||
slots: { title: 'ramTitle' },
|
||||
width: '30%'
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
options () {
|
||||
return {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: ''
|
||||
}
|
||||
},
|
||||
tableSource () {
|
||||
return this.computeItems.map((item) => {
|
||||
var cpuNumberValue = item.cpunumber + ''
|
||||
var cpuSpeedValue = (item.cpuspeed !== null && item.cpuspeed !== undefined && item.cpuspeed > 0) ? parseFloat(item.cpuspeed / 1000.0).toFixed(2) + '' : ''
|
||||
var ramValue = item.memory + ''
|
||||
if (item.iscustomized === true) {
|
||||
cpuNumberValue = ''
|
||||
ramValue = ''
|
||||
if ('serviceofferingdetails' in item &&
|
||||
'mincpunumber' in item.serviceofferingdetails &&
|
||||
'maxcpunumber' in item.serviceofferingdetails) {
|
||||
cpuNumberValue = item.serviceofferingdetails.mincpunumber + '-' + item.serviceofferingdetails.maxcpunumber
|
||||
}
|
||||
if ('serviceofferingdetails' in item &&
|
||||
'minmemory' in item.serviceofferingdetails &&
|
||||
'maxmemory' in item.serviceofferingdetails) {
|
||||
ramValue = item.serviceofferingdetails.minmemory + '-' + item.serviceofferingdetails.maxmemory
|
||||
}
|
||||
}
|
||||
return {
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
cpu: cpuNumberValue.length > 0 ? `${cpuNumberValue} CPU x ${cpuSpeedValue} Ghz` : '',
|
||||
ram: ramValue.length > 0 ? `${ramValue} MB` : ''
|
||||
}
|
||||
})
|
||||
},
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onChange: this.onSelectRow
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (newValue, oldValue) {
|
||||
if (newValue && newValue !== oldValue) {
|
||||
this.selectedRowKeys = [newValue]
|
||||
}
|
||||
},
|
||||
loading () {
|
||||
if (!this.loading) {
|
||||
if (this.preFillContent.computeofferingid) {
|
||||
this.selectedRowKeys = [this.preFillContent.computeofferingid]
|
||||
this.$emit('select-compute-item', this.preFillContent.computeofferingid)
|
||||
} else {
|
||||
this.selectedRowKeys = []
|
||||
this.$emit('select-compute-item', null)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelectRow (value) {
|
||||
this.selectedRowKeys = value
|
||||
this.$emit('select-compute-item', value[0])
|
||||
},
|
||||
handleSearch (value) {
|
||||
this.filter = value
|
||||
this.options.keyword = this.filter
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
},
|
||||
handleTableChange (pagination) {
|
||||
this.options.page = pagination.current
|
||||
this.options.pageSize = pagination.pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-table-wrapper {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -16,122 +16,228 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-input-search
|
||||
style="width: 25vw;float: right;margin-bottom: 10px; z-index: 8"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="handleSearch" />
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
:loading="loading"
|
||||
size="middle"
|
||||
@change="handleTableChange"
|
||||
:scroll="{ y: 225 }"
|
||||
>
|
||||
<span slot="cpuTitle"><a-icon type="appstore" /> {{ $t('cpu') }}</span>
|
||||
<span slot="ramTitle"><a-icon type="bulb" /> {{ $t('memory') }}</span>
|
||||
</a-table>
|
||||
</div>
|
||||
<a-card>
|
||||
<a-col>
|
||||
<a-row>
|
||||
<a-col :md="colContraned" :lg="colContraned">
|
||||
<a-form-item
|
||||
:label="this.$t('cpunumber')"
|
||||
:validate-status="errors.cpu.status"
|
||||
:help="errors.cpu.message">
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="10" :lg="10" v-show="isConstrained">
|
||||
<a-slider
|
||||
:min="minCpu"
|
||||
:max="maxCpu"
|
||||
v-model="cpuNumberInputValue"
|
||||
@change="($event) => updateComputeCpuNumber($event)"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :md="4" :lg="4">
|
||||
<a-input-number
|
||||
v-model="cpuNumberInputValue"
|
||||
:formatter="value => `${value}`"
|
||||
@change="($event) => updateComputeCpuNumber($event)"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="8" :lg="8" v-show="!isConstrained">
|
||||
<a-form-item
|
||||
:label="this.$t('cpuspeed')"
|
||||
:validate-status="errors.cpuspeed.status"
|
||||
:help="errors.cpuspeed.message">
|
||||
<a-input-number
|
||||
v-model="cpuSpeedInputValue"
|
||||
@change="($event) => updateComputeCpuSpeed($event)"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :md="colContraned" :lg="colContraned">
|
||||
<a-form-item
|
||||
:label="this.$t('memory')"
|
||||
:validate-status="errors.memory.status"
|
||||
:help="errors.memory.message">
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="10" :lg="10" v-show="isConstrained">
|
||||
<a-slider
|
||||
:min="minMemory"
|
||||
:max="maxMemory"
|
||||
v-model="memoryInputValue"
|
||||
@change="($event) => updateComputeMemory($event)"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :md="4" :lg="4">
|
||||
<a-input-number
|
||||
v-model="memoryInputValue"
|
||||
:formatter="value => `${value} MB`"
|
||||
:parser="value => value.replace(' MB', '')"
|
||||
@change="($event) => updateComputeMemory($event)"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ComputeSelection',
|
||||
props: {
|
||||
computeItems: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
computeOfferingId: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
value: {
|
||||
isConstrained: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
minCpu: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
maxCpu: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
minMemory: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
maxMemory: {
|
||||
type: Number,
|
||||
default: 256
|
||||
},
|
||||
cpunumberInputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
cpuspeedInputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
memoryInputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
preFillContent: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
filter: '',
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('serviceOfferingId'),
|
||||
width: '40%'
|
||||
cpuNumberInputValue: 1,
|
||||
cpuSpeedInputValue: 1,
|
||||
memoryInputValue: 1,
|
||||
errors: {
|
||||
cpu: {
|
||||
status: '',
|
||||
message: ''
|
||||
},
|
||||
{
|
||||
dataIndex: 'cpu',
|
||||
slots: { title: 'cpuTitle' },
|
||||
width: '30%'
|
||||
cpuspeed: {
|
||||
status: '',
|
||||
message: ''
|
||||
},
|
||||
{
|
||||
dataIndex: 'ram',
|
||||
slots: { title: 'ramTitle' },
|
||||
width: '30%'
|
||||
memory: {
|
||||
status: '',
|
||||
message: ''
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
options () {
|
||||
return {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
keyword: ''
|
||||
}
|
||||
},
|
||||
tableSource () {
|
||||
return this.computeItems.map((item) => {
|
||||
return {
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
cpu: `${item.cpunumber} CPU x ${parseFloat(item.cpuspeed / 1000.0).toFixed(2)} Ghz`,
|
||||
ram: `${item.memory} MB`
|
||||
}
|
||||
})
|
||||
},
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onChange: this.onSelectRow
|
||||
}
|
||||
colContraned () {
|
||||
return this.isConstrained ? 12 : 8
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (newValue, oldValue) {
|
||||
if (newValue && newValue !== oldValue) {
|
||||
this.selectedRowKeys = [newValue]
|
||||
computeOfferingId (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
this.cpuNumberInputValue = this.minCpu
|
||||
this.memoryInputValue = this.minMemory
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.cpuNumberInputValue = this.minCpu
|
||||
this.memoryInputValue = this.minMemory
|
||||
this.fillValue()
|
||||
},
|
||||
methods: {
|
||||
onSelectRow (value) {
|
||||
this.selectedRowKeys = value
|
||||
this.$emit('select-compute-item', value[0])
|
||||
fillValue () {
|
||||
if (this.preFillContent.cpunumber) {
|
||||
this.cpuNumberInputValue = this.preFillContent.cpunumber
|
||||
}
|
||||
if (this.preFillContent.cpuspeed) {
|
||||
this.cpuSpeedInputValue = this.preFillContent.cpuspeed
|
||||
}
|
||||
if (this.preFillContent.memory) {
|
||||
this.memoryInputValue = this.preFillContent.memory
|
||||
}
|
||||
},
|
||||
handleSearch (value) {
|
||||
this.filter = value
|
||||
this.options.keyword = this.filter
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
updateComputeCpuNumber (value) {
|
||||
if (!this.validateInput('cpu', value)) {
|
||||
return
|
||||
}
|
||||
this.$emit('update-compute-cpunumber', this.cpunumberInputDecorator, value)
|
||||
},
|
||||
handleTableChange (pagination) {
|
||||
this.options.page = pagination.current
|
||||
this.options.pageSize = pagination.pageSize
|
||||
this.$emit('handle-search-filter', this.options)
|
||||
updateComputeCpuSpeed (value) {
|
||||
this.$emit('update-compute-cpuspeed', this.cpuspeedInputDecorator, value)
|
||||
},
|
||||
updateComputeMemory (value) {
|
||||
if (!this.validateInput('memory', value)) {
|
||||
return
|
||||
}
|
||||
this.$emit('update-compute-memory', this.memoryInputDecorator, value)
|
||||
},
|
||||
validateInput (input, value) {
|
||||
this.errors[input].status = ''
|
||||
this.errors[input].message = ''
|
||||
|
||||
if (value === null || value === undefined || value.length === 0) {
|
||||
this.errors[input].status = 'error'
|
||||
this.errors[input].message = this.$t('message.error.required.input')
|
||||
return false
|
||||
}
|
||||
|
||||
if (!this.isConstrained) {
|
||||
return true
|
||||
}
|
||||
|
||||
let min
|
||||
let max
|
||||
|
||||
switch (input) {
|
||||
case 'cpu':
|
||||
min = this.minCpu
|
||||
max = this.maxCpu
|
||||
break
|
||||
case 'memory':
|
||||
min = this.minMemory
|
||||
max = this.maxMemory
|
||||
break
|
||||
}
|
||||
|
||||
if (!this.checkValidRange(value, min, max)) {
|
||||
this.errors[input].status = 'error'
|
||||
this.errors[input].message = this.$t('message.error.invalid.range', { min: min, max: max })
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
checkValidRange (value, min, max) {
|
||||
if (value < min || value > max) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-table-wrapper {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -16,22 +16,26 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-form-item :label="this.$t('diskSize')">
|
||||
<a-row>
|
||||
<a-col :span="10">
|
||||
<a-form-item
|
||||
:label="this.$t('diskSize')"
|
||||
class="form-item">
|
||||
<a-row :gutter="12">
|
||||
<a-col :md="10" :lg="10">
|
||||
<a-slider
|
||||
:min="0"
|
||||
:max="1024"
|
||||
v-model="inputValue"
|
||||
@change="($event) => updateDickSize($event)"
|
||||
@change="($event) => updateDiskSize($event)"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<a-input-number
|
||||
v-model="inputValue"
|
||||
:formatter="value => `${value} GB`"
|
||||
:parser="value => value.replace(' GB', '')"
|
||||
/>
|
||||
<a-col :md="4" :lg="4">
|
||||
<span style="display: inline-flex">
|
||||
<a-input-number
|
||||
v-model="inputValue"
|
||||
@change="($event) => updateDiskSize($event)"
|
||||
/>
|
||||
<span style="padding-top: 6px">GB</span>
|
||||
</span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form-item {
|
||||
margin: 0 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -28,13 +28,13 @@
|
|||
<template slot="ipAddress" slot-scope="text, record">
|
||||
<a-input
|
||||
style="width: 150px;"
|
||||
:placeholder="$t('ipAddress')"
|
||||
:placeholder="$t('ipaddress')"
|
||||
@change="($event) => updateNetworkData('ipAddress', record.id, $event.target.value)" />
|
||||
</template>
|
||||
<template slot="macAddress" slot-scope="text, record">
|
||||
<a-input
|
||||
style="width: 150px;"
|
||||
:placeholder="$t('macAddress')"
|
||||
:placeholder="$t('macaddress')"
|
||||
@change="($event) => updateNetworkData('macAddress', record.id, $event.target.value)" />
|
||||
</template>
|
||||
</a-table>
|
||||
|
|
@ -51,6 +51,10 @@ export default {
|
|||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
preFillContent: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,13 @@
|
|||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="handleSearch" />
|
||||
<a-tooltip
|
||||
arrowPointAtCenter
|
||||
placement="bottomRight">
|
||||
<template slot="title">
|
||||
{{ $t('addNewNetworks') }}
|
||||
</template>
|
||||
</a-tooltip>
|
||||
<a-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
|
|
@ -70,6 +77,14 @@ export default {
|
|||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
zoneId: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
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)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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 () {
|
||||
|
|
|
|||
|
|
@ -17,26 +17,74 @@
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<a-input-search
|
||||
class="search-input"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="filterDataSource"/>
|
||||
<span class="filter-group">
|
||||
<a-input-search
|
||||
class="search-input"
|
||||
placeholder="Search"
|
||||
v-model="filter"
|
||||
@search="filterDataSource">
|
||||
<a-popover
|
||||
placement="bottomRight"
|
||||
slot="addonAfter"
|
||||
trigger="click"
|
||||
v-model="visibleFilter">
|
||||
<template slot="content">
|
||||
<a-form
|
||||
style="width: 170px"
|
||||
:form="form"
|
||||
layout="vertical"
|
||||
@submit="handleSubmit">
|
||||
<a-form-item :label="$t('filter')">
|
||||
<a-select
|
||||
allowClear
|
||||
v-decorator="['filter']">
|
||||
<a-select-option
|
||||
v-for="(opt) in filterOpts"
|
||||
:key="opt.id">{{ $t(opt.name) }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<div class="filter-group-button">
|
||||
<a-button
|
||||
class="filter-group-button-clear"
|
||||
type="default"
|
||||
size="small"
|
||||
icon="stop"
|
||||
@click="onClear">Clear</a-button>
|
||||
<a-button
|
||||
class="filter-group-button-search"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="search"
|
||||
@click="handleSubmit">Search</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</template>
|
||||
<a-button
|
||||
class="filter-group-button"
|
||||
icon="filter"
|
||||
size="small"/>
|
||||
</a-popover>
|
||||
</a-input-search>
|
||||
</span>
|
||||
<a-spin :spinning="loading">
|
||||
<a-tabs
|
||||
tabPosition="top"
|
||||
:animated="false"
|
||||
:defaultActiveKey="Object.keys(dataSource)[0]">
|
||||
:defaultActiveKey="Object.keys(dataSource)[0]"
|
||||
tabPosition="top"
|
||||
v-model="osType"
|
||||
@change="changeOsName">
|
||||
<a-tab-pane v-for="(osList, osName) in dataSource" :key="osName">
|
||||
<span slot="tab">
|
||||
<os-logo :os-name="osName"></os-logo>
|
||||
</span>
|
||||
<TemplateIsoRadioGroup
|
||||
:osType="osName"
|
||||
v-if="osType===osName"
|
||||
:osType="osType"
|
||||
:osList="dataSource[osName]"
|
||||
:input-decorator="inputDecorator"
|
||||
:selected="checkedValue"
|
||||
:itemCount="itemCount[osName]"
|
||||
:preFillContent="preFillContent"
|
||||
@handle-filter-tag="filterDataSource"
|
||||
@emit-update-template-iso="updateTemplateIso"
|
||||
></TemplateIsoRadioGroup>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue