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/05/09 05:16:02 UTC
[cloudstack-primate] branch master updated: image: handle copy and
delete actions for template/iso zone tab (#284)
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 c84af39 image: handle copy and delete actions for template/iso zone tab (#284)
c84af39 is described below
commit c84af39d1d6697fb50c92cd61b6250dd22b4969d
Author: Abhishek Kumar <ab...@gmail.com>
AuthorDate: Sat May 9 10:45:52 2020 +0530
image: handle copy and delete actions for template/iso zone tab (#284)
Fixes #279
Implements copy and delete actions in the template/iso zones tab.
Also implements template/iso filter by dropdown.
Signed-off-by: Abhishek Kumar <ab...@gmail.com>
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
Co-authored-by: Rohit Yadav <ro...@shapeblue.com>
---
src/config/router.js | 2 +
src/config/section/image.js | 37 +---
src/locales/en.json | 2 +
src/views/AutogenView.vue | 68 +++++--
src/views/image/IsoZones.vue | 274 ++++++++++++++++++++++-----
src/views/image/RegisterOrUploadTemplate.vue | 25 ++-
src/views/image/TemplateZones.vue | 267 +++++++++++++++++++++-----
7 files changed, 520 insertions(+), 155 deletions(-)
diff --git a/src/config/router.js b/src/config/router.js
index b718773..58bc558 100644
--- a/src/config/router.js
+++ b/src/config/router.js
@@ -61,6 +61,7 @@ export function generateRouterMap (section) {
docHelp: child.docHelp,
permission: child.permission,
resourceType: child.resourceType,
+ filters: child.filters,
params: child.params ? child.params : {},
columns: child.columns,
details: child.details,
@@ -122,6 +123,7 @@ export function generateRouterMap (section) {
map.meta.resourceType = section.resourceType
map.meta.details = section.details
map.meta.actions = section.actions
+ map.meta.filters = section.filters
map.meta.treeView = section.treeView ? section.treeView : false
map.meta.tabs = section.treeView ? section.tabs : {}
diff --git a/src/config/section/image.js b/src/config/section/image.js
index 39031b8..39df4f2 100644
--- a/src/config/section/image.js
+++ b/src/config/section/image.js
@@ -27,8 +27,9 @@ export default {
title: 'Templates',
icon: 'save',
permission: ['listTemplates'],
- params: { templatefilter: 'executable' },
+ params: { templatefilter: 'self' },
resourceType: 'Template',
+ filters: ['self', 'shared', 'featured', 'community'],
columns: ['name', 'ostypename', 'status', 'hypervisor', 'account', 'domain', 'order'],
details: ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'format', 'ostypename', 'size', 'isready', 'passwordenabled', 'directdownload', 'isextractable', 'isdynamicallyscalable', 'ispublic', 'isfeatured', 'crosszones', 'type', 'account', 'domain', 'created'],
related: [{
@@ -95,21 +96,6 @@ export default {
popup: true,
show: (record, store) => { return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) || record.templatetype !== 'BUILTIN') },
component: () => import('@/views/image/UpdateTemplateIsoPermissions')
- },
- {
- api: 'copyTemplate',
- icon: 'copy',
- label: 'Copy Template',
- args: ['sourcezoneid', 'destzoneids'],
- dataView: true
- },
- {
- api: 'deleteTemplate',
- icon: 'delete',
- label: 'Delete Template',
- args: ['zoneid'],
- dataView: true,
- groupAction: true
}
]
},
@@ -118,8 +104,9 @@ export default {
title: 'ISOs',
icon: 'usb',
permission: ['listIsos'],
- params: { isofilter: 'executable' },
+ params: { isofilter: 'self' },
resourceType: 'ISO',
+ filters: ['self', 'shared', 'featured', 'community'],
columns: ['name', 'ostypename', 'account', 'domain'],
details: ['name', 'id', 'displaytext', 'checksum', 'ostypename', 'size', 'bootable', 'isready', 'directdownload', 'isextractable', 'ispublic', 'isfeatured', 'crosszones', 'account', 'domain', 'created'],
related: [{
@@ -180,24 +167,10 @@ export default {
icon: 'reconciliation',
label: 'Update ISO Permissions',
dataView: true,
+ args: ['op', 'accounts', 'projectids'],
popup: true,
show: (record, store) => { return (['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) || record.templatetype !== 'BUILTIN') },
component: () => import('@/views/image/UpdateTemplateIsoPermissions')
- },
- {
- api: 'copyIso',
- icon: 'copy',
- label: 'Copy ISO',
- args: ['sourcezoneid', 'destzoneids'],
- dataView: true
- },
- {
- api: 'deleteIso',
- icon: 'delete',
- label: 'Delete ISO',
- args: ['zoneid'],
- dataView: true,
- groupAction: true
}
]
},
diff --git a/src/locales/en.json b/src/locales/en.json
index ae303d1..1905f31 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -1279,7 +1279,9 @@
"filter": "Filter",
"featured": "Featured",
"community": "Community",
+"self": "Mine",
"selfexecutable": "Self",
+"shared": "Shared",
"sharedexecutable": "Shared",
"fixed": "Fixed Offering",
"customconstrained": "Custom Constrained",
diff --git a/src/views/AutogenView.vue b/src/views/AutogenView.vue
index 38fb535..b78d3f6 100644
--- a/src/views/AutogenView.vue
+++ b/src/views/AutogenView.vue
@@ -19,7 +19,7 @@
<div>
<a-card class="breadcrumb-card">
<a-row>
- <a-col :span="14" style="padding-left: 6px">
+ <a-col :span="12" style="padding-left: 6px">
<breadcrumb :resource="resource">
<a-tooltip placement="bottom" slot="end">
<template slot="title">
@@ -37,7 +37,7 @@
</a-tooltip>
</breadcrumb>
</a-col>
- <a-col :span="10">
+ <a-col :span="12">
<span style="float: right">
<action-button
style="margin-bottom: 5px"
@@ -47,6 +47,17 @@
:dataView="dataView"
:resource="resource"
@exec-action="execAction"/>
+ <a-select
+ v-if="filters && filters.length > 0"
+ placeholder="Filter By"
+ :value="$t(selectedFilter)"
+ style="min-width: 100px; margin-left: 10px"
+ @change="changeFilter">
+ <a-icon slot="suffixIcon" type="filter" />
+ <a-select-option v-for="filter in filters" :key="filter">
+ {{ $t(filter) }}
+ </a-select-option>
+ </a-select>
<a-input-search
style="width: 20vw; margin-left: 10px"
placeholder="Search"
@@ -305,6 +316,8 @@ export default {
showAction: false,
dataView: false,
treeView: false,
+ selectedFilter: '',
+ filters: [],
actions: [],
treeData: [],
treeSelected: {},
@@ -337,6 +350,7 @@ export default {
this.searchQuery = ''
this.page = 1
this.itemCount = 0
+ this.selectedFilter = ''
this.fetchData()
}
},
@@ -347,7 +361,7 @@ export default {
}
},
methods: {
- fetchData () {
+ fetchData (params = { listall: true }) {
if (this.routeName !== this.$route.name) {
this.routeName = this.$route.name
this.items = []
@@ -357,11 +371,12 @@ export default {
}
this.apiName = ''
this.actions = []
+ this.filters = this.$route.meta.filters || []
this.columns = []
this.columnKeys = []
this.treeData = []
this.treeSelected = {}
- var params = { listall: true }
+
if (Object.keys(this.$route.query).length > 0) {
Object.assign(params, this.$route.query)
} else if (this.$route.meta.params) {
@@ -393,6 +408,26 @@ export default {
return
}
+ if (['listTemplates', 'listIsos'].includes(this.apiName) && !this.dataView) {
+ if (['Admin'].includes(this.$store.getters.userInfo.roletype)) {
+ this.filters = ['all', ...this.filters]
+ if (this.selectedFilter === '') {
+ this.selectedFilter = 'all'
+ }
+ }
+ if (this.selectedFilter === '') {
+ this.selectedFilter = 'self'
+ }
+ }
+
+ if (this.selectedFilter && this.filters.length > 0) {
+ if (this.$route.path.startsWith('/template')) {
+ params.templatefilter = this.selectedFilter
+ } else if (this.$route.path.startsWith('/iso')) {
+ params.isofilter = this.selectedFilter
+ }
+ }
+
if (this.searchQuery !== '') {
if (this.apiName === 'listRoles') {
params.name = this.searchQuery
@@ -450,12 +485,6 @@ export default {
delete params.treeView
}
- if (['listTemplates', 'listIsos'].includes(this.apiName) && !this.dataView) {
- if (['Admin'].includes(this.$store.getters.userInfo.roletype)) {
- params.templatefilter = 'all'
- }
- }
-
api(this.apiName, params).then(json => {
var responseName
var objectName
@@ -524,6 +553,13 @@ export default {
this.loading = false
})
},
+ removeStringStartSubstringIfPresent (str, searchstr) {
+ var index = str.indexOf(searchstr)
+ if (index !== 0) {
+ return str
+ }
+ return str.slice(index + searchstr.length)
+ },
onSearch (value) {
this.searchQuery = value
this.page = 1
@@ -576,7 +612,7 @@ export default {
}
}
this.currentAction.loading = false
- if (action.dataView && action.icon === 'edit') {
+ if (action.dataView && ['copy', 'edit'].includes(action.icon)) {
this.fillEditFormFieldValues()
}
},
@@ -585,8 +621,11 @@ export default {
return
}
var paramName = param.name
+ var extractedParamName = paramName.replace('ids', '').replace('id', '').toLowerCase()
+ extractedParamName = this.removeStringStartSubstringIfPresent(extractedParamName, 'source')
+ extractedParamName = this.removeStringStartSubstringIfPresent(extractedParamName, 'dest')
var params = { listall: true }
- const possibleName = 'list' + paramName.replace('ids', '').replace('id', '').toLowerCase() + 's'
+ const possibleName = 'list' + extractedParamName + 's'
var possibleApi
if (this.currentAction.mapping && param.name in this.currentAction.mapping && this.currentAction.mapping[param.name].api) {
possibleApi = this.currentAction.mapping[param.name].api
@@ -668,6 +707,7 @@ export default {
let fieldName = null
if (field.type === 'uuid' || field.type === 'list' || field.name === 'account' || (this.currentAction.mapping && field.name in this.currentAction.mapping)) {
fieldName = field.name.replace('ids', 'name').replace('id', 'name')
+ fieldName = this.removeStringStartSubstringIfPresent(fieldName, 'source')
} else {
fieldName = field.name
}
@@ -774,6 +814,10 @@ export default {
}
})
},
+ changeFilter (filter) {
+ this.selectedFilter = filter
+ this.fetchData()
+ },
changePage (page, pageSize) {
this.page = page
this.pageSize = pageSize
diff --git a/src/views/image/IsoZones.vue b/src/views/image/IsoZones.vue
index 34cbe1b..afff771 100644
--- a/src/views/image/IsoZones.vue
+++ b/src/views/image/IsoZones.vue
@@ -16,35 +16,100 @@
// under the License.
<template>
- <div class="row-iso-zone">
- <a-row :gutter="12">
- <a-col :md="24" :lg="24">
- <a-table
- size="small"
- style="overflow-y: auto"
- :loading="loading || fetchLoading"
- :columns="columns"
- :dataSource="dataSource"
- :pagination="false"
- :rowKey="record => record.zoneid || record.id">
- <div slot="isready" slot-scope="text, record">
- <span v-if="record.isready">{{ $t('Yes') }}</span>
- <span v-else>{{ $t('No') }}</span>
- </div>
- </a-table>
- <a-pagination
- class="row-element"
- size="small"
- :current="page"
- :pageSize="pageSize"
- :total="itemCount"
- :showTotal="total => `Total ${total} items`"
- :pageSizeOptions="['10', '20', '40', '80', '100']"
- @change="handleChangePage"
- @showSizeChange="handleChangePageSize"
- showSizeChanger/>
- </a-col>
- </a-row>
+ <div>
+ <a-table
+ size="small"
+ style="overflow-y: auto"
+ :loading="loading || fetchLoading"
+ :columns="columns"
+ :dataSource="dataSource"
+ :pagination="false"
+ :rowKey="record => record.zoneid">
+ <div slot="isready" slot-scope="text, record">
+ <span v-if="record.isready">{{ $t('Yes') }}</span>
+ <span v-else>{{ $t('No') }}</span>
+ </div>
+ <template slot="action" slot-scope="text, record">
+ <span style="margin-right: 5px">
+ <a-button
+ :disabled="!('copyIso' in $store.getters.apis)"
+ icon="copy"
+ shape="circle"
+ :loading="copyLoading"
+ @click="showCopyIso(record)" />
+ </span>
+ <span style="margin-right: 5px">
+ <a-popconfirm
+ v-if="'deleteIso' in $store.getters.apis"
+ placement="topRight"
+ title="Delete the ISO for this zone?"
+ :ok-text="$t('Yes')"
+ :cancel-text="$t('No')"
+ :loading="deleteLoading"
+ @confirm="deleteIso(record)"
+ >
+ <a-button
+ type="danger"
+ icon="delete"
+ shape="circle" />
+ </a-popconfirm>
+ </span>
+ </template>
+ </a-table>
+ <a-pagination
+ class="row-element"
+ size="small"
+ :current="page"
+ :pageSize="pageSize"
+ :total="itemCount"
+ :showTotal="total => `Total ${total} items`"
+ :pageSizeOptions="['10', '20', '40', '80', '100']"
+ @change="handleChangePage"
+ @showSizeChange="handleChangePageSize"
+ showSizeChanger/>
+
+ <a-modal
+ v-if="'copyIso' in $store.getters.apis"
+ style="top: 20px;"
+ :title="$t('label.action.copy.ISO')"
+ :visible="showCopyActionForm"
+ :closable="true"
+ @ok="handleCopyIsoSubmit"
+ @cancel="onCloseCopyForm"
+ :confirmLoading="copyLoading"
+ centered>
+ <a-spin :spinning="copyLoading">
+ <a-form
+ :form="form"
+ @submit="handleCopyIsoSubmit"
+ layout="vertical">
+ <a-form-item :label="$t('zoneid')">
+ <a-select
+ id="zone-selection"
+ mode="multiple"
+ placeholder="Select Zones"
+ v-decorator="['zoneid', {
+ rules: [
+ {
+ required: true,
+ message: 'Please select option'
+ }
+ ]
+ }]"
+ showSearch
+ optionFilterProp="children"
+ :filterOption="(input, option) => {
+ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+ }"
+ :loading="zoneLoading">
+ <a-select-option v-for="zone in zones" :key="zone.id">
+ {{ zone.name }}
+ </a-select-option>
+ </a-select>
+ </a-form-item>
+ </a-form>
+ </a-spin>
+ </a-modal>
</div>
</template>
@@ -67,33 +132,49 @@ export default {
return {
columns: [],
dataSource: [],
- detailColumn: [],
- detail: [],
page: 1,
pageSize: 10,
itemCount: 0,
- fetchLoading: false
+ fetchLoading: false,
+ showCopyActionForm: false,
+ currentRecord: {},
+ zones: [],
+ zoneLoading: false,
+ copyLoading: false,
+ deleteLoading: false
}
},
+ beforeCreate () {
+ this.form = this.$form.createForm(this)
+ this.apiConfigParams = (this.$store.getters.apis.copyIso && this.$store.getters.apis.copyIso.params) || []
+ this.apiParams = {}
+ this.apiConfigParams.forEach(param => {
+ this.apiParams[param.name] = param
+ })
+ },
created () {
this.columns = [
{
- title: this.$t('name'),
- dataIndex: 'zonename',
- scopedSlots: { customRender: 'name' }
+ title: this.$t('zonename'),
+ dataIndex: 'zonename'
},
{
title: this.$t('status'),
- dataIndex: 'status',
- scopedSlots: { customRender: 'status' }
+ dataIndex: 'status'
},
{
title: this.$t('isready'),
dataIndex: 'isready',
scopedSlots: { customRender: 'isready' }
+ },
+ {
+ title: '',
+ dataIndex: 'action',
+ fixed: 'right',
+ width: 100,
+ scopedSlots: { customRender: 'action' }
}
]
- this.detailColumn = ['name', 'id', 'zonename', 'zoneid']
},
mounted () {
this.fetchData()
@@ -108,24 +189,18 @@ export default {
methods: {
fetchData () {
const params = {}
- params.listAll = true
params.id = this.resource.id
- params.isofilter = 'self'
+ params.isofilter = 'executable'
+ params.listall = true
params.page = this.page
params.pagesize = this.pageSize
this.dataSource = []
this.itemCount = 0
this.fetchLoading = true
-
api('listIsos', params).then(json => {
- const listIsos = json.listisosresponse.iso
- const count = json.listisosresponse.count
-
- if (listIsos) {
- this.dataSource = listIsos
- this.itemCount = count
- }
+ this.dataSource = json.listisosresponse.iso || []
+ this.itemCount = json.listisosresponse.count || 0
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
@@ -141,6 +216,105 @@ export default {
this.page = currentPage
this.pageSize = pageSize
this.fetchData()
+ },
+ deleteIso (record) {
+ const params = {
+ id: record.id,
+ zoneid: record.zoneid
+ }
+ this.deleteLoading = true
+ api('deleteIso', params).then(json => {
+ const jobId = json.deleteisoresponse.jobid
+ this.$store.dispatch('AddAsyncJob', {
+ title: this.$t('label.action.delete.ISO'),
+ jobid: jobId,
+ description: this.resource.name,
+ status: 'progress'
+ })
+ const singleZone = (this.dataSource.length === 1)
+ this.$pollJob({
+ jobId,
+ successMethod: result => {
+ if (singleZone) {
+ this.$router.go(-1)
+ } else {
+ this.fetchData()
+ }
+ },
+ errorMethod: () => this.fetchData(),
+ loadingMessage: `Deleting ISO ${this.resource.name} in progress`,
+ catchMessage: 'Error encountered while fetching async job result'
+ })
+ }).catch(error => {
+ this.$notifyError(error)
+ }).finally(() => {
+ this.deleteLoading = false
+ this.fetchData()
+ })
+ },
+ fetchZoneData () {
+ this.zones = []
+ this.zoneLoading = true
+ api('listZones', { listall: true }).then(json => {
+ const zones = json.listzonesresponse.zone || []
+ this.zones = [...zones.filter((zone) => this.currentRecord.zoneid !== zone.id)]
+ }).finally(() => {
+ this.zoneLoading = false
+ })
+ },
+ showCopyIso (record) {
+ this.currentRecord = record
+ this.form.setFieldsValue({
+ zoneid: []
+ })
+ this.fetchZoneData()
+ this.showCopyActionForm = true
+ },
+ onCloseCopyForm () {
+ this.currentRecord = {}
+ this.showCopyActionForm = false
+ },
+ handleCopyIsoSubmit (e) {
+ e.preventDefault()
+ this.form.validateFields((err, values) => {
+ if (err) {
+ return
+ }
+ const params = {
+ id: this.currentRecord.id,
+ sourcezoneid: this.currentRecord.zoneid,
+ destzoneids: values.zoneid.join()
+ }
+ this.copyLoading = true
+ api('copyIso', params).then(json => {
+ const jobId = json.copytemplateresponse.jobid
+ this.$store.dispatch('AddAsyncJob', {
+ title: this.$t('label.action.copy.ISO'),
+ jobid: jobId,
+ description: this.resource.name,
+ status: 'progress'
+ })
+ this.$pollJob({
+ jobId,
+ successMethod: result => {
+ this.fetchData()
+ },
+ errorMethod: () => this.fetchData(),
+ loadingMessage: `Copy ISO ${this.resource.name} in progress`,
+ catchMessage: 'Error encountered while fetching async job result'
+ })
+ }).catch(error => {
+ this.$notification.error({
+ message: 'Request Failed',
+ description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
+ })
+ }).finally(() => {
+ this.copyLoading = false
+ this.$emit('refresh-data')
+ this.onCloseCopyForm()
+ this.fetchData()
+ })
+ })
}
}
}
@@ -151,8 +325,4 @@ export default {
margin-top: 15px;
margin-bottom: 15px;
}
-
-.action-button button {
- margin-right: 5px;
-}
</style>
diff --git a/src/views/image/RegisterOrUploadTemplate.vue b/src/views/image/RegisterOrUploadTemplate.vue
index abbaf49..ef4d45d 100644
--- a/src/views/image/RegisterOrUploadTemplate.vue
+++ b/src/views/image/RegisterOrUploadTemplate.vue
@@ -79,14 +79,14 @@
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item
- :label="$t('zoneids')"
+ :label="$t('zone')"
:validate-status="zoneError"
:help="zoneErrorMessage">
<a-select
v-decorator="['zoneids', {
rules: [
{
- required: false,
+ required: true,
message: 'Please select option',
type: 'array'
}
@@ -96,7 +96,7 @@
mode="multiple"
:placeholder="apiParams.zoneids.description"
@change="handlerSelectZone">
- <a-select-option v-for="opt in zones.opts" :key="opt.name || opt.description">
+ <a-select-option v-for="opt in zones.opts" :key="opt.id">
{{ opt.name || opt.description }}
</a-select-option>
</a-select>
@@ -720,6 +720,12 @@ export default {
description: 'VHD'
})
break
+ case 'Simulator':
+ format.push({
+ id: 'VHD',
+ description: 'VHD'
+ })
+ break
case 'VMware':
this.hyperVMWShow = true
format.push({
@@ -764,9 +770,8 @@ export default {
this.resetSelect()
const params = {}
- const allZoneExists = value.filter(zone => zone === this.$t('label.all.zone'))
- if (allZoneExists.length > 0) {
+ if (value.includes(this.$t('label.all.zone'))) {
params.listAll = true
this.fetchHyperVisor(params)
return
@@ -817,15 +822,7 @@ export default {
params.zoneids = '-1'
continue
}
- const zonesSelected = []
- for (const index in input) {
- const name = input[index]
- const zone = this.zones.opts.filter(zone => zone.name === name)
- if (zone && zone[0]) {
- zonesSelected.push(zone[0].id)
- }
- }
- params[key] = zonesSelected.join(',')
+ params[key] = input.join()
} else if (key === 'zoneid') {
params[key] = values[key]
} else if (key === 'ostypeid') {
diff --git a/src/views/image/TemplateZones.vue b/src/views/image/TemplateZones.vue
index 0583d8f..87e81a6 100644
--- a/src/views/image/TemplateZones.vue
+++ b/src/views/image/TemplateZones.vue
@@ -16,35 +16,100 @@
// under the License.
<template>
- <div class="row-template-zone">
- <a-row :gutter="12">
- <a-col :md="24" :lg="24">
- <a-table
- size="small"
- style="overflow-y: auto"
- :loading="loading || fetchLoading"
- :columns="columns"
- :dataSource="dataSource"
- :pagination="false"
- :rowKey="record => record.zoneid">
- <div slot="isready" slot-scope="text, record">
- <span v-if="record.isready">{{ $t('Yes') }}</span>
- <span v-else>{{ $t('No') }}</span>
- </div>
- </a-table>
- <a-pagination
- class="row-element"
- size="small"
- :current="page"
- :pageSize="pageSize"
- :total="itemCount"
- :showTotal="total => `Total ${total} items`"
- :pageSizeOptions="['10', '20', '40', '80', '100']"
- @change="handleChangePage"
- @showSizeChange="handleChangePageSize"
- showSizeChanger/>
- </a-col>
- </a-row>
+ <div>
+ <a-table
+ size="small"
+ style="overflow-y: auto"
+ :loading="loading || fetchLoading"
+ :columns="columns"
+ :dataSource="dataSource"
+ :pagination="false"
+ :rowKey="record => record.zoneid">
+ <div slot="isready" slot-scope="text, record">
+ <span v-if="record.isready">{{ $t('Yes') }}</span>
+ <span v-else>{{ $t('No') }}</span>
+ </div>
+ <template slot="action" slot-scope="text, record">
+ <span style="margin-right: 5px">
+ <a-button
+ :disabled="!('copyTemplate' in $store.getters.apis)"
+ icon="copy"
+ shape="circle"
+ :loading="copyLoading"
+ @click="showCopyTemplate(record)" />
+ </span>
+ <span style="margin-right: 5px">
+ <a-popconfirm
+ v-if="'deleteTemplate' in $store.getters.apis"
+ placement="topRight"
+ title="Delete the template for this zone?"
+ :ok-text="$t('Yes')"
+ :cancel-text="$t('No')"
+ :loading="deleteLoading"
+ @confirm="deleteTemplate(record)"
+ >
+ <a-button
+ type="danger"
+ icon="delete"
+ shape="circle" />
+ </a-popconfirm>
+ </span>
+ </template>
+ </a-table>
+ <a-pagination
+ class="row-element"
+ size="small"
+ :current="page"
+ :pageSize="pageSize"
+ :total="itemCount"
+ :showTotal="total => `Total ${total} items`"
+ :pageSizeOptions="['10', '20', '40', '80', '100']"
+ @change="handleChangePage"
+ @showSizeChange="handleChangePageSize"
+ showSizeChanger/>
+
+ <a-modal
+ v-if="'copyTemplate' in $store.getters.apis"
+ style="top: 20px;"
+ :title="$t('label.action.copy.template')"
+ :visible="showCopyActionForm"
+ :closable="true"
+ @ok="handleCopyTemplateSubmit"
+ @cancel="onCloseCopyForm"
+ :confirmLoading="copyLoading"
+ centered>
+ <a-spin :spinning="copyLoading">
+ <a-form
+ :form="form"
+ @submit="handleCopyTemplateSubmit"
+ layout="vertical">
+ <a-form-item :label="$t('zoneid')">
+ <a-select
+ id="zone-selection"
+ mode="multiple"
+ placeholder="Select Zones"
+ v-decorator="['zoneid', {
+ rules: [
+ {
+ required: true,
+ message: 'Please select option'
+ }
+ ]
+ }]"
+ showSearch
+ optionFilterProp="children"
+ :filterOption="(input, option) => {
+ return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+ }"
+ :loading="zoneLoading">
+ <a-select-option v-for="zone in zones" :key="zone.id">
+ {{ zone.name }}
+ </a-select-option>
+ </a-select>
+ </a-form-item>
+ </a-form>
+ </a-spin>
+ </a-modal>
</div>
</template>
@@ -70,25 +135,44 @@ export default {
page: 1,
pageSize: 10,
itemCount: 0,
- fetchLoading: false
+ fetchLoading: false,
+ showCopyActionForm: false,
+ currentRecord: {},
+ zones: [],
+ zoneLoading: false,
+ copyLoading: false,
+ deleteLoading: false
}
},
+ beforeCreate () {
+ this.form = this.$form.createForm(this)
+ this.apiConfigParams = (this.$store.getters.apis.copyTemplate && this.$store.getters.apis.copyTemplate.params) || []
+ this.apiParams = {}
+ this.apiConfigParams.forEach(param => {
+ this.apiParams[param.name] = param
+ })
+ },
created () {
this.columns = [
{
- title: this.$t('name'),
- dataIndex: 'zonename',
- scopedSlots: { customRender: 'name' }
+ title: this.$t('zonename'),
+ dataIndex: 'zonename'
},
{
title: this.$t('status'),
- dataIndex: 'status',
- scopedSlots: { customRender: 'status' }
+ dataIndex: 'status'
},
{
title: this.$t('isready'),
dataIndex: 'isready',
scopedSlots: { customRender: 'isready' }
+ },
+ {
+ title: '',
+ dataIndex: 'action',
+ fixed: 'right',
+ width: 100,
+ scopedSlots: { customRender: 'action' }
}
]
},
@@ -105,24 +189,18 @@ export default {
methods: {
fetchData () {
const params = {}
- params.listAll = true
params.id = this.resource.id
- params.templatefilter = 'self'
+ params.templatefilter = 'executable'
+ params.listall = true
params.page = this.page
params.pagesize = this.pageSize
this.dataSource = []
this.itemCount = 0
this.fetchLoading = true
-
api('listTemplates', params).then(json => {
- const listTemplates = json.listtemplatesresponse.template
- const count = json.listtemplatesresponse.count
-
- if (listTemplates) {
- this.dataSource = listTemplates
- this.itemCount = count
- }
+ this.dataSource = json.listtemplatesresponse.template || []
+ this.itemCount = json.listtemplatesresponse.count || 0
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
@@ -138,6 +216,105 @@ export default {
this.page = currentPage
this.pageSize = pageSize
this.fetchData()
+ },
+ deleteTemplate (record) {
+ const params = {
+ id: record.id,
+ zoneid: record.zoneid
+ }
+ this.deleteLoading = true
+ api('deleteTemplate', params).then(json => {
+ const jobId = json.deletetemplateresponse.jobid
+ this.$store.dispatch('AddAsyncJob', {
+ title: this.$t('label.action.delete.template'),
+ jobid: jobId,
+ description: this.resource.name,
+ status: 'progress'
+ })
+ const singleZone = (this.dataSource.length === 1)
+ this.$pollJob({
+ jobId,
+ successMethod: result => {
+ if (singleZone) {
+ this.$router.go(-1)
+ } else {
+ this.fetchData()
+ }
+ },
+ errorMethod: () => this.fetchData(),
+ loadingMessage: `Deleting template ${this.resource.name} in progress`,
+ catchMessage: 'Error encountered while fetching async job result'
+ })
+ }).catch(error => {
+ this.$notifyError(error)
+ }).finally(() => {
+ this.deleteLoading = false
+ this.fetchData()
+ })
+ },
+ fetchZoneData () {
+ this.zones = []
+ this.zoneLoading = true
+ api('listZones', { listall: true }).then(json => {
+ const zones = json.listzonesresponse.zone || []
+ this.zones = [...zones.filter((zone) => this.currentRecord.zoneid !== zone.id)]
+ }).finally(() => {
+ this.zoneLoading = false
+ })
+ },
+ showCopyTemplate (record) {
+ this.currentRecord = record
+ this.form.setFieldsValue({
+ zoneid: []
+ })
+ this.fetchZoneData()
+ this.showCopyActionForm = true
+ },
+ onCloseCopyForm () {
+ this.currentRecord = {}
+ this.showCopyActionForm = false
+ },
+ handleCopyTemplateSubmit (e) {
+ e.preventDefault()
+ this.form.validateFields((err, values) => {
+ if (err) {
+ return
+ }
+ const params = {
+ id: this.currentRecord.id,
+ sourcezoneid: this.currentRecord.zoneid,
+ destzoneids: values.zoneid.join()
+ }
+ this.copyLoading = true
+ api('copyTemplate', params).then(json => {
+ const jobId = json.copytemplateresponse.jobid
+ this.$store.dispatch('AddAsyncJob', {
+ title: this.$t('label.action.copy.template'),
+ jobid: jobId,
+ description: this.resource.name,
+ status: 'progress'
+ })
+ this.$pollJob({
+ jobId,
+ successMethod: result => {
+ this.fetchData()
+ },
+ errorMethod: () => this.fetchData(),
+ loadingMessage: `Copy template ${this.resource.name} in progress`,
+ catchMessage: 'Error encountered while fetching async job result'
+ })
+ }).catch(error => {
+ this.$notification.error({
+ message: 'Request Failed',
+ description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
+ })
+ }).finally(() => {
+ this.copyLoading = false
+ this.$emit('refresh-data')
+ this.onCloseCopyForm()
+ this.fetchData()
+ })
+ })
}
}
}