Skip to content

Commit 5682a5a

Browse files
committed
modify delete flow for dns zone and server, ask to key in name
1 parent bde9fd9 commit 5682a5a

File tree

4 files changed

+356
-15
lines changed

4 files changed

+356
-15
lines changed

server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,10 @@ public boolean deleteDnsZone(Long zoneId) {
310310

311311
boolean dbResult = Transaction.execute((TransactionCallback<Boolean>) status -> {
312312
DnsZoneNetworkMapVO networkMapVO = dnsZoneNetworkMapDao.findByZoneId(zoneId);
313+
DnsProvider provider = getProviderByType(server.getProviderType());
314+
// Remove DNS records from nic_details if there are any
313315
if (networkMapVO != null) {
314-
// Remove DNS records from nic_details if there are any
315316
try {
316-
DnsProvider provider = getProviderByType(server.getProviderType());
317317
List<DnsRecord> records = provider.listRecords(server, dnsZone);
318318
if (CollectionUtils.isNotEmpty(records)) {
319319
List<String> dnsRecordNames = records.stream().map(DnsRecord::getName).filter(Objects::nonNull)
@@ -324,19 +324,19 @@ public boolean deleteDnsZone(Long zoneId) {
324324
} catch (Exception ex) {
325325
logger.warn("Failed to fetch DNS records for dnsZone: {}, perform manual cleanup.", dnsZoneName, ex);
326326
}
327-
// Remove DNS zone from provider and cleanup DB
328-
try {
329-
DnsProvider provider = getProviderByType(server.getProviderType());
330-
provider.deleteZone(server, dnsZone);
331-
logger.debug("Deleted DNS zone: {} from provider", dnsZoneName);
332-
} catch (DnsNotFoundException ex) {
333-
logger.warn("DNS zone: {} is not present in the provider, proceeding with cleanup", dnsZoneName);
334-
} catch (Exception ex) {
335-
logger.error("Failed to delete DNS zone from provider", ex);
336-
throw new CloudRuntimeException(String.format("Failed to delete DNS zone: %s.", dnsZoneName));
337-
}
338327
dnsZoneNetworkMapDao.removeNetworkMappingByZoneId(zoneId);
339328
}
329+
330+
// Remove DNS zone from provider and cleanup DB
331+
try {
332+
provider.deleteZone(server, dnsZone);
333+
logger.debug("Deleted DNS zone: {} from provider", dnsZoneName);
334+
} catch (DnsNotFoundException ex) {
335+
logger.warn("DNS zone: {} is not present in the provider, proceeding with cleanup", dnsZoneName);
336+
} catch (Exception ex) {
337+
logger.error("Failed to delete DNS zone from provider", ex);
338+
throw new CloudRuntimeException(String.format("Failed to delete DNS zone: %s.", dnsZoneName));
339+
}
340340
return dnsZoneDao.remove(zoneId);
341341
});
342342

ui/src/config/section/network.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,7 @@ export default {
15651565
message: 'message.action.delete.dns.server',
15661566
dataView: true,
15671567
popup: true,
1568+
component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/DeleteDnsServer.vue'))),
15681569
show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) },
15691570
groupAction: false,
15701571
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
@@ -1615,9 +1616,9 @@ export default {
16151616
message: 'message.action.delete.dns.zone',
16161617
dataView: true,
16171618
popup: true,
1618-
groupAction: false,
1619+
component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/DeleteDnsZone.vue'))),
16191620
show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) },
1620-
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
1621+
groupAction: false
16211622
}
16221623
]
16231624
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
<template>
19+
<div class="form-layout" v-ctrl-enter="handleSubmit">
20+
<a-spin :spinning="loading">
21+
<div style="margin-bottom: 20px;">
22+
<a-alert type="warning">
23+
<template #message>
24+
<div v-html="$t('message.action.delete.dns.server')"></div>
25+
</template>
26+
</a-alert>
27+
</div>
28+
29+
<div style="margin-bottom: 20px; word-break: break-all;">
30+
Please type <strong>{{ resource.name }}</strong> to confirm.
31+
</div>
32+
33+
<div v-if="dnsZones && dnsZones.length > 0" style="margin-bottom: 20px;">
34+
<a-alert type="error">
35+
<template #message>
36+
<div>
37+
<strong>This action will impact {{ this.totalDnsZones }} DNS Zone(s):</strong>
38+
<ul style="margin-top: 10px; margin-bottom: 0; padding-left: 20px;">
39+
<li v-for="zone in dnsZones.slice(0, this.maxResults)" :key="zone.id">{{ zone.name }}</li>
40+
</ul>
41+
<div v-if="this.totalDnsZones > this.maxResults" style="margin-top: 5px; font-style: italic;">
42+
...and {{ this.totalDnsZones - this.maxResults }} more
43+
</div>
44+
</div>
45+
</template>
46+
</a-alert>
47+
</div>
48+
49+
<a-form
50+
ref="formRef"
51+
:model="form"
52+
:rules="rules"
53+
layout="vertical">
54+
55+
<a-form-item name="name" ref="name">
56+
<a-input
57+
v-model:value="form.name"
58+
:placeholder="resource.name"
59+
v-focus="true" />
60+
</a-form-item>
61+
62+
<div class="action-button">
63+
<a-button @click="closeAction">
64+
{{ $t('label.cancel') }}
65+
</a-button>
66+
<a-button
67+
type="primary"
68+
danger
69+
:disabled="form.name !== resource.name"
70+
:loading="loading"
71+
@click="handleSubmit">
72+
{{ $t('label.delete') }}
73+
</a-button>
74+
</div>
75+
</a-form>
76+
</a-spin>
77+
</div>
78+
</template>
79+
80+
<script>
81+
import { getAPI, postAPI } from '@/api'
82+
83+
export default {
84+
name: 'DeleteDnsServer',
85+
props: {
86+
resource: {
87+
type: Object,
88+
required: true
89+
}
90+
},
91+
data () {
92+
return {
93+
loading: false,
94+
dnsZones: [],
95+
form: {
96+
name: ''
97+
},
98+
rules: {
99+
name: [{ required: true, message: this.$t('message.error.required.input') }]
100+
},
101+
maxResults: 5,
102+
totalDnsZones: 0
103+
}
104+
},
105+
created () {
106+
this.fetchDnsZones()
107+
},
108+
methods: {
109+
async fetchDnsZones () {
110+
this.loading = true
111+
try {
112+
const response = await getAPI('listDnsZones', { dnsserverid: this.resource.id, page: 1, pagesize: this.maxResults, filter: 'name' })
113+
this.dnsZones = response?.listdnszonesresponse?.dnszone || []
114+
this.totalDnsZones = response?.listdnszonesresponse?.count || 0
115+
} catch (error) {
116+
console.error('Failed to fetch DNS zones', error)
117+
} finally {
118+
this.loading = false
119+
}
120+
},
121+
async handleSubmit () {
122+
if (this.loading || this.form.name !== this.resource.name) return
123+
124+
this.loading = true
125+
126+
try {
127+
const params = {
128+
id: this.resource.id
129+
}
130+
131+
const response = await postAPI('deleteDnsServer', params)
132+
const jobId = response?.deletednsserverresponse?.jobid
133+
if (jobId) {
134+
this.$pollJob({
135+
jobId: jobId,
136+
title: this.$t('label.dns.delete.server'),
137+
description: this.resource.name,
138+
successMethod: () => {
139+
this.$notification.success({
140+
message: this.$t('label.dns.delete.server'),
141+
description: `Successfully deleted DNS server ${this.resource.name}`
142+
})
143+
},
144+
loadingMessage: `${this.$t('label.dns.delete.server')} ${this.$t('label.in.progress')}`,
145+
catchMessage: this.$t('error.fetching.async.job.result')
146+
})
147+
}
148+
149+
if (this.$route.path !== '/dnsserver') {
150+
this.$router.push({ path: '/dnsserver' })
151+
} else {
152+
this.$emit('refresh-data')
153+
}
154+
this.closeAction()
155+
} catch (error) {
156+
this.$notification.error({
157+
message: this.$t('message.request.failed'),
158+
description: error?.response?.headers['x-description'] || error.message,
159+
duration: 0
160+
})
161+
} finally {
162+
this.loading = false
163+
}
164+
},
165+
closeAction () {
166+
this.$emit('close-action')
167+
}
168+
}
169+
}
170+
</script>
171+
172+
<style lang="less" scoped>
173+
.form-layout {
174+
width: 80vw;
175+
@media (min-width: 600px) {
176+
width: 450px;
177+
}
178+
}
179+
180+
.action-button {
181+
text-align: right;
182+
margin-top: 20px;
183+
}
184+
.action-button button {
185+
margin-left: 8px;
186+
}
187+
</style>

0 commit comments

Comments
 (0)