You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2019/12/14 15:29:04 UTC

[cloudstack-primate] branch master updated: compute: VM assign to other account/project/domain (#69)

This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git


The following commit(s) were added to refs/heads/master by this push:
     new b384cd4  compute: VM assign to other account/project/domain  (#69)
b384cd4 is described below

commit b384cd4b5a298d5b958f0bb2631ff0d2d0dd6901
Author: Ritchie Vincent <rf...@gmail.com>
AuthorDate: Sat Dec 14 15:28:56 2019 +0000

    compute: VM assign to other account/project/domain  (#69)
    
    Custom form to assign a VM to either an account or a project.
    
    Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
---
 src/config/section/compute.js        |   2 +
 src/locales/en.json                  |   3 +
 src/utils/request.js                 |   4 +-
 src/views/AutogenView.vue            |   9 +-
 src/views/compute/AssignInstance.vue | 274 +++++++++++++++++++++++++++++++++++
 5 files changed, 288 insertions(+), 4 deletions(-)

diff --git a/src/config/section/compute.js b/src/config/section/compute.js
index a4241a2..51a7ffc 100644
--- a/src/config/section/compute.js
+++ b/src/config/section/compute.js
@@ -241,6 +241,8 @@ export default {
           icon: 'user-add',
           label: 'Assign Instance to Another Account',
           dataView: true,
+          component: () => import('@/views/compute/AssignInstance'),
+          popup: true,
           show: (record) => { return ['Stopped'].includes(record.state) },
           args: ['virtualmachineid', 'account', 'domainid'],
           mapping: {
diff --git a/src/locales/en.json b/src/locales/en.json
index ec0282e..d330ce2 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -631,6 +631,7 @@
 "memoryusedgb": "Used",
 "memused": "Memory Usage",
 "message.edit.account": "Edit (\"-1\" indicates no limit to the amount of resources create)",
+"message.assign.instance.another": "Please specify the account type, domain, account name and network (optional) of the new account. <br> If the default nic of the vm is on a shared network, CloudStack will check if the network can be used by the new account if you do not specify one network. <br> If the default nic of the vm is on a isolated network, and the new account has more one isolated networks, you should specify one.",
 "minCPUNumber": "Min CPU Cores",
 "minInstance": "Min Instances",
 "minIops": "Min IOPS",
@@ -754,6 +755,7 @@
 "redundantvpcrouter": "Redundant VPC",
 "reenterpassword": "Re-enter Password",
 "relationaloperator": "Operator",
+"required": "Required",
 "requireshvm": "HVM",
 "requiresupgrade": "Requires Upgrade",
 "reservedSystemEndIp": "End Reserved system IP",
@@ -836,6 +838,7 @@
 "storagepolicy": "Storage policy",
 "storagetype": "Storage Type",
 "subdomainaccess": "Subdomain Access",
+"submit": "Submit",
 "supportedServices": "Supported Services",
 "supportspublicaccess": "Supports Public Access",
 "supportsregionLevelvpc": "Supports Region Level VPC",
diff --git a/src/utils/request.js b/src/utils/request.js
index c38e7f4..235793a 100644
--- a/src/utils/request.js
+++ b/src/utils/request.js
@@ -57,10 +57,10 @@ const err = (error) => {
 
 // request interceptor
 service.interceptors.request.use(config => {
-  const project = Vue.ls.get(CURRENT_PROJECT)
   if (config && config.params) {
     config.params.response = 'json'
-    if (project && project.id) {
+    const project = Vue.ls.get(CURRENT_PROJECT)
+    if (!config.params.projectid && project && project.id) {
       config.params.projectid = project.id
     }
   }
diff --git a/src/views/AutogenView.vue b/src/views/AutogenView.vue
index 0050239..1e57073 100644
--- a/src/views/AutogenView.vue
+++ b/src/views/AutogenView.vue
@@ -19,10 +19,10 @@
   <div>
     <a-card class="breadcrumb-card">
       <a-row>
-        <a-col :span="14">
+        <a-col :span="12">
           <breadcrumb style="padding-top: 6px" />
         </a-col>
-        <a-col :span="10">
+        <a-col :span="12">
           <span style="float: right">
             <a-tooltip placement="bottom">
               <template slot="title">
@@ -263,6 +263,11 @@ export default {
     Status
   },
   mixins: [mixinDevice],
+  provide: function () {
+    return {
+      parentFetchData: this.fetchData
+    }
+  },
   data () {
     return {
       apiName: '',
diff --git a/src/views/compute/AssignInstance.vue b/src/views/compute/AssignInstance.vue
new file mode 100644
index 0000000..3945e90
--- /dev/null
+++ b/src/views/compute/AssignInstance.vue
@@ -0,0 +1,274 @@
+// 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>
+    <p v-html="$t('message.assign.instance.another')"></p>
+
+    <div class="form">
+
+      <div v-if="loading" class="loading">
+        <a-icon type="loading" style="color: #1890ff;"></a-icon>
+      </div>
+
+      <div class="form__item">
+        <p class="form__label">{{ $t('accounttype') }}</p>
+        <a-select v-model="selectedAccountType" defaultValue="account">
+          <a-select-option :value="$t('account')">{{ $t('account') }}</a-select-option>
+          <a-select-option :value="$t('project')">{{ $t('project') }}</a-select-option>
+        </a-select>
+      </div>
+
+      <div class="form__item">
+        <p class="form__label"><span class="required">*</span>{{ $t('domain') }}</p>
+        <a-select @change="changeDomain" v-model="selectedDomain" :defaultValue="selectedDomain">
+          <a-select-option v-for="domain in domains" :key="domain.name" :value="domain.id">
+            {{ domain.path }}
+          </a-select-option>
+        </a-select>
+      </div>
+
+      <template v-if="selectedAccountType === 'Account'">
+        <div class="form__item">
+          <p class="form__label"><span class="required">*</span>{{ $t('account') }}</p>
+          <a-select @change="changeAccount" v-model="selectedAccount">
+            <a-select-option v-for="account in accounts" :key="account.name" :value="account.name">
+              {{ account.name }}
+            </a-select-option>
+          </a-select>
+          <span v-if="accountError" class="required">{{ $t('required') }}</span>
+        </div>
+      </template>
+
+      <template v-else>
+        <div class="form__item">
+          <p class="form__label"><span class="required">*</span>{{ $t('project') }}</p>
+          <a-select @change="changeProject" v-model="selectedProject">
+            <a-select-option v-for="project in projects" :key="project.id" :value="project.id">
+              {{ project.name }}
+            </a-select-option>
+          </a-select>
+          <span v-if="projectError" class="required">{{ $t('required') }}</span>
+        </div>
+      </template>
+
+      <div class="form__item">
+        <p class="form__label">{{ $t('network') }}</p>
+        <a-select v-model="selectedNetwork">
+          <a-select-option v-for="network in networks" :key="network.id" :value="network.id">
+            {{ network.name ? network.name : '-' }}
+          </a-select-option>
+        </a-select>
+      </div>
+
+      <a-button type="primary" class="submit-btn" @click="submitData">
+        {{ $t('submit') }}
+      </a-button>
+
+    </div>
+
+  </div>
+</template>
+
+<script>
+import { api } from '@/api'
+
+export default {
+  name: 'AssignInstance',
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    }
+  },
+  inject: ['parentFetchData'],
+  data () {
+    return {
+      domains: [],
+      accounts: [],
+      projects: [],
+      networks: [],
+      selectedAccountType: 'Account',
+      selectedDomain: null,
+      selectedAccount: null,
+      selectedProject: null,
+      selectedNetwork: null,
+      accountError: false,
+      projectError: false,
+      loading: false
+    }
+  },
+  mounted () {
+    this.fetchData()
+  },
+  methods: {
+    fetchData () {
+      this.loading = true
+      api('listDomains', {
+        response: 'json',
+        listAll: true,
+        details: 'min'
+      }).then(response => {
+        this.domains = response.listdomainsresponse.domain
+        this.selectedDomain = this.domains[0].id
+        this.fetchAccounts()
+        this.fetchProjects()
+      })
+    },
+    fetchAccounts () {
+      this.loading = true
+      api('listAccounts', {
+        response: 'json',
+        domainId: this.selectedDomain,
+        state: 'Enabled',
+        listAll: true
+      }).then(response => {
+        this.accounts = response.listaccountsresponse.account
+        this.loading = false
+      })
+    },
+    fetchProjects () {
+      this.loading = true
+      api('listProjects', {
+        response: 'json',
+        domainId: this.selectedDomain,
+        state: 'Active',
+        details: 'min',
+        listAll: true
+      }).then(response => {
+        this.projects = response.listprojectsresponse.project
+        this.loading = false
+      })
+    },
+    fetchNetworks () {
+      this.loading = true
+      api('listNetworks', {
+        response: 'json',
+        domainId: this.selectedDomain,
+        listAll: true,
+        isrecursive: false,
+        account: this.selectedAccount,
+        projectid: this.selectedProject
+      }).then(response => {
+        this.networks = response.listnetworksresponse.network
+        this.loading = false
+      })
+    },
+    changeDomain () {
+      this.selectedAccount = null
+      this.fetchAccounts()
+      this.fetchProjects()
+    },
+    changeAccount () {
+      this.selectedProject = null
+      this.fetchNetworks()
+    },
+    changeProject () {
+      this.selectedAccount = null
+      this.fetchNetworks()
+    },
+    submitData () {
+      let variableKey = ''
+      let variableValue = ''
+
+      if (this.selectedAccountType === 'Account') {
+        if (!this.selectedAccount) {
+          this.accountError = true
+          return
+        }
+        variableKey = 'account'
+        variableValue = this.selectedAccount
+      } else if (this.selectedAccountType === 'Project') {
+        if (!this.selectedProject) {
+          this.projectError = true
+          return
+        }
+        variableKey = 'projectid'
+        variableValue = this.selectedProject
+      }
+
+      this.loading = true
+      api('assignVirtualMachine', {
+        response: 'json',
+        virtualmachineid: this.resource.id,
+        domainid: this.selectedDomain,
+        [variableKey]: variableValue,
+        networkids: this.selectedNetwork
+      }).then(response => {
+        this.$notification.success({
+          message: 'Successfully assigned instance'
+        })
+        this.loading = false
+        this.$parent.$parent.close()
+        this.parentFetchData()
+      }).catch(error => {
+        this.$notification.error({
+          message: 'Failed to assign instance',
+          description: error.response.data.assignvirtualmachineresponse.errortext && error.response.data.assignvirtualmachineresponse.errortext
+        })
+        this.$parent.$parent.close()
+        this.parentFetchData()
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+  .form {
+    display: flex;
+    flex-direction: column;
+
+    &__item {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+      margin-bottom: 10px;
+    }
+
+    &__label {
+      display: flex;
+      font-weight: bold;
+      margin-bottom: 5px;
+    }
+
+  }
+
+  .submit-btn {
+    margin-top: 10px;
+    align-self: flex-end;
+  }
+
+  .required {
+    margin-right: 2px;
+    color: red;
+    font-size: 0.7rem;
+  }
+
+  .loading {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 3rem;
+  }
+</style>