diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmd.java index 53c697db9f2..5ffffc7c304 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AssociateDnsZoneToNetworkCmd.java @@ -29,13 +29,13 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DnsZoneNetworkMapResponse; import org.apache.cloudstack.api.response.DnsZoneResponse; import org.apache.cloudstack.api.response.NetworkResponse; -import org.apache.cloudstack.dns.DnsZone; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; import com.cloud.user.Account; @APICommand(name = "associateDnsZoneToNetwork", @@ -73,9 +73,9 @@ public class AssociateDnsZoneToNetworkCmd extends BaseCmd { @Override public long getEntityOwnerId() { - DnsZone zone = _entityMgr.findById(DnsZone.class, dnsZoneId); - if (zone != null) { - return zone.getAccountId(); + Network network = _entityMgr.findById(Network.class, networkId); + if (network != null) { + return network.getAccountId(); } return Account.ACCOUNT_ID_SYSTEM; } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDao.java index 2ca901fa3e5..1d32ea20f1e 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDao.java @@ -16,10 +16,14 @@ // under the License. package com.cloud.vm.dao; +import java.util.List; + import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; import com.cloud.utils.db.GenericDao; import com.cloud.vm.NicDetailVO; public interface NicDetailsDao extends GenericDao, ResourceDetailsDao { + + void removeDetailsForValuesIn(String resourceName, List values); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDaoImpl.java index 2a0b02aae1d..9ee2e41b34b 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDetailsDaoImpl.java @@ -17,17 +17,40 @@ package com.cloud.vm.dao; +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; import org.springframework.stereotype.Component; import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.vm.NicDetailVO; @Component public class NicDetailsDaoImpl extends ResourceDetailsDaoBase implements NicDetailsDao { + private final SearchBuilder NameValuesSearch; + + public NicDetailsDaoImpl() { + super(); + NameValuesSearch = createSearchBuilder(); + NameValuesSearch.and(ApiConstants.NAME, NameValuesSearch.entity().getName(), SearchCriteria.Op.EQ); + NameValuesSearch.and(ApiConstants.VALUE, NameValuesSearch.entity().getValue(), SearchCriteria.Op.IN); + NameValuesSearch.done(); + } + @Override public void addDetail(long resourceId, String key, String value, boolean display) { super.addDetail(new NicDetailVO(resourceId, key, value, display)); } + + @Override + public void removeDetailsForValuesIn(String resourceName, List values) { + SearchCriteria sc = NameValuesSearch.create(); + sc.setParameters(ApiConstants.NAME, resourceName); + sc.setParameters(ApiConstants.VALUE, values.toArray()); + remove(sc); + } } diff --git a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java index 8374680aeda..6fb0592ba88 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.dns; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -59,6 +60,7 @@ import org.apache.cloudstack.dns.vo.DnsServerVO; import org.apache.cloudstack.dns.vo.DnsZoneJoinVO; import org.apache.cloudstack.dns.vo.DnsZoneNetworkMapVO; import org.apache.cloudstack.dns.vo.DnsZoneVO; +import org.apache.commons.collections.CollectionUtils; import org.apache.logging.log4j.util.Strings; import org.springframework.stereotype.Component; @@ -280,10 +282,11 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa if (cmd.getCleanup()) { List dnsZones = dnsZoneDao.findDnsZonesByServerId(dnsServerId); for (DnsZoneVO dnsZone : dnsZones) { - long dnsZoneId = dnsZone.getId(); - dnsZoneNetworkMapDao.removeNetworkMappingByZoneId(dnsZoneId); - // ToDo: delete nic_record_urls from vm_details if present before removing dnsZone - dnsZoneDao.remove(dnsZoneId); + try { + deleteDnsZone(dnsZone.getId()); + } catch (Exception ex) { + logger.error("Error cleaning up DNS zone: {} during DNS server: {} deletion", dnsZone.getName(), dnsServer.getName()); + } } } return dnsServerDao.remove(dnsServerId); @@ -304,22 +307,39 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa if (server == null) { throw new CloudRuntimeException(String.format("The DNS server not found for DNS zone: %s", dnsZoneName)); } - try { - DnsProvider provider = getProviderByType(server.getProviderType()); - provider.deleteZone(server, dnsZone); - logger.debug("Deleted DNS zone: {} from provider", dnsZoneName); - } catch (DnsNotFoundException ex) { - logger.warn("DNS zone: {} is not present in the provider, proceeding with cleanup", dnsZoneName); - } catch (Exception ex) { - logger.error("Failed to delete DNS zone from provider", ex); - throw new CloudRuntimeException(String.format("Failed to delete DNS zone: %s.", dnsZoneName)); - } boolean dbResult = Transaction.execute((TransactionCallback) status -> { - dnsZoneNetworkMapDao.removeNetworkMappingByZoneId(zoneId); - // ToDo: delete nic_record_urls from vm_details if present before removing dnsZone + DnsZoneNetworkMapVO networkMapVO = dnsZoneNetworkMapDao.findByZoneId(zoneId); + if (networkMapVO != null) { + // Remove DNS records from nic_details if there are any + try { + DnsProvider provider = getProviderByType(server.getProviderType()); + List records = provider.listRecords(server, dnsZone); + if (CollectionUtils.isNotEmpty(records)) { + List dnsRecordNames = records.stream().map(DnsRecord::getName).filter(Objects::nonNull) + .map(name -> name.replaceAll("\\.+$", "")) + .collect(Collectors.toList()); + nicDetailsDao.removeDetailsForValuesIn(ApiConstants.NIC_DNS_RECORD, dnsRecordNames); + } + } catch (Exception ex) { + logger.warn("Failed to fetch DNS records for dnsZone: {}, perform manual cleanup.", dnsZoneName, ex); + } + // Remove DNS zone from provider and cleanup DB + try { + DnsProvider provider = getProviderByType(server.getProviderType()); + provider.deleteZone(server, dnsZone); + logger.debug("Deleted DNS zone: {} from provider", dnsZoneName); + } catch (DnsNotFoundException ex) { + logger.warn("DNS zone: {} is not present in the provider, proceeding with cleanup", dnsZoneName); + } catch (Exception ex) { + logger.error("Failed to delete DNS zone from provider", ex); + throw new CloudRuntimeException(String.format("Failed to delete DNS zone: %s.", dnsZoneName)); + } + dnsZoneNetworkMapDao.removeNetworkMappingByZoneId(zoneId); + } return dnsZoneDao.remove(zoneId); }); + if (!dbResult) { logger.error("Failed to remove DNS zone {} from DB after provider deletion", dnsZoneName); } @@ -612,7 +632,10 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa throw new InvalidParameterValueException("DNS zone not found."); } accountMgr.checkAccess(caller, null, true, dnsZone); - + DnsServerVO dnsServer = dnsServerDao.findById(dnsZone.getDnsServerId()); + if (dnsServer == null) { + throw new CloudRuntimeException("The underlying DNS server for this DNS zone is missing."); + } NetworkVO network = networkDao.findById(cmd.getNetworkId()); if (network == null) { throw new InvalidParameterValueException("Network not found."); @@ -621,11 +644,11 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa throw new CloudRuntimeException(String.format("Operation is not permitted for network type: %s", network.getGuestType())); } accountMgr.checkAccess(caller, null, true, network); - DnsZoneNetworkMapVO existing = dnsZoneNetworkMapDao.findByNetworkId(network.getId()); if (existing != null) { throw new InvalidParameterValueException("Network has existing DNS zone associated to it."); } + DnsZoneNetworkMapVO mapping = new DnsZoneNetworkMapVO(dnsZone.getId(), network.getId(), cmd.getSubDomain()); dnsZoneNetworkMapDao.persist(mapping); DnsZoneNetworkMapResponse response = new DnsZoneNetworkMapResponse(); diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDao.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDao.java index 8a884620c4b..ebbe0248197 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDao.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDao.java @@ -33,7 +33,5 @@ public interface DnsServerDao extends GenericDao { List listDnsServerIdsByAccountId(Long accountId); - Pair, Integer> searchDnsServers(Long id, String keyword, String provider, Long accountId, Filter filter); - Pair, Integer> searchDnsServer(Long dnsServerId, Long accountId, Set domainIds, DnsProviderType providerType, String keyword, Filter filter); } diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java index 8eff7397c8f..b9d545e54ea 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java @@ -82,24 +82,6 @@ public class DnsServerDaoImpl extends GenericDaoBase implemen return customSearch(sc, null); } - @Override - public Pair, Integer> searchDnsServers(Long id, String keyword, String provider, Long accountId, Filter filter) { - SearchCriteria sc = AllFieldsSearch.create(); - if (id != null) { - sc.setParameters(ApiConstants.ID, id); - } - if (keyword != null) { - sc.setParameters(ApiConstants.NAME, "%" + keyword + "%"); - } - if (provider != null) { - sc.setParameters(ApiConstants.PROVIDER_TYPE, provider); - } - if (accountId != null) { - sc.setParameters(ApiConstants.ACCOUNT_ID, accountId); - } - return searchAndCount(sc, filter); - } - @Override public Pair, Integer> searchDnsServer(Long dnsServerId, Long accountId, Set domainIds, DnsProviderType providerType, String keyword, Filter filter) { diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneDaoImpl.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneDaoImpl.java index 8245fc01f30..70e8c6f9c0a 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneDaoImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneDaoImpl.java @@ -41,7 +41,7 @@ public class DnsZoneDaoImpl extends GenericDaoBase implements D super(); DnsServerSearch = createSearchBuilder(); - DnsServerSearch.selectFields(DnsServerSearch.entity().getDnsServerId()); + DnsServerSearch.selectFields(DnsServerSearch.entity().getId()); DnsServerSearch.and(ApiConstants.DNS_SERVER_ID, DnsServerSearch.entity().getDnsServerId(), SearchCriteria.Op.EQ); DnsServerSearch.done(); diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneNetworkMapDao.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneNetworkMapDao.java index 1936ba3fe66..72669348c3d 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneNetworkMapDao.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneNetworkMapDao.java @@ -24,4 +24,6 @@ import com.cloud.utils.db.GenericDao; public interface DnsZoneNetworkMapDao extends GenericDao { void removeNetworkMappingByZoneId(long dnsZoneId); DnsZoneNetworkMapVO findByNetworkId(long networkId); + + DnsZoneNetworkMapVO findByZoneId(long networkId); } diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneNetworkMapDaoImpl.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneNetworkMapDaoImpl.java index ce61e26f799..7dcae3c4e32 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneNetworkMapDaoImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneNetworkMapDaoImpl.java @@ -54,4 +54,11 @@ public class DnsZoneNetworkMapDaoImpl extends GenericDaoBase sc = ZoneNetworkSearch.create(); + sc.setParameters(ApiConstants.DNS_ZONE_ID, dnsZoneId); + return findOneBy(sc); + } } diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index b919acd4ff8..a51783a3b4f 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -1528,7 +1528,7 @@ export default { { name: 'dnsserver', title: 'label.dns.server', - icon: 'global-outlined', + icon: 'cloud-server-outlined', permission: ['listDnsServers'], columns: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers', 'publicdomainsuffix'], details: ['name', 'url', 'provider', 'ispublic', 'port', 'nameservers', 'publicdomainsuffix', 'domain', 'account'], @@ -1555,6 +1555,7 @@ export default { label: 'label.dns.update.server', dataView: true, popup: true, + show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) }, component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/UpdateDnsServer.vue'))) }, { @@ -1564,6 +1565,7 @@ export default { message: 'message.action.delete.dns.server', dataView: true, popup: true, + show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) }, groupAction: false, groupMap: (selection) => { return selection.map(x => { return { id: x } }) } } @@ -1572,7 +1574,7 @@ export default { { name: 'dnszone', title: 'label.dns.zones', - icon: 'global-outlined', + icon: 'apartment-outlined', permission: ['listDnsZones'], columns: ['name', 'state', 'dnsservername', 'account', 'description'], details: ['name', 'id', 'state', 'dnsservername', 'dnsserverid', 'account', 'domainpath', 'description'], @@ -1603,8 +1605,8 @@ export default { label: 'label.dns.update.zone', dataView: true, popup: true, - component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/UpdateDnsZone.vue'))), - show: (record) => { return true } + show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) }, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/dns/UpdateDnsZone.vue'))) }, { api: 'deleteDnsZone', @@ -1614,8 +1616,7 @@ export default { dataView: true, popup: true, groupAction: false, - disabled: (record) => false, - show: (record) => { return true }, + show: (record, store) => { return record.account === store.userInfo.account || isAdminOrDomainAdmin(store.userInfo.roletype) }, groupMap: (selection) => { return selection.map(x => { return { id: x } }) } } ]