wip: dns provider framework

This commit is contained in:
Manoj Kumar 2026-02-04 23:04:56 +05:30
parent 408e8c079d
commit 7b9fc0e48e
No known key found for this signature in database
GPG Key ID: E952B7234D2C6F88
22 changed files with 1211 additions and 1 deletions

View File

@ -30,6 +30,9 @@ import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.backup.BackupRepositoryService;
import org.apache.cloudstack.config.Configuration;
import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet;
import org.apache.cloudstack.dns.DnsRecord;
import org.apache.cloudstack.dns.DnsServer;
import org.apache.cloudstack.dns.DnsZone;
import org.apache.cloudstack.extension.Extension;
import org.apache.cloudstack.extension.ExtensionCustomAction;
import org.apache.cloudstack.gpu.GpuCard;
@ -859,6 +862,14 @@ public class EventTypes {
public static final String EVENT_BACKUP_REPOSITORY_ADD = "BACKUP.REPOSITORY.ADD";
public static final String EVENT_BACKUP_REPOSITORY_UPDATE = "BACKUP.REPOSITORY.UPDATE";
// DNS Framework Events
public static final String EVENT_DNS_SERVER_ADD = "DNS.SERVER.ADD";
public static final String EVENT_DNS_SERVER_DELETE = "DNS.SERVER.DELETE";
public static final String EVENT_DNS_ZONE_CREATE = "DNS.ZONE.CREATE";
public static final String EVENT_DNS_ZONE_DELETE = "DNS.ZONE.DELETE";
public static final String EVENT_DNS_RECORD_CREATE = "DNS.RECORD.CREATE";
public static final String EVENT_DNS_RECORD_DELETE = "DNS.RECORD.DELETE";
static {
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking
@ -1397,6 +1408,15 @@ public class EventTypes {
// Backup Repository
entityEventDetails.put(EVENT_BACKUP_REPOSITORY_ADD, BackupRepositoryService.class);
entityEventDetails.put(EVENT_BACKUP_REPOSITORY_UPDATE, BackupRepositoryService.class);
// DNS Framework Events
entityEventDetails.put(EVENT_DNS_SERVER_ADD, DnsServer.class);
entityEventDetails.put(EVENT_DNS_SERVER_DELETE, DnsServer.class);
entityEventDetails.put(EVENT_DNS_ZONE_CREATE, DnsZone.class);
entityEventDetails.put(EVENT_DNS_ZONE_DELETE, DnsZone.class);
entityEventDetails.put(EVENT_DNS_RECORD_CREATE, DnsRecord.class);
entityEventDetails.put(EVENT_DNS_RECORD_DELETE, DnsRecord.class);
}
public static boolean isNetworkEvent(String eventType) {

View File

@ -40,6 +40,7 @@ import org.apache.cloudstack.affinity.AffinityGroupService;
import org.apache.cloudstack.alert.AlertService;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.dns.DnsProviderManager;
import org.apache.cloudstack.gpu.GpuService;
import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService;
@ -230,6 +231,9 @@ public abstract class BaseCmd {
@Inject
public RoutedIpv4Manager routedIpv4Manager;
@Inject
public DnsProviderManager dnsProviderManager;
public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException,
ResourceAllocationException, NetworkRuleConflictException;

View File

@ -0,0 +1,79 @@
// 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.
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.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.DnsServerResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.dns.DnsProviderManager;
import org.apache.cloudstack.dns.DnsServer;
@APICommand(name = "addDnsServer", description = "Adds a new external DNS server",
responseObject = DnsServerResponse.class, requestHasSensitiveInfo = true)
public class AddDnsServerCmd extends BaseCmd {
@Inject
DnsProviderManager dnsProviderManager;
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
///
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the DNS server")
private String name;
@Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "API URL of the provider")
private String url;
@Parameter(name = "provider", type = CommandType.STRING, required = true, description = "Provider type (e.g., PowerDNS)")
private String provider;
@Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, description = "API Username")
private String username;
@Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, description = "API Password or Token")
private String password;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public String getName() { return name; }
public String getUrl() { return url; }
public String getProvider() { return provider; }
public String getUsername() { return username; }
public String getPassword() { return password; }
@Override
public void execute() {
DnsServer server = dnsProviderManager.addDnsServer(this);
DnsServerResponse response = dnsProviderManager.createDnsServerResponse(server);
response.setResponseName(getCommandName());
setResponseObject(response);
}
@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();
}
}

View File

@ -0,0 +1,60 @@
package org.apache.cloudstack.api.command.user.dns;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DnsRecordResponse;
import org.apache.cloudstack.api.response.DnsZoneResponse;
import org.apache.cloudstack.context.CallContext;
import com.cloud.event.EventTypes;
@APICommand(name = "createDnsRecord", description = "Creates a DNS record directly on the provider",
responseObject = DnsRecordResponse.class)
public class CreateDnsRecordCmd extends BaseAsyncCmd {
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, required = true)
private Long zoneId;
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Record name")
private String name;
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true, description = "Record type (A, CNAME)")
private String type;
@Parameter(name = "content", type = CommandType.STRING, required = true, description = "IP or target")
private String content;
@Parameter(name = "ttl", type = CommandType.INTEGER, description = "Time to live")
private Integer ttl;
// Getters
public Long getZoneId() { return zoneId; }
public String getName() { return name; }
public String getType() { return type; }
public String getContent() { return content; }
public Integer getTtl() { return (ttl == null) ? 3600 : ttl; }
@Override
public void execute() {
try {
DnsRecordResponse response = dnsProviderManager.createDnsRecord(this);
response.setResponseName(getCommandName());
setResponseObject(response);
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create DNS Record: " + e.getMessage());
}
}
@Override
public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); }
@Override
public String getEventType() { return EventTypes.EVENT_DNS_RECORD_CREATE; }
@Override
public String getEventDescription() { return "Creating DNS Record: " + getName(); }
}

View File

@ -0,0 +1,134 @@
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.BaseAsyncCreateCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DnsServerResponse;
import org.apache.cloudstack.api.response.DnsZoneResponse;
import org.apache.cloudstack.api.response.NetworkResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.dns.DnsProviderManager;
import org.apache.cloudstack.dns.DnsZone;
import com.cloud.event.EventTypes;
import com.cloud.exception.ResourceAllocationException;
@APICommand(name = "createDnsZone", description = "Creates a new DNS Zone on a specific server",
responseObject = DnsZoneResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class CreateDnsZoneCmd extends BaseAsyncCreateCmd {
private static final String s_name = "creatednszoneresponse";
@Inject
DnsProviderManager dnsProviderManager;
/////////////////////////////////////////////////////
//////////////// API Parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true,
description = "The name of the DNS zone (e.g. example.com)")
private String name;
@Parameter(name = "dnsserverid", type = CommandType.UUID, entityType = DnsServerResponse.class,
required = true, description = "The ID of the DNS server to host this zone")
private Long dnsServerId;
@Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class,
description = "Optional: The Guest Network to associate with this zone for auto-registration")
private Long networkId;
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING,
description = "The type of zone (Public, Private). Defaults to Public.")
private String type;
// Standard CloudStack ownership parameters (account/domain) are handled
// automatically by the BaseCmd parent if we access them via getEntityOwnerId()
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public String getName() {
return name;
}
public Long getDnsServerId() {
return dnsServerId;
}
public Long getNetworkId() {
return networkId;
}
public String getType() {
return type;
}
/////////////////////////////////////////////////////
/////////////// Implementation //////////////////////
/////////////////////////////////////////////////////
@Override
public void create() throws ResourceAllocationException {
// Phase 1: DB Persist
// The manager should create the DnsZoneVO in 'Allocating' state
try {
DnsZone zone = dnsProviderManager.allocDnsZone(this);
if (zone != null) {
setEntityId(zone.getId());
setEntityUuid(zone.getUuid());
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create DNS Zone entity");
}
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to allocate DNS Zone: " + e.getMessage());
}
}
@Override
public void execute() {
// Phase 2: Action (Call Plugin)
// The manager should retrieve the zone by ID, call the plugin, and update state to 'Ready'
try {
// Note: We use getEntityId() which was set in the create() phase
DnsZone result = dnsProviderManager.provisionDnsZone(getEntityId());
if (result != null) {
DnsZoneResponse response = dnsProviderManager.createDnsZoneResponse(result);
response.setResponseName(getCommandName());
setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to provision DNS Zone on external provider");
}
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to provision DNS Zone: " + e.getMessage());
}
}
@Override
public String getCommandName() {
return s_name;
}
@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_DNS_ZONE_CREATE; // You must add this constant to EventTypes.java
}
@Override
public String getEventDescription() {
return "creating DNS zone: " + getName();
}
}

View File

@ -0,0 +1,57 @@
package org.apache.cloudstack.api.command.user.dns;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DnsZoneResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.context.CallContext;
import com.cloud.event.EventTypes;
@APICommand(name = "deleteDnsRecord", description = "Deletes a DNS record from the external provider",
responseObject = SuccessResponse.class)
public class DeleteDnsRecordCmd extends BaseAsyncCmd {
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class,
required = true, description = "The ID of the DNS zone")
private Long zoneId;
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true)
private String name;
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, required = true)
private String type;
// Getters
public Long getZoneId() { return zoneId; }
public String getName() { return name; }
public String getType() { return type; }
@Override
public void execute() {
try {
boolean result = dnsProviderManager.deleteDnsRecord(this);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS Record");
}
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Error deleting DNS Record: " + e.getMessage());
}
}
@Override
public long getEntityOwnerId() { return CallContext.current().getCallingAccount().getId(); }
@Override
public String getEventType() { return EventTypes.EVENT_DNS_RECORD_DELETE; }
@Override
public String getEventDescription() { return "Deleting DNS Record: " + getName(); }
}

View File

@ -0,0 +1,80 @@
package org.apache.cloudstack.api.command.user.dns;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DnsServerResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.dns.DnsServer;
import com.cloud.event.EventTypes;
import com.cloud.user.Account;
@APICommand(name = "deleteDnsServer", description = "Removes a DNS server integration",
responseObject = SuccessResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class DeleteDnsServerCmd extends BaseAsyncCmd {
private static final String COMMAND_RESPONSE_NAME = "deletednsserverresponse";
/////////////////////////////////////////////////////
//////////////// API Parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsServerResponse.class,
required = true, description = "the ID of the DNS server")
private Long id;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getId() {
return id;
}
/////////////////////////////////////////////////////
/////////////// Implementation //////////////////////
/////////////////////////////////////////////////////
@Override
public void execute() {
try {
boolean result = dnsProviderManager.deleteDnsServer(this);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS server");
}
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS server: " + e.getMessage());
}
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
@Override
public long getEntityOwnerId() {
// Look up the server to find its owner.
// This allows the Framework to check: Is Caller == Owner?
DnsServer server = dnsProviderManager.getDnsServer(id);
if (server != null) {
return server.getAccountId();
}
// If server not found, return System to fail safely (or let manager handle 404)
return Account.ACCOUNT_ID_SYSTEM;
}
@Override
public String getEventType() { return EventTypes.EVENT_DNS_SERVER_DELETE; }
@Override
public String getEventDescription() { return "Deleting DNS Server ID: " + getId(); }
}

View File

@ -0,0 +1,84 @@
package org.apache.cloudstack.api.command.user.dns;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.DnsZoneResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.dns.DnsZone;
import com.cloud.event.EventTypes;
import com.cloud.user.Account;
@APICommand(name = "deleteDnsZone", description = "Removes a DNS Zone from CloudStack and the external provider",
responseObject = SuccessResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class DeleteDnsZoneCmd extends BaseAsyncCmd {
private static final String COMMAND_RESPONSE_NAME = "deletednszoneresponse";
/////////////////////////////////////////////////////
//////////////// API Parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class,
required = true, description = "the ID of the DNS zone")
private Long id;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getId() {
return id;
}
/////////////////////////////////////////////////////
/////////////// API Implementation //////////////////////
/////////////////////////////////////////////////////
@Override
public void execute() {
try {
// The Manager handles both DB removal and Plugin execution
boolean result = dnsProviderManager.deleteDnsZone(this);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS Zone");
}
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete DNS Zone: " + e.getMessage());
}
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
@Override
public long getEntityOwnerId() {
// Look up the Zone to find the Account Owner
DnsZone zone = dnsProviderManager.getDnsZone(id);
if (zone != null) {
return zone.getAccountId();
}
// Fallback or System if not found (likely to fail in execute() anyway)
return Account.ACCOUNT_ID_SYSTEM;
}
@Override
public String getEventType() {
return EventTypes.EVENT_DNS_ZONE_DELETE; // Ensure this constant is added to EventTypes
}
@Override
public String getEventDescription() {
return "Deleting DNS Zone ID: " + getId();
}
}

View File

@ -0,0 +1,53 @@
// 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.
package org.apache.cloudstack.api.command.user.dns;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.response.DnsProviderResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.dns.DnsProviderManager;
@APICommand(name = "listDnsProviders", description = "Lists available DNS plugin providers",
responseObject = DnsProviderResponse.class, requestHasSensitiveInfo = false)
public class ListDnsProvidersCmd extends BaseListCmd {
@Inject
DnsProviderManager dnsManager;
@Override
public void execute() {
List<String> providers = dnsManager.listProviderNames();
ListResponse<DnsProviderResponse> response = new ListResponse<>();
List<DnsProviderResponse> responses = new ArrayList<>();
for (String name : providers) {
DnsProviderResponse resp = new DnsProviderResponse();
resp.setName(name);
resp.setObjectName("dnsprovider");
responses.add(resp);
}
response.setResponses(responses);
response.setResponseName(getCommandName());
setResponseObject(response);
}
}

View File

@ -0,0 +1,30 @@
package org.apache.cloudstack.api.command.user.dns;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.DnsRecordResponse;
import org.apache.cloudstack.api.response.DnsZoneResponse;
import org.apache.cloudstack.api.response.ListResponse;
@APICommand(name = "listDnsRecords", description = "Lists DNS records from the external provider",
responseObject = DnsRecordResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class ListDnsRecordsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class,
required = true, description = "the ID of the DNS zone to list records from")
private Long zoneId;
public Long getZoneId() {
return zoneId;
}
@Override
public void execute() {
// The manager will fetch live data from the plugin
ListResponse<DnsRecordResponse> response = dnsProviderManager.listDnsRecords(this);
response.setResponseName(getCommandName());
setResponseObject(response);
}
}

View File

@ -0,0 +1,61 @@
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.BaseListAccountResourcesCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.DnsServerResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.dns.DnsProviderManager;
@APICommand(name = "listDnsServers", description = "Lists DNS servers owned by the account.",
responseObject = DnsServerResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class ListDnsServersCmd extends BaseListAccountResourcesCmd {
private static final String COMMAND_RESPONSE_NAME = "listdnsserversresponse";
@Inject
DnsProviderManager dnsProviderManager;
/////////////////////////////////////////////////////
//////////////// API Parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsServerResponse.class,
description = "the ID of the DNS server")
private Long id;
@Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING,
description = "filter by provider type (e.g. PowerDNS, Cloudflare)")
private String provider;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getId() {
return id;
}
public String getProvider() {
return provider;
}
/////////////////////////////////////////////////////
/////////////// Implementation //////////////////////
/////////////////////////////////////////////////////
@Override
public void execute() {
ListResponse<DnsServerResponse> response = dnsProviderManager.listDnsServers(this);
response.setResponseName(getCommandName());
setResponseObject(response);
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
}

View File

@ -0,0 +1,49 @@
package org.apache.cloudstack.api.command.user.dns;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListAccountResourcesCmd;
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.",
responseObject = DnsZoneResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
public class ListDnsZonesCmd extends BaseListAccountResourcesCmd {
private static final String COMMAND_RESPONSE_NAME = "listdnszonesresponse";
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
///
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneResponse.class,
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")
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;
public Long getId() { return id; }
public Long getDnsServerId() { return dnsServerId; }
public Long getNetworkId() { return networkId; }
@Override
public void execute() {
ListResponse<DnsZoneResponse> response = dnsProviderManager.listDnsZones(this);
response.setResponseName(getCommandName());
setResponseObject(response);
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
}

View File

@ -0,0 +1,48 @@
// 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.
package org.apache.cloudstack.api.response;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
public class DnsProviderResponse extends BaseResponse {
@SerializedName(ApiConstants.NAME)
@Param(description = "The name of the DNS provider (e.g. PowerDNS, Cloudflare)")
private String name;
// Constructors
public DnsProviderResponse() {}
public DnsProviderResponse(String name) {
this.name = name;
setObjectName("dnsprovider"); // Sets the JSON wrapper name
}
// Accessors
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,71 @@
// 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.
package org.apache.cloudstack.api.response;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.dns.DnsRecord;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
public class DnsRecordResponse extends BaseResponse {
@SerializedName(ApiConstants.NAME)
@Param(description = "the name of the DNS record")
private String name;
@SerializedName(ApiConstants.TYPE)
@Param(description = "the type of the DNS record (A, CNAME, etc)")
private String type;
@SerializedName("content")
@Param(description = "the content of the record (IP address or target)")
private String content;
@SerializedName("ttl")
@Param(description = "the time to live (TTL) in seconds")
private Integer ttl;
@SerializedName(ApiConstants.ZONE_ID)
@Param(description = "the ID of the zone this record belongs to")
private String zoneId;
@SerializedName("sourceid")
@Param(description = "the external ID of the record on the provider")
private String sourceId;
public DnsRecordResponse() {
super();
setObjectName("dnsrecord");
}
// Setters
public void setName(String name) { this.name = name; }
// Accepts String or Enum.toString()
public void setType(String type) { this.type = type; }
public void setType(DnsRecord.RecordType type) {
this.type = (type != null) ? type.name() : null;
}
public void setContent(String content) { this.content = content; }
public void setTtl(Integer ttl) { this.ttl = ttl; }
public void setZoneId(String zoneId) { this.zoneId = zoneId; }
public void setSourceId(String sourceId) { this.sourceId = sourceId; }
}

View File

@ -0,0 +1,67 @@
// 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.
package org.apache.cloudstack.api.response;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference;
import org.apache.cloudstack.dns.DnsServer;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
@EntityReference(value = DnsServer.class)
public class DnsServerResponse extends BaseResponse {
@SerializedName(ApiConstants.ID)
@Param(description = "the ID of the DNS server")
private String id;
@SerializedName(ApiConstants.NAME)
@Param(description = "the name of the DNS server")
private String name;
@SerializedName(ApiConstants.URL)
@Param(description = "the URL of the DNS server API")
private String url;
@SerializedName(ApiConstants.PROVIDER)
@Param(description = "the provider type of the DNS server")
private String provider;
@SerializedName(ApiConstants.ACCOUNT)
@Param(description = "the account associated with the DNS server")
private String accountName;
@SerializedName(ApiConstants.PROJECT)
@Param(description = "the project name of the DNS server")
private String projectName;
@SerializedName(ApiConstants.DOMAIN_ID)
@Param(description = "the domain ID of the DNS server")
private String domainId;
@SerializedName(ApiConstants.DOMAIN)
@Param(description = "the domain name of the DNS server")
private String domainName;
public DnsServerResponse() {
super();
setObjectName("dnsserver");
}
}

View File

@ -0,0 +1,62 @@
// 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.
package org.apache.cloudstack.api.response;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference;
import org.apache.cloudstack.dns.DnsZone;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
@EntityReference(value = DnsZone.class)
public class DnsZoneResponse extends BaseResponse {
@SerializedName(ApiConstants.ID)
@Param(description = "the ID of the DNS zone")
private String id;
@SerializedName(ApiConstants.NAME)
@Param(description = "the name of the DNS zone")
private String name;
@SerializedName("dnsserverid")
@Param(description = "the ID of the DNS server this zone belongs to")
private String dnsServerId;
@SerializedName("dnsservername")
@Param(description = "the name of the DNS server this zone belongs to")
private String dnsServerName;
@SerializedName(ApiConstants.NETWORK_ID)
@Param(description = "the ID of the network this zone is associated with")
private String networkId;
@SerializedName(ApiConstants.NETWORK_NAME)
@Param(description = "the name of the network this zone is associated with")
private String networkName;
@SerializedName(ApiConstants.TYPE)
@Param(description = "the type of the zone (Public/Private)")
private String type;
public DnsZoneResponse() {
super();
setObjectName("dnszone");
}
}

View File

@ -0,0 +1,39 @@
// 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.
package org.apache.cloudstack.dns;
import java.util.List;
public interface DnsProvider {
// Returns the provider type string (e.g., "PowerDNS")
String getProviderType();
// Validates connectivity to the server
boolean validate(DnsServer server);
// Zone Operations
boolean createZone(DnsServer server, DnsZone zone);
boolean deleteZone(DnsServer server, DnsZone zone);
DnsRecord createRecord(DnsServer server, DnsZone zone, DnsRecord record);
boolean updateRecord(DnsServer server, DnsZone zone, DnsRecord record);
boolean deleteRecord(DnsServer server, DnsZone zone, DnsRecord record);
List<DnsRecord> listRecords(DnsServer server, DnsZone zone);
}

View File

@ -0,0 +1,67 @@
// 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.
package org.apache.cloudstack.dns;
import java.util.List;
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;
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.response.DnsRecordResponse;
import org.apache.cloudstack.api.response.DnsServerResponse;
import org.apache.cloudstack.api.response.DnsZoneResponse;
import org.apache.cloudstack.api.response.ListResponse;
import com.cloud.utils.component.Manager;
public interface DnsProviderManager extends Manager {
DnsServer addDnsServer(AddDnsServerCmd cmd);
ListResponse<DnsServerResponse> listDnsServers(ListDnsServersCmd cmd);
boolean deleteDnsServer(DeleteDnsServerCmd cmd);
DnsServerResponse createDnsServerResponse(DnsServer server);
DnsServer getDnsServer(Long id);
DnsZone createDnsZone(CreateDnsZoneCmd cmd);
boolean deleteDnsZone(DeleteDnsZoneCmd cmd);
ListResponse<DnsZoneResponse> listDnsZones(ListDnsZonesCmd cmd);
DnsZone getDnsZone(long id);
DnsRecordResponse createDnsRecord(CreateDnsRecordCmd cmd);
boolean deleteDnsRecord(DeleteDnsRecordCmd cmd);
ListResponse<DnsRecordResponse> listDnsRecords(ListDnsRecordsCmd cmd);
List<String> listProviderNames();
// Allocates the DB row (State: Allocating)
DnsZone allocDnsZone(CreateDnsZoneCmd cmd);
// Calls the Plugin (State: Allocating -> Ready/Error)
DnsZone provisionDnsZone(long zoneId);
// Helper to create the response object
DnsZoneResponse createDnsZoneResponse(DnsZone zone);
}

View File

@ -0,0 +1,64 @@
// 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.
package org.apache.cloudstack.dns;
import com.cloud.utils.exception.CloudRuntimeException;
public class DnsRecord {
public enum RecordType {
A, AAAA, CNAME, MX, TXT, SRV, PTR, NS;
public static RecordType fromString(String type) {
if (type == null) return null;
try {
return RecordType.valueOf(type.toUpperCase());
} catch (IllegalArgumentException e) {
throw new CloudRuntimeException("Invalid DNS Record Type: " + type +
". Supported: " + java.util.Arrays.toString(values()));
}
}
}
private String name;
private RecordType type; // Enforced Enum here
private String content;
private int ttl;
public DnsRecord() {}
public DnsRecord(String name, RecordType type, String content, int ttl) {
this.name = name;
this.type = type;
this.content = content;
this.ttl = ttl;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public RecordType getType() { return type; }
public void setType(RecordType type) { this.type = type; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public int getTtl() { return ttl; }
public void setTtl(int ttl) { this.ttl = ttl; }
}

View File

@ -0,0 +1,39 @@
// 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.
package org.apache.cloudstack.dns;
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
public interface DnsServer extends InternalIdentity, Identity {
enum ProviderType {
PowerDNS
}
String getName();
String getUrl();
ProviderType getProviderType();
String getKey();
long getDomainId();
long getAccountId();
}

View File

@ -0,0 +1,41 @@
// 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.
package org.apache.cloudstack.dns;
import java.util.List;
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
public interface DnsZone extends InternalIdentity, Identity {
enum ZoneType {
Public, Private
}
String getName();
long getDnsServerId();
long getAccountId();
String getDescription();
ZoneType getType();
List<Long> getAssociatedNetworks();
}

View File

@ -273,7 +273,8 @@ known_categories = {
'Extensions' : 'Extension',
'CustomAction' : 'Extension',
'CustomActions' : 'Extension',
'ImportVmTask': 'Import VM Task'
'ImportVmTask': 'Import VM Task',
'Dns': 'DNS'
}