This commit is contained in:
Imvedansh 2026-01-22 15:02:59 +01:00 committed by GitHub
commit 962abd38ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 208 additions and 3 deletions

View File

@ -932,6 +932,7 @@
"label.endpoint": "Endpoint",
"label.endport": "End port",
"label.enter.account.name": "Enter the account name",
"label.enter.domain.name": "Enter the domain name",
"label.enter.code": "Enter 2FA code to verify",
"label.enter.static.pin": "Enter static PIN to verify",
"label.enter.token": "Enter token",
@ -2970,6 +2971,9 @@
"message.delete.account.processing": "Deleting account",
"message.delete.account.success": "Successfully deleted account",
"message.delete.account.warning": "Deleting this account will delete all of the instances, volumes and snapshots associated with the account.",
"message.delete.domain.confirm": "Please confirm that you want to delete this domain by entering the name of the domain below.",
"message.delete.domain.warning": "This domain may contain accounts, users, or sub-domains. All of these must be removed before the domain can be deleted. This action cannot be undone.",
"message.delete.domain.failed": "Delete domain failed",
"message.delete.acl.processing": "Removing ACL rule...",
"message.delete.acl.rule": "Remove ACL rule",
"message.delete.acl.rule.failed": "Failed to remove ACL rule.",

View File

@ -0,0 +1,155 @@
// 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
:visible="true"
:title="$t('label.action.delete.domain') + ': ' + domain.name"
:okText="$t('label.delete.domain')"
okType="danger"
:confirmLoading="loading"
:ok-button-props="{ disabled: !canDelete }"
@cancel="emitClose"
@ok="emitConfirm">
<a-alert
type="warning"
show-icon
style="margin-bottom: 16px">
<template #message>
<div v-html="$t('message.delete.domain.warning')"></div>
</template>
</a-alert>
<a-spin v-if="loading" />
<a-table
v-else
size="small"
:columns="columns"
:dataSource="accountVmSummary"
:pagination="false"
rowKey="account" />
<div style="margin-top: 16px">
<a-alert style="margin-bottom: 10px">
<template #message>
<div v-html="$t('message.delete.domain.confirm')"></div>
</template>
</a-alert>
<a-input
v-model:value="confirmText"
:placeholder="$t('label.enter.domain.name')" />
</div>
</a-modal>
</template>
<script>
import { api } from '@/api'
export default {
name: 'DomainDeleteConfirm',
props: {
domain: {
type: Object,
required: true
}
},
data () {
return {
loading: false,
confirmText: '',
accountVmSummary: []
}
},
computed: {
canDelete () {
return this.confirmText.trim() === this.domain.name.trim()
},
columns () {
return [
{ title: this.$t('label.account'), dataIndex: 'account' },
{ title: this.$t('label.total') + ' VMs', dataIndex: 'total' },
{ title: this.$t('label.running') + ' VMs', dataIndex: 'running' },
{ title: this.$t('label.stopped') + ' VMs', dataIndex: 'stopped' }
]
}
},
mounted () {
this.fetchDomainImpact()
},
methods: {
emitClose () {
this.$emit('close')
},
emitConfirm () {
if (this.canDelete) {
this.$emit('confirm')
}
},
async fetchDomainImpact () {
this.loading = true
try {
const accResp = await api('listAccounts', {
domainid: this.domain.id,
listall: true
})
const accounts =
accResp.listaccountsresponse &&
accResp.listaccountsresponse.account
? accResp.listaccountsresponse.account
: []
const vmResp = await api('listVirtualMachines', {
domainid: this.domain.id,
listall: true
})
const vms =
vmResp.listvirtualmachinesresponse &&
vmResp.listvirtualmachinesresponse.virtualmachine
? vmResp.listvirtualmachinesresponse.virtualmachine
: []
this.accountVmSummary = accounts.map(account => {
const accountVms = vms.filter(vm => vm.account === account.name)
const running = accountVms.filter(vm => vm.state === 'Running').length
const stopped = accountVms.length - running
return {
account: account.name,
total: accountVms.length,
running,
stopped
}
})
} catch (e) {
this.$notification.error({
message: this.$t('message.request.failed'),
description: e.response?.headers['x-description'] || this.$t('message.request.failed')
})
} finally {
this.loading = false
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -74,6 +74,11 @@
:resource="resource"
:action="action"/>
</div>
<domain-delete-confirm
v-if="showDeleteConfirm"
:domain="deleteDomainResource"
@close="showDeleteConfirm = false"
@confirm="confirmDeleteDomain" />
</div>
</template>
@ -87,6 +92,7 @@ import ActionButton from '@/components/view/ActionButton'
import TreeView from '@/components/view/TreeView'
import DomainActionForm from '@/views/iam/DomainActionForm'
import ResourceView from '@/components/view/ResourceView'
import DomainDeleteConfirm from '@/components/view/DomainDeleteConfirm'
import eventBus from '@/config/eventBus'
export default {
@ -96,7 +102,8 @@ export default {
ActionButton,
TreeView,
DomainActionForm,
ResourceView
ResourceView,
DomainDeleteConfirm
},
mixins: [mixinDevice],
data () {
@ -111,7 +118,9 @@ export default {
action: {},
dataView: false,
domainStore: {},
treeDeletedKey: null
treeDeletedKey: null,
showDeleteConfirm: false,
deleteDomainResource: null
}
},
computed: {
@ -205,7 +214,12 @@ export default {
})
},
execAction (action) {
this.treeDeletedKey = action.api === 'deleteDomain' ? this.resource.key : null
if (action.api === 'deleteDomain') {
this.deleteDomainResource = this.resource
this.showDeleteConfirm = true
return
}
this.treeDeletedKey = null
this.actionData = []
this.action = action
this.action.params = store.getters.apis[this.action.api].params
@ -319,6 +333,38 @@ export default {
closeAction () {
this.showAction = false
},
confirmDeleteDomain () {
const domain = this.deleteDomainResource
const params = { id: domain.id, cleanup: true }
api('deleteDomain', params).then(json => {
const jobId = json.deletedomainresponse.jobid
this.$pollJob({
jobId,
title: this.$t('label.action.delete.domain'),
description: domain.name,
loadingMessage: `${this.$t('label.action.delete.domain')} ${domain.name}`,
successMessage: `${this.$t('label.action.delete.domain')} ${domain.name}`,
catchMessage: this.$t('error.fetching.async.job.result'),
successMethod: () => {
if (this.$route.params.id === domain.id) {
this.$router.push({ path: '/domain' })
}
this.fetchData()
}
})
}).catch(error => {
this.$notification.error({
message: this.$t('message.request.failed'),
description: error.response?.headers['x-description'] || this.$t('message.request.failed')
})
}).finally(() => {
this.showDeleteConfirm = false
this.deleteDomainResource = null
this.treeDeletedKey = null
})
},
forceRerender () {
this.treeViewKey += 1
}