mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
compute: VM deployment wizard
This adds a work in progress VM deployment wizard page. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
bb9ab291df
commit
cb9c85706f
@ -16,7 +16,7 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-card :bordered="true">
|
<a-card :bordered="true" :title="title">
|
||||||
<a-skeleton active v-if="loading" />
|
<a-skeleton active v-if="loading" />
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="resource-details">
|
<div class="resource-details">
|
||||||
@ -30,7 +30,7 @@
|
|||||||
<slot name="name">
|
<slot name="name">
|
||||||
<h4>
|
<h4>
|
||||||
{{ resource.displayname || resource.name }}
|
{{ resource.displayname || resource.name }}
|
||||||
<console :resource="resource" size="default" />
|
<console :resource="resource" size="default" v-if="resource.id" />
|
||||||
</h4>
|
</h4>
|
||||||
<a-tag v-if="resource.instancename">
|
<a-tag v-if="resource.instancename">
|
||||||
{{ resource.instancename }}
|
{{ resource.instancename }}
|
||||||
@ -73,11 +73,6 @@
|
|||||||
<span style="margin-left: 8px">{{ resource.ostypename }}</span>
|
<span style="margin-left: 8px">{{ resource.ostypename }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="resource-detail-item">
|
|
||||||
<slot name="details">
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="resource-detail-item" v-if="resource.keypair">
|
<div class="resource-detail-item" v-if="resource.keypair">
|
||||||
<a-icon type="key" />
|
<a-icon type="key" />
|
||||||
<router-link :to="{ path: '/ssh/' + resource.keypair }">{{ resource.keypair }}</router-link>
|
<router-link :to="{ path: '/ssh/' + resource.keypair }">{{ resource.keypair }}</router-link>
|
||||||
@ -170,6 +165,12 @@
|
|||||||
<span v-if="resource.nic && resource.nic.length > 0">{{ resource.nic.filter(e => { return e.ipaddress }).map(e => { return e.ipaddress }).join(', ') }}</span>
|
<span v-if="resource.nic && resource.nic.length > 0">{{ resource.nic.filter(e => { return e.ipaddress }).map(e => { return e.ipaddress }).join(', ') }}</span>
|
||||||
<span v-else>{{ resource.ipaddress }}</span>
|
<span v-else>{{ resource.ipaddress }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="resource-detail-item">
|
||||||
|
<slot name="details">
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="resource-detail-item" v-if="resource.virtualmachineid">
|
<div class="resource-detail-item" v-if="resource.virtualmachineid">
|
||||||
<a-icon type="desktop" class="resource-detail-item"/>
|
<a-icon type="desktop" class="resource-detail-item"/>
|
||||||
<router-link :to="{ path: '/vm/' + resource.virtualmachineid }">{{ resource.vmname || resource.vm || resource.virtualmachinename || resource.virtualmachineid }} </router-link>
|
<router-link :to="{ path: '/vm/' + resource.virtualmachineid }">{{ resource.vmname || resource.vm || resource.virtualmachinename || resource.virtualmachineid }} </router-link>
|
||||||
@ -187,6 +188,10 @@
|
|||||||
<a-icon type="picture" class="resource-detail-item"/>
|
<a-icon type="picture" class="resource-detail-item"/>
|
||||||
<router-link :to="{ path: '/template/' + resource.templateid }">{{ resource.templatename || resource.templateid }} </router-link>
|
<router-link :to="{ path: '/template/' + resource.templateid }">{{ resource.templatename || resource.templateid }} </router-link>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="resource-detail-item" v-if="resource.diskofferingname && resource.diskofferingid">
|
||||||
|
<a-icon type="hdd" class="resource-detail-item"/>
|
||||||
|
<router-link :to="{ path: '/diskoffering/' + resource.diskofferingid }">{{ resource.diskofferingname || resource.diskofferingid }} </router-link>
|
||||||
|
</div>
|
||||||
<div class="resource-detail-item" v-if="resource.networkofferingid">
|
<div class="resource-detail-item" v-if="resource.networkofferingid">
|
||||||
<a-icon type="wifi" class="resource-detail-item"/>
|
<a-icon type="wifi" class="resource-detail-item"/>
|
||||||
<router-link :to="{ path: '/networkoffering/' + resource.networkofferingid }">{{ resource.networkofferingname || resource.networkofferingid }} </router-link>
|
<router-link :to="{ path: '/networkoffering/' + resource.networkofferingid }">{{ resource.networkofferingname || resource.networkofferingid }} </router-link>
|
||||||
@ -203,6 +208,7 @@
|
|||||||
<a-icon type="gateway" class="resource-detail-item"/>
|
<a-icon type="gateway" class="resource-detail-item"/>
|
||||||
<router-link :to="{ path: '/guestnetwork/' + resource.guestnetworkid }">{{ resource.guestnetworkname || resource.guestnetworkid }} </router-link>
|
<router-link :to="{ path: '/guestnetwork/' + resource.guestnetworkid }">{{ resource.guestnetworkname || resource.guestnetworkid }} </router-link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="resource-detail-item" v-if="resource.storageid">
|
<div class="resource-detail-item" v-if="resource.storageid">
|
||||||
<a-icon type="database" class="resource-detail-item"/>
|
<a-icon type="database" class="resource-detail-item"/>
|
||||||
<router-link :to="{ path: '/storagepool/' + resource.storageid }">{{ resource.storage || resource.storageid }} </router-link>
|
<router-link :to="{ path: '/storagepool/' + resource.storageid }">{{ resource.storage || resource.storageid }} </router-link>
|
||||||
@ -401,6 +407,10 @@ export default {
|
|||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
|||||||
@ -938,5 +938,10 @@
|
|||||||
"zone": "Zone",
|
"zone": "Zone",
|
||||||
"zoneId": "Zone",
|
"zoneId": "Zone",
|
||||||
"zoneid": "Zone",
|
"zoneid": "Zone",
|
||||||
"zonename": "Zone"
|
"zonename": "Zone",
|
||||||
|
"instance": "Instance",
|
||||||
|
"yourInstance": "Your instance",
|
||||||
|
"newInstance": "New instance",
|
||||||
|
"cpu": "CPU",
|
||||||
|
"ram": "RAM"
|
||||||
}
|
}
|
||||||
|
|||||||
37
ui/src/utils/icons.js
Normal file
37
ui/src/utils/icons.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
const osMapping = {
|
||||||
|
centos: 'centos',
|
||||||
|
ubuntu: 'ubuntu',
|
||||||
|
suse: 'suse',
|
||||||
|
redhat: 'redhat',
|
||||||
|
fedora: 'fedora',
|
||||||
|
linux: 'linux',
|
||||||
|
bsd: 'freebsd',
|
||||||
|
apple: 'apple',
|
||||||
|
dos: 'windows',
|
||||||
|
windows: 'windows',
|
||||||
|
oracle: 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNormalizedOsName = (osName) => {
|
||||||
|
osName = osName.toLowerCase()
|
||||||
|
return _.find(osMapping, (value, key) => osName.includes(key)) || 'linux'
|
||||||
|
}
|
||||||
@ -118,7 +118,7 @@
|
|||||||
:footer="null"
|
:footer="null"
|
||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<component :is="currentAction.component"/></component>
|
<component :is="currentAction.component" :resource="resource" :loading="loading" v-bind="{currentAction}" />
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
<a-modal
|
<a-modal
|
||||||
|
|||||||
@ -17,29 +17,307 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
Finish this component
|
<a-row :gutter="12">
|
||||||
<a-steps direction="vertical" :current="1">
|
<a-col :md="24" :lg="17">
|
||||||
<a-step title="Finished" description="This is a description." />
|
<a-card :bordered="true" :title="this.$t('newInstance')">
|
||||||
<a-step title="In Progress" description="This is a description." />
|
<a-form
|
||||||
<a-step title="Waiting" description="This is a description." />
|
:form="form"
|
||||||
</a-steps>
|
@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"
|
||||||
|
>
|
||||||
|
<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-form-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel :header="this.$t('ISOs')" key="isos">
|
||||||
|
<!-- ToDo: Add iso 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>
|
||||||
|
<a-button type="primary" @click="handleSubmit">{{ this.$t('submit') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
</a-row>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import { api } from '@/api'
|
||||||
|
import store from '@/store'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import InfoCard from '@/components/view/InfoCard'
|
||||||
|
import ComputeSelection from './wizard/ComputeSelection'
|
||||||
|
import TemplateSelection from './wizard/TemplateSelection'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DeployVM',
|
name: 'Wizard',
|
||||||
components: {
|
components: {
|
||||||
|
InfoCard,
|
||||||
|
ComputeSelection,
|
||||||
|
TemplateSelection
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
vm: {},
|
||||||
|
params: [],
|
||||||
|
visibleParams: [
|
||||||
|
'name',
|
||||||
|
'templateid',
|
||||||
|
'serviceofferingid',
|
||||||
|
'diskofferingid',
|
||||||
|
'zoneid',
|
||||||
|
'rootdisksize'
|
||||||
|
],
|
||||||
|
instanceConfig: [],
|
||||||
|
template: {},
|
||||||
|
serviceOffering: {},
|
||||||
|
diskOffering: {},
|
||||||
|
zone: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
filteredParams () {
|
||||||
|
return this.visibleParams.map((fieldName) => {
|
||||||
|
return this.params.find((param) => fieldName === param.name)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
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)
|
||||||
|
|
||||||
|
if (this.zone) {
|
||||||
|
this.vm['zoneid'] = this.zone.id
|
||||||
|
this.vm['zonename'] = this.zone.name
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.template) {
|
||||||
|
this.vm['templateid'] = this.template.id
|
||||||
|
this.vm['templatename'] = this.template.displaytext
|
||||||
|
this.vm['ostypeid'] = this.template.ostypeid
|
||||||
|
this.vm['ostypename'] = this.template.ostypename
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.serviceOffering) {
|
||||||
|
this.vm['serviceofferingid'] = this.serviceOffering.id
|
||||||
|
this.vm['serviceofferingname'] = this.serviceOffering.displaytext
|
||||||
|
this.vm['cpunumber'] = this.serviceOffering.cpunumber
|
||||||
|
this.vm['cpuspeed'] = this.serviceOffering.cpuspeed
|
||||||
|
this.vm['memory'] = this.serviceOffering.memory
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.diskOffering) {
|
||||||
|
this.vm['diskofferingid'] = this.diskOffering.id
|
||||||
|
this.vm['diskofferingname'] = this.diskOffering.displaytext
|
||||||
|
this.vm['diskofferingsize'] = this.diskOffering.disksize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.form = this.$form.createForm(this, {
|
||||||
|
onValuesChange: (props, fields) => {
|
||||||
|
this.instanceConfig = { ...this.form.getFieldsValue(), ...fields }
|
||||||
|
this.vm = this.instanceConfig
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.form.getFieldDecorator('computeofferingid', { initialValue: [], preserve: true })
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.params = store.getters.apis[this.$route.name]['params']
|
||||||
|
this.filteredParams.forEach((param) => {
|
||||||
|
this.fetchOptions(param)
|
||||||
|
})
|
||||||
|
Vue.nextTick().then(() => {
|
||||||
|
this.instanceConfig = this.form.getFieldsValue() // ToDo: maybe initialize with some other defaults
|
||||||
|
})
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
updateComputeOffering (id) {
|
||||||
|
this.form.setFieldsValue({
|
||||||
|
computeofferingid: id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getParam (paramName) {
|
||||||
|
return this.params.find((param) => param.name === paramName)
|
||||||
|
},
|
||||||
|
getText (option) {
|
||||||
|
return _.get(option, 'displaytext', _.get(option, 'name'))
|
||||||
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
param.loading = true
|
||||||
|
param.opts = []
|
||||||
|
const params = {}
|
||||||
|
params.listall = true
|
||||||
|
if (possibleApi === 'listTemplates') {
|
||||||
|
params.templatefilter = 'executable'
|
||||||
|
}
|
||||||
|
api(possibleApi, params).then((response) => {
|
||||||
|
param.loading = false
|
||||||
|
_.map(response, (responseItem, responseKey) => {
|
||||||
|
if (!responseKey.includes('response')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_.map(responseItem, (response, key) => {
|
||||||
|
if (key === 'count') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
param.opts = response
|
||||||
|
this.$forceUpdate()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.log(error.stack)
|
||||||
|
param.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="less" scoped>
|
||||||
|
.card-footer {
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
button + button {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-list-item-meta-avatar {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-collapse {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
110
ui/src/views/compute/wizard/ComputeSelection.vue
Normal file
110
ui/src/views/compute/wizard/ComputeSelection.vue
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// 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="false"
|
||||||
|
:scroll="{x: 0, y: 320}"
|
||||||
|
:rowSelection="rowSelection"
|
||||||
|
size="middle"
|
||||||
|
>
|
||||||
|
<span slot="cpuTitle"><a-icon type="appstore" /> {{ $t('cpu') }}</span>
|
||||||
|
<span slot="ramTitle"><a-icon type="bulb" /> {{ $t('ram') }}</span>
|
||||||
|
</a-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ComputeSelection',
|
||||||
|
props: {
|
||||||
|
computeItems: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: '40%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'cpu',
|
||||||
|
slots: { title: 'cpuTitle' },
|
||||||
|
width: '30%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'ram',
|
||||||
|
slots: { title: 'ramTitle' },
|
||||||
|
width: '30%'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
selectedRowKeys: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
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,
|
||||||
|
onSelect: (row) => {
|
||||||
|
this.$emit('select-compute-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>
|
||||||
|
|
||||||
|
<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>
|
||||||
86
ui/src/views/compute/wizard/TemplateSelection.vue
Normal file
86
ui/src/views/compute/wizard/TemplateSelection.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// 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…
x
Reference in New Issue
Block a user