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 2020/01/22 08:05:10 UTC

[cloudstack-primate] branch master updated: infra: reusable dedicated domain component (#113)

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 bfc2d7e  infra: reusable dedicated domain component (#113)
bfc2d7e is described below

commit bfc2d7e8022f1967094ccf44c9ae7e4c4f455425
Author: Ritchie Vincent <rf...@gmail.com>
AuthorDate: Wed Jan 22 08:05:04 2020 +0000

    infra: reusable dedicated domain component (#113)
    
    Adds dedicated section for zone/pod/cluster/host to dedicate that resource to a domain (and account). Implements the list, dedicate and release APIs.
    
    Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
    Co-authored-by: Rohit Yadav <ro...@apache.org>
---
 src/components/view/DedicateData.vue   | 331 +++++++++++++++++++++++++++++++++
 src/components/view/DedicateDomain.vue | 145 +++++++++++++++
 src/components/view/DedicateModal.vue  | 286 ++++++++++++++++++++++++++++
 src/components/view/DetailsTab.vue     |  20 ++
 src/config/section/infra/clusters.js   |  26 ---
 src/config/section/infra/hosts.js      |  26 ---
 src/config/section/infra/pods.js       |  26 ---
 src/config/section/infra/zones.js      |  26 ---
 8 files changed, 782 insertions(+), 104 deletions(-)

diff --git a/src/components/view/DedicateData.vue b/src/components/view/DedicateData.vue
new file mode 100644
index 0000000..63954b1
--- /dev/null
+++ b/src/components/view/DedicateData.vue
@@ -0,0 +1,331 @@
+// 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>
+    <div>
+      <a-list-item v-if="dedicatedDomainId">
+        <div>
+          <div style="margin-bottom: 10px;">
+            <strong>{{ $t('dedicated') }}</strong>
+            <div>Yes</div>
+          </div>
+          <p>
+            <strong>{{ $t('domainid') }}</strong><br/>
+            <router-link :to="{ path: '/domain/' + dedicatedDomainId }">{{ dedicatedDomainId }}</router-link>
+          </p>
+          <p v-if="dedicatedAccountId">
+            <strong>{{ $t('account') }}</strong><br/>
+            <router-link :to="{ path: '/account/' + dedicatedAccountId }">{{ dedicatedAccountId }}</router-link>
+          </p>
+          <a-button style="margin-top: 10px;" type="danger" @click="handleRelease">
+            {{ releaseButtonLabel }}
+          </a-button>
+        </div>
+      </a-list-item>
+      <a-list-item v-else>
+        <div>
+          <strong>{{ $t('dedicated') }}</strong>
+          <div>No</div>
+          <a-button type="primary" style="margin-top: 10px;" @click="modalActive = true">
+            {{ dedicatedButtonLabel }}
+          </a-button>
+        </div>
+      </a-list-item>
+    </div>
+    <DedicateModal
+      :resource="resource"
+      :active="modalActive"
+      :label="dedicatedModalLabel"
+      @close="modalActive = false"
+      :fetchData="fetchData" />
+  </div>
+</template>
+
+<script>
+import { api } from '@/api'
+import DedicateModal from './DedicateModal'
+
+export default {
+  props: {
+    resource: {
+      type: Object,
+      required: true
+    }
+  },
+  components: {
+    DedicateModal
+  },
+  inject: ['parentFetchData'],
+  data () {
+    return {
+      modalActive: false,
+      dedicatedButtonLabel: 'Dedicate',
+      releaseButtonLabel: 'Release',
+      dedicatedModalLabel: 'Dedicate',
+      dedicatedDomainId: null,
+      dedicatedAccountId: null
+    }
+  },
+  watch: {
+    resource (newItem, oldItem) {
+      if (this.resource && this.resource.id && newItem && newItem.id !== oldItem.id) {
+        this.fetchData()
+      }
+    }
+  },
+  methods: {
+    fetchData () {
+      if (this.$route.meta.name === 'zone') {
+        this.fetchDedicatedZones()
+        this.releaseButtonLabel = this.$t('label.release.dedicated.zone')
+        this.dedicatedButtonLabel = this.$t('label.dedicate.zone')
+        this.dedicatedModalLabel = this.$t('label.dedicate.zone')
+      }
+      if (this.$route.meta.name === 'pod') {
+        this.fetchDedicatedPods()
+        this.releaseButtonLabel = this.$t('label.release.dedicated.pod')
+        this.dedicatedButtonLabel = this.$t('label.dedicate.pod')
+        this.dedicatedModalLabel = this.$t('label.dedicate.pod')
+      }
+      if (this.$route.meta.name === 'cluster') {
+        this.fetchDedicatedClusters()
+        this.releaseButtonLabel = this.$t('label.release.dedicated.cluster')
+        this.dedicatedButtonLabel = this.$t('label.dedicate.cluster')
+        this.dedicatedModalLabel = this.$t('label.dedicate.cluster')
+      }
+      if (this.$route.meta.name === 'host') {
+        this.fetchDedicatedHosts()
+        this.releaseButtonLabel = this.$t('label.release.dedicated.host')
+        this.dedicatedButtonLabel = this.$t('label.dedicate.host')
+        this.dedicatedModalLabel = this.$t('label.dedicate.host')
+      }
+    },
+    fetchDedicatedZones () {
+      api('listDedicatedZones', {
+        zoneid: this.resource.id
+      }).then(response => {
+        if (response.listdedicatedzonesresponse.dedicatedzone &&
+          response.listdedicatedzonesresponse.dedicatedzone.length > 0) {
+          this.dedicatedDomainId = response.listdedicatedzonesresponse.dedicatedzone[0].domainid
+          this.dedicatedAccountId = response.listdedicatedzonesresponse.dedicatedzone[0].accountid
+        }
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    fetchDedicatedPods () {
+      api('listDedicatedPods', {
+        podid: this.resource.id
+      }).then(response => {
+        if (response.listdedicatedpodsresponse.dedicatedpod &&
+          response.listdedicatedpodsresponse.dedicatedpod.length > 0) {
+          this.dedicatedDomainId = response.listdedicatedpodsresponse.dedicatedpod[0].domainid
+          this.dedicatedAccountId = response.listdedicatedpodsresponse.dedicatedpod[0].accountid
+        }
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    fetchDedicatedClusters () {
+      api('listDedicatedClusters', {
+        clusterid: this.resource.id
+      }).then(response => {
+        if (response.listdedicatedclustersresponse.dedicatedcluster &&
+          response.listdedicatedclustersresponse.dedicatedcluster.length > 0) {
+          this.dedicatedDomainId = response.listdedicatedclustersresponse.dedicatedcluster[0].domainid
+          this.dedicatedAccountId = response.listdedicatedclustersresponse.dedicatedcluster[0].accountid
+        }
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    fetchDedicatedHosts () {
+      api('listDedicatedHosts', {
+        hostid: this.resource.id
+      }).then(response => {
+        if (response.listdedicatedhostsresponse.dedicatedhost &&
+          response.listdedicatedhostsresponse.dedicatedhost.length > 0) {
+          this.dedicatedDomainId = response.listdedicatedhostsresponse.dedicatedhost[0].domainid
+          this.dedicatedAccountId = response.listdedicatedhostsresponse.dedicatedhost[0].accountid
+        }
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    releaseDedidcatedZone () {
+      api('releaseDedicatedZone', {
+        zoneid: this.resource.id
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.releasededicatedzoneresponse.jobid,
+          successMessage: `Successfully released dedicated zone`,
+          successMethod: () => {
+            this.parentFetchData()
+            this.dedicatedDomainId = null
+            this.$store.dispatch('AddAsyncJob', {
+              title: 'Successfully released dedicated zone',
+              jobid: response.releasededicatedzoneresponse.jobid,
+              status: 'progress'
+            })
+          },
+          errorMessage: 'Failed to release dedicated zone',
+          errorMethod: () => {
+            this.parentFetchData()
+          },
+          loadingMessage: `Releasing dedicated zone...`,
+          catchMessage: 'Error encountered while fetching async job result',
+          catchMethod: () => {
+            this.parentFetchData()
+          }
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    releaseDedidcatedPod () {
+      api('releaseDedicatedPod', {
+        podid: this.resource.id
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.releasededicatedpodresponse.jobid,
+          successMessage: `Successfully released dedicated pod`,
+          successMethod: () => {
+            this.parentFetchData()
+            this.dedicatedDomainId = null
+            this.$store.dispatch('AddAsyncJob', {
+              title: 'Successfully released dedicated pod',
+              jobid: response.releasededicatedpodresponse.jobid,
+              status: 'progress'
+            })
+          },
+          errorMessage: 'Failed to release dedicated pod',
+          errorMethod: () => {
+            this.parentFetchData()
+          },
+          loadingMessage: `Releasing dedicated pod...`,
+          catchMessage: 'Error encountered while fetching async job result',
+          catchMethod: () => {
+            this.parentFetchData()
+          }
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    releaseDedidcatedCluster () {
+      api('releaseDedicatedCluster', {
+        clusterid: this.resource.id
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.releasededicatedclusterresponse.jobid,
+          successMessage: `Successfully released dedicated cluster`,
+          successMethod: () => {
+            this.parentFetchData()
+            this.dedicatedDomainId = null
+            this.$store.dispatch('AddAsyncJob', {
+              title: 'Successfully released dedicated cluster',
+              jobid: response.releasededicatedclusterresponse.jobid,
+              status: 'progress'
+            })
+          },
+          errorMessage: 'Failed to release dedicated cluster',
+          errorMethod: () => {
+            this.parentFetchData()
+          },
+          loadingMessage: `Releasing dedicated cluster...`,
+          catchMessage: 'Error encountered while fetching async job result',
+          catchMethod: () => {
+            this.parentFetchData()
+          }
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    releaseDedidcatedHost () {
+      api('releaseDedicatedHost', {
+        hostid: this.resource.id
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.releasededicatedhostresponse.jobid,
+          successMessage: `Successfully released dedicated host`,
+          successMethod: () => {
+            this.parentFetchData()
+            this.dedicatedDomainId = null
+            this.$store.dispatch('AddAsyncJob', {
+              title: 'Successfully released dedicated host',
+              jobid: response.releasededicatedhostresponse.jobid,
+              status: 'progress'
+            })
+          },
+          errorMessage: 'Failed to release dedicated host',
+          errorMethod: () => {
+            this.parentFetchData()
+          },
+          loadingMessage: `Releasing dedicated host...`,
+          catchMessage: 'Error encountered while fetching async job result',
+          catchMethod: () => {
+            this.parentFetchData()
+          }
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    handleRelease () {
+      this.modalActive = false
+      if (this.$route.meta.name === 'zone') {
+        this.releaseDedidcatedZone()
+      }
+      if (this.$route.meta.name === 'pod') {
+        this.releaseDedidcatedPod()
+      }
+      if (this.$route.meta.name === 'cluster') {
+        this.releaseDedidcatedCluster()
+      }
+      if (this.$route.meta.name === 'host') {
+        this.releaseDedidcatedHost()
+      }
+    }
+  }
+}
+</script>
diff --git a/src/components/view/DedicateDomain.vue b/src/components/view/DedicateDomain.vue
new file mode 100644
index 0000000..eebf6e8
--- /dev/null
+++ b/src/components/view/DedicateDomain.vue
@@ -0,0 +1,145 @@
+// 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">
+    <div class="form__item" :class="{'error': domainError}">
+      <a-spin :spinning="domainsLoading">
+        <p class="form__label">{{ $t('domain') }}<span class="required">*</span></p>
+        <p class="required required-label">{{ $t('required') }}</p>
+        <a-select style="width: 100%" @change="handleChangeDomain" v-model="domainId">
+          <a-select-option v-for="(domain, index) in domainsList" :value="domain.id" :key="index">
+            {{ domain.name }}
+          </a-select-option>
+        </a-select>
+      </a-spin>
+    </div>
+    <div class="form__item" v-if="accountsList">
+      <p class="form__label">Account</p>
+      <a-select style="width: 100%" @change="handleChangeAccount">
+        <a-select-option v-for="(account, index) in accountsList" :value="account.name" :key="index">
+          {{ account.name }}
+        </a-select-option>
+      </a-select>
+    </div>
+  </div>
+</template>
+
+<script>
+import { api } from '@/api'
+
+export default {
+  name: 'DedicateDomain',
+  props: {
+    error: {
+      type: Boolean,
+      requried: true
+    }
+  },
+  data () {
+    return {
+      domainsLoading: false,
+      domainId: null,
+      accountsList: null,
+      domainsList: null,
+      domainError: false
+    }
+  },
+  watch: {
+    error () {
+      this.domainError = this.error
+    }
+  },
+  mounted () {
+    this.fetchData()
+  },
+  methods: {
+    fetchData () {
+      this.domainsLoading = true
+      api('listDomains', {
+        listAll: true,
+        details: 'min'
+      }).then(response => {
+        this.domainsList = response.listdomainsresponse.domain
+
+        if (this.domainsList[0]) {
+          this.domainId = this.domainsList[0].id
+          this.handleChangeDomain(this.domainId)
+        }
+        this.domainsLoading = false
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+        this.domainsLoading = false
+      })
+    },
+    fetchAccounts () {
+      api('listAccounts', {
+        domainid: this.domainId
+      }).then(response => {
+        this.accountsList = response.listaccountsresponse.account || []
+        if (this.accountsList && this.accountsList.length === 0) {
+          this.handleChangeAccount(null)
+        }
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+      })
+    },
+    handleChangeDomain (e) {
+      this.$emit('domainChange', e)
+      this.domainError = false
+      this.fetchAccounts()
+    },
+    handleChangeAccount (e) {
+      this.$emit('accountChange', e)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+  .form {
+    &__item {
+      margin-bottom: 20px;
+    }
+
+    &__label {
+      font-weight: bold;
+      margin-bottom: 5px;
+    }
+  }
+
+  .required {
+    color: #ff0000;
+    font-size: 12px;
+
+    &-label {
+      display: none;
+    }
+  }
+
+  .error {
+    .required-label {
+      display: block;
+    }
+  }
+</style>
diff --git a/src/components/view/DedicateModal.vue b/src/components/view/DedicateModal.vue
new file mode 100644
index 0000000..6dcbfed
--- /dev/null
+++ b/src/components/view/DedicateModal.vue
@@ -0,0 +1,286 @@
+// 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-modal
+    v-model="dedicatedDomainModal"
+    :title="label"
+    @cancel="closeModal"
+    @ok="handleDedicateForm">
+    <DedicateDomain
+      @domainChange="id => domainId = id"
+      @accountChange="id => dedicatedAccount = id"
+      :error="domainError" />
+  </a-modal>
+</template>
+
+<script>
+import { api } from '@/api'
+import DedicateDomain from './DedicateDomain'
+
+export default {
+  components: {
+    DedicateDomain
+  },
+  inject: ['parentFetchData'],
+  props: {
+    active: {
+      type: Boolean,
+      required: true
+    },
+    label: {
+      type: String,
+      required: true
+    },
+    resource: {
+      type: Object,
+      required: true
+    },
+    fetchData: {
+      type: Function,
+      required: true
+    }
+  },
+  data () {
+    return {
+      dedicatedDomainModal: false,
+      domainId: null,
+      dedicatedAccount: null,
+      domainError: false
+    }
+  },
+  watch: {
+    active () {
+      this.dedicatedDomainModal = this.active
+    }
+  },
+  mounted () {
+    this.dedicatedDomainModal = this.active
+  },
+  methods: {
+    fetchParentData () {
+      this.fetchData()
+    },
+    closeModal () {
+      this.$emit('close')
+    },
+    dedicateZone () {
+      if (!this.domainId) {
+        this.domainError = true
+        return
+      }
+      api('dedicateZone', {
+        zoneId: this.resource.id,
+        domainId: this.domainId,
+        account: this.dedicatedAccount
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.dedicatezoneresponse.jobid,
+          successMessage: `Successfully dedicated zone`,
+          successMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainId = this.domainId
+            this.dedicatedDomainModal = false
+            this.$store.dispatch('AddAsyncJob', {
+              title: 'Successfully dedicated zone',
+              jobid: response.dedicatezoneresponse.jobid,
+              description: `Domain ID: ${this.dedicatedDomainId}`,
+              status: 'progress'
+            })
+          },
+          errorMessage: 'Failed to dedicate zone',
+          errorMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainModal = false
+          },
+          loadingMessage: `Dedicating zone...`,
+          catchMessage: 'Error encountered while fetching async job result',
+          catchMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainModal = false
+          }
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+        this.dedicatedDomainModal = false
+      })
+    },
+    dedicatePod () {
+      if (!this.domainId) {
+        this.domainError = true
+        return
+      }
+      api('dedicatePod', {
+        podId: this.resource.id,
+        domainId: this.domainId,
+        account: this.dedicatedAccount
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.dedicatepodresponse.jobid,
+          successMessage: `Successfully dedicated pod`,
+          successMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainId = this.domainId
+            this.dedicatedDomainModal = false
+            this.$store.dispatch('AddAsyncJob', {
+              title: 'Successfully dedicated pod',
+              jobid: response.dedicatepodresponse.jobid,
+              description: `Domain ID: ${this.dedicatedDomainId}`,
+              status: 'progress'
+            })
+          },
+          errorMessage: 'Failed to dedicate pod',
+          errorMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainModal = false
+          },
+          loadingMessage: `Dedicating pod...`,
+          catchMessage: 'Error encountered while fetching async job result',
+          catchMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainModal = false
+          }
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+        this.dedicatedDomainModal = false
+      })
+    },
+    dedicateCluster () {
+      if (!this.domainId) {
+        this.domainError = true
+        return
+      }
+      api('dedicateCluster', {
+        clusterId: this.resource.id,
+        domainId: this.domainId,
+        account: this.dedicatedAccount
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.dedicateclusterresponse.jobid,
+          successMessage: `Successfully dedicated cluster`,
+          successMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainId = this.domainId
+            this.dedicatedDomainModal = false
+            this.$store.dispatch('AddAsyncJob', {
+              title: 'Successfully dedicated cluster',
+              jobid: response.dedicateclusterresponse.jobid,
+              description: `Domain ID: ${this.dedicatedDomainId}`,
+              status: 'progress'
+            })
+          },
+          errorMessage: 'Failed to dedicate cluster',
+          errorMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainModal = false
+          },
+          loadingMessage: `Dedicating cluster...`,
+          catchMessage: 'Error encountered while fetching async job result',
+          catchMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainModal = false
+          }
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+        this.dedicatedDomainModal = false
+      })
+    },
+    dedicateHost () {
+      if (!this.domainId) {
+        this.domainError = true
+        return
+      }
+      api('dedicateHost', {
+        hostId: this.resource.id,
+        domainId: this.domainId,
+        account: this.dedicatedAccount
+      }).then(response => {
+        this.$pollJob({
+          jobId: response.dedicatehostresponse.jobid,
+          successMessage: `Successfully dedicated host`,
+          successMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainId = this.domainId
+            this.dedicatedDomainModal = false
+            this.$store.dispatch('AddAsyncJob', {
+              title: 'Successfully dedicated host',
+              jobid: response.dedicatehostresponse.jobid,
+              description: `Domain ID: ${this.dedicatedDomainId}`,
+              status: 'progress'
+            })
+          },
+          errorMessage: 'Failed to dedicate host',
+          errorMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainModal = false
+          },
+          loadingMessage: `Dedicating host...`,
+          catchMessage: 'Error encountered while fetching async job result',
+          catchMethod: () => {
+            this.parentFetchData()
+            this.fetchParentData()
+            this.dedicatedDomainModal = false
+          }
+        })
+      }).catch(error => {
+        this.$notification.error({
+          message: `Error ${error.response.status}`,
+          description: error.response.data.errorresponse.errortext
+        })
+        this.dedicatedDomainModal = false
+      })
+    },
+    handleDedicateForm () {
+      if (this.$route.meta.name === 'zone') {
+        this.dedicateZone()
+      }
+      if (this.$route.meta.name === 'pod') {
+        this.dedicatePod()
+      }
+      if (this.$route.meta.name === 'cluster') {
+        this.dedicateCluster()
+      }
+      if (this.$route.meta.name === 'host') {
+        this.dedicateHost()
+      }
+    }
+  }
+}
+
+</script>
diff --git a/src/components/view/DetailsTab.vue b/src/components/view/DetailsTab.vue
index e0baffb..a6569e3 100644
--- a/src/components/view/DetailsTab.vue
+++ b/src/components/view/DetailsTab.vue
@@ -28,12 +28,19 @@
         </div>
       </div>
     </a-list-item>
+    <DedicateData :resource="resource" v-if="dedicatedSectionActive" />
   </a-list>
 </template>
 
 <script>
+
+import DedicateData from './DedicateData'
+
 export default {
   name: 'DetailsTab',
+  components: {
+    DedicateData
+  },
   props: {
     resource: {
       type: Object,
@@ -43,6 +50,19 @@ export default {
       type: Boolean,
       default: false
     }
+  },
+  data () {
+    return {
+      dedicatedSectionActive: false
+    }
+  },
+  created () {
+    this.dedicatedSectionActive = ['zone', 'pod', 'cluster', 'host'].includes(this.$route.meta.name)
+  },
+  watch: {
+    $route () {
+      this.dedicatedSectionActive = ['zone', 'pod', 'cluster', 'host'].includes(this.$route.meta.name)
+    }
   }
 }
 </script>
diff --git a/src/config/section/infra/clusters.js b/src/config/section/infra/clusters.js
index 03fbaaa..2c3216a 100644
--- a/src/config/section/infra/clusters.js
+++ b/src/config/section/infra/clusters.js
@@ -59,32 +59,6 @@ export default {
       show: (record) => { return record.allocationstate === 'Enabled' }
     },
     {
-      api: 'dedicateCluster',
-      icon: 'user-add',
-      label: 'label.dedicate.cluster',
-      dataView: true,
-      show: (record) => { return !record.domainid },
-      args: ['clusterid', 'domainid', 'account'],
-      mapping: {
-        clusterid: {
-          value: (record) => { return record.id }
-        }
-      }
-    },
-    {
-      api: 'releaseDedicatedCluster',
-      icon: 'user-delete',
-      label: 'label.release.dedicated.cluster',
-      dataView: true,
-      show: (record) => { return record.domainid },
-      args: ['clusterid'],
-      mapping: {
-        clusterid: {
-          value: (record) => { return record.id }
-        }
-      }
-    },
-    {
       api: 'updateCluster',
       icon: 'plus-square',
       label: 'Manage Cluster',
diff --git a/src/config/section/infra/hosts.js b/src/config/section/infra/hosts.js
index f4bfa7c..83ea7f8 100644
--- a/src/config/section/infra/hosts.js
+++ b/src/config/section/infra/hosts.js
@@ -81,32 +81,6 @@ export default {
       show: (record) => { return record.resourcestate === 'Disabled' }
     },
     {
-      api: 'dedicateHost',
-      icon: 'user-add',
-      label: 'label.dedicate.host',
-      dataView: true,
-      show: (record) => { return !record.domainid },
-      args: ['hostid', 'domainid', 'account'],
-      mapping: {
-        hostid: {
-          value: (record) => { return record.id }
-        }
-      }
-    },
-    {
-      api: 'releaseDedicatedHost',
-      icon: 'user-delete',
-      label: 'label.release.dedicated.host',
-      dataView: true,
-      show: (record) => { return record.domainid },
-      args: ['hostid'],
-      mapping: {
-        hostid: {
-          value: (record) => { return record.id }
-        }
-      }
-    },
-    {
       api: 'prepareHostForMaintenance',
       icon: 'plus-square',
       label: 'label.action.enable.maintenance.mode',
diff --git a/src/config/section/infra/pods.js b/src/config/section/infra/pods.js
index 19ef030..ad2cfc9 100644
--- a/src/config/section/infra/pods.js
+++ b/src/config/section/infra/pods.js
@@ -47,32 +47,6 @@ export default {
       args: ['name', 'netmask', 'gateway']
     },
     {
-      api: 'dedicatePod',
-      icon: 'user-add',
-      label: 'label.dedicate.pod',
-      dataView: true,
-      show: (record) => { return !record.domainid },
-      args: ['podid', 'domainid', 'account'],
-      mapping: {
-        podid: {
-          value: (record) => { return record.id }
-        }
-      }
-    },
-    {
-      api: 'releaseDedicatedPod',
-      icon: 'user-delete',
-      label: 'label.release.dedicated.pod',
-      dataView: true,
-      show: (record) => { return record.domainid },
-      args: ['podid'],
-      mapping: {
-        podid: {
-          value: (record) => { return record.id }
-        }
-      }
-    },
-    {
       api: 'updatePod',
       icon: 'play-circle',
       label: 'label.action.enable.pod',
diff --git a/src/config/section/infra/zones.js b/src/config/section/infra/zones.js
index 696c440..1d1938f 100644
--- a/src/config/section/infra/zones.js
+++ b/src/config/section/infra/zones.js
@@ -100,32 +100,6 @@ export default {
       show: (record) => { return record.allocationstate === 'Disabled' }
     },
     {
-      api: 'dedicateZone',
-      icon: 'user-add',
-      label: 'label.dedicate.zone',
-      dataView: true,
-      show: (record) => { return !record.domainid },
-      args: ['zoneid', 'domainid', 'account'],
-      mapping: {
-        zoneid: {
-          value: (record) => { return record.id }
-        }
-      }
-    },
-    {
-      api: 'releaseDedicatedZone',
-      icon: 'user-delete',
-      label: 'label.release.dedicated.zone',
-      dataView: true,
-      show: (record) => { return record.domainid },
-      args: ['zoneid'],
-      mapping: {
-        zoneid: {
-          value: (record) => { return record.id }
-        }
-      }
-    },
-    {
       api: 'enableOutOfBandManagementForZone',
       icon: 'plus-circle',
       label: 'label.outofbandmanagement.enable',