diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmd.java index b15357620c0..6b17f5249f4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsZoneCmd.java @@ -23,8 +23,7 @@ public class DeleteDnsZoneCmd extends BaseAsyncCmd { //////////////// API Parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, - required = true, description = "the ID of the DNS zone") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, required = true, description = "The ID of the DNS zone") private Long id; ///////////////////////////////////////////////////// @@ -42,9 +41,7 @@ public class DeleteDnsZoneCmd extends BaseAsyncCmd { @Override public void execute() { try { - // The Manager handles both DB removal and Plugin execution - boolean result = dnsProviderManager.deleteDnsZone(this); - + boolean result = dnsProviderManager.deleteDnsZone(getId()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); setResponseObject(response); @@ -74,7 +71,7 @@ public class DeleteDnsZoneCmd extends BaseAsyncCmd { @Override public String getEventType() { - return EventTypes.EVENT_DNS_ZONE_DELETE; // Ensure this constant is added to EventTypes + return EventTypes.EVENT_DNS_ZONE_DELETE; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmd.java index 0d7750c339c..edc65eef926 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/ListDnsZonesCmd.java @@ -7,9 +7,8 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.DnsServerResponse; import org.apache.cloudstack.api.response.DnsZoneResponse; import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.api.response.NetworkResponse; -@APICommand(name = "listDnsZones", description = "Lists DNS Zones.", +@APICommand(name = "listDnsZones", description = "Lists DNS zones.", responseObject = DnsZoneResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListDnsZonesCmd extends BaseListAccountResourcesCmd { @@ -20,20 +19,19 @@ public class ListDnsZonesCmd extends BaseListAccountResourcesCmd { ///////////////////////////////////////////////////// /// @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, - description = "list DNS zone by ID") + description = "List DNS zone by ID") private Long id; @Parameter(name = "dnsserverid", type = CommandType.UUID, entityType = DnsServerResponse.class, - description = "list DNS zones belonging to a specific DNS Server") + description = "List DNS zones belonging to a specific DNS server") private Long dnsServerId; - @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, - description = "list DNS zones associated with a specific Network") - private Long networkId; + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List by zone name") + private String name; public Long getId() { return id; } public Long getDnsServerId() { return dnsServerId; } - public Long getNetworkId() { return networkId; } + public String getName() { return name; } @Override public void execute() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmd.java new file mode 100644 index 00000000000..d679db70646 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsZoneCmd.java @@ -0,0 +1,78 @@ +package org.apache.cloudstack.api.command.user.dns; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DnsZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.dns.DnsProviderManager; +import org.apache.cloudstack.dns.DnsZone; + +@APICommand(name = "updateDnsZone", description = "Updates a DNS Zone's metadata", + responseObject = DnsZoneResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class UpdateDnsZoneCmd extends BaseCmd { + + private static final String COMMAND_RESPONSE_NAME = "updatednszoneresponse"; + + @Inject + DnsProviderManager dnsProviderManager; + + ///////////////////////////////////////////////////// + //////////////// API Parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, + required = true, description = "The ID of the DNS zone") + private Long id; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "Display text for the zone") + private String description; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getDescription() { + return description; + } + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// Implementation ////////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + try { + DnsZone result = dnsProviderManager.updateDnsZone(this); + if (result != null) { + DnsZoneResponse response = dnsProviderManager.createDnsZoneResponse(result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update DNS Zone on external provider"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update DNS Zone: " + e.getMessage()); + } + } + + @Override + public String getCommandName() { + return COMMAND_RESPONSE_NAME; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java index f8ef89824c4..a2b19d32fa1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DnsZoneResponse.java @@ -59,6 +59,10 @@ public class DnsZoneResponse extends BaseResponse { @Param(description = "The state of the zone (Active/Inactive)") private DnsZone.State state; + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description for the DNS zone") + private String description; + public DnsZoneResponse() { super(); setObjectName("dnszone"); @@ -91,4 +95,12 @@ public class DnsZoneResponse extends BaseResponse { public void setState(DnsZone.State state) { this.state = state; } + + public void setId(String id) { + this.id = id; + } + + public void setDescription(String description) { + this.description = description; + } } diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java b/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java index 68ebd2a682d..74c74ad8a5f 100644 --- a/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsProvider.java @@ -25,11 +25,11 @@ public interface DnsProvider extends Adapter { DnsProviderType getProviderType(); // Validates connectivity to the server - boolean validate(DnsServer server) throws Exception; + void validate(DnsServer server) throws Exception; // Zone Operations - boolean provisionZone(DnsServer server, DnsZone zone) throws Exception; - boolean deleteZone(DnsServer server, DnsZone zone); + void provisionZone(DnsServer server, DnsZone zone); + void deleteZone(DnsServer server, DnsZone zone) ; DnsRecord createRecord(DnsServer server, DnsZone zone, DnsRecord record); boolean updateRecord(DnsServer server, DnsZone zone, DnsRecord record); diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsProviderManager.java b/api/src/main/java/org/apache/cloudstack/dns/DnsProviderManager.java index 0421e8fffb6..fc4bb004df6 100644 --- a/api/src/main/java/org/apache/cloudstack/dns/DnsProviderManager.java +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsProviderManager.java @@ -24,11 +24,11 @@ import org.apache.cloudstack.api.command.user.dns.CreateDnsRecordCmd; import org.apache.cloudstack.api.command.user.dns.CreateDnsZoneCmd; import org.apache.cloudstack.api.command.user.dns.DeleteDnsRecordCmd; import org.apache.cloudstack.api.command.user.dns.DeleteDnsServerCmd; -import org.apache.cloudstack.api.command.user.dns.DeleteDnsZoneCmd; import org.apache.cloudstack.api.command.user.dns.ListDnsRecordsCmd; import org.apache.cloudstack.api.command.user.dns.ListDnsServersCmd; import org.apache.cloudstack.api.command.user.dns.ListDnsZonesCmd; import org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd; +import org.apache.cloudstack.api.command.user.dns.UpdateDnsZoneCmd; import org.apache.cloudstack.api.response.DnsRecordResponse; import org.apache.cloudstack.api.response.DnsServerResponse; import org.apache.cloudstack.api.response.DnsZoneResponse; @@ -47,8 +47,14 @@ public interface DnsProviderManager extends Manager, PluggableService { DnsServer getDnsServer(Long id); - DnsZone createDnsZone(CreateDnsZoneCmd cmd); - boolean deleteDnsZone(DeleteDnsZoneCmd cmd); + // Allocates the DB row (State: Inactive) + DnsZone allocateDnsZone(CreateDnsZoneCmd cmd); + // Calls the Plugin (State: Inactive -> Active) + DnsZone provisionDnsZone(long zoneId); + + DnsZone getDnsZone(Long id); + DnsZone updateDnsZone(UpdateDnsZoneCmd cmd); + boolean deleteDnsZone(Long id); ListResponse listDnsZones(ListDnsZonesCmd cmd); DnsZone getDnsZone(long id); @@ -60,11 +66,6 @@ public interface DnsProviderManager extends Manager, PluggableService { List listProviderNames(); - // Allocates the DB row (State: Inactive) - DnsZone allocateDnsZone(CreateDnsZoneCmd cmd); - - // Calls the Plugin (State: Inactive -> Active) - DnsZone provisionDnsZone(long zoneId); // Helper to create the response object DnsZoneResponse createDnsZoneResponse(DnsZone zone); diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql index 7eb199c0205..e0731301bbc 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql @@ -92,7 +92,6 @@ CREATE TABLE `cloud`.`dns_zone` ( `removed` datetime DEFAULT NULL COMMENT 'Date removed (soft delete)', PRIMARY KEY (`id`), CONSTRAINT `uc_dns_zone__uuid` UNIQUE (`uuid`), - CONSTRAINT `uc_dns_zone__name_server_type` UNIQUE (`name`, `dns_server_id`, `type`), KEY `i_dns_zone__dns_server` (`dns_server_id`), KEY `i_dns_zone__account_id` (`account_id`), CONSTRAINT `fk_dns_zone__dns_server_id` FOREIGN KEY (`dns_server_id`) REFERENCES `dns_server` (`id`) ON DELETE CASCADE, diff --git a/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsClient.java b/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsClient.java index 23b23e7e7e2..244cef704b4 100644 --- a/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsClient.java +++ b/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsClient.java @@ -18,11 +18,14 @@ package org.apache.cloudstack.dns.powerdns; import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.List; import org.apache.http.HttpStatus; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; @@ -45,16 +48,15 @@ public class PowerDnsClient implements AutoCloseable { private final CloseableHttpClient httpClient; public void validate(String baseUrl, String apiKey) { - String checkUrl = buildApiUrl(baseUrl, "/api/v1/servers"); + String checkUrl = buildApiUrl(baseUrl, "/servers"); HttpGet request = new HttpGet(checkUrl); request.addHeader("X-API-Key", apiKey); + request.addHeader("Content-Type", "application/json"); request.addHeader("Accept", "application/json"); try (CloseableHttpResponse response = httpClient.execute(request)) { int statusCode = response.getStatusLine().getStatusCode(); - String body = response.getEntity() != null - ? EntityUtils.toString(response.getEntity()) - : null; + String body = response.getEntity() != null ? EntityUtils.toString(response.getEntity()) : null; if (statusCode == HttpStatus.SC_OK) { JsonNode root = MAPPER.readTree(body); @@ -88,27 +90,25 @@ public class PowerDnsClient implements AutoCloseable { } public void createZone(String baseUrl, String apiKey, String zoneName, List nameservers) { - String url = buildApiUrl(baseUrl, "/servers/localhost/zones"); - ObjectNode json = MAPPER.createObjectNode(); - json.put("name", zoneName.endsWith(".") ? zoneName : zoneName + "."); - json.put("kind", "Native"); - json.put("dnssec", false); - - if (nameservers != null && !nameservers.isEmpty()) { - ArrayNode nsArray = json.putArray("nameservers"); - for (String ns : nameservers) { - nsArray.add(ns.endsWith(".") ? ns : ns + "."); - } - } - - logger.debug("Creating PowerDNS zone: {} using URL: {}", zoneName, url); - - HttpPost request = new HttpPost(url); - request.addHeader("X-API-Key", apiKey); - request.addHeader("Content-Type", "application/json"); - request.addHeader("Accept", "application/json"); - + String normalizedZone = zoneName.endsWith(".") ? zoneName : zoneName + "."; try { + String url = buildApiUrl(baseUrl, "/servers/localhost/zones"); + ObjectNode json = MAPPER.createObjectNode(); + json.put("name", normalizedZone); + json.put("kind", "Native"); + json.put("dnssec", false); + + if (nameservers != null && !nameservers.isEmpty()) { + ArrayNode nsArray = json.putArray("nameservers"); + for (String ns : nameservers) { + nsArray.add(ns.endsWith(".") ? ns : ns + "."); + } + } + + HttpPost request = new HttpPost(url); + request.addHeader("X-API-Key", apiKey); + request.addHeader("Content-Type", "application/json"); + request.addHeader("Accept", "application/json"); request.setEntity(new StringEntity(json.toString())); try (CloseableHttpResponse response = httpClient.execute(request)) { @@ -127,21 +127,55 @@ public class PowerDnsClient implements AutoCloseable { throw new CloudRuntimeException("Zone already exists: " + zoneName); } - if (statusCode == HttpStatus.SC_UNAUTHORIZED || - statusCode == HttpStatus.SC_FORBIDDEN) { + if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { throw new CloudRuntimeException("Invalid PowerDNS API key"); } logger.debug("Unexpected PowerDNS response: HTTP {} Body: {}", statusCode, body); - throw new CloudRuntimeException(String.format("Failed to create zone %s (HTTP %d)", zoneName, statusCode)); } - } catch (IOException e) { throw new CloudRuntimeException("Error while creating PowerDNS zone " + zoneName, e); } } + public void deleteZone(String baseUrl, String apiKey, String zoneName) { + String normalizedZone = zoneName.endsWith(".") ? zoneName : zoneName + "."; + try { + String encodedZone = URLEncoder.encode(normalizedZone, StandardCharsets.UTF_8); + String url = buildApiUrl(baseUrl, "/servers/localhost/zones/" + encodedZone); + HttpDelete request = new HttpDelete(url); + request.addHeader("X-API-Key", apiKey); + request.addHeader("Content-Type", "application/json"); + request.addHeader("Accept", "application/json"); + + try (CloseableHttpResponse response = httpClient.execute(request)) { + + int statusCode = response.getStatusLine().getStatusCode(); + String body = response.getEntity() != null ? EntityUtils.toString(response.getEntity()) : null; + + if (statusCode == HttpStatus.SC_NO_CONTENT) { + logger.debug("Zone {} deleted successfully", normalizedZone); + return; + } + + if (statusCode == HttpStatus.SC_NOT_FOUND) { + logger.debug("Zone {} not found in PowerDNS", normalizedZone); + return; + } + + if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { + throw new CloudRuntimeException("Invalid PowerDNS API key"); + } + + logger.debug("Unexpected PowerDNS response while deleting zone: HTTP {} Body: {}", statusCode, body); + throw new CloudRuntimeException(String.format("Failed to delete zone %s (HTTP %d)", normalizedZone, statusCode)); + } + } catch (IOException e) { + throw new CloudRuntimeException("Error while deleting PowerDNS zone " + zoneName, e); + } + } + public PowerDnsClient() { RequestConfig config = RequestConfig.custom() diff --git a/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsProvider.java b/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsProvider.java index a5056b045f9..3e9b5d5f83d 100644 --- a/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsProvider.java +++ b/plugins/dns/powerdns/src/main/java/org/apache/cloudstack/dns/powerdns/PowerDnsProvider.java @@ -38,38 +38,21 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { return DnsProviderType.PowerDNS; } - public boolean validate(DnsServer server) { - if (StringUtils.isBlank(server.getUrl())) { - throw new IllegalArgumentException("PowerDNS API URL cannot be empty"); - } - if (StringUtils.isBlank(server.getApiKey())) { - throw new IllegalArgumentException("PowerDNS API key cannot be empty"); - } + public void validate(DnsServer server) { + validateServerParams(server); client.validate(server.getUrl(), server.getApiKey()); - logger.debug("PowerDNS credentials validated for {}", server.getUrl()); - return true; } @Override - public boolean provisionZone(DnsServer server, DnsZone zone) throws Exception { - if (StringUtils.isBlank(zone.getName())) { - throw new IllegalArgumentException("Zone name cannot be empty"); - } - - if (StringUtils.isBlank(server.getUrl())) { - throw new IllegalArgumentException("PowerDNS API URL cannot be empty"); - } - - if (StringUtils.isBlank(server.getApiKey())) { - throw new IllegalArgumentException("PowerDNS API key cannot be empty"); - } + public void provisionZone(DnsServer server, DnsZone zone) { + validateServerZoneParams(server, zone); client.createZone(server.getUrl(), server.getApiKey(), zone.getName(), null); - return true; } @Override - public boolean deleteZone(DnsServer server, DnsZone zone) { - return false; + public void deleteZone(DnsServer server, DnsZone zone) { + validateServerZoneParams(server, zone); + client.deleteZone(server.getUrl(), server.getApiKey(), zone.getName()); } @Override @@ -92,6 +75,24 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { return List.of(); } + void validateServerZoneParams(DnsServer server, DnsZone zone) { + validateServerParams(server); + if (StringUtils.isBlank(zone.getName())) { + throw new IllegalArgumentException("Zone name cannot be empty"); + } + } + + void validateServerParams(DnsServer server) { + if (StringUtils.isBlank(server.getUrl())) { + throw new IllegalArgumentException("PowerDNS API URL cannot be empty"); + } + if (StringUtils.isBlank(server.getApiKey())) { + throw new IllegalArgumentException("PowerDNS API key cannot be empty"); + } + } + + + @Override public boolean configure(String name, Map params) { if (client == null) { 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 7192261087d..6cc1b62e13d 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -22,6 +22,7 @@ import java.util.List; import javax.inject.Inject; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd; import org.apache.cloudstack.api.command.user.dns.CreateDnsRecordCmd; import org.apache.cloudstack.api.command.user.dns.CreateDnsZoneCmd; @@ -33,6 +34,7 @@ import org.apache.cloudstack.api.command.user.dns.ListDnsRecordsCmd; import org.apache.cloudstack.api.command.user.dns.ListDnsServersCmd; import org.apache.cloudstack.api.command.user.dns.ListDnsZonesCmd; import org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd; +import org.apache.cloudstack.api.command.user.dns.UpdateDnsZoneCmd; import org.apache.cloudstack.api.response.DnsRecordResponse; import org.apache.cloudstack.api.response.DnsServerResponse; import org.apache.cloudstack.api.response.DnsZoneResponse; @@ -84,7 +86,6 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa throw new InvalidParameterValueException( "This Account already has a DNS Server integration for URL: " + cmd.getUrl()); } - DnsProviderType type = DnsProviderType.fromString(cmd.getProvider()); DnsProvider provider = getProvider(type); DnsServerVO server = new DnsServerVO(cmd.getName(), cmd.getUrl(), type, cmd.getCredentials(), cmd.getPort(), @@ -230,18 +231,74 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa } @Override - public DnsZone createDnsZone(CreateDnsZoneCmd cmd) { - return null; + public boolean deleteDnsZone(Long zoneId) { + DnsZoneVO zone = dnsZoneDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("DNS Zone with ID " + zoneId + " not found."); + } + + Account caller = CallContext.current().getCallingAccount(); + accountMgr.checkAccess(caller, null, true, zone); + DnsServerVO server = dnsServerDao.findById(zone.getDnsServerId()); + if (server != null && zone.getState() == DnsZone.State.Active) { + try { + DnsProvider provider = getProvider(server.getProviderType()); + logger.debug("Deleting DNS zone {} from provider.", zone.getName()); + provider.deleteZone(server, zone); + } catch (Exception ex) { + logger.error("Failed to delete zone from provider", ex); + throw new CloudRuntimeException("Failed to delete DNS zone."); + } + } + return dnsZoneDao.remove(zoneId); } @Override - public boolean deleteDnsZone(DeleteDnsZoneCmd cmd) { - return false; + public DnsZone getDnsZone(Long id) { + return dnsZoneDao.findById(id); + } + + @Override + public DnsZone updateDnsZone(UpdateDnsZoneCmd cmd) { + DnsZoneVO zone = dnsZoneDao.findById(cmd.getId()); + if (zone == null) { + throw new InvalidParameterValueException("DNS zone not found."); + } + + // ACL Check + Account caller = CallContext.current().getCallingAccount(); + accountMgr.checkAccess(caller, null, true, zone); + + // Update fields + boolean updated = false; + if (cmd.getDescription() != null) { + zone.setDescription(cmd.getDescription()); + updated = true; + } + + if (updated) { + dnsZoneDao.update(zone.getId(), zone); + } + return zone; } @Override public ListResponse listDnsZones(ListDnsZonesCmd cmd) { - return null; + Account caller = CallContext.current().getCallingAccount(); + Filter searchFilter = new Filter(DnsZoneVO.class, ApiConstants.ID, true, cmd.getStartIndex(), cmd.getPageSizeVal()); + Long accountIdFilter = caller.getAccountId(); + String keyword = cmd.getKeyword(); + if (cmd.getName() != null) { + keyword = cmd.getName(); + } + Pair, Integer> result = dnsZoneDao.searchZones(cmd.getId(), cmd.getDnsServerId(), keyword, accountIdFilter, searchFilter); + List zoneResponses = new ArrayList<>(); + for (DnsZoneVO zone : result.first()) { + zoneResponses.add(createDnsZoneResponse(zone)); + } + ListResponse response = new ListResponse<>(); + response.setResponses(zoneResponses, result.second()); + return response; } @Override @@ -313,20 +370,15 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa try { DnsProvider provider = getProvider(server.getProviderType()); logger.debug("Provision DNS zone: {} on DNS server: {}", dnsZone.getName(), server.getName()); - boolean success = provider.provisionZone(server, dnsZone); - if (success) { - dnsZone.setState(DnsZone.State.Active); - dnsZoneDao.update(dnsZone.getId(), dnsZone); - return dnsZone; - } else { - logger.error("DNS provider failed to provision zone: {}", dnsZone.getName()); - throw new CloudRuntimeException("DNS provider failed to provision zone"); - } + provider.provisionZone(server, dnsZone); + dnsZone.setState(DnsZone.State.Active); + dnsZoneDao.update(dnsZone.getId(), dnsZone); } catch (Exception ex) { logger.error("Failed to provision zone: {} on server: {}", dnsZone.getName(), server.getName(), ex); dnsZoneDao.remove(zoneId); throw new CloudRuntimeException("Failed to provision zone: " + dnsZone.getName()); } + return dnsZone; } @Override @@ -336,6 +388,8 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa res.setDnsServerId(zone.getDnsServerId()); res.setType(zone.getType()); res.setState(zone.getState()); + res.setId(zone.getUuid()); + res.setDescription(zone.getDescription()); return res; } @@ -364,6 +418,7 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa cmdList.add(CreateDnsZoneCmd.class); cmdList.add(ListDnsZonesCmd.class); cmdList.add(DeleteDnsZoneCmd.class); + cmdList.add(UpdateDnsZoneCmd.class); // DNS Record Commands cmdList.add(CreateDnsRecordCmd.class); diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneDao.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneDao.java index d618597757d..a2489323e8e 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneDao.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsZoneDao.java @@ -22,10 +22,13 @@ import java.util.List; import org.apache.cloudstack.dns.DnsZone; import org.apache.cloudstack.dns.vo.DnsZoneVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; public interface DnsZoneDao extends GenericDao { List listByServerId(long serverId); List listByAccount(long accountId); DnsZoneVO findByNameServerAndType(String name, long dnsServerId, DnsZone.ZoneType type); + Pair, Integer> searchZones(Long id, Long dnsServerId, String keyword, Long accountId, 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 cb693338ef5..b45d10723a1 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 @@ -24,6 +24,8 @@ import org.apache.cloudstack.dns.DnsZone; import org.apache.cloudstack.dns.vo.DnsZoneVO; import org.springframework.stereotype.Component; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; @@ -34,6 +36,7 @@ public class DnsZoneDaoImpl extends GenericDaoBase implements D SearchBuilder ServerSearch; SearchBuilder AccountSearch; SearchBuilder NameServerTypeSearch; + SearchBuilder AllFieldsSearch; public DnsZoneDaoImpl() { super(); @@ -50,6 +53,13 @@ public class DnsZoneDaoImpl extends GenericDaoBase implements D NameServerTypeSearch.and(DNS_SERVER_ID, NameServerTypeSearch.entity().getDnsServerId(), SearchCriteria.Op.EQ); NameServerTypeSearch.and(ApiConstants.TYPE, NameServerTypeSearch.entity().getType(), SearchCriteria.Op.EQ); NameServerTypeSearch.done(); + + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and(ApiConstants.ID, AllFieldsSearch.entity().getId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and(DNS_SERVER_ID, AllFieldsSearch.entity().getDnsServerId(), SearchCriteria.Op.EQ); + AllFieldsSearch.and(ApiConstants.NAME, AllFieldsSearch.entity().getName(), SearchCriteria.Op.LIKE); + AllFieldsSearch.and(ApiConstants.ACCOUNT_ID, AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + AllFieldsSearch.done(); } @Override @@ -74,4 +84,22 @@ public class DnsZoneDaoImpl extends GenericDaoBase implements D sc.setParameters(ApiConstants.TYPE, type); return findOneBy(sc); } + + @Override + public Pair, Integer> searchZones(Long id, Long dnsServerId, String keyword, Long accountId, Filter filter) { + SearchCriteria sc = AllFieldsSearch.create(); + if (id != null) { + sc.setParameters(ApiConstants.ID, id); + } + if (dnsServerId != null) { + sc.setParameters(DNS_SERVER_ID, dnsServerId); + } + if (keyword != null) { + sc.setParameters(ApiConstants.NAME, "%" + keyword + "%"); + } + if (accountId != null) { + sc.setParameters(ApiConstants.ACCOUNT_ID, accountId); + } + return searchAndCount(sc, filter); + } } diff --git a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsZoneVO.java b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsZoneVO.java index 0d47c1abad0..2988ee49acd 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsZoneVO.java +++ b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsZoneVO.java @@ -153,4 +153,8 @@ public class DnsZoneVO implements DnsZone { } public void setState(State state) { this.state = state; } + + public void setDescription(String description) { + this.description = description; + } }