compute: startVirtualMachine - Fetching pods, clusters && hosts from the vm's zone (#887)
Custom component for start VM for admins
diff --git a/src/config/section/compute.js b/src/config/section/compute.js
index d485772..cfacfd0 100644
--- a/src/config/section/compute.js
+++ b/src/config/section/compute.js
@@ -98,24 +98,10 @@
docHelp: 'adminguide/virtual_machines.html#stopping-and-starting-vms',
dataView: true,
groupAction: true,
+ popup: true,
groupMap: (selection) => { return selection.map(x => { return { id: x } }) },
show: (record) => { return ['Stopped'].includes(record.state) },
- args: (record, store, group) => {
- var fields = []
- if (group) {
- return fields
- }
- if (store.userInfo.roletype === 'Admin') {
- fields = ['podid', 'clusterid', 'hostid']
- }
- if (record.hypervisor === 'VMware') {
- if (store.apis.startVirtualMachine.params.filter(x => x.name === 'bootintosetup').length > 0) {
- fields.push('bootintosetup')
- }
- }
- return fields
- },
- response: (result) => { return result.virtualmachine && result.virtualmachine.password ? `Password of the VM is ${result.virtualmachine.password}` : null }
+ component: () => import('@/views/compute/StartVirtualMachine.vue')
},
{
api: 'stopVirtualMachine',
diff --git a/src/views/compute/StartVirtualMachine.vue b/src/views/compute/StartVirtualMachine.vue
new file mode 100644
index 0000000..5bca860
--- /dev/null
+++ b/src/views/compute/StartVirtualMachine.vue
@@ -0,0 +1,291 @@
+// 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 class="form-layout">
+ <a-spin :spinning="loading">
+ <a-alert type="warning">
+ <span slot="message" v-html="$t('message.action.start.instance')" />
+ </a-alert>
+ <br />
+ <a-form
+ :form="form"
+ @submit="handleSubmit"
+ layout="vertical">
+ <div v-if="this.$store.getters.userInfo.roletype === 'Admin'">
+ <a-form-item>
+ <span slot="label">
+ {{ $t('label.podid') }}
+ <a-tooltip :title="apiParams.podid.description">
+ <a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
+ </a-tooltip>
+ </span>
+ <a-select
+ v-decorator="['podid', {}]"
+ showSearch
+ optionFilterProp="children"
+ :filterOption="(input, option) => {
+ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+ }"
+ :loading="podsLoading"
+ :placeholder="apiParams.podid.description"
+ @change="handlePodChange">
+ <a-select-option v-for="pod in this.pods" :key="pod.id">
+ {{ pod.name }}
+ </a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item>
+ <span slot="label">
+ {{ $t('label.clusterid') }}
+ <a-tooltip :title="apiParams.clusterid.description">
+ <a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
+ </a-tooltip>
+ </span>
+ <a-select
+ id="cluster-selection"
+ v-decorator="['clusterid', {}]"
+ showSearch
+ optionFilterProp="children"
+ :filterOption="(input, option) => {
+ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+ }"
+ :loading="clustersLoading"
+ :placeholder="apiParams.clusterid.description"
+ @change="handleClusterChange">
+ <a-select-option v-for="cluster in this.clusters" :key="cluster.id">
+ {{ cluster.name }}
+ </a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item>
+ <span slot="label">
+ {{ $t('label.hostid') }}
+ <a-tooltip :title="apiParams.hostid.description">
+ <a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
+ </a-tooltip>
+ </span>
+ <a-select
+ id="host-selection"
+ v-decorator="['hostid', {}]"
+ showSearch
+ optionFilterProp="children"
+ :filterOption="(input, option) => {
+ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+ }"
+ :loading="hostsLoading"
+ :placeholder="apiParams.hostid.description">
+ <a-select-option v-for="host in this.hosts" :key="host.id">
+ {{ host.name }}
+ </a-select-option>
+ </a-select>
+ </a-form-item>
+ </div>
+
+ <a-form-item v-if="resource.hypervisor === 'VMware'">
+ <span slot="label">
+ {{ $t('label.bootintosetup') }}
+ <a-tooltip :title="apiParams.bootintosetup.description">
+ <a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
+ </a-tooltip>
+ </span>
+ <a-switch
+ v-decorator="['bootintosetup']">
+ </a-switch>
+ </a-form-item>
+
+ <div :span="24" class="action-button">
+ <a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
+ <a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('label.ok') }}</a-button>
+ </div>
+ </a-form>
+ </a-spin>
+ </div>
+</template>
+
+<script>
+import { api } from '@/api'
+
+export default {
+ name: 'StartVirtualMachine',
+ props: {
+ resource: {
+ type: Object,
+ required: true
+ }
+ },
+ data () {
+ return {
+ pods: [],
+ clusters: [],
+ hosts: [],
+ podsLoading: false,
+ clustersLoading: false,
+ hostsLoading: false,
+ loading: false
+ }
+ },
+ inject: ['parentFetchData'],
+ beforeCreate () {
+ this.form = this.$form.createForm(this)
+ this.apiConfig = this.$store.getters.apis.startVirtualMachine || {}
+ this.apiParams = {}
+ this.apiConfig.params.forEach(param => {
+ this.apiParams[param.name] = param
+ })
+ },
+ mounted () {
+ if (this.$store.getters.userInfo.roletype === 'Admin') {
+ this.fetchPods()
+ this.fetchClusters()
+ this.fetchHosts()
+ }
+ },
+ methods: {
+ fetchPods () {
+ this.pods = []
+ this.podsLoading = true
+ const params = { zoneid: this.resource.zoneid }
+ api('listPods', params).then(json => {
+ this.pods = json.listpodsresponse.pod || []
+ if (this.pods.length === 0) {
+ this.$notification.error({
+ message: 'No pods found',
+ duration: 0
+ })
+ }
+ }).finally(() => {
+ this.podsLoading = false
+ })
+ },
+ fetchClusters (podid) {
+ this.clusters = []
+ this.clustersLoading = true
+ const params = { zoneid: this.resource.zoneid }
+ if (podid) {
+ params.podid = podid
+ }
+ api('listClusters', params).then(json => {
+ this.clusters = json.listclustersresponse.cluster || []
+ if (this.clusters.length === 0) {
+ this.$notification.error({
+ message: 'No clusters found',
+ duration: 0
+ })
+ }
+ }).finally(() => {
+ this.clustersLoading = false
+ })
+ },
+ fetchHosts (podid, clusterid) {
+ this.hosts = []
+ this.hostsLoading = true
+ const params = { zoneid: this.resource.zoneid, type: 'Routing', state: 'Up' }
+ if (podid) {
+ params.podid = podid
+ }
+ if (clusterid) {
+ params.clusterid = clusterid
+ }
+ api('listHosts', params).then(json => {
+ this.hosts = json.listhostsresponse.host || []
+ if (this.hosts.length === 0) {
+ this.$notification.error({
+ message: 'No hosts found',
+ duration: 0
+ })
+ }
+ }).finally(() => {
+ this.hostsLoading = false
+ })
+ },
+ handlePodChange (podid) {
+ this.form.clearField('clusterid')
+ this.form.clearField('hostid')
+ this.fetchClusters(podid)
+ this.fetchHosts(podid)
+ },
+ handleClusterChange (clusterid) {
+ this.form.clearField('hostid')
+ this.fetchHosts('', clusterid)
+ },
+ handleSubmit (e) {
+ e.preventDefault()
+ this.form.validateFields((err, values) => {
+ if (err) {
+ return
+ }
+
+ this.loading = true
+ const params = {
+ id: this.resource.id
+ }
+ for (const key in values) {
+ if (values[key]) {
+ params[key] = values[key]
+ }
+ }
+ api('startVirtualMachine', params).then(json => {
+ const jobId = json.startvirtualmachineresponse.jobid
+ this.$store.dispatch('AddAsyncJob', {
+ title: this.$t('label.action.start.instance'),
+ jobid: jobId,
+ description: this.resource.name,
+ status: 'progress'
+ })
+ this.$pollJob({
+ jobId,
+ loadingMessage: `${this.$t('label.action.start.instance')} ${this.resource.name}`,
+ catchMessage: this.$t('error.fetching.async.job.result'),
+ successMessage: `${this.$t('label.action.start.instance')} ${this.resource.name}`,
+ successMethod: () => {
+ this.parentFetchData()
+ },
+ response: (result) => { return result.virtualmachine && result.virtualmachine.password ? `Password of the VM is ${result.virtualmachine.password}` : null }
+ })
+ this.closeAction()
+ }).catch(error => {
+ this.$notifyError(error)
+ }).finally(() => {
+ this.loading = false
+ })
+ })
+ },
+ closeAction () {
+ this.$emit('close-action')
+ }
+ }
+}
+</script>
+
+<style scoped lang="less">
+ .form-layout {
+ width: 60vw;
+
+ @media (min-width: 500px) {
+ width: 450px;
+ }
+ }
+
+ .action-button {
+ text-align: right;
+
+ button {
+ margin-right: 5px;
+ }
+ }
+</style>