following things are done:

1. Registerdnsrecordforvm api
2. removednsrecordforvm api
3. cleanup; fixed license, dao logic
This commit is contained in:
Manoj Kumar 2026-02-17 17:21:26 +05:30
parent c5972aea4e
commit 99f8c7dad8
No known key found for this signature in database
GPG Key ID: E952B7234D2C6F88
24 changed files with 488 additions and 73 deletions

View File

@ -38,7 +38,8 @@ public interface Resource {
backup_storage("backup_storage", 13),
bucket("bucket", 14),
object_storage("object_storage", 15),
gpu("gpu", 16);
gpu("gpu", 16),
dns_zone("dns_zone", 17);
private String name;
private int ordinal;

View File

@ -56,6 +56,8 @@ public interface ResourceLimitService {
"The default maximum number of GPU devices that can be used for a domain", false);
static final ConfigKey<Long> DefaultMaxProjectGpus = new ConfigKey<>("Project Defaults",Long.class,"max.project.gpus","20",
"The default maximum number of GPU devices that can be used for a project", false);
ConfigKey<Long> DefaultMaxDnsAccounts = new ConfigKey<>("Account Defaults",Long.class, "max.account.dns_zones","10",
"The default maximum number of DNS zones that can be created by an Account", true);
static final List<ResourceType> HostTagsSupportingTypes = List.of(ResourceType.user_vm, ResourceType.cpu, ResourceType.memory, ResourceType.gpu);
static final List<ResourceType> StorageTagsSupportingTypes = List.of(ResourceType.volume, ResourceType.primary_storage);

View File

@ -33,8 +33,8 @@ import org.apache.cloudstack.dns.DnsProviderManager;
import org.apache.cloudstack.dns.DnsServer;
import org.apache.commons.lang3.BooleanUtils;
@APICommand(name = "addDnsServer", description = "Adds a new external DNS server",
responseObject = DnsServerResponse.class, requestHasSensitiveInfo = true)
@APICommand(name = "addDnsServer", description = "Adds a new external DNS server", responseObject = DnsServerResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0")
public class AddDnsServerCmd extends BaseCmd {
@Inject

View File

@ -34,7 +34,8 @@ import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
@APICommand(name = "associateDnsZoneToNetwork", description = "Associates a DNS Zone with a Network for VM auto-registration",
responseObject = DnsZoneNetworkMapResponse.class, requestHasSensitiveInfo = false)
responseObject = DnsZoneNetworkMapResponse.class, requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false, since = "4.23.0")
public class AssociateDnsZoneToNetworkCmd extends BaseCmd {
@Parameter(name = ApiConstants.DNS_ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class,

View File

@ -1,3 +1,20 @@
// 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.List;
@ -18,7 +35,8 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.EnumUtils;
@APICommand(name = "createDnsRecord", description = "Creates a DNS record directly on the provider",
responseObject = DnsRecordResponse.class)
responseObject = DnsRecordResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.23.0")
public class CreateDnsRecordCmd extends BaseAsyncCmd {
@Parameter(name = ApiConstants.DNS_ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, required = true,

View File

@ -1,3 +1,20 @@
// 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;
@ -19,10 +36,9 @@ 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)
responseObject = DnsZoneResponse.class, requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false, since = "4.23.0")
public class CreateDnsZoneCmd extends BaseAsyncCreateCmd {
private static final String COMMAND_RESPONSE_NAME = "creatednszoneresponse";
@Inject
DnsProviderManager dnsProviderManager;
@ -109,11 +125,6 @@ public class CreateDnsZoneCmd extends BaseAsyncCreateCmd {
}
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();

View File

@ -1,3 +1,20 @@
// 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 org.apache.cloudstack.api.APICommand;
@ -16,7 +33,8 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.EnumUtils;
@APICommand(name = "deleteDnsRecord", description = "Deletes a DNS record from the external provider",
responseObject = SuccessResponse.class)
responseObject = SuccessResponse.class, requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false, since = "4.23.0")
public class DeleteDnsRecordCmd extends BaseAsyncCmd {
@Parameter(name = ApiConstants.DNS_ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class,

View File

@ -1,3 +1,20 @@
// 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 org.apache.cloudstack.api.APICommand;
@ -14,10 +31,9 @@ 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)
responseObject = SuccessResponse.class, requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false, since = "4.23.0")
public class DeleteDnsServerCmd extends BaseAsyncCmd {
private static final String COMMAND_RESPONSE_NAME = "deletednsserverresponse";
/////////////////////////////////////////////////////
//////////////// API Parameters /////////////////////
@ -54,11 +70,6 @@ public class DeleteDnsServerCmd extends BaseAsyncCmd {
}
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
@Override
public long getEntityOwnerId() {
// Look up the server to find its owner.

View File

@ -1,3 +1,20 @@
// 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 org.apache.cloudstack.api.APICommand;
@ -14,10 +31,9 @@ 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)
responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
since = "4.23.0")
public class DeleteDnsZoneCmd extends BaseAsyncCmd {
private static final String COMMAND_RESPONSE_NAME = "deletednszoneresponse";
/////////////////////////////////////////////////////
//////////////// API Parameters /////////////////////
@ -53,11 +69,6 @@ public class DeleteDnsZoneCmd extends BaseAsyncCmd {
}
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
@Override
public long getEntityOwnerId() {
// Look up the Zone to find the Account Owner

View File

@ -34,7 +34,7 @@ import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.Account;
@APICommand(name = "disassociateDnsZoneFromNetwork", description = "Removes the association between a DNS Zone and a Network",
responseObject = SuccessResponse.class)
responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0")
public class DisassociateDnsZoneFromNetworkCmd extends BaseCmd {
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = DnsZoneNetworkMapResponse.class,

View File

@ -29,7 +29,8 @@ 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)
responseObject = DnsProviderResponse.class, requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false, since = "4.23.0")
public class ListDnsProvidersCmd extends BaseListCmd {
@Inject

View File

@ -1,3 +1,20 @@
// 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 org.apache.cloudstack.api.APICommand;
@ -9,8 +26,8 @@ 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)
responseObject = DnsRecordResponse.class, requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false, since = "4.23.0")
public class ListDnsRecordsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.DNS_ZONE_ID, type = CommandType.UUID, entityType = DnsZoneResponse.class, required = true,

View File

@ -1,3 +1,20 @@
// 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;
@ -11,10 +28,9 @@ 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)
responseObject = DnsServerResponse.class, requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false, since = "4.23.0")
public class ListDnsServersCmd extends BaseListAccountResourcesCmd {
private static final String COMMAND_RESPONSE_NAME = "listdnsserversresponse";
@Inject
DnsProviderManager dnsProviderManager;
@ -54,9 +70,4 @@ public class ListDnsServersCmd extends BaseListAccountResourcesCmd {
response.setObjectName("dnsserver");
setResponseObject(response);
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
}

View File

@ -1,3 +1,20 @@
// 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 org.apache.cloudstack.api.APICommand;
@ -8,11 +25,9 @@ import org.apache.cloudstack.api.response.DnsServerResponse;
import org.apache.cloudstack.api.response.DnsZoneResponse;
import org.apache.cloudstack.api.response.ListResponse;
@APICommand(name = "listDnsZones", description = "Lists DNS zones.",
responseObject = DnsZoneResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@APICommand(name = "listDnsZones", description = "Lists DNS zones.", responseObject = DnsZoneResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0")
public class ListDnsZonesCmd extends BaseListAccountResourcesCmd {
private static final String COMMAND_RESPONSE_NAME = "listdnszonesresponse";
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
@ -39,9 +54,4 @@ public class ListDnsZonesCmd extends BaseListAccountResourcesCmd {
response.setResponseName(getCommandName());
setResponseObject(response);
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
}

View File

@ -0,0 +1,74 @@
// 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 org.apache.cloudstack.acl.RoleType;
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.NetworkResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.context.CallContext;
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;
@APICommand(name = "registerDnsRecordForVm", description = "Automatically registers a DNS record for a VM based on its associated Network and DNS Zone mapping",
responseObject = SuccessResponse.class,
since = "4.23.0",
authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.User})
public class RegisterDnsRecordForVmCmd extends BaseCmd {
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class,
required = true, description = "The ID of the Virtual Machine")
private Long vmId;
@Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class,
description = "The ID of the network. If not specified, the VM's default NIC network is used.")
private Long networkId;
public Long getVmId() { return vmId; }
public Long getNetworkId() { return networkId; }
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try {
boolean result = dnsProviderManager.registerDnsRecordForVm(this);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to register DNS record for VM");
}
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccountId();
}
}

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 org.apache.cloudstack.acl.RoleType;
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.NetworkResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.context.CallContext;
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;
@APICommand(name = "removeDnsRecordForVm", description = "Removes the auto-registered DNS record for a VM",
responseObject = SuccessResponse.class,
since = "4.23.0",
authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.User})
public class RemoveDnsRecordForVmCmd extends BaseCmd {
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class,
required = true, description = "The ID of the Virtual Machine")
private Long vmId;
@Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class,
description = "The ID of the network. If not specified, the VM's default NIC network is used.")
private Long networkId;
public Long getVmId() {
return vmId;
}
public Long getNetworkId() {
return networkId;
}
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
try {
boolean result = dnsProviderManager.removeDnsRecordForVm(this);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove DNS record for VM");
}
} catch (Exception e) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccountId();
}
}

View File

@ -34,8 +34,8 @@ import org.apache.commons.lang3.StringUtils;
import com.cloud.utils.EnumUtils;
@APICommand(name = "updateDnsServer", description = "Update DNS server",
responseObject = DnsServerResponse.class, requestHasSensitiveInfo = true)
@APICommand(name = "updateDnsServer", description = "Update DNS server", responseObject = DnsServerResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0")
public class UpdateDnsServerCmd extends BaseCmd {
@Inject

View File

@ -1,3 +1,20 @@
// 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;
@ -13,13 +30,10 @@ 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)
@APICommand(name = "updateDnsZone", description = "Updates a DNS Zone's metadata", responseObject = DnsZoneResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0")
public class UpdateDnsZoneCmd extends BaseCmd {
private static final String COMMAND_RESPONSE_NAME = "updatednszoneresponse";
@Inject
DnsProviderManager dnsProviderManager;
@ -66,11 +80,6 @@ public class UpdateDnsZoneCmd extends BaseCmd {
}
}
@Override
public String getCommandName() {
return COMMAND_RESPONSE_NAME;
}
@Override
public long getEntityOwnerId() {
return CallContext.current().getCallingAccount().getId();

View File

@ -29,6 +29,8 @@ import org.apache.cloudstack.api.command.user.dns.DisassociateDnsZoneFromNetwork
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.RegisterDnsRecordForVmCmd;
import org.apache.cloudstack.api.command.user.dns.RemoveDnsRecordForVmCmd;
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;
@ -74,4 +76,7 @@ public interface DnsProviderManager extends Manager, PluggableService {
DnsZoneNetworkMapResponse associateZoneToNetwork(AssociateDnsZoneToNetworkCmd cmd);
boolean disassociateZoneFromNetwork(DisassociateDnsZoneFromNetworkCmd cmd);
boolean registerDnsRecordForVm(RegisterDnsRecordForVmCmd cmd);
boolean removeDnsRecordForVm(RemoveDnsRecordForVmCmd cmd);
}

View File

@ -55,7 +55,7 @@ CREATE TABLE IF NOT EXISTS `cloud`.`webhook_filter` (
-- DNS Framework Schema
-- ======================================================================
-- 1. DNS Server Table (Stores DNS Server Configurations)
-- DNS Server Table (Stores DNS Server Configurations)
CREATE TABLE `cloud`.`dns_server` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the dns server',
`uuid` varchar(40) COMMENT 'uuid of the dns server',
@ -76,7 +76,7 @@ CREATE TABLE `cloud`.`dns_server` (
CONSTRAINT `fk_dns_server__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 2. DNS Zone Table (Stores DNS Zone Metadata)
-- DNS Zone Table (Stores DNS Zone Metadata)
CREATE TABLE `cloud`.`dns_zone` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the dns zone',
`uuid` varchar(40) COMMENT 'uuid of the dns zone',
@ -99,7 +99,7 @@ CREATE TABLE `cloud`.`dns_zone` (
CONSTRAINT `fk_dns_zone__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 3. DNS Zone Network Map (One-to-Many Link)
-- DNS Zone Network Map (One-to-Many Link)
CREATE TABLE `cloud`.`dns_zone_network_map` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the dns zone to network mapping',
`uuid` varchar(40),
@ -115,3 +115,7 @@ CREATE TABLE `cloud`.`dns_zone_network_map` (
CONSTRAINT `fk_dns_map__zone_id` FOREIGN KEY (`dns_zone_id`) REFERENCES `dns_zone` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_dns_map__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Set default limit to 10 DNS zones for standard Accounts
INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `description`, `default_value`)
VALUES ('Advanced', 'DEFAULT', 'ResourceLimitManager', 'max.account.dns_zones', '10', 'The default maximum number of DNS zones that can be created by an Account', '10');

View File

@ -322,6 +322,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
accountResourceLimitMap.put(Resource.ResourceType.backup_storage.name(), Long.parseLong(_configDao.getValue(BackupManager.DefaultMaxAccountBackupStorage.key())));
accountResourceLimitMap.put(Resource.ResourceType.bucket.name(), Long.parseLong(_configDao.getValue(BucketApiService.DefaultMaxAccountBuckets.key())));
accountResourceLimitMap.put(Resource.ResourceType.object_storage.name(), Long.parseLong(_configDao.getValue(BucketApiService.DefaultMaxAccountObjectStorage.key())));
accountResourceLimitMap.put(ResourceType.dns_zone.name(), DefaultMaxDnsAccounts.value());
domainResourceLimitMap.put(Resource.ResourceType.public_ip.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainPublicIPs.key())));
domainResourceLimitMap.put(Resource.ResourceType.snapshot.name(), Long.parseLong(_configDao.getValue(Config.DefaultMaxDomainSnapshots.key())));

View File

@ -35,6 +35,8 @@ import org.apache.cloudstack.api.command.user.dns.ListDnsProvidersCmd;
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.RegisterDnsRecordForVmCmd;
import org.apache.cloudstack.api.command.user.dns.RemoveDnsRecordForVmCmd;
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;
@ -62,6 +64,10 @@ import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.component.PluggableService;
import com.cloud.utils.db.Filter;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.NicVO;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.UserVmDao;
@Component
public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderManager, PluggableService {
@ -76,6 +82,10 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
NetworkDao networkDao;
@Inject
DnsZoneNetworkMapDao dnsZoneNetworkMapDao;
@Inject
UserVmDao userVmDao;
@Inject
NicDao nicDao;
private DnsProvider getProvider(DnsProviderType type) {
if (type == null) {
@ -493,6 +503,10 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
if (network == null) {
throw new InvalidParameterValueException("Network not found.");
}
if (!NetworkVO.GuestType.Shared.equals(network.getGuestType())) {
throw new CloudRuntimeException(String.format("Operation is not permitted for network type: %s", network.getGuestType()));
}
accountMgr.checkAccess(caller, null, true, network);
DnsZoneNetworkMapVO existing = dnsZoneNetworkMapDao.findByZoneAndNetwork(zone.getId(), network.getId());
if (existing != null) {
@ -525,6 +539,103 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
return dnsZoneNetworkMapDao.remove(mapping.getId());
}
@Override
public boolean registerDnsRecordForVm(RegisterDnsRecordForVmCmd cmd) {
return processDnsRecordForInstance(cmd.getVmId(), cmd.getNetworkId(), true);
}
@Override
public boolean removeDnsRecordForVm(RemoveDnsRecordForVmCmd cmd) {
return processDnsRecordForInstance(cmd.getVmId(), cmd.getNetworkId(), false);
}
/**
* Helper method to handle both Register and Remove logic for Instance
*/
private boolean processDnsRecordForInstance(Long instanceId, Long networkId, boolean isAdd) {
// 1. Fetch VM and verify access
UserVmVO instance = userVmDao.findById(instanceId);
if (instance == null) {
throw new InvalidParameterValueException("Instance not found.");
}
accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, instance);
// 2. Resolve the NIC and Network
NicVO nic;
if (networkId != null) {
nic = nicDao.findByNtwkIdAndInstanceId(networkId, instance.getId());
} else {
nic = nicDao.findDefaultNicForVM(instance.getId());
networkId = nic != null ? nic.getNetworkId() : null;
}
if (nic == null) {
throw new CloudRuntimeException("No valid NIC found for this Instance on the specified Network.");
}
// 3. Find if this network is linked to any DNS Zones
List<DnsZoneNetworkMapVO> mappings = dnsZoneNetworkMapDao.listByNetworkId(networkId);
if (mappings == null || mappings.isEmpty()) {
throw new CloudRuntimeException("No DNS zones are mapped to this network. Please associate a zone first.");
}
boolean atLeastOneSuccess = false;
// 4. Iterate over mapped zones and push the record
for (DnsZoneNetworkMapVO map : mappings) {
DnsZoneVO zone = dnsZoneDao.findById(map.getDnsZoneId());
if (zone == null || zone.getState() != DnsZone.State.Active) {
continue;
}
DnsServerVO server = dnsServerDao.findById(zone.getDnsServerId());
// Construct FQDN Prefix (e.g., "instance-id" or "instance-id.subdomain")
String recordName = String.valueOf(instance.getInstanceName());
if (map.getSubDomain() != null && !map.getSubDomain().isEmpty()) {
recordName = recordName + "." + map.getSubDomain();
}
try {
DnsProvider provider = getProvider(server.getProviderType());
// Handle IPv4 (A Record)
if (nic.getIPv4Address() != null) {
DnsRecord recordA = new DnsRecord(recordName, DnsRecord.RecordType.A, java.util.Arrays.asList(nic.getIPv4Address()), 3600);
if (isAdd) {
provider.addRecord(server, zone, recordA);
} else {
provider.deleteRecord(server, zone, recordA);
}
atLeastOneSuccess = true;
}
// Handle IPv6 (AAAA Record) if it exists
if (nic.getIPv6Address() != null) {
DnsRecord recordAAAA = new DnsRecord(recordName, DnsRecord.RecordType.AAAA, java.util.Arrays.asList(nic.getIPv6Address()), 3600);
if (isAdd) {
provider.addRecord(server, zone, recordAAAA);
} else {
provider.deleteRecord(server, zone, recordAAAA);
}
atLeastOneSuccess = true;
}
} catch (Exception ex) {
logger.error(
"Failed to {} DNS record for Instance {} in zone {}",
isAdd ? "register" : "remove",
instance.getHostName(),
zone.getName(),
ex
);
}
}
if (!atLeastOneSuccess) {
throw new CloudRuntimeException("Failed to process DNS records. Ensure the Instance has a valid IP address.");
}
return true;
}
@Override
public boolean start() {
if (dnsProviders == null || dnsProviders.isEmpty()) {
@ -557,6 +668,8 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa
cmdList.add(CreateDnsRecordCmd.class);
cmdList.add(ListDnsRecordsCmd.class);
cmdList.add(DeleteDnsRecordCmd.class);
cmdList.add(RegisterDnsRecordForVmCmd.class);
cmdList.add(RemoveDnsRecordForVmCmd.class);
return cmdList;
}

View File

@ -8,6 +8,6 @@ import com.cloud.utils.db.GenericDao;
public interface DnsZoneNetworkMapDao extends GenericDao<DnsZoneNetworkMapVO, Long> {
List<DnsZoneNetworkMapVO> listByDnsZoneId(long dnsZoneId);
DnsZoneNetworkMapVO findByZoneAndNetwork(long zoneId, long networkId);
DnsZoneNetworkMapVO findByZoneAndNetwork(long dnsZoneId, long networkId);
List<DnsZoneNetworkMapVO> listByNetworkId(long networkId);
}

View File

@ -2,6 +2,7 @@ package org.apache.cloudstack.dns.dao;
import java.util.List;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.dns.vo.DnsZoneNetworkMapVO;
import org.springframework.stereotype.Component;
@ -11,28 +12,45 @@ import com.cloud.utils.db.SearchCriteria;
@Component
public class DnsZoneNetworkMapDaoImpl extends GenericDaoBase<DnsZoneNetworkMapVO, Long> implements DnsZoneNetworkMapDao {
final SearchBuilder<DnsZoneNetworkMapVO> ZoneSearch;
private final SearchBuilder<DnsZoneNetworkMapVO> ZoneNetworkSearch;
private final SearchBuilder<DnsZoneNetworkMapVO> ZoneSearch;
private final SearchBuilder<DnsZoneNetworkMapVO> NetworkSearch;
public DnsZoneNetworkMapDaoImpl() {
super();
ZoneNetworkSearch = createSearchBuilder();
ZoneNetworkSearch.and(ApiConstants.DNS_ZONE_ID, ZoneNetworkSearch.entity().getDnsZoneId(), SearchCriteria.Op.EQ);
ZoneNetworkSearch.and(ApiConstants.NETWORK_ID, ZoneNetworkSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
ZoneNetworkSearch.done();
ZoneSearch = createSearchBuilder();
ZoneSearch.and("dnsZoneId", ZoneSearch.entity().getDnsZoneId(), SearchCriteria.Op.EQ);
ZoneSearch.and(ApiConstants.DNS_ZONE_ID, ZoneSearch.entity().getDnsZoneId(), SearchCriteria.Op.EQ);
ZoneSearch.done();
NetworkSearch = createSearchBuilder();
NetworkSearch.and(ApiConstants.NETWORK_ID, NetworkSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
NetworkSearch.done();
}
@Override
public List<DnsZoneNetworkMapVO> listByDnsZoneId(long dnsZoneId) {
SearchCriteria<DnsZoneNetworkMapVO> sc = ZoneSearch.create();
sc.setParameters("dnsZoneId", dnsZoneId);
sc.setParameters(ApiConstants.DNS_ZONE_ID, dnsZoneId);
return listBy(sc);
}
@Override
public DnsZoneNetworkMapVO findByZoneAndNetwork(long zoneId, long networkId) {
return null;
public DnsZoneNetworkMapVO findByZoneAndNetwork(long dnsZoneId, long networkId) {
SearchCriteria<DnsZoneNetworkMapVO> sc = ZoneNetworkSearch.create();
sc.setParameters(ApiConstants.DNS_ZONE_ID, dnsZoneId);
sc.setParameters(ApiConstants.NETWORK_ID, networkId);
return findOneBy(sc);
}
@Override
public List<DnsZoneNetworkMapVO> listByNetworkId(long networkId) {
return List.of();
SearchCriteria<DnsZoneNetworkMapVO> sc = NetworkSearch.create();
sc.setParameters(ApiConstants.NETWORK_ID, networkId);
return listBy(sc);
}
}