mirror of https://github.com/apache/cloudstack.git
compute: work-in-progress VM deployment wizard (#7)
This implements a work-in-progress VM deployment wizard. Co-authored-by: Rohit Yadav <rohit@apache.org> Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
2e50c068c7
commit
ef189cea3f
|
|
@ -15839,7 +15839,8 @@
|
|||
"lodash.get": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.identity": {
|
||||
"version": "3.0.0",
|
||||
|
|
@ -15871,11 +15872,6 @@
|
|||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.pick": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
|
||||
"integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
|
||||
},
|
||||
"lodash.pickby": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz",
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@
|
|||
"core-js": "^3.6.1",
|
||||
"enquire.js": "^2.1.6",
|
||||
"js-cookie": "^2.2.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.pick": "^4.4.0",
|
||||
"lodash": "^4.17.15",
|
||||
"md5": "^2.2.1",
|
||||
"moment": "^2.24.0",
|
||||
"node-emoji": "^1.10.0",
|
||||
|
|
|
|||
|
|
@ -425,6 +425,16 @@
|
|||
<a-icon type="calendar" />{{ resource.created }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-detail-item" v-if="resource.affinitygroup && resource.affinitygroup.length > 0">
|
||||
<a-icon type="swap" />
|
||||
<span
|
||||
v-for="(group, index) in resource.affinitygroup"
|
||||
:key="group.id"
|
||||
>
|
||||
<router-link :to="{ path: '/affinitygroup/' + group.id }">{{ group.name }}</router-link>
|
||||
<span v-if="index + 1 < resource.affinitygroup.length">, </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="account-center-tags" v-if="$route.meta.related">
|
||||
|
|
|
|||
|
|
@ -1055,7 +1055,17 @@
|
|||
"instance": "Instance",
|
||||
"yourInstance": "Your instance",
|
||||
"newInstance": "New instance",
|
||||
"defaultNetwork": "Default network",
|
||||
"cpu": "CPU",
|
||||
"ram": "RAM",
|
||||
"minMaxIops": "Min IOPS / Max IOPS",
|
||||
"isSelf": "Self",
|
||||
"isShared": "Shared",
|
||||
"networks": "Networks",
|
||||
"BasicSetup": "Basic setup",
|
||||
"templateIso": "Template/ISO",
|
||||
"addAnotherNetwork": "Add another network",
|
||||
"addNewNetworks": "Add new networks",
|
||||
"existingNetworks": "Existing networks",
|
||||
"sshKeyPairs": "SSH keypairs",
|
||||
"wednesday": "Wednesday"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,88 +25,120 @@
|
|||
@submit="handleSubmit"
|
||||
layout="vertical"
|
||||
>
|
||||
<a-form-item :label="this.$t('name')">
|
||||
<a-input
|
||||
v-decorator="['name']"
|
||||
:placeholder="this.$t('vm.name.description')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="this.$t('zoneid')">
|
||||
<a-select
|
||||
v-decorator="['zoneid', {
|
||||
rules: [{ required: zoneId.required, message: 'Please select option' }]
|
||||
}]"
|
||||
:placeholder="this.$t('vm.zone.description')"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(opt, optIndex) in zoneId.opts"
|
||||
:key="optIndex"
|
||||
:value="opt.id"
|
||||
>
|
||||
{{ opt.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-collapse
|
||||
:accordion="true"
|
||||
defaultActiveKey="templates"
|
||||
:accordion="false"
|
||||
:bordered="false"
|
||||
defaultActiveKey="basic"
|
||||
>
|
||||
<a-collapse-panel :header="this.$t('Templates')" key="templates">
|
||||
<template-selection
|
||||
:templates="templateId.opts"
|
||||
></template-selection>
|
||||
<a-form-item :label="this.$t('diskSize')">
|
||||
<a-row>
|
||||
<a-col :span="10">
|
||||
<a-slider
|
||||
:min="0"
|
||||
:max="1024"
|
||||
v-decorator="['rootdisksize']"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<a-input-number
|
||||
v-decorator="['rootdisksize', {
|
||||
rules: [{ required: false, message: 'Please enter a number' }]
|
||||
}]"
|
||||
:placeholder="this.$t('vm.rootdisksize')"
|
||||
:formatter="value => `${value} GB`"
|
||||
:parser="value => value.replace(' GB', '')"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-collapse-panel :header="this.$t('BasicSetup')" key="basic">
|
||||
<a-form-item :label="this.$t('name')">
|
||||
<a-input
|
||||
v-decorator="['name']"
|
||||
:placeholder="this.$t('vm.name.description')"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="this.$t('zoneid')">
|
||||
<a-select
|
||||
v-decorator="['zoneid', {
|
||||
rules: [{ required: true, message: 'Please select option' }]
|
||||
}]"
|
||||
:placeholder="this.$t('vm.zone.description')"
|
||||
:options="zoneSelectOptions"
|
||||
@change="onSelectZoneId"
|
||||
></a-select>
|
||||
</a-form-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel :header="this.$t('ISOs')" key="isos">
|
||||
<!-- ToDo: Add iso selection -->
|
||||
|
||||
<a-collapse-panel :header="this.$t('templateIso')" key="templates-isos">
|
||||
<a-collapse
|
||||
:accordion="true"
|
||||
defaultActiveKey="templates"
|
||||
@change="onTemplatesIsosCollapseChange"
|
||||
>
|
||||
<a-collapse-panel :header="this.$t('Templates')" key="templates">
|
||||
<template-iso-selection
|
||||
input-decorator="templateid"
|
||||
:items="options.templates"
|
||||
></template-iso-selection>
|
||||
<disk-size-selection
|
||||
input-decorator="rootdisksize"
|
||||
></disk-size-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('ISOs')" key="isos">
|
||||
<template-iso-selection
|
||||
input-decorator="isoid"
|
||||
:items="options.isos"
|
||||
></template-iso-selection>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('serviceOfferingId')" key="compute">
|
||||
<compute-selection
|
||||
:compute-items="options.serviceOfferings"
|
||||
:value="serviceOffering ? serviceOffering.id : ''"
|
||||
@select-compute-item="($event) => updateComputeOffering($event)"
|
||||
></compute-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('diskOfferingId')" key="disk">
|
||||
<disk-offering-selection
|
||||
:items="options.diskOfferings"
|
||||
:value="diskOffering ? diskOffering.id : ''"
|
||||
@select-disk-offering-item="($event) => updateDiskOffering($event)"
|
||||
></disk-offering-selection>
|
||||
<disk-size-selection
|
||||
v-if="diskOffering && diskOffering.iscustomized"
|
||||
input-decorator="size"
|
||||
></disk-size-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('Affinity Groups')" key="affinity">
|
||||
<affinity-group-selection
|
||||
:items="options.affinityGroups"
|
||||
:value="affinityGroupIds"
|
||||
@select-affinity-group-item="($event) => updateAffinityGroups($event)"
|
||||
></affinity-group-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('networks')" key="networks">
|
||||
<a-collapse
|
||||
:accordion="false"
|
||||
>
|
||||
<a-collapse-panel
|
||||
:header="$t('existingNetworks')"
|
||||
>
|
||||
<network-selection
|
||||
:items="options.networks"
|
||||
:value="networkOfferingIds"
|
||||
@select-network-item="($event) => updateNetworks($event)"
|
||||
></network-selection>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel
|
||||
:header="$t('addNewNetworks')"
|
||||
>
|
||||
<network-creation></network-creation>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
|
||||
<network-configuration
|
||||
v-if="networks.length > 0"
|
||||
:items="networks"
|
||||
></network-configuration>
|
||||
</a-collapse-panel>
|
||||
|
||||
<a-collapse-panel :header="this.$t('sshKeyPairs')" key="sshKeyPairs">
|
||||
<ssh-key-pair-selection
|
||||
:items="options.sshKeyPairs"
|
||||
:value="sshKeyPair ? sshKeyPair.name : ''"
|
||||
@select-ssh-key-pair-item="($event) => updateSshKeyPairs($event)"
|
||||
></ssh-key-pair-selection>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
|
||||
<compute-selection
|
||||
:compute-items="serviceOfferingId.opts"
|
||||
:value="serviceOffering ? serviceOffering.id : ''"
|
||||
@select-compute-item="($event) => updateComputeOffering($event)"
|
||||
></compute-selection>
|
||||
|
||||
<a-form-item :label="this.$t('diskOfferingId')">
|
||||
<a-select
|
||||
v-decorator="['diskofferingid', {
|
||||
rules: [{ required: diskOfferingId.required, message: 'Please select option' }]
|
||||
}]"
|
||||
:placeholder="this.$t('vm.diskoffering.description')"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="(opt, optIndex) in diskOfferingId.opts"
|
||||
:key="optIndex"
|
||||
:value="opt.id"
|
||||
>
|
||||
{{ opt.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<div class="card-footer">
|
||||
<!-- ToDo extract as component -->
|
||||
<a-button @click="() => this.$router.back()">{{ this.$t('cancel') }}</a-button>
|
||||
|
|
@ -115,17 +147,22 @@
|
|||
</a-form>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="24" :lg="7">
|
||||
<info-card :resource="vm" :title="this.$t('yourInstance')" >
|
||||
<div slot="details" v-if="vm.diskofferingid || instanceConfig.rootdisksize">
|
||||
<a-icon type="hdd"></a-icon>
|
||||
<span style="margin-left: 10px">
|
||||
<span v-if="instanceConfig.rootdisksize">{{ instanceConfig.rootdisksize }} GB (Root)</span>
|
||||
<span v-if="instanceConfig.rootdisksize && instanceConfig.diskofferingid"> | </span>
|
||||
<span v-if="instanceConfig.diskofferingid">{{ diskOffering.disksize }} GB (Data)</span>
|
||||
</span>
|
||||
</div>
|
||||
</info-card>
|
||||
<a-col :md="24" :lg="7" v-if="!isMobile()">
|
||||
<a-affix :offsetTop="75">
|
||||
<info-card :resource="vm" :title="this.$t('yourInstance')">
|
||||
<!-- ToDo: Refactor this, maybe move everything to the info-card component -->
|
||||
<div slot="details" v-if="diskSize" style="margin-bottom: 12px;">
|
||||
<a-icon type="hdd"></a-icon>
|
||||
<span style="margin-left: 10px">{{ diskSize }}</span>
|
||||
</div>
|
||||
<div slot="details" v-if="networks">
|
||||
<div v-for="network in networks" :key="network.id" style="margin-bottom: 12px;">
|
||||
<a-icon type="api"></a-icon>
|
||||
<span style="margin-left: 10px">{{ network.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</info-card>
|
||||
</a-affix>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
|
@ -134,69 +171,144 @@
|
|||
<script>
|
||||
import Vue from 'vue'
|
||||
import { api } from '@/api'
|
||||
import store from '@/store'
|
||||
import _ from 'lodash'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin.js'
|
||||
import store from '@/store'
|
||||
|
||||
import InfoCard from '@/components/view/InfoCard'
|
||||
import ComputeSelection from './wizard/ComputeSelection'
|
||||
import TemplateSelection from './wizard/TemplateSelection'
|
||||
import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
|
||||
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
|
||||
import TemplateIsoSelection from '@views/compute/wizard/TemplateIsoSelection'
|
||||
import AffinityGroupSelection from '@views/compute/wizard/AffinityGroupSelection'
|
||||
import NetworkSelection from '@views/compute/wizard/NetworkSelection'
|
||||
import NetworkConfiguration from '@views/compute/wizard/NetworkConfiguration'
|
||||
import NetworkCreation from '@views/compute/wizard/NetworksCreation'
|
||||
import SshKeyPairSelection from '@views/compute/wizard/SshKeyPairSelection'
|
||||
|
||||
export default {
|
||||
name: 'Wizard',
|
||||
components: {
|
||||
SshKeyPairSelection,
|
||||
NetworkCreation,
|
||||
NetworkConfiguration,
|
||||
NetworkSelection,
|
||||
AffinityGroupSelection,
|
||||
TemplateIsoSelection,
|
||||
DiskSizeSelection,
|
||||
DiskOfferingSelection,
|
||||
InfoCard,
|
||||
ComputeSelection,
|
||||
TemplateSelection
|
||||
ComputeSelection
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
mixins: [mixin, mixinDevice],
|
||||
data () {
|
||||
return {
|
||||
vm: {},
|
||||
params: [],
|
||||
visibleParams: [
|
||||
'name',
|
||||
'templateid',
|
||||
'serviceofferingid',
|
||||
'diskofferingid',
|
||||
'zoneid',
|
||||
'rootdisksize'
|
||||
],
|
||||
options: {
|
||||
templates: [],
|
||||
isos: [],
|
||||
serviceOfferings: [],
|
||||
diskOfferings: [],
|
||||
zones: [],
|
||||
affinityGroups: [],
|
||||
networks: [],
|
||||
sshKeyPairs: []
|
||||
},
|
||||
instanceConfig: [],
|
||||
template: {},
|
||||
iso: {},
|
||||
serviceOffering: {},
|
||||
diskOffering: {},
|
||||
zone: {}
|
||||
affinityGroups: [],
|
||||
networks: [],
|
||||
zone: {},
|
||||
sshKeyPair: {},
|
||||
isoFilter: [
|
||||
'executable',
|
||||
'selfexecutable',
|
||||
'sharedexecutable'
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredParams () {
|
||||
return this.visibleParams.map((fieldName) => {
|
||||
return this.params.find((param) => fieldName === param.name)
|
||||
diskSize () {
|
||||
const rootDiskSize = _.get(this.instanceConfig, 'rootdisksize', 0)
|
||||
const customDiskSize = _.get(this.instanceConfig, 'size', 0)
|
||||
const diskOfferingDiskSize = _.get(this.diskOffering, 'disksize', 0)
|
||||
const dataDiskSize = diskOfferingDiskSize > 0 ? diskOfferingDiskSize : customDiskSize
|
||||
const size = []
|
||||
if (rootDiskSize > 0) {
|
||||
size.push(`${rootDiskSize} GB (Root)`)
|
||||
}
|
||||
if (dataDiskSize > 0) {
|
||||
size.push(`${dataDiskSize} GB (Data)`)
|
||||
}
|
||||
return size.join(' | ')
|
||||
},
|
||||
affinityGroupIds () {
|
||||
return _.map(this.affinityGroups, 'id')
|
||||
},
|
||||
params () {
|
||||
return {
|
||||
templates: {
|
||||
list: 'listTemplates',
|
||||
options: {
|
||||
templatefilter: 'executable',
|
||||
zoneid: _.get(this.zone, 'id')
|
||||
}
|
||||
},
|
||||
serviceOfferings: {
|
||||
list: 'listServiceOfferings'
|
||||
},
|
||||
diskOfferings: {
|
||||
list: 'listDiskOfferings'
|
||||
},
|
||||
zones: {
|
||||
list: 'listZones'
|
||||
},
|
||||
affinityGroups: {
|
||||
list: 'listAffinityGroups'
|
||||
},
|
||||
sshKeyPairs: {
|
||||
list: 'listSSHKeyPairs'
|
||||
},
|
||||
networks: {
|
||||
list: 'listNetworks',
|
||||
options: {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
canusefordeploy: true,
|
||||
projectid: store.getters.project.id
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
networkOfferingIds () {
|
||||
return _.map(this.networks, 'id')
|
||||
},
|
||||
zoneSelectOptions () {
|
||||
return this.options.zones.map((zone) => {
|
||||
return {
|
||||
label: zone.name,
|
||||
value: zone.id
|
||||
}
|
||||
})
|
||||
},
|
||||
templateId () {
|
||||
return this.getParam('templateid')
|
||||
},
|
||||
serviceOfferingId () {
|
||||
return this.getParam('serviceofferingid')
|
||||
},
|
||||
diskOfferingId () {
|
||||
return this.getParam('diskofferingid')
|
||||
},
|
||||
zoneId () {
|
||||
return this.getParam('zoneid')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
instanceConfig (instanceConfig) {
|
||||
this.template = this.templateId.opts.find((option) => option.id === instanceConfig.templateid)
|
||||
this.serviceOffering = this.serviceOfferingId.opts.find((option) => option.id === instanceConfig.computeofferingid)
|
||||
this.diskOffering = this.diskOfferingId.opts.find((option) => option.id === instanceConfig.diskofferingid)
|
||||
this.zone = this.zoneId.opts.find((option) => option.id === instanceConfig.zoneid)
|
||||
this.template = _.find(this.options.templates, (option) => option.id === instanceConfig.templateid)
|
||||
this.iso = _.find(this.options.isos, (option) => option.id === instanceConfig.isoid)
|
||||
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)
|
||||
this.affinityGroups = _.filter(this.options.affinityGroups, (option) => _.includes(instanceConfig.affinitygroupids, option.id))
|
||||
this.networks = _.filter(this.options.networks, (option) => _.includes(instanceConfig.networkids, option.id))
|
||||
this.sshKeyPair = _.find(this.options.sshKeyPairs, (option) => option.name === instanceConfig.keypair)
|
||||
|
||||
if (this.zone) {
|
||||
this.vm.zoneid = this.zone.id
|
||||
|
|
@ -210,6 +322,13 @@ export default {
|
|||
this.vm.ostypename = this.template.ostypename
|
||||
}
|
||||
|
||||
if (this.iso) {
|
||||
this.vm.templateid = this.iso.id
|
||||
this.vm.templatename = this.iso.displaytext
|
||||
this.vm.ostypeid = this.iso.ostypeid
|
||||
this.vm.ostypename = this.iso.ostypename
|
||||
}
|
||||
|
||||
if (this.serviceOffering) {
|
||||
this.vm.serviceofferingid = this.serviceOffering.id
|
||||
this.vm.serviceofferingname = this.serviceOffering.displaytext
|
||||
|
|
@ -223,22 +342,36 @@ export default {
|
|||
this.vm.diskofferingname = this.diskOffering.displaytext
|
||||
this.vm.diskofferingsize = this.diskOffering.disksize
|
||||
}
|
||||
|
||||
if (this.affinityGroups) {
|
||||
this.vm.affinitygroup = this.affinityGroups
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this, {
|
||||
onValuesChange: (props, fields) => {
|
||||
if (fields.isoid) {
|
||||
this.form.setFieldsValue({
|
||||
templateid: null,
|
||||
rootdisksize: 0
|
||||
})
|
||||
} else if (fields.templateid) {
|
||||
this.form.setFieldsValue({ isoid: null })
|
||||
}
|
||||
this.instanceConfig = { ...this.form.getFieldsValue(), ...fields }
|
||||
this.vm = this.instanceConfig
|
||||
}
|
||||
})
|
||||
this.form.getFieldDecorator('computeofferingid', { initialValue: [], preserve: true })
|
||||
this.form.getFieldDecorator('diskofferingid', { initialValue: [], preserve: true })
|
||||
this.form.getFieldDecorator('affinitygroupids', { initialValue: [], preserve: true })
|
||||
this.form.getFieldDecorator('isoid', { initialValue: [], preserve: true })
|
||||
this.form.getFieldDecorator('networkids', { initialValue: [], preserve: true })
|
||||
this.form.getFieldDecorator('keypair', { initialValue: [], preserve: true })
|
||||
},
|
||||
created () {
|
||||
this.params = store.getters.apis[this.$route.name].params
|
||||
this.filteredParams.forEach((param) => {
|
||||
this.fetchOptions(param)
|
||||
})
|
||||
_.each(this.params, this.fetchOptions)
|
||||
Vue.nextTick().then(() => {
|
||||
this.instanceConfig = this.form.getFieldsValue() // ToDo: maybe initialize with some other defaults
|
||||
})
|
||||
|
|
@ -249,8 +382,25 @@ export default {
|
|||
computeofferingid: id
|
||||
})
|
||||
},
|
||||
getParam (paramName) {
|
||||
return this.params.find((param) => param.name === paramName)
|
||||
updateDiskOffering (id) {
|
||||
this.form.setFieldsValue({
|
||||
diskofferingid: id
|
||||
})
|
||||
},
|
||||
updateAffinityGroups (ids) {
|
||||
this.form.setFieldsValue({
|
||||
affinitygroupids: ids
|
||||
})
|
||||
},
|
||||
updateNetworks (ids) {
|
||||
this.form.setFieldsValue({
|
||||
networkids: ids
|
||||
})
|
||||
},
|
||||
updateSshKeyPairs (name) {
|
||||
this.form.setFieldsValue({
|
||||
keypair: name
|
||||
})
|
||||
},
|
||||
getText (option) {
|
||||
return _.get(option, 'displaytext', _.get(option, 'name'))
|
||||
|
|
@ -258,30 +408,12 @@ export default {
|
|||
handleSubmit () {
|
||||
console.log('wizard submit')
|
||||
},
|
||||
fetchOptions (param) {
|
||||
const paramName = param.name
|
||||
const possibleName = `list${paramName.replace('id', '').toLowerCase()}s`
|
||||
let possibleApi
|
||||
if (paramName === 'id') {
|
||||
possibleApi = this.apiName
|
||||
} else {
|
||||
possibleApi = _.filter(Object.keys(store.getters.apis), (api) => {
|
||||
return api.toLowerCase().startsWith(possibleName)
|
||||
})[0]
|
||||
}
|
||||
|
||||
if (!possibleApi) {
|
||||
return
|
||||
}
|
||||
|
||||
fetchOptions (param, name) {
|
||||
param.loading = true
|
||||
param.opts = []
|
||||
const params = {}
|
||||
params.listall = true
|
||||
if (possibleApi === 'listTemplates') {
|
||||
params.templatefilter = 'executable'
|
||||
}
|
||||
api(possibleApi, params).then((response) => {
|
||||
const options = param.options || {}
|
||||
options.listall = true
|
||||
api(param.list, options).then((response) => {
|
||||
param.loading = false
|
||||
_.map(response, (responseItem, responseKey) => {
|
||||
if (!responseKey.includes('response')) {
|
||||
|
|
@ -292,6 +424,7 @@ export default {
|
|||
return
|
||||
}
|
||||
param.opts = response
|
||||
this.options[name] = response
|
||||
this.$forceUpdate()
|
||||
})
|
||||
})
|
||||
|
|
@ -299,6 +432,40 @@ export default {
|
|||
console.log(error.stack)
|
||||
param.loading = false
|
||||
})
|
||||
},
|
||||
fetchIsos (isoFilter) {
|
||||
api('listIsos', {
|
||||
zoneid: _.get(this.zone, 'id'),
|
||||
isofilter: isoFilter,
|
||||
bootable: true
|
||||
}).then((response) => {
|
||||
const concatedIsos = _.concat(this.options.isos, _.get(response, 'listisosresponse.iso', []))
|
||||
this.options.isos = _.uniqWith(concatedIsos, _.isEqual)
|
||||
this.$forceUpdate()
|
||||
}).catch((reason) => {
|
||||
// ToDo: Handle errors
|
||||
console.log(reason)
|
||||
})
|
||||
},
|
||||
fetchAllIsos () {
|
||||
this.options.isos = []
|
||||
this.isoFilter.forEach((filter) => {
|
||||
this.fetchIsos(filter)
|
||||
})
|
||||
},
|
||||
onTemplatesIsosCollapseChange (key) {
|
||||
if (key === 'isos' && this.options.isos.length === 0) {
|
||||
this.fetchAllIsos()
|
||||
}
|
||||
},
|
||||
onSelectZoneId () {
|
||||
this.$nextTick(() => {
|
||||
if (this.options.isos.length !== 0) {
|
||||
this.fetchAllIsos()
|
||||
}
|
||||
this.fetchOptions(this.params.templates, 'templates')
|
||||
this.fetchOptions(this.params.networks, 'networks')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -307,6 +474,7 @@ export default {
|
|||
<style lang="less" scoped>
|
||||
.card-footer {
|
||||
text-align: right;
|
||||
margin-top: 2rem;
|
||||
|
||||
button + button {
|
||||
margin-left: 8px;
|
||||
|
|
@ -321,3 +489,20 @@ export default {
|
|||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
@import url('../../style/index');
|
||||
|
||||
.ant-table-selection-column {
|
||||
// Fix for the table header if the row selection use radio buttons instead of checkboxes
|
||||
> div:empty {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-collapse-borderless > .ant-collapse-item {
|
||||
border: 1px solid @border-color-split;
|
||||
border-radius: @border-radius-base !important;
|
||||
margin: 0 0 1.2rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
// 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>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="items"
|
||||
:rowKey="record => record.id"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
|
||||
export default {
|
||||
name: 'AffinityGroupSelection',
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('Affinity Groups'),
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'description',
|
||||
title: this.$t('description'),
|
||||
width: '60%'
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onChange: (rows) => {
|
||||
this.$emit('select-affinity-group-item', rows)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (newValue, oldValue) {
|
||||
if (newValue && !_.isEqual(newValue, oldValue)) {
|
||||
this.selectedRowKeys = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-table-wrapper {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -19,13 +19,12 @@
|
|||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="false"
|
||||
:scroll="{x: 0, y: 320}"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
>
|
||||
<span slot="cpuTitle"><a-icon type="appstore" /> {{ $t('cpu') }}</span>
|
||||
<span slot="ramTitle"><a-icon type="bulb" /> {{ $t('ram') }}</span>
|
||||
<span slot="ramTitle"><a-icon type="bulb" /> {{ $t('memory') }}</span>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
|
|
@ -47,6 +46,7 @@ export default {
|
|||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('serviceOfferingId'),
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
|
|
@ -99,12 +99,3 @@ export default {
|
|||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.ant-table-selection-column {
|
||||
// Fix for the table header if the row selection use radio buttons instead of checkboxes
|
||||
> div:empty {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
// 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>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
>
|
||||
<span slot="diskSizeTitle"><a-icon type="hdd" /> {{ $t('disksize') }}</span>
|
||||
<span slot="iopsTitle"><a-icon type="rocket" /> {{ $t('minMaxIops') }}</span>
|
||||
<template slot="diskSize" slot-scope="text, record">
|
||||
<div v-if="record.isCustomized">{{ $t('isCustomized') }}</div>
|
||||
<div v-else>{{ record.diskSize }} GB</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DiskOfferingSelection',
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('diskOffering'),
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'diskSize',
|
||||
slots: { title: 'diskSizeTitle' },
|
||||
width: '30%',
|
||||
scopedSlots: { customRender: 'diskSize' }
|
||||
},
|
||||
{
|
||||
dataIndex: 'iops',
|
||||
slots: { title: 'iopsTitle' },
|
||||
width: '30%'
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableSource () {
|
||||
return this.items.map((item) => {
|
||||
return {
|
||||
key: item.id,
|
||||
name: item.name,
|
||||
diskSize: item.disksize,
|
||||
iops: `${item.miniops} – ${item.maxiops}`,
|
||||
isCustomized: item.iscustomized
|
||||
}
|
||||
})
|
||||
},
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onSelect: (row) => {
|
||||
this.$emit('select-disk-offering-item', row.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (newValue, oldValue) {
|
||||
if (newValue && newValue !== oldValue) {
|
||||
this.selectedRowKeys = [newValue]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-table-wrapper {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// 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>
|
||||
<a-form-item :label="this.$t('diskSize')">
|
||||
<a-row>
|
||||
<a-col :span="10">
|
||||
<a-slider
|
||||
:min="0"
|
||||
:max="1024"
|
||||
v-decorator="[inputDecorator]"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :span="4">
|
||||
<a-input-number
|
||||
v-decorator="[inputDecorator, {
|
||||
rules: [{ required: false, message: 'Please enter a number' }]
|
||||
}]"
|
||||
:formatter="value => `${value} GB`"
|
||||
:parser="value => value.replace(' GB', '')"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DiskSizeSelection',
|
||||
props: {
|
||||
inputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// 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>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="items"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
:rowKey="record => record.id"
|
||||
size="middle"
|
||||
>
|
||||
<template v-slot:ipAddress="text">
|
||||
<a-input
|
||||
:value="text"
|
||||
></a-input>
|
||||
</template>
|
||||
<template v-slot:macAddress="text">
|
||||
<a-input
|
||||
:value="text"
|
||||
></a-input>
|
||||
</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NetworkConfiguration',
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('defaultNetwork'),
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'ip',
|
||||
title: this.$t('ip'),
|
||||
width: '30%',
|
||||
scopedSlots: { customRender: 'ipAddress' }
|
||||
},
|
||||
{
|
||||
dataIndex: 'mac',
|
||||
title: this.$t('macaddress'),
|
||||
width: '30%',
|
||||
scopedSlots: { customRender: 'macAddress' }
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onSelect: (row) => {
|
||||
this.$emit('select-default-network-item', row.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (newValue, oldValue) {
|
||||
if (newValue && newValue !== oldValue) {
|
||||
this.selectedRowKeys = [newValue]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-table-wrapper {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
// 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>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="networkItems"
|
||||
:rowKey="record => record.id"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
>
|
||||
<a-list
|
||||
slot="expandedRowRender"
|
||||
slot-scope="record"
|
||||
:key="record.id"
|
||||
:dataSource="getDetails(record)"
|
||||
size="small"
|
||||
>
|
||||
<a-list-item slot="renderItem" slot-scope="item" :key="item.id">
|
||||
<a-list-item-meta
|
||||
:description="item.description"
|
||||
>
|
||||
<template v-slot:title>{{ item.title }}</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import { api } from '@/api'
|
||||
import store from '@/store'
|
||||
|
||||
export default {
|
||||
name: 'NetworkSelection',
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
selectedRowKeys: [],
|
||||
vpcs: [],
|
||||
filteredInfo: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
columns () {
|
||||
let vpcFilter = []
|
||||
if (this.vpcs) {
|
||||
vpcFilter = this.vpcs.map((vpc) => {
|
||||
return {
|
||||
text: vpc.displaytext,
|
||||
value: vpc.id
|
||||
}
|
||||
})
|
||||
}
|
||||
return [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('networks'),
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'type',
|
||||
title: this.$t('guestIpType'),
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'vpcName',
|
||||
title: this.$t('VPC'),
|
||||
width: '30%',
|
||||
filters: vpcFilter,
|
||||
filteredValue: _.get(this.filteredInfo, 'id'),
|
||||
onFilter: (value, record) => {
|
||||
return record.vpcid === value
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'checkbox',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onChange: (rows) => {
|
||||
this.$emit('select-network-item', rows)
|
||||
}
|
||||
}
|
||||
},
|
||||
networkItems () {
|
||||
return this.items.map((network) => {
|
||||
const vpc = _.find(this.vpcs, { id: network.vpcid })
|
||||
return {
|
||||
...network,
|
||||
...{
|
||||
vpcName: _.get(vpc, 'displaytext')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (newValue, oldValue) {
|
||||
if (newValue && !_.isEqual(newValue, oldValue)) {
|
||||
this.selectedRowKeys = newValue
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
api('listVPCs', {
|
||||
projectid: store.getters.project.id
|
||||
}).then((response) => {
|
||||
this.vpcs = _.get(response, 'listvpcsresponse.vpc')
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getDetails (network) {
|
||||
return [
|
||||
{
|
||||
title: this.$t('description'),
|
||||
description: network.displaytext
|
||||
},
|
||||
{
|
||||
title: this.$t('networkOfferingId'),
|
||||
description: network.networkofferingdisplaytext
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-table-wrapper {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
// 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-table
|
||||
v-if="networkItems.length > 0"
|
||||
:columns="columns"
|
||||
:dataSource="networkItems"
|
||||
:pagination="false"
|
||||
>
|
||||
<template v-slot:name="text">
|
||||
<a-input
|
||||
:value="text"
|
||||
></a-input>
|
||||
</template>
|
||||
<template v-slot:operation>
|
||||
<a-popconfirm
|
||||
v-if="networkItems.length"
|
||||
title="Sure to delete?"
|
||||
@confirm="removeItem()"
|
||||
>
|
||||
<a-button type="link">Delete</a-button>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
<template v-slot:networkOffering>
|
||||
<a-select
|
||||
:placeholder="$t('networkOfferingId')"
|
||||
:options="networkOfferingOptions"
|
||||
></a-select>
|
||||
</template>
|
||||
<template v-slot:vpc>
|
||||
<a-select
|
||||
:placeholder="$t('vpc')"
|
||||
:options="vpcOptions"
|
||||
></a-select>
|
||||
</template>
|
||||
</a-table>
|
||||
|
||||
<div style="text-align: right; margin-top: 1rem;">
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="addNewItem"
|
||||
>{{ $t('addAnotherNetwork') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import store from '@/store'
|
||||
import _ from 'lodash'
|
||||
|
||||
/*
|
||||
* ToDo: Implement real functionality
|
||||
*/
|
||||
export default {
|
||||
name: 'NetworkCreation',
|
||||
data () {
|
||||
return {
|
||||
networkItems: [{}],
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('networks'),
|
||||
scopedSlots: { customRender: 'name' },
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'offering',
|
||||
title: this.$t('networkOfferingId'),
|
||||
scopedSlots: { customRender: 'networkOffering' },
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'vpcName',
|
||||
title: this.$t('VPC'),
|
||||
scopedSlots: { customRender: 'vpc' },
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'action',
|
||||
scopedSlots: { customRender: 'operation' },
|
||||
width: '10%'
|
||||
}
|
||||
],
|
||||
networkOfferings: [],
|
||||
vpcs: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
networkOfferingOptions () {
|
||||
return this.networkOfferings.map((offering) => {
|
||||
return {
|
||||
label: offering.name,
|
||||
value: offering.id
|
||||
}
|
||||
})
|
||||
},
|
||||
vpcOptions () {
|
||||
return this.vpcs.map((vpc) => {
|
||||
return {
|
||||
label: vpc.name,
|
||||
value: vpc.id
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created () {
|
||||
api('listNetworkOfferings', {
|
||||
// ToDo: Add the zoneId
|
||||
}).then((response) => {
|
||||
this.networkOfferings = _.get(response, 'listnetworkofferingsresponse.networkoffering')
|
||||
})
|
||||
// ToDo: Remove this redundant api call – see the NetworkSelection component
|
||||
api('listVPCs', {
|
||||
projectid: store.getters.project.id
|
||||
}).then((response) => {
|
||||
this.vpcs = _.get(response, 'listvpcsresponse.vpc')
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
addNewItem () {
|
||||
this.networkItems.push({})
|
||||
},
|
||||
removeItem () {
|
||||
this.networkItems.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// 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>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:dataSource="tableSource"
|
||||
:pagination="{showSizeChanger: true}"
|
||||
:rowSelection="rowSelection"
|
||||
size="middle"
|
||||
>
|
||||
<template v-slot:account><a-icon type="user" /> {{ $t('account') }}</template>
|
||||
<template v-slot:domain><a-icon type="block" /> {{ $t('domain') }}</template>
|
||||
</a-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SshKeyPairSelection',
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'name',
|
||||
title: this.$t('sshKeyPairs'),
|
||||
width: '40%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'account',
|
||||
slots: { title: 'account' },
|
||||
width: '30%'
|
||||
},
|
||||
{
|
||||
dataIndex: 'domain',
|
||||
slots: { title: 'domain' },
|
||||
width: '30%'
|
||||
}
|
||||
],
|
||||
selectedRowKeys: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tableSource () {
|
||||
return this.items.map((item) => {
|
||||
return {
|
||||
key: item.name,
|
||||
name: item.name,
|
||||
account: item.account,
|
||||
domain: item.domain
|
||||
}
|
||||
})
|
||||
},
|
||||
rowSelection () {
|
||||
return {
|
||||
type: 'radio',
|
||||
selectedRowKeys: this.selectedRowKeys,
|
||||
onSelect: (row) => {
|
||||
this.$emit('select-ssh-key-pair-item', row.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (newValue, oldValue) {
|
||||
if (newValue && newValue !== oldValue) {
|
||||
this.selectedRowKeys = [newValue]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ant-table-wrapper {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 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>
|
||||
<a-form-item>
|
||||
<a-radio-group
|
||||
v-for="(os, osIndex) in osList"
|
||||
:key="osIndex"
|
||||
class="radio-group"
|
||||
v-decorator="[inputDecorator, {
|
||||
rules: [{ required: true, message: 'Please select option' }]
|
||||
}]"
|
||||
>
|
||||
<a-radio
|
||||
class="radio-group__radio"
|
||||
:value="os.id"
|
||||
>
|
||||
{{ os.displaytext }}
|
||||
<a-tag
|
||||
:visible="os.ispublic && !os.isfeatured"
|
||||
color="blue"
|
||||
>{{ $t('isPublic') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="os.isfeatured"
|
||||
color="green"
|
||||
>{{ $t('isFeatured') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="isSelf(os)"
|
||||
color="orange"
|
||||
>{{ $t('isSelf') }}</a-tag>
|
||||
<a-tag
|
||||
:visible="isShared(os)"
|
||||
color="cyan"
|
||||
>{{ $t('isShared') }}</a-tag>
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from '@/store'
|
||||
|
||||
export default {
|
||||
name: 'TemplateIsoRadioGroup',
|
||||
props: {
|
||||
osList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
inputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isShared (item) {
|
||||
return !item.ispublic && (item.account !== store.getters.userInfo.account)
|
||||
},
|
||||
isSelf (item) {
|
||||
return !item.ispublic && (item.account === store.getters.userInfo.account)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.radio-group {
|
||||
display: block;
|
||||
|
||||
&__radio {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tag {
|
||||
margin-left: 0.4rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// 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>
|
||||
<a-tabs :defaultActiveKey="Object.keys(osTypes)[0]" v-if="view === TAB_VIEW">
|
||||
<a-button icon="search" slot="tabBarExtraContent" @click="() => toggleView(FILTER_VIEW)"/>
|
||||
<a-tab-pane v-for="(osList, osName) in osTypes" :key="osName">
|
||||
<span slot="tab">
|
||||
<os-logo :os-name="osName"></os-logo>
|
||||
</span>
|
||||
<TemplateIsoRadioGroup
|
||||
:osList="osList"
|
||||
:input-decorator="inputDecorator"
|
||||
></TemplateIsoRadioGroup>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<div v-else>
|
||||
<a-input class="search-input" v-model="filter">
|
||||
<a-icon slot="prefix" type="search"/>
|
||||
<a-icon slot="addonAfter" type="close" @click="toggleView(TAB_VIEW)"/>
|
||||
</a-input>
|
||||
<TemplateIsoRadioGroup
|
||||
:osList="filteredItems"
|
||||
:input-decorator="inputDecorator"
|
||||
></TemplateIsoRadioGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OsLogo from '@/components/widgets/OsLogo'
|
||||
import { getNormalizedOsName } from '@/utils/icons'
|
||||
import _ from 'lodash'
|
||||
import TemplateIsoRadioGroup from '@views/compute/wizard/TemplateIsoRadioGroup'
|
||||
|
||||
export const TAB_VIEW = 1
|
||||
export const FILTER_VIEW = 2
|
||||
|
||||
export default {
|
||||
name: 'TemplateIsoSelection',
|
||||
components: { TemplateIsoRadioGroup, OsLogo },
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
inputDecorator: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
TAB_VIEW: TAB_VIEW,
|
||||
FILTER_VIEW: FILTER_VIEW,
|
||||
visible: false,
|
||||
filter: '',
|
||||
filteredItems: this.items,
|
||||
view: TAB_VIEW
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
osTypes () {
|
||||
let mappedItems = {}
|
||||
this.items.forEach((os) => {
|
||||
const osName = getNormalizedOsName(os.ostypename)
|
||||
if (Array.isArray(mappedItems[osName])) {
|
||||
mappedItems[osName].push(os)
|
||||
} else {
|
||||
mappedItems[osName] = [os]
|
||||
}
|
||||
})
|
||||
mappedItems = _.mapValues(mappedItems, (list) => {
|
||||
let featuredItems = list.filter((item) => item.isfeatured)
|
||||
let nonFeaturedItems = list.filter((item) => !item.isfeatured)
|
||||
featuredItems = _.sortBy(featuredItems, (item) => item.displaytext.toLowerCase())
|
||||
nonFeaturedItems = _.sortBy(nonFeaturedItems, (item) => item.displaytext.toLowerCase())
|
||||
return featuredItems.concat(nonFeaturedItems) // pin featured isos/templates at the top
|
||||
})
|
||||
return mappedItems
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
items (items) {
|
||||
this.filteredItems = items
|
||||
},
|
||||
filter (filterString) {
|
||||
if (filterString !== '') {
|
||||
this.filteredItems = this.filteredItems.filter((item) => item.displaytext.toLowerCase().includes(filterString))
|
||||
} else {
|
||||
this.filteredItems = this.items
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleView (view) {
|
||||
this.view = view
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.search-input {
|
||||
margin: 0.5rem 0 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
// 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>
|
||||
<a-tabs :defaultActiveKey="Object.keys(osTypes)[0]">
|
||||
<a-tab-pane v-for="(osList, osName) in osTypes" :key="osName">
|
||||
<span slot="tab">
|
||||
<os-logo :os-name="osName"></os-logo>
|
||||
</span>
|
||||
<a-form-item>
|
||||
<a-radio-group
|
||||
v-for="(os, osIndex) in osList"
|
||||
:key="osIndex"
|
||||
class="radio-group"
|
||||
v-decorator="['templateid', {
|
||||
rules: [{ required: true, message: 'Please select option' }]
|
||||
}]"
|
||||
>
|
||||
<a-radio
|
||||
class="radio-group__radio"
|
||||
:value="os.id"
|
||||
>{{ os.displaytext }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import OsLogo from '@/components/widgets/OsLogo'
|
||||
import { getNormalizedOsName } from '@/utils/icons'
|
||||
|
||||
export default {
|
||||
name: 'TemplateSelection',
|
||||
components: { OsLogo },
|
||||
props: {
|
||||
templates: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
osTypes () {
|
||||
const mappedTemplates = {}
|
||||
this.templates.forEach((os) => {
|
||||
const osName = getNormalizedOsName(os.ostypename)
|
||||
if (Array.isArray(mappedTemplates[osName])) {
|
||||
mappedTemplates[osName].push(os)
|
||||
} else {
|
||||
mappedTemplates[osName] = [os]
|
||||
}
|
||||
})
|
||||
return mappedTemplates
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__radio {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue