mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
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 <abhishek.mrt22@gmail.com> Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
eb5a9431b8
commit
4247ec00aa
@ -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 : {}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@ -1279,7 +1279,9 @@
|
||||
"filter": "Filter",
|
||||
"featured": "Featured",
|
||||
"community": "Community",
|
||||
"self": "Mine",
|
||||
"selfexecutable": "Self",
|
||||
"shared": "Shared",
|
||||
"sharedexecutable": "Shared",
|
||||
"fixed": "Fixed Offering",
|
||||
"customconstrained": "Custom Constrained",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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') {
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user