diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java index fcf38e8978a..c84aa7cf455 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmd.java @@ -17,7 +17,10 @@ package org.apache.cloudstack.api.command.user.dns; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -30,6 +33,7 @@ import org.apache.cloudstack.api.response.DnsServerResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.dns.DnsProviderType; import org.apache.cloudstack.dns.DnsServer; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.BooleanUtils; import com.cloud.exception.InvalidParameterValueException; @@ -82,9 +86,9 @@ public class AddDnsServerCmd extends BaseCmd { description = "Comma separated list of name servers; used to create NS records for the DNS Zone (for example, ns1.example.com, ns2.example.com)") private List nameServers; - @Parameter(name = "externalserverid", type = CommandType.STRING, - description = "External server id or hostname for the DNS server, e.g., 'localhost' for PowerDNS") - private String externalServerId; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using " + + "format details[i].keyname=keyvalue. Example: details[0].pdnsServerId=localhost") + protected Map details; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -145,11 +149,21 @@ public class AddDnsServerCmd extends BaseCmd { } } - public String getExternalServerId() { - return externalServerId; - } - public String getDnsUserName() { return dnsUserName; } + + public Map getDetails() { + Map detailsMap = new HashMap<>(); + if (MapUtils.isNotEmpty(details)) { + Collection props = details.values(); + for (Object prop : props) { + HashMap detail = (HashMap) prop; + for (Map.Entry entry: detail.entrySet()) { + detailsMap.put(entry.getKey(),entry.getValue()); + } + } + } + return detailsMap; + } } diff --git a/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java b/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java index 956bc708e7b..f34f94726d1 100644 --- a/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java +++ b/api/src/main/java/org/apache/cloudstack/dns/DnsServer.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.dns; import java.util.Date; import java.util.List; +import java.util.Map; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.Identity; @@ -49,7 +50,13 @@ public interface DnsServer extends InternalIdentity, Identity, ControlledEntity String getPublicDomainSuffix(); - String getExternalServerId(); - Integer getPort(); + + Map getDetails(); + + String getDetail(String name); + + void setDetails(Map details); + + void appendDetails(String name, String value); } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmdTest.java index 3ebad8b91ea..2824a8dacb9 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/dns/AddDnsServerCmdTest.java @@ -45,7 +45,6 @@ public class AddDnsServerCmdTest extends BaseDnsCmdTest { setField(cmd, "isPublic", true); setField(cmd, "publicDomainSuffix", "public.example.com"); setField(cmd, "nameServers", Arrays.asList("ns1.example.com", "ns2.example.com")); - setField(cmd, "externalServerId", "localhost"); setField(cmd, "dnsUserName", "admin@example.com"); return cmd; } @@ -62,7 +61,6 @@ public class AddDnsServerCmdTest extends BaseDnsCmdTest { assertEquals("public.example.com", cmd.getPublicDomainSuffix()); assertEquals(Arrays.asList("ns1.example.com", "ns2.example.com"), cmd.getNameServers()); assertEquals(DnsProviderType.PowerDNS, cmd.getProvider()); - assertEquals("localhost", cmd.getExternalServerId()); assertEquals("admin@example.com", cmd.getDnsUserName()); } diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 4720137b568..d97f5a137f4 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -318,5 +318,6 @@ + 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 dc9a68a2f08..633cbdb53f1 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 @@ -132,7 +132,6 @@ CREATE TABLE IF NOT EXISTS `cloud`.`dns_server` ( `url` varchar(1024) NOT NULL COMMENT 'dns server url', `dns_username` varchar(255) COMMENT 'username or email for dns server credentials', `dns_api_key` varchar(255) NOT NULL COMMENT 'api key or token for the dns server ', - `external_server_id` varchar(255) COMMENT 'dns server id e.g. localhost for powerdns', `port` int(11) DEFAULT NULL COMMENT 'optional dns server port', `name_servers` varchar(1024) DEFAULT NULL COMMENT 'Comma separated list of name servers', `is_public` tinyint(1) NOT NULL DEFAULT '0', @@ -147,6 +146,17 @@ CREATE TABLE IF NOT EXISTS `cloud`.`dns_server` ( CONSTRAINT `fk_dns_server__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- DNS Server Details Table +CREATE TABLE IF NOT EXISTS `cloud`.`dns_server_details` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `dns_server_id` bigint unsigned NOT NULL COMMENT 'dns_server the detail is related to', + `name` varchar(255) NOT NULL COMMENT 'name of the detail', + `value` varchar(255) NOT NULL COMMENT 'value of the detail', + `display` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should detail be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_dns_server_details__dns_server_id` FOREIGN KEY (`dns_server_id`) REFERENCES `dns_server`(`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + -- DNS Zone Table (Stores DNS Zone Metadata) CREATE TABLE IF NOT EXISTS `cloud`.`dns_zone` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the dns zone', 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 38715c511e0..c9c68f532d9 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 @@ -28,14 +28,15 @@ import org.apache.cloudstack.dns.DnsRecord; import org.apache.cloudstack.dns.DnsServer; import org.apache.cloudstack.dns.DnsZone; import org.apache.cloudstack.dns.exception.DnsProviderException; +import org.apache.logging.log4j.util.Strings; import com.cloud.utils.StringUtils; import com.cloud.utils.component.AdapterBase; import com.fasterxml.jackson.databind.JsonNode; public class PowerDnsProvider extends AdapterBase implements DnsProvider { - private PowerDnsClient client; + static String PDNS_SERVER_ID = "pdnsServerId"; @Override public DnsProviderType getProviderType() { @@ -44,13 +45,17 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { public void validate(DnsServer server) throws DnsProviderException { validateRequiredServerFields(server); - client.validateServerId(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getExternalServerId()); + client.validateServerId(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getDetail(PDNS_SERVER_ID)); } @Override public String validateAndResolveServer(DnsServer server) throws Exception { validateRequiredServerFields(server); - return client.resolveServerId(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getExternalServerId()); + String resolvedDnsServerId = client.resolveServerId(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getDetail(PDNS_SERVER_ID)); + if (Strings.isNotBlank(resolvedDnsServerId)) { + server.appendDetails(PDNS_SERVER_ID, resolvedDnsServerId); + } + return resolvedDnsServerId; } @Override @@ -60,7 +65,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { server.getUrl(), server.getPort(), server.getDnsApiKey(), - server.getExternalServerId(), + server.getDetail(PDNS_SERVER_ID), zone.getName(), ApiConstants.NATIVE_ZONE, false, server.getNameServers() ); @@ -69,7 +74,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { @Override public void deleteZone(DnsServer server, DnsZone zone) throws DnsProviderException { validateRequiredServerAndZoneFields(server, zone); - client.deleteZone(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getExternalServerId(), zone.getName()); + client.deleteZone(server.getUrl(), server.getPort(), server.getDnsApiKey(), server.getDetail(PDNS_SERVER_ID), zone.getName()); } @Override @@ -79,7 +84,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { server.getUrl(), server.getPort(), server.getDnsApiKey(), - server.getExternalServerId(), + server.getDetail(PDNS_SERVER_ID), zone.getName(), ApiConstants.NATIVE_ZONE, false, server.getNameServers()); } @@ -94,7 +99,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { server.getUrl(), server.getPort(), server.getDnsApiKey(), - server.getExternalServerId(), + server.getDetail(PDNS_SERVER_ID), zone.getName(), record, ChangeType.REPLACE); } @@ -110,7 +115,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { return applyRecord(server.getUrl(), server.getPort(), server.getDnsApiKey(), - server.getExternalServerId(), + server.getDetail(PDNS_SERVER_ID), zone.getName(), record, ChangeType.DELETE); } @@ -126,7 +131,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { validateRequiredServerAndZoneFields(server, zone); List records = new ArrayList<>(); Iterable rrsetNodes = client.listRecords(server.getUrl(), server.getPort(), server.getDnsApiKey(), - server.getExternalServerId(), zone.getName()); + server.getDetail(PDNS_SERVER_ID), zone.getName()); for (JsonNode rrset : rrsetNodes) { String name = rrset.path(ApiConstants.NAME).asText(); @@ -155,7 +160,7 @@ public class PowerDnsProvider extends AdapterBase implements DnsProvider { public boolean dnsRecordExists(DnsServer server, DnsZone zone, String recordName, String recordType) throws DnsProviderException { return client.dnsRecordExists(server.getUrl(), server.getPort(), server.getDnsApiKey(), - server.getExternalServerId(), zone.getName(), recordName, recordType); + server.getDetail(PDNS_SERVER_ID), zone.getName(), recordName, recordType); } void validateRequiredServerAndZoneFields(DnsServer server, DnsZone zone) { diff --git a/plugins/dns/powerdns/src/test/java/org/apache/cloudstack/dns/powerdns/PowerDnsProviderTest.java b/plugins/dns/powerdns/src/test/java/org/apache/cloudstack/dns/powerdns/PowerDnsProviderTest.java index 08e855a102c..eae1c986714 100644 --- a/plugins/dns/powerdns/src/test/java/org/apache/cloudstack/dns/powerdns/PowerDnsProviderTest.java +++ b/plugins/dns/powerdns/src/test/java/org/apache/cloudstack/dns/powerdns/PowerDnsProviderTest.java @@ -75,7 +75,7 @@ public class PowerDnsProviderTest { when(serverMock.getUrl()).thenReturn("http://pdns:8081"); when(serverMock.getDnsApiKey()).thenReturn("secret"); when(serverMock.getPort()).thenReturn(8081); - when(serverMock.getExternalServerId()).thenReturn("localhost"); + when(serverMock.getDetail(PowerDnsProvider.PDNS_SERVER_ID)).thenReturn("localhost"); when(serverMock.getNameServers()).thenReturn(Arrays.asList("ns1.example.com")); when(zoneMock.getName()).thenReturn("example.com"); 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 8415251d2e8..1ce9ac5b9a2 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -57,25 +57,27 @@ import org.apache.cloudstack.api.response.DnsZoneNetworkMapResponse; import org.apache.cloudstack.api.response.DnsZoneResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.dns.dao.NicDnsJoinDao; import org.apache.cloudstack.dns.dao.DnsServerDao; +import org.apache.cloudstack.dns.dao.DnsServerDetailsDao; import org.apache.cloudstack.dns.dao.DnsServerJoinDao; import org.apache.cloudstack.dns.dao.DnsZoneDao; import org.apache.cloudstack.dns.dao.DnsZoneJoinDao; import org.apache.cloudstack.dns.dao.DnsZoneNetworkMapDao; +import org.apache.cloudstack.dns.dao.NicDnsJoinDao; import org.apache.cloudstack.dns.exception.DnsConflictException; import org.apache.cloudstack.dns.exception.DnsNotFoundException; import org.apache.cloudstack.dns.exception.DnsProviderException; import org.apache.cloudstack.dns.exception.DnsTransportException; -import org.apache.cloudstack.dns.vo.NicDnsJoinVO; import org.apache.cloudstack.dns.vo.DnsServerJoinVO; import org.apache.cloudstack.dns.vo.DnsServerVO; import org.apache.cloudstack.dns.vo.DnsZoneJoinVO; import org.apache.cloudstack.dns.vo.DnsZoneNetworkMapVO; import org.apache.cloudstack.dns.vo.DnsZoneVO; +import org.apache.cloudstack.dns.vo.NicDnsJoinVO; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.MessageSubscriber; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.springframework.stereotype.Component; import com.cloud.domain.Domain; @@ -142,6 +144,8 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa VMInstanceDao vmInstanceDao; @Inject NicDnsJoinDao nicDnsJoinDao; + @Inject + DnsServerDetailsDao dnsServerDetailsDao; private static final Set VM_ALLOWED_STATES = EnumSet.of(VirtualMachine.State.Running, VirtualMachine.State.Stopped); @@ -180,15 +184,17 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa } DnsProviderType type = cmd.getProvider(); - DnsServerVO server = new DnsServerVO(cmd.getName(), cmd.getUrl(), cmd.getPort(), cmd.getExternalServerId(), type, + DnsServerVO server = new DnsServerVO(cmd.getName(), cmd.getUrl(), cmd.getPort(), type, cmd.getDnsUserName(), cmd.getDnsApiKey(), isDnsPublic, publicDomainSuffix, cmd.getNameServers(), caller.getAccountId(), caller.getDomainId()); + + if (MapUtils.isNotEmpty(cmd.getDetails())) { + server.setDetails(cmd.getDetails()); + } + try { DnsProvider provider = getProviderByType(type); - String dnsServerId = provider.validateAndResolveServer(server); // returns localhost for PowerDNS - if (StringUtils.isNotBlank(dnsServerId)) { - server.setExternalServerId(dnsServerId); - } + provider.validateAndResolveServer(server); return dnsServerDao.persist(server); } catch (Exception ex) { logger.error("Failed to validate DNS server", ex); diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDao.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDao.java index ebbe0248197..7d16370ccd6 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDao.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDao.java @@ -34,4 +34,8 @@ public interface DnsServerDao extends GenericDao { List listDnsServerIdsByAccountId(Long accountId); Pair, Integer> searchDnsServer(Long dnsServerId, Long accountId, Set domainIds, DnsProviderType providerType, String keyword, Filter filter); + + void loadDetails(DnsServer dnsServer); + + void saveDetails(DnsServer dnsServer); } diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java index b9d545e54ea..7540e37d942 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDaoImpl.java @@ -17,12 +17,17 @@ package org.apache.cloudstack.dns.dao; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; +import javax.inject.Inject; + import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.dns.DnsProviderType; import org.apache.cloudstack.dns.DnsServer; +import org.apache.cloudstack.dns.vo.DnsServerDetailVO; import org.apache.cloudstack.dns.vo.DnsServerVO; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -33,9 +38,14 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; @Component public class DnsServerDaoImpl extends GenericDaoBase implements DnsServerDao { + @Inject + DnsServerDetailsDao dnsServerDetailsDao; + SearchBuilder AllFieldsSearch; SearchBuilder AccountUrlSearch; GenericSearchBuilder DnsServerIdsByAccountSearch; @@ -121,4 +131,56 @@ public class DnsServerDaoImpl extends GenericDaoBase implemen sc.setParameters(ApiConstants.STATE, DnsServer.State.Enabled); return searchAndCount(sc, filter); } + + @Override + public DnsServerVO persist(DnsServerVO dnsServer) { + return Transaction.execute((TransactionCallback) status -> { + DnsServerVO dnsServerDb = super.persist(dnsServer); + saveDetails(dnsServer); + loadDetails(dnsServerDb); + return dnsServerDb; + }); + } + + @Override + public boolean update(Long id, DnsServerVO dnsServer) { + return Transaction.execute((TransactionCallback) status -> { + boolean result = super.update(id, dnsServer); + if (result) { + saveDetails(dnsServer); + } + return result; + }); + } + + @Override + public boolean remove(Long dnsServerId) { + return Transaction.execute((TransactionCallback) status -> { + boolean result = super.remove(dnsServerId); + if (result) { + dnsServerDetailsDao.removeDetails(dnsServerId); + } + return result; + }); + } + + @Override + public void loadDetails(DnsServer dnsServer) { + Map details = dnsServerDetailsDao.listDetailsKeyPairs(dnsServer.getId()); + dnsServer.setDetails(details); + } + + @Override + public void saveDetails(DnsServer dnsServer) { + Map detailsStr = dnsServer.getDetails(); + if (detailsStr == null) { + return; + } + List details = new ArrayList<>(); + for (String key : detailsStr.keySet()) { + DnsServerDetailVO detail = new DnsServerDetailVO(dnsServer.getId(), key, detailsStr.get(key), true); + details.add(detail); + } + dnsServerDetailsDao.saveDetails(details); + } } diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDetailsDao.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDetailsDao.java new file mode 100644 index 00000000000..ae900fbcddf --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDetailsDao.java @@ -0,0 +1,26 @@ +// 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.dao; + +import org.apache.cloudstack.dns.vo.DnsServerDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.utils.db.GenericDao; + +public interface DnsServerDetailsDao extends GenericDao, ResourceDetailsDao { +} diff --git a/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDetailsDaoImpl.java b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDetailsDaoImpl.java new file mode 100644 index 00000000000..7f62921290f --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/dns/dao/DnsServerDetailsDaoImpl.java @@ -0,0 +1,29 @@ +// 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.dao; + +import org.apache.cloudstack.dns.vo.DnsServerDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; + + +public class DnsServerDetailsDaoImpl extends ResourceDetailsDaoBase implements DnsServerDetailsDao { + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new DnsServerDetailVO(resourceId, key, value, display)); + } +} diff --git a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerDetailVO.java b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerDetailVO.java new file mode 100644 index 00000000000..040e891a945 --- /dev/null +++ b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerDetailVO.java @@ -0,0 +1,99 @@ +// 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.vo; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "dns_server_details") +public class DnsServerDetailVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "dns_server_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value") + private String value; + + @Column(name = "display") + private boolean display = true; + + protected DnsServerDetailVO() { + } + + public DnsServerDetailVO(long dnsServerId, String name, String value, boolean display) { + this.resourceId = dnsServerId; + this.name = name; + this.value = value; + this.display = display; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getResourceId() { + return resourceId; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public boolean isDisplay() { + return display; + } + + public void setId(long id) { + this.id = id; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java index 8f703dcbc7d..ff1e355671e 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java +++ b/server/src/main/java/org/apache/cloudstack/dns/vo/DnsServerVO.java @@ -20,7 +20,9 @@ package org.apache.cloudstack.dns.vo; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import javax.persistence.Column; @@ -33,6 +35,7 @@ import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.persistence.Transient; import org.apache.cloudstack.dns.DnsProviderType; import org.apache.cloudstack.dns.DnsServer; @@ -72,9 +75,6 @@ public class DnsServerVO implements DnsServer { @Column(name = "dns_api_key") private String dnsApiKey; - @Column(name = "external_server_id") - private String externalServerId; - @Column(name = "is_public") private boolean publicServer; @@ -102,18 +102,20 @@ public class DnsServerVO implements DnsServer { @Temporal(value = TemporalType.TIMESTAMP) private Date removed = null; + @Transient + Map details; + DnsServerVO() { this.uuid = UUID.randomUUID().toString(); this.created = new Date(); } - public DnsServerVO(String name, String url, Integer port, String externalServerId, DnsProviderType providerType, String dnsUserName, String dnsApiKey, + public DnsServerVO(String name, String url, Integer port, DnsProviderType providerType, String dnsUserName, String dnsApiKey, boolean isPublic, String publicDomainSuffix, List nameServers, Long accountId, Long domainId) { this(); this.name = name; this.url = url; this.port = port; - this.externalServerId = externalServerId; this.providerType = providerType; this.dnsUserName = dnsUserName; this.dnsApiKey = dnsApiKey; @@ -241,15 +243,29 @@ public class DnsServerVO implements DnsServer { return publicDomainSuffix; } - public String getExternalServerId() { - return externalServerId; - } - - public void setExternalServerId(String externalServerId) { - this.externalServerId = externalServerId; - } - public Integer getPort() { return this.port; } + + @Override + public Map getDetails() { + return details; + } + + @Override + public String getDetail(String name) { + return details != null ? details.get(name) : null; + } + + public void setDetails(Map details) { + this.details = details; + } + + @Override + public void appendDetails(String name, String value) { + if (details == null) { + details = new HashMap<>(); + } + details.put(name, value); + } } diff --git a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java index 9b1ca59916a..e7550f37291 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -156,7 +156,7 @@ public class DnsProviderManagerImplTest { when(callerMock.getDomainId()).thenReturn(DOMAIN_ID); serverVO = Mockito.spy( - new DnsServerVO("test-server", "http://pdns:8081", 8081, "localhost", DnsProviderType.PowerDNS, null, + new DnsServerVO("test-server", "http://pdns:8081", 8081, DnsProviderType.PowerDNS, null, "apikey", false, null, Collections.singletonList("ns1.example.com"), ACCOUNT_ID, DOMAIN_ID)); Mockito.lenient().doReturn(SERVER_ID).when(serverVO).getId(); diff --git a/server/src/test/java/org/apache/cloudstack/dns/dao/DnsServerDaoImplTest.java b/server/src/test/java/org/apache/cloudstack/dns/dao/DnsServerDaoImplTest.java index 4c5e0925572..120f86beb5a 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/dao/DnsServerDaoImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/dao/DnsServerDaoImplTest.java @@ -50,7 +50,7 @@ public class DnsServerDaoImplTest { @Before public void setUp() { dao = spy(new DnsServerDaoImpl()); - mockServer = new DnsServerVO("test-server", "http://pdns:8081", 8081, "localhost", DnsProviderType.PowerDNS, null, "apikey", false, null, Collections.singletonList("ns1.example.com"), 1L, 10L); + mockServer = new DnsServerVO("test-server", "http://pdns:8081", 8081, DnsProviderType.PowerDNS, null, "apikey", false, null, Collections.singletonList("ns1.example.com"), 1L, 10L); } @Test diff --git a/server/src/test/java/org/apache/cloudstack/dns/vo/DnsServerVOTest.java b/server/src/test/java/org/apache/cloudstack/dns/vo/DnsServerVOTest.java index e878fe76d77..df8586c46a3 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/vo/DnsServerVOTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/vo/DnsServerVOTest.java @@ -42,14 +42,13 @@ public class DnsServerVOTest { @Test public void testParameterizedConstructor() { List nameServers = Arrays.asList("ns1.example.com", "ns2.example.com"); - DnsServerVO vo = new DnsServerVO("test-server", "http://pdns:8081", 8081, - "localhost", DnsProviderType.PowerDNS, "admin", "api-key-123", + DnsServerVO vo = new DnsServerVO("test-server", "http://pdns:8081", 8081, DnsProviderType.PowerDNS, "admin", "api-key-123", true, "public.example.com", nameServers, 10L, 20L); assertEquals("test-server", vo.getName()); assertEquals("http://pdns:8081", vo.getUrl()); assertEquals(Integer.valueOf(8081), vo.getPort()); - assertEquals("localhost", vo.getExternalServerId()); +// assertEquals("localhost", vo.getDetail()); assertEquals(DnsProviderType.PowerDNS, vo.getProviderType()); assertEquals("api-key-123", vo.getDnsApiKey()); assertTrue(vo.getPublicServer()); @@ -91,9 +90,6 @@ public class DnsServerVOTest { vo.setPublicDomainSuffix("new.suffix.com"); assertEquals("new.suffix.com", vo.getPublicDomainSuffix()); - vo.setExternalServerId("remote-host"); - assertEquals("remote-host", vo.getExternalServerId()); - vo.setState(DnsServer.State.Disabled); assertEquals(DnsServer.State.Disabled, vo.getState()); @@ -124,8 +120,7 @@ public class DnsServerVOTest { @Test public void testToString() { List nameServers = Collections.singletonList("ns1.example.com"); - DnsServerVO vo = new DnsServerVO("test-server", "http://pdns:8081", 8081, - "localhost", DnsProviderType.PowerDNS, null, "secret-key", + DnsServerVO vo = new DnsServerVO("test-server", "http://pdns:8081", 8081, DnsProviderType.PowerDNS, null, "secret-key", false, null, nameServers, 1L, 10L); String result = vo.toString(); @@ -139,7 +134,7 @@ public class DnsServerVOTest { @Test public void testConstructorNotPublicServer() { List nameServers = Collections.singletonList("ns1.example.com"); - DnsServerVO vo = new DnsServerVO("srv", "http://url", null, null, + DnsServerVO vo = new DnsServerVO("srv", "http://url", null, DnsProviderType.PowerDNS, null, "key", false, null, nameServers, 1L, 1L); diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 5d02daa8b4d..ca50ce34ca7 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -958,7 +958,7 @@ "label.dns.delete.server": "Delete DNS Server", "label.dns.delete.zone": "Delete DNS Zone", "label.dns.dnsapikey": "DNS API key", -"label.dns.externalserverid": "DNS server ID", +"label.dns.pdnsserverid": "PowerDNS server ID", "label.dns.name": "DNS Name", "label.dns.publicdomainsuffix": "Public domain suffix", "label.dns.records": "DNS Records", diff --git a/ui/src/views/network/dns/AddDnsServer.vue b/ui/src/views/network/dns/AddDnsServer.vue index 8b64e2d52d0..c76420b6647 100644 --- a/ui/src/views/network/dns/AddDnsServer.vue +++ b/ui/src/views/network/dns/AddDnsServer.vue @@ -91,15 +91,15 @@ :placeholder="apiParams.dnsapikey?.description || 'Enter API Key'" /> - + + v-model:value="form.pdnsserverid" + :placeholder="'Enter PowerDNS Server ID (optional if localhost)'" /> @@ -175,7 +175,7 @@ export default { nameservers: [], ispublic: false, publicdomainsuffix: '', - externalserverid: 'localhost' + pdnsserverid: '' }, rules: {}, fetchingProviders: false, @@ -196,7 +196,6 @@ export default { ], provider: [{ required: true, message: this.$t('message.error.required.input') }], dnsapikey: [{ required: true, message: this.$t('message.error.required.input') }], - externalserverid: [{ required: true, message: this.$t('message.error.required.input') }], nameservers: [ { required: true, type: 'array', min: 1, message: this.$t('message.error.required.input') }, { validator: this.validateNameservers } @@ -226,13 +225,15 @@ export default { port: this.form.port, provider: this.form.provider, dnsapikey: this.form.dnsapikey?.trim(), - externalserverid: this.form.externalserverid?.trim(), nameservers: this.form.nameservers || [], ispublic: this.form.ispublic } if (this.form.publicdomainsuffix) { params.publicdomainsuffix = this.form.publicdomainsuffix?.toLowerCase().trim() } + if (this.form.pdnsserverid) { + params['details[0].pdnsServerId'] = this.form.pdnsserverid?.trim() + } await postAPI('addDnsServer', params) this.$notification.success({ message: this.$t('label.dns.add.server'),