From 9714982136b5f02446679f5f85227b91ae4ea188 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Fri, 7 Aug 2026 10:47:37 +0200 Subject: [PATCH 01/13] validate DNS server URLs in provider framework --- .../dns/DnsProviderManagerImpl.java | 21 ++++++++++++ .../dns/DnsProviderManagerImplTest.java | 32 +++++++++++++++---- 2 files changed, 47 insertions(+), 6 deletions(-) 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 b451da1baf72..3718967ba5aa 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -96,6 +96,7 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; +import com.cloud.utils.UriUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.PluggableService; import com.cloud.utils.db.Filter; @@ -162,9 +163,28 @@ private DnsProvider getProviderByType(DnsProviderType type) { throw new CloudRuntimeException("No plugin found for DNS provider type: " + type); } + /** + * Rejects DNS provider URLs that resolve to an illegal address (per {@link UriUtils#validateUrl(String)}, + * currently any-local/link-local/loopback/multicast; RFC1918 site-local coverage follows once #271/#277 + * lands) before any provider client is given the chance to connect to it. A scheme is assumed to be + * `http` when the caller omits one, matching how DNS provider clients (e.g. PowerDnsClient) already + * tolerate bare host/IP values. + */ + private void validateDnsServerUrl(String url) { + if (StringUtils.isBlank(url)) { + return; + } + String urlToValidate = url.trim(); + if (!urlToValidate.startsWith("http://") && !urlToValidate.startsWith("https://")) { + urlToValidate = "http://" + urlToValidate; + } + UriUtils.validateUrl(urlToValidate); + } + @Override @ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription = "Adding a DNS Server") public DnsServer addDnsServer(AddDnsServerCmd cmd) { + validateDnsServerUrl(cmd.getUrl()); Account caller = CallContext.current().getCallingAccount(); DnsServer existing = dnsServerDao.findByUrlAndAccount(cmd.getUrl(), caller.getId()); if (existing != null) { @@ -252,6 +272,7 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { if (cmd.getUrl() != null) { if (!cmd.getUrl().equals(originalUrl)) { + validateDnsServerUrl(cmd.getUrl()); DnsServer duplicate = dnsServerDao.findByUrlAndAccount(cmd.getUrl(), dnsServer.getAccountId()); if (duplicate != null && duplicate.getId() != dnsServer.getId()) { throw new InvalidParameterValueException("Another DNS server with this URL already exists."); 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 309f5e5d9cfd..ec239239abdb 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -718,7 +718,7 @@ public void testAddDnsServerSuccess() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); - when(cmd.getUrl()).thenReturn("http://newpdns:8081"); + when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id"); @@ -781,18 +781,26 @@ public void testListDnsZones() { public void testAddDnsServerAlreadyExists() { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); - when(cmd.getUrl()).thenReturn("http://newpdns:8081"); + when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(serverVO); manager.addDnsServer(cmd); } + @Test(expected = IllegalArgumentException.class) + public void testAddDnsServerRejectsLoopbackUrl() { + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + when(cmd.getUrl()).thenReturn("http://127.0.0.1:8081"); + manager.addDnsServer(cmd); + } + @Test public void testAddDnsServerNormalUser() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); when(accountMgr.isDomainAdmin(callerMock.getId())).thenReturn(false); - when(cmd.getUrl()).thenReturn("http://newpdns:8081"); + when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(cmd.getNameServers()).thenReturn(Collections.emptyList()); when(cmd.isPublic()).thenReturn(true); @@ -811,7 +819,7 @@ public void testAddDnsServerValidationFailure() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); - when(cmd.getUrl()).thenReturn("http://newpdns:8081"); + when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(cmd.getNameServers()).thenReturn(Collections.emptyList()); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); @@ -824,7 +832,7 @@ public void testUpdateDnsServerUrlDuplicate() { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); - when(cmd.getUrl()).thenReturn("http://duplicate:8081"); + when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); DnsServerVO existingServer = mock(DnsServerVO.class); when(existingServer.getId()).thenReturn(SERVER_ID + 1); // Different ID implies duplicate @@ -835,12 +843,24 @@ public void testUpdateDnsServerUrlDuplicate() { manager.updateDnsServer(cmd); } + @Test(expected = IllegalArgumentException.class) + public void testUpdateDnsServerRejectsLoopbackUrl() { + org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + when(cmd.getId()).thenReturn(SERVER_ID); + when(cmd.getUrl()).thenReturn("http://127.0.0.1:8081"); + when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); + Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); + + manager.updateDnsServer(cmd); + } + @Test public void testUpdateDnsServerUrlValid() throws Exception { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); - when(cmd.getUrl()).thenReturn("http://new-url:8081"); + when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); From 1fa16387a05526981a3acecf9021932e485dcc94 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Fri, 7 Aug 2026 11:38:57 +0200 Subject: [PATCH 02/13] fixes --- .../dns/DnsProviderManagerImpl.java | 33 +++++------ .../dns/DnsProviderManagerImplTest.java | 55 +++++++++++++++++-- 2 files changed, 64 insertions(+), 24 deletions(-) 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 3718967ba5aa..f8a0aa5dd84f 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -164,32 +164,28 @@ private DnsProvider getProviderByType(DnsProviderType type) { } /** - * Rejects DNS provider URLs that resolve to an illegal address (per {@link UriUtils#validateUrl(String)}, - * currently any-local/link-local/loopback/multicast; RFC1918 site-local coverage follows once #271/#277 - * lands) before any provider client is given the chance to connect to it. A scheme is assumed to be - * `http` when the caller omits one, matching how DNS provider clients (e.g. PowerDnsClient) already - * tolerate bare host/IP values. + * Rejects a DNS provider URL that resolves to an illegal address before any provider client is given + * the chance to connect to it. See {@link UriUtils#validateUrl(String)} for the exact rules enforced + * (including the requirement that the URL declares an {@code http}/{@code https} scheme). + * Expects {@code url} to already be trimmed. */ private void validateDnsServerUrl(String url) { if (StringUtils.isBlank(url)) { return; } - String urlToValidate = url.trim(); - if (!urlToValidate.startsWith("http://") && !urlToValidate.startsWith("https://")) { - urlToValidate = "http://" + urlToValidate; - } - UriUtils.validateUrl(urlToValidate); + UriUtils.validateUrl(url); } @Override @ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription = "Adding a DNS Server") public DnsServer addDnsServer(AddDnsServerCmd cmd) { - validateDnsServerUrl(cmd.getUrl()); + String url = StringUtils.trim(cmd.getUrl()); + validateDnsServerUrl(url); Account caller = CallContext.current().getCallingAccount(); - DnsServer existing = dnsServerDao.findByUrlAndAccount(cmd.getUrl(), caller.getId()); + DnsServer existing = dnsServerDao.findByUrlAndAccount(url, caller.getId()); if (existing != null) { throw new InvalidParameterValueException( - "This Account already has a DNS server integration for URL: " + cmd.getUrl()); + "This Account already has a DNS server integration for URL: " + url); } boolean isDnsPublic = cmd.isPublic(); @@ -205,7 +201,7 @@ public DnsServer addDnsServer(AddDnsServerCmd cmd) { } DnsProviderType type = cmd.getProvider(); - DnsServerVO server = new DnsServerVO(cmd.getName(), cmd.getUrl(), cmd.getPort(), type, + DnsServerVO server = new DnsServerVO(cmd.getName(), url, cmd.getPort(), type, cmd.getDnsUserName(), cmd.getDnsApiKey(), isDnsPublic, publicDomainSuffix, cmd.getNameServers(), caller.getAccountId(), caller.getDomainId()); @@ -271,13 +267,14 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { } if (cmd.getUrl() != null) { - if (!cmd.getUrl().equals(originalUrl)) { - validateDnsServerUrl(cmd.getUrl()); - DnsServer duplicate = dnsServerDao.findByUrlAndAccount(cmd.getUrl(), dnsServer.getAccountId()); + String url = StringUtils.trim(cmd.getUrl()); + if (!url.equals(originalUrl)) { + validateDnsServerUrl(url); + DnsServer duplicate = dnsServerDao.findByUrlAndAccount(url, dnsServer.getAccountId()); if (duplicate != null && duplicate.getId() != dnsServer.getId()) { throw new InvalidParameterValueException("Another DNS server with this URL already exists."); } - dnsServer.setUrl(cmd.getUrl()); + dnsServer.setUrl(url); validationRequired = true; } } 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 ec239239abdb..94efacad2859 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -718,7 +718,7 @@ public void testAddDnsServerSuccess() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); - when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); + when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id"); @@ -781,11 +781,28 @@ public void testListDnsZones() { public void testAddDnsServerAlreadyExists() { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); - when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); + when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(serverVO); manager.addDnsServer(cmd); } + @Test + public void testAddDnsServerTrimsUrlBeforeDuplicateCheckAndPersistence() throws Exception { + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); + when(cmd.getUrl()).thenReturn(" http://192.0.2.1:8081 "); + when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); + when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); + when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id"); + when(dnsServerDao.persist(any())).thenReturn(serverVO); + + manager.addDnsServer(cmd); + + verify(dnsServerDao).findByUrlAndAccount(eq("http://192.0.2.1:8081"), anyLong()); + verify(dnsServerDao).persist(Mockito.argThat(s -> "http://192.0.2.1:8081".equals(((DnsServerVO) s).getUrl()))); + } + @Test(expected = IllegalArgumentException.class) public void testAddDnsServerRejectsLoopbackUrl() { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( @@ -794,13 +811,21 @@ public void testAddDnsServerRejectsLoopbackUrl() { manager.addDnsServer(cmd); } + @Test(expected = IllegalArgumentException.class) + public void testAddDnsServerRejectsUrlWithoutScheme() { + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + when(cmd.getUrl()).thenReturn("192.0.2.1:8081"); + manager.addDnsServer(cmd); + } + @Test public void testAddDnsServerNormalUser() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); when(accountMgr.isDomainAdmin(callerMock.getId())).thenReturn(false); - when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); + when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(cmd.getNameServers()).thenReturn(Collections.emptyList()); when(cmd.isPublic()).thenReturn(true); @@ -819,7 +844,7 @@ public void testAddDnsServerValidationFailure() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); - when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); + when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(cmd.getNameServers()).thenReturn(Collections.emptyList()); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); @@ -832,7 +857,7 @@ public void testUpdateDnsServerUrlDuplicate() { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); - when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); + when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); DnsServerVO existingServer = mock(DnsServerVO.class); when(existingServer.getId()).thenReturn(SERVER_ID + 1); // Different ID implies duplicate @@ -855,12 +880,30 @@ public void testUpdateDnsServerRejectsLoopbackUrl() { manager.updateDnsServer(cmd); } + @Test + public void testUpdateDnsServerTreatsWhitespaceOnlyUrlChangeAsUnchanged() throws Exception { + org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + Integer unchangedPort = serverVO.getPort(); + when(cmd.getId()).thenReturn(SERVER_ID); + when(cmd.getUrl()).thenReturn(" http://192.0.2.1:8081 "); + when(cmd.getPort()).thenReturn(unchangedPort); + when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); + Mockito.doReturn("http://192.0.2.1:8081").when(serverVO).getUrl(); + when(dnsServerDao.update(anyLong(), any())).thenReturn(true); + + DnsServer result = manager.updateDnsServer(cmd); + assertNotNull(result); + verify(dnsProviderMock, never()).validate(any()); + verify(serverVO, never()).setUrl(anyString()); + } + @Test public void testUpdateDnsServerUrlValid() throws Exception { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); - when(cmd.getUrl()).thenReturn("http://93.184.216.34:8081"); + when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); From 6981138f89f17682426345da2038e721af6d13e8 Mon Sep 17 00:00:00 2001 From: dahn Date: Mon, 10 Aug 2026 14:20:28 +0200 Subject: [PATCH 03/13] Apply suggestion from @DaanHoogland --- .../java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f8a0aa5dd84f..69b202d3b40f 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -171,7 +171,7 @@ private DnsProvider getProviderByType(DnsProviderType type) { */ private void validateDnsServerUrl(String url) { if (StringUtils.isBlank(url)) { - return; + throw new IllegalArgumentException("URL cannot be blank."); } UriUtils.validateUrl(url); } From b88552badd9c630171363db2759f9792d6e549de Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Sat, 15 Aug 2026 09:27:15 +0200 Subject: [PATCH 04/13] address (some) review comments --- .../dns/DnsProviderManagerImpl.java | 29 ++++++++++++------- .../dns/DnsProviderManagerImplTest.java | 6 ++-- 2 files changed, 21 insertions(+), 14 deletions(-) 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 69b202d3b40f..83c9bb36e7f3 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -164,23 +164,30 @@ private DnsProvider getProviderByType(DnsProviderType type) { } /** - * Rejects a DNS provider URL that resolves to an illegal address before any provider client is given - * the chance to connect to it. See {@link UriUtils#validateUrl(String)} for the exact rules enforced - * (including the requirement that the URL declares an {@code http}/{@code https} scheme). - * Expects {@code url} to already be trimmed. + * Trims and rejects a DNS provider URL that resolves to an illegal address before any provider client + * is given the chance to connect to it. See {@link UriUtils#validateUrl(String)} for the exact rules + * enforced (including the requirement that the URL declares an {@code http}/{@code https} scheme). + * + * @return the trimmed URL. + * @throws InvalidParameterValueException if the URL is blank or fails validation. */ - private void validateDnsServerUrl(String url) { - if (StringUtils.isBlank(url)) { - throw new IllegalArgumentException("URL cannot be blank."); + private String validateDnsServerUrl(String url) { + String trimmedUrl = StringUtils.trim(url); + if (StringUtils.isBlank(trimmedUrl)) { + throw new InvalidParameterValueException("URL cannot be blank."); } - UriUtils.validateUrl(url); + try { + UriUtils.validateUrl(trimmedUrl); + } catch (IllegalArgumentException e) { + throw new InvalidParameterValueException(e.getMessage()); + } + return trimmedUrl; } @Override @ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription = "Adding a DNS Server") public DnsServer addDnsServer(AddDnsServerCmd cmd) { - String url = StringUtils.trim(cmd.getUrl()); - validateDnsServerUrl(url); + String url = validateDnsServerUrl(cmd.getUrl()); Account caller = CallContext.current().getCallingAccount(); DnsServer existing = dnsServerDao.findByUrlAndAccount(url, caller.getId()); if (existing != null) { @@ -269,7 +276,7 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { if (cmd.getUrl() != null) { String url = StringUtils.trim(cmd.getUrl()); if (!url.equals(originalUrl)) { - validateDnsServerUrl(url); + url = validateDnsServerUrl(url); DnsServer duplicate = dnsServerDao.findByUrlAndAccount(url, dnsServer.getAccountId()); if (duplicate != null && duplicate.getId() != dnsServer.getId()) { throw new InvalidParameterValueException("Another DNS server with this URL already exists."); 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 94efacad2859..7008edf1bd1c 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -803,7 +803,7 @@ public void testAddDnsServerTrimsUrlBeforeDuplicateCheckAndPersistence() throws verify(dnsServerDao).persist(Mockito.argThat(s -> "http://192.0.2.1:8081".equals(((DnsServerVO) s).getUrl()))); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = InvalidParameterValueException.class) public void testAddDnsServerRejectsLoopbackUrl() { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); @@ -811,7 +811,7 @@ public void testAddDnsServerRejectsLoopbackUrl() { manager.addDnsServer(cmd); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = InvalidParameterValueException.class) public void testAddDnsServerRejectsUrlWithoutScheme() { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); @@ -868,7 +868,7 @@ public void testUpdateDnsServerUrlDuplicate() { manager.updateDnsServer(cmd); } - @Test(expected = IllegalArgumentException.class) + @Test(expected = InvalidParameterValueException.class) public void testUpdateDnsServerRejectsLoopbackUrl() { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); From 1545adde35dd68a90291687fc3f7cec23b1e13f6 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Tue, 18 Aug 2026 15:12:06 +0530 Subject: [PATCH 05/13] restrict pvt/site-local urls to root admin only --- .../dns/DnsProviderManagerImpl.java | 18 ++++-- .../dns/DnsProviderManagerImplTest.java | 57 +++++++++++++++++++ 2 files changed, 70 insertions(+), 5 deletions(-) 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 83c9bb36e7f3..fbb06b27511f 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -97,6 +97,7 @@ import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.UriUtils; +import com.cloud.utils.net.NetUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.PluggableService; import com.cloud.utils.db.Filter; @@ -167,28 +168,35 @@ private DnsProvider getProviderByType(DnsProviderType type) { * Trims and rejects a DNS provider URL that resolves to an illegal address before any provider client * is given the chance to connect to it. See {@link UriUtils#validateUrl(String)} for the exact rules * enforced (including the requirement that the URL declares an {@code http}/{@code https} scheme). + * Private/site-local addresses (e.g. {@code 192.168.0.0/16}) are only permitted for root admin callers. * * @return the trimmed URL. - * @throws InvalidParameterValueException if the URL is blank or fails validation. + * @throws InvalidParameterValueException if the URL is blank, fails validation, or is a private address + * requested by a non-root-admin caller. */ - private String validateDnsServerUrl(String url) { + private String validateDnsServerUrl(String url, Account caller) { String trimmedUrl = StringUtils.trim(url); if (StringUtils.isBlank(trimmedUrl)) { throw new InvalidParameterValueException("URL cannot be blank."); } + Pair hostAndPort; try { - UriUtils.validateUrl(trimmedUrl); + hostAndPort = UriUtils.validateUrl(trimmedUrl); } catch (IllegalArgumentException e) { throw new InvalidParameterValueException(e.getMessage()); } + if (!accountMgr.isRootAdmin(caller.getId()) && NetUtils.isSiteLocalAddress(hostAndPort.first())) { + throw new InvalidParameterValueException( + "Only root admin accounts can configure a DNS server on a private/internal network address."); + } return trimmedUrl; } @Override @ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription = "Adding a DNS Server") public DnsServer addDnsServer(AddDnsServerCmd cmd) { - String url = validateDnsServerUrl(cmd.getUrl()); Account caller = CallContext.current().getCallingAccount(); + String url = validateDnsServerUrl(cmd.getUrl(), caller); DnsServer existing = dnsServerDao.findByUrlAndAccount(url, caller.getId()); if (existing != null) { throw new InvalidParameterValueException( @@ -276,7 +284,7 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { if (cmd.getUrl() != null) { String url = StringUtils.trim(cmd.getUrl()); if (!url.equals(originalUrl)) { - url = validateDnsServerUrl(url); + url = validateDnsServerUrl(url, caller); DnsServer duplicate = dnsServerDao.findByUrlAndAccount(url, dnsServer.getAccountId()); if (duplicate != null && duplicate.getId() != dnsServer.getId()) { throw new InvalidParameterValueException("Another DNS server with this URL already exists."); 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 7008edf1bd1c..512c417ccc5c 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -819,6 +819,31 @@ public void testAddDnsServerRejectsUrlWithoutScheme() { manager.addDnsServer(cmd); } + @Test(expected = InvalidParameterValueException.class) + public void testAddDnsServerRejectsPrivateAddressForNonRootAdmin() { + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); + when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); + manager.addDnsServer(cmd); + } + + @Test + public void testAddDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); + when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); + when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); + when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); + when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id"); + when(dnsServerDao.persist(any())).thenReturn(serverVO); + + DnsServer result = manager.addDnsServer(cmd); + assertNotNull(result); + verify(dnsServerDao).persist(any()); + } + @Test public void testAddDnsServerNormalUser() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( @@ -880,6 +905,38 @@ public void testUpdateDnsServerRejectsLoopbackUrl() { manager.updateDnsServer(cmd); } + @Test(expected = InvalidParameterValueException.class) + public void testUpdateDnsServerRejectsPrivateAddressForNonRootAdmin() { + org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); + when(cmd.getId()).thenReturn(SERVER_ID); + when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); + when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); + Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); + + manager.updateDnsServer(cmd); + } + + @Test + public void testUpdateDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { + org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); + when(cmd.getId()).thenReturn(SERVER_ID); + when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); + when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); + Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); + Mockito.doReturn(DnsProviderType.PowerDNS).when(serverVO).getProviderType(); + when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); + doNothing().when(dnsProviderMock).validate(any()); + when(dnsServerDao.update(anyLong(), any())).thenReturn(true); + + DnsServer result = manager.updateDnsServer(cmd); + assertNotNull(result); + verify(dnsProviderMock).validate(any()); + } + @Test public void testUpdateDnsServerTreatsWhitespaceOnlyUrlChangeAsUnchanged() throws Exception { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( From 28624dfa2a45b2036f8d927b6ce5715bf0a7228c Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Tue, 18 Aug 2026 17:51:04 +0530 Subject: [PATCH 06/13] trim url before passing to validation method --- .../dns/DnsProviderManagerImpl.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) 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 fbb06b27511f..af0208e18aa7 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -174,8 +174,7 @@ private DnsProvider getProviderByType(DnsProviderType type) { * @throws InvalidParameterValueException if the URL is blank, fails validation, or is a private address * requested by a non-root-admin caller. */ - private String validateDnsServerUrl(String url, Account caller) { - String trimmedUrl = StringUtils.trim(url); + private String validateDnsServerUrl(String trimmedUrl, Account caller) { if (StringUtils.isBlank(trimmedUrl)) { throw new InvalidParameterValueException("URL cannot be blank."); } @@ -196,11 +195,12 @@ private String validateDnsServerUrl(String url, Account caller) { @ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription = "Adding a DNS Server") public DnsServer addDnsServer(AddDnsServerCmd cmd) { Account caller = CallContext.current().getCallingAccount(); - String url = validateDnsServerUrl(cmd.getUrl(), caller); - DnsServer existing = dnsServerDao.findByUrlAndAccount(url, caller.getId()); + String trimmedUrl = StringUtils.trim(cmd.getUrl()); + String dnsUrl = validateDnsServerUrl(trimmedUrl, caller); + DnsServer existing = dnsServerDao.findByUrlAndAccount(dnsUrl, caller.getId()); if (existing != null) { throw new InvalidParameterValueException( - "This Account already has a DNS server integration for URL: " + url); + "This Account already has a DNS server integration for URL: " + dnsUrl); } boolean isDnsPublic = cmd.isPublic(); @@ -216,7 +216,7 @@ public DnsServer addDnsServer(AddDnsServerCmd cmd) { } DnsProviderType type = cmd.getProvider(); - DnsServerVO server = new DnsServerVO(cmd.getName(), url, cmd.getPort(), type, + DnsServerVO server = new DnsServerVO(cmd.getName(), dnsUrl, cmd.getPort(), type, cmd.getDnsUserName(), cmd.getDnsApiKey(), isDnsPublic, publicDomainSuffix, cmd.getNameServers(), caller.getAccountId(), caller.getDomainId()); @@ -282,14 +282,14 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { } if (cmd.getUrl() != null) { - String url = StringUtils.trim(cmd.getUrl()); - if (!url.equals(originalUrl)) { - url = validateDnsServerUrl(url, caller); - DnsServer duplicate = dnsServerDao.findByUrlAndAccount(url, dnsServer.getAccountId()); + String trimmedUrl = StringUtils.trim(cmd.getUrl()); + if (!trimmedUrl.equals(originalUrl)) { + String dnsUrl = validateDnsServerUrl(trimmedUrl, caller); + DnsServer duplicate = dnsServerDao.findByUrlAndAccount(dnsUrl, dnsServer.getAccountId()); if (duplicate != null && duplicate.getId() != dnsServer.getId()) { throw new InvalidParameterValueException("Another DNS server with this URL already exists."); } - dnsServer.setUrl(url); + dnsServer.setUrl(dnsUrl); validationRequired = true; } } From 05b86b3ad379d7cd4623d320f78a0fe6803667e0 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Tue, 18 Aug 2026 18:28:55 +0530 Subject: [PATCH 07/13] fix minor comment --- .../dns/DnsProviderManagerImpl.java | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) 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 af0208e18aa7..752f70efbe9b 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -109,9 +109,7 @@ import com.cloud.vm.Nic; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; -import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicDetailsDao; -import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @Component @@ -128,10 +126,6 @@ public class DnsProviderManagerImpl extends ManagerBase implements DnsProviderMa @Inject DnsZoneNetworkMapDao dnsZoneNetworkMapDao; @Inject - UserVmDao userVmDao; - @Inject - NicDao nicDao; - @Inject DomainDao domainDao; @Inject DnsZoneJoinDao dnsZoneJoinDao; @@ -170,11 +164,10 @@ private DnsProvider getProviderByType(DnsProviderType type) { * enforced (including the requirement that the URL declares an {@code http}/{@code https} scheme). * Private/site-local addresses (e.g. {@code 192.168.0.0/16}) are only permitted for root admin callers. * - * @return the trimmed URL. * @throws InvalidParameterValueException if the URL is blank, fails validation, or is a private address * requested by a non-root-admin caller. */ - private String validateDnsServerUrl(String trimmedUrl, Account caller) { + private void validateDnsServerUrl(String trimmedUrl, Account caller) { if (StringUtils.isBlank(trimmedUrl)) { throw new InvalidParameterValueException("URL cannot be blank."); } @@ -188,15 +181,14 @@ private String validateDnsServerUrl(String trimmedUrl, Account caller) { throw new InvalidParameterValueException( "Only root admin accounts can configure a DNS server on a private/internal network address."); } - return trimmedUrl; } @Override @ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription = "Adding a DNS Server") public DnsServer addDnsServer(AddDnsServerCmd cmd) { Account caller = CallContext.current().getCallingAccount(); - String trimmedUrl = StringUtils.trim(cmd.getUrl()); - String dnsUrl = validateDnsServerUrl(trimmedUrl, caller); + String dnsUrl = StringUtils.trim(cmd.getUrl()); + validateDnsServerUrl(dnsUrl, caller); DnsServer existing = dnsServerDao.findByUrlAndAccount(dnsUrl, caller.getId()); if (existing != null) { throw new InvalidParameterValueException( @@ -282,9 +274,9 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { } if (cmd.getUrl() != null) { - String trimmedUrl = StringUtils.trim(cmd.getUrl()); - if (!trimmedUrl.equals(originalUrl)) { - String dnsUrl = validateDnsServerUrl(trimmedUrl, caller); + String dnsUrl = StringUtils.trim(cmd.getUrl()); + if (!dnsUrl.equals(originalUrl)) { + validateDnsServerUrl(dnsUrl, caller); DnsServer duplicate = dnsServerDao.findByUrlAndAccount(dnsUrl, dnsServer.getAccountId()); if (duplicate != null && duplicate.getId() != dnsServer.getId()) { throw new InvalidParameterValueException("Another DNS server with this URL already exists."); From 34f233d17a610bc56ae5fce278ab7db260107b79 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Fri, 21 Aug 2026 15:43:19 +0530 Subject: [PATCH 08/13] restrict Add/Update/Delete Dns server api for root admin --- .../api/command/user/dns/AddDnsServerCmd.java | 2 +- .../command/user/dns/DeleteDnsServerCmd.java | 2 +- .../command/user/dns/UpdateDnsServerCmd.java | 2 +- ...ic_dns_view.sql => cloud.nic_dns_view.sql} | 0 .../dns/DnsProviderManagerImpl.java | 23 +++++----- .../dns/DnsProviderManagerImplTest.java | 42 ------------------- 6 files changed, 16 insertions(+), 55 deletions(-) rename engine/schema/src/main/resources/META-INF/db/views/{nic_dns_view.sql => cloud.nic_dns_view.sql} (100%) 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 298ddd64a31c..21279b8719fa 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 @@ -46,7 +46,7 @@ requestHasSensitiveInfo = true, responseHasSensitiveInfo = false, since = "4.23.0", - authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) + authorized = {RoleType.Admin}) public class AddDnsServerCmd extends BaseCmd { ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmd.java index 099fc62f354c..cb001f69523c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/DeleteDnsServerCmd.java @@ -40,7 +40,7 @@ entityType = {DnsServer.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0", - authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) + authorized = {RoleType.Admin}) public class DeleteDnsServerCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmd.java index 6b790fa8ade8..7a84c54dc666 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/dns/UpdateDnsServerCmd.java @@ -41,7 +41,7 @@ entityType = {DnsServer.class}, requestHasSensitiveInfo = true, responseHasSensitiveInfo = false, since = "4.23.0", - authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) + authorized = {RoleType.Admin}) public class UpdateDnsServerCmd extends BaseCmd { ///////////////////////////////////////////////////// diff --git a/engine/schema/src/main/resources/META-INF/db/views/nic_dns_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.nic_dns_view.sql similarity index 100% rename from engine/schema/src/main/resources/META-INF/db/views/nic_dns_view.sql rename to engine/schema/src/main/resources/META-INF/db/views/cloud.nic_dns_view.sql 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 752f70efbe9b..08377966a742 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -97,7 +97,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.UriUtils; -import com.cloud.utils.net.NetUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.PluggableService; import com.cloud.utils.db.Filter; @@ -162,31 +161,26 @@ private DnsProvider getProviderByType(DnsProviderType type) { * Trims and rejects a DNS provider URL that resolves to an illegal address before any provider client * is given the chance to connect to it. See {@link UriUtils#validateUrl(String)} for the exact rules * enforced (including the requirement that the URL declares an {@code http}/{@code https} scheme). - * Private/site-local addresses (e.g. {@code 192.168.0.0/16}) are only permitted for root admin callers. * - * @throws InvalidParameterValueException if the URL is blank, fails validation, or is a private address - * requested by a non-root-admin caller. + * @throws InvalidParameterValueException if the URL is blank, fails validation */ private void validateDnsServerUrl(String trimmedUrl, Account caller) { if (StringUtils.isBlank(trimmedUrl)) { throw new InvalidParameterValueException("URL cannot be blank."); } - Pair hostAndPort; try { - hostAndPort = UriUtils.validateUrl(trimmedUrl); + UriUtils.validateUrl(trimmedUrl); } catch (IllegalArgumentException e) { throw new InvalidParameterValueException(e.getMessage()); } - if (!accountMgr.isRootAdmin(caller.getId()) && NetUtils.isSiteLocalAddress(hostAndPort.first())) { - throw new InvalidParameterValueException( - "Only root admin accounts can configure a DNS server on a private/internal network address."); - } } @Override @ActionEvent(eventType = EventTypes.EVENT_DNS_SERVER_ADD, eventDescription = "Adding a DNS Server") public DnsServer addDnsServer(AddDnsServerCmd cmd) { Account caller = CallContext.current().getCallingAccount(); + enforceRootAdminOnly(caller.getId()); + String dnsUrl = StringUtils.trim(cmd.getUrl()); validateDnsServerUrl(dnsUrl, caller); DnsServer existing = dnsServerDao.findByUrlAndAccount(dnsUrl, caller.getId()); @@ -263,6 +257,8 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { } Account caller = CallContext.current().getCallingAccount(); + enforceRootAdminOnly(caller.getId()); + accountMgr.checkAccess(caller, null, true, dnsServer); boolean validationRequired = false; @@ -342,6 +338,7 @@ public boolean deleteDnsServer(DeleteDnsServerCmd cmd) { throw new InvalidParameterValueException(String.format("DNS server with ID: %s not found.", dnsServerId)); } Account caller = CallContext.current().getCallingAccount(); + enforceRootAdminOnly(caller.getId()); accountMgr.checkAccess(caller, null, true, dnsServer); return Transaction.execute((TransactionCallback) status -> { if (cmd.getCleanup()) { @@ -1252,4 +1249,10 @@ public void syncDnsRecordsState(Long instanceId, String dnsRecordUrl, long dnsZo provider.addRecord(dnsServer, dnsZone, recordIpv6); } } + + void enforceRootAdminOnly(Long callerId) { + if (!accountMgr.isRootAdmin(callerId)) { + throw new PermissionDeniedException("This API can only be called by root admin"); + } + } } 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 512c417ccc5c..ff1680d2fc07 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -819,15 +819,6 @@ public void testAddDnsServerRejectsUrlWithoutScheme() { manager.addDnsServer(cmd); } - @Test(expected = InvalidParameterValueException.class) - public void testAddDnsServerRejectsPrivateAddressForNonRootAdmin() { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); - when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); - when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); - manager.addDnsServer(cmd); - } - @Test public void testAddDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( @@ -844,26 +835,6 @@ public void testAddDnsServerAllowsPrivateAddressForRootAdmin() throws Exception verify(dnsServerDao).persist(any()); } - @Test - public void testAddDnsServerNormalUser() throws Exception { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); - when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); - when(accountMgr.isDomainAdmin(callerMock.getId())).thenReturn(false); - when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); - when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); - when(cmd.getNameServers()).thenReturn(Collections.emptyList()); - when(cmd.isPublic()).thenReturn(true); - when(cmd.getPublicDomainSuffix()).thenReturn("example.com"); - when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); - when(dnsProviderMock.validateAndResolveServer(any())).thenReturn("resolved-id"); - when(dnsServerDao.persist(any())).thenReturn(serverVO); - DnsServer result = manager.addDnsServer(cmd); - assertNotNull(result); - verify(dnsServerDao).persist(Mockito.argThat( - s -> !((DnsServerVO) s).getPublicServer() && ((DnsServerVO) s).getPublicDomainSuffix() == null)); - } - @Test(expected = CloudRuntimeException.class) public void testAddDnsServerValidationFailure() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( @@ -905,19 +876,6 @@ public void testUpdateDnsServerRejectsLoopbackUrl() { manager.updateDnsServer(cmd); } - @Test(expected = InvalidParameterValueException.class) - public void testUpdateDnsServerRejectsPrivateAddressForNonRootAdmin() { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); - when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); - when(cmd.getId()).thenReturn(SERVER_ID); - when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); - when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); - Mockito.doReturn("http://original:8081").when(serverVO).getUrl(); - - manager.updateDnsServer(cmd); - } - @Test public void testUpdateDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( From 265dab6c93f54e7cf9ddd4da7f8bb4cb099d2b8d Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Fri, 21 Aug 2026 15:59:20 +0530 Subject: [PATCH 09/13] fix minor comment --- .../java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 08377966a742..f86d23ae43c8 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -269,7 +269,7 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { dnsServer.setName(cmd.getName()); } - if (cmd.getUrl() != null) { + if (StringUtils.isNotBlank(cmd.getUrl())) { String dnsUrl = StringUtils.trim(cmd.getUrl()); if (!dnsUrl.equals(originalUrl)) { validateDnsServerUrl(dnsUrl, caller); From 8968e1b5e197f4d90ce715849ac5cbf4c1f56871 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Fri, 21 Aug 2026 16:43:08 +0530 Subject: [PATCH 10/13] fix unit test --- .../dns/DnsProviderManagerImplTest.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) 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 ff1680d2fc07..d2df647a61bd 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -175,6 +175,8 @@ public void setUp() throws Exception { doNothing().when(accountMgr).checkAccess(any(Account.class), nullable(org.apache.cloudstack.acl.SecurityChecker.AccessType.class), eq(true), any()); + + when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); } @After @@ -717,7 +719,6 @@ public void testConfigure() throws Exception { public void testAddDnsServerSuccess() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); - when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); @@ -790,7 +791,6 @@ public void testAddDnsServerAlreadyExists() { public void testAddDnsServerTrimsUrlBeforeDuplicateCheckAndPersistence() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); - when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); when(cmd.getUrl()).thenReturn(" http://192.0.2.1:8081 "); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); @@ -823,7 +823,6 @@ public void testAddDnsServerRejectsUrlWithoutScheme() { public void testAddDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); - when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); @@ -839,7 +838,6 @@ public void testAddDnsServerAllowsPrivateAddressForRootAdmin() throws Exception public void testAddDnsServerValidationFailure() throws Exception { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); - when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(cmd.getNameServers()).thenReturn(Collections.emptyList()); @@ -848,6 +846,14 @@ public void testAddDnsServerValidationFailure() throws Exception { manager.addDnsServer(cmd); } + @Test(expected = PermissionDeniedException.class) + public void testAddDnsServerNormalUser() throws Exception { + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( + org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); + manager.addDnsServer(cmd); + } + @Test(expected = InvalidParameterValueException.class) public void testUpdateDnsServerUrlDuplicate() { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( @@ -880,7 +886,6 @@ public void testUpdateDnsServerRejectsLoopbackUrl() { public void testUpdateDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); - when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); From 738bc24408cdecd848d4893480c0ff634cb75092 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Fri, 21 Aug 2026 20:57:25 +0530 Subject: [PATCH 11/13] address review comments --- .../org/apache/cloudstack/dns/DnsProviderManagerImpl.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 f86d23ae43c8..0d08540d129a 100644 --- a/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/dns/DnsProviderManagerImpl.java @@ -158,13 +158,13 @@ private DnsProvider getProviderByType(DnsProviderType type) { } /** - * Trims and rejects a DNS provider URL that resolves to an illegal address before any provider client + * Rejects a DNS provider URL that resolves to an illegal address before any provider client * is given the chance to connect to it. See {@link UriUtils#validateUrl(String)} for the exact rules * enforced (including the requirement that the URL declares an {@code http}/{@code https} scheme). * * @throws InvalidParameterValueException if the URL is blank, fails validation */ - private void validateDnsServerUrl(String trimmedUrl, Account caller) { + private void validateDnsServerUrl(String trimmedUrl) { if (StringUtils.isBlank(trimmedUrl)) { throw new InvalidParameterValueException("URL cannot be blank."); } @@ -182,7 +182,7 @@ public DnsServer addDnsServer(AddDnsServerCmd cmd) { enforceRootAdminOnly(caller.getId()); String dnsUrl = StringUtils.trim(cmd.getUrl()); - validateDnsServerUrl(dnsUrl, caller); + validateDnsServerUrl(dnsUrl); DnsServer existing = dnsServerDao.findByUrlAndAccount(dnsUrl, caller.getId()); if (existing != null) { throw new InvalidParameterValueException( @@ -272,7 +272,7 @@ public DnsServer updateDnsServer(UpdateDnsServerCmd cmd) { if (StringUtils.isNotBlank(cmd.getUrl())) { String dnsUrl = StringUtils.trim(cmd.getUrl()); if (!dnsUrl.equals(originalUrl)) { - validateDnsServerUrl(dnsUrl, caller); + validateDnsServerUrl(dnsUrl); DnsServer duplicate = dnsServerDao.findByUrlAndAccount(dnsUrl, dnsServer.getAccountId()); if (duplicate != null && duplicate.getId() != dnsServer.getId()) { throw new InvalidParameterValueException("Another DNS server with this URL already exists."); From 3892328010afd0d0e01ce73d3ce8c6c05ed6bd94 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Mon, 24 Aug 2026 16:04:27 +0200 Subject: [PATCH 12/13] fix build error --- .../org/apache/cloudstack/dns/DnsProviderManagerImplTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 85b01964947f..b4da56452900 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -893,7 +893,7 @@ public void testAddDnsServerPublicWithoutSuffixRejected() { org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); - when(cmd.getUrl()).thenReturn("http://newpdns:8081"); + when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.isPublic()).thenReturn(true); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); manager.addDnsServer(cmd); From 22ff345fe1d25500bbc83cdf146ee1460b841d9e Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Mon, 24 Aug 2026 16:20:24 +0200 Subject: [PATCH 13/13] server: optimize DnsProviderManagerImplTest --- .../dns/DnsProviderManagerImplTest.java | 306 +++++++++--------- 1 file changed, 144 insertions(+), 162 deletions(-) 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 b4da56452900..f70d5915a653 100644 --- a/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/dns/DnsProviderManagerImplTest.java @@ -42,11 +42,20 @@ import java.util.List; import java.util.Map; +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd; +import org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd; +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.DisassociateDnsZoneFromNetworkCmd; import org.apache.cloudstack.api.command.user.dns.ListDnsRecordsCmd; +import org.apache.cloudstack.api.command.user.dns.ListDnsServersCmd; +import org.apache.cloudstack.api.command.user.dns.ListDnsZonesCmd; +import org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd; import org.apache.cloudstack.api.command.user.dns.UpdateDnsZoneCmd; import org.apache.cloudstack.api.response.DnsRecordResponse; import org.apache.cloudstack.api.response.DnsServerResponse; @@ -83,6 +92,8 @@ import org.springframework.test.util.ReflectionTestUtils; import com.cloud.domain.dao.DomainDao; +import com.cloud.event.ActionEventUtils; +import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.network.dao.NetworkDao; @@ -90,9 +101,13 @@ import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; +import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicDetailsDao; @@ -174,7 +189,7 @@ public void setUp() throws Exception { manager.setDnsProviders(Collections.singletonList(dnsProviderMock)); doNothing().when(accountMgr).checkAccess(any(Account.class), - nullable(org.apache.cloudstack.acl.SecurityChecker.AccessType.class), eq(true), any()); + nullable(SecurityChecker.AccessType.class), eq(true), any()); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); } @@ -426,7 +441,7 @@ public void testDeleteDnsServerWithCleanup() throws Exception { when(cmd.getCleanup()).thenReturn(true); when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); doNothing().when(accountMgr).checkAccess(any(Account.class), - nullable(org.apache.cloudstack.acl.SecurityChecker.AccessType.class), eq(true), any()); + nullable(SecurityChecker.AccessType.class), eq(true), any()); List zones = Collections.singletonList(ZONE_ID); when(dnsZoneDao.findDnsZoneIdsByServerId(SERVER_ID)).thenReturn(zones); @@ -453,7 +468,7 @@ public void testDeleteDnsZoneSuccess() throws Exception { when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); when(dnsServerDao.findById(anyLong())).thenReturn(serverVO); doNothing().when(accountMgr).checkAccess(any(Account.class), - nullable(org.apache.cloudstack.acl.SecurityChecker.AccessType.class), eq(true), any()); + nullable(SecurityChecker.AccessType.class), eq(true), any()); when(dnsZoneNetworkMapDao.findByZoneId(ZONE_ID)).thenReturn(null); when(dnsZoneDao.remove(ZONE_ID)).thenReturn(true); @@ -617,8 +632,8 @@ public void testCheckDnsServerPermissionNonOwnerPublicOutsideDomain() { Mockito.doReturn(true).when(serverVO).getPublicServer(); when(serverOwner.getDomainId()).thenReturn(20L); when(callerMock.getDomainId()).thenReturn(DOMAIN_ID); - ReflectionTestUtils.setField(manager, "accountDao", Mockito.mock(com.cloud.user.dao.AccountDao.class)); - com.cloud.user.dao.AccountDao accountDaoMock = (com.cloud.user.dao.AccountDao) ReflectionTestUtils + ReflectionTestUtils.setField(manager, "accountDao", Mockito.mock(AccountDao.class)); + AccountDao accountDaoMock = (AccountDao) ReflectionTestUtils .getField(manager, "accountDao"); when(accountDaoMock.findByIdIncludingRemoved(ACCOUNT_ID)).thenReturn(serverOwner); when(domainDao.isChildDomain(20L, DOMAIN_ID)).thenReturn(false); @@ -661,8 +676,7 @@ public void testStartWithProviders() { @Test(expected = InvalidParameterValueException.class) public void testAssociateZoneToNetworkZoneNotFound() { - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd.class); + AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(dnsZoneDao.findById(ZONE_ID)).thenReturn(null); manager.associateZoneToNetwork(cmd); @@ -670,8 +684,7 @@ public void testAssociateZoneToNetworkZoneNotFound() { @Test(expected = InvalidParameterValueException.class) public void testAssociateZoneToNetworkNetworkNotFound() { - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd.class); + AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(cmd.getNetworkId()).thenReturn(NETWORK_ID); when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); @@ -682,8 +695,7 @@ public void testAssociateZoneToNetworkNetworkNotFound() { @Test(expected = CloudRuntimeException.class) public void testAssociateZoneToNetworkNonSharedNetwork() { - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd.class); + AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(cmd.getNetworkId()).thenReturn(NETWORK_ID); when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); @@ -696,8 +708,7 @@ public void testAssociateZoneToNetworkNonSharedNetwork() { @Test public void testAssociateZoneToNetworkSuccess() { - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd.class); + AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(cmd.getNetworkId()).thenReturn(NETWORK_ID); @@ -717,8 +728,7 @@ public void testAssociateZoneToNetworkSuccess() { @Test(expected = InvalidParameterValueException.class) public void testAssociateZoneToNetworkAlreadyAssociated() { - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AssociateDnsZoneToNetworkCmd.class); + AssociateDnsZoneToNetworkCmd cmd = mock(AssociateDnsZoneToNetworkCmd.class); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(cmd.getNetworkId()).thenReturn(NETWORK_ID); when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); @@ -733,8 +743,7 @@ public void testAssociateZoneToNetworkAlreadyAssociated() { @Test public void testCreateDnsRecordSuccess() throws Exception { - org.apache.cloudstack.api.command.user.dns.CreateDnsRecordCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.CreateDnsRecordCmd.class); + CreateDnsRecordCmd cmd = mock(CreateDnsRecordCmd.class); when(cmd.getName()).thenReturn("www"); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(cmd.getType()).thenReturn(DnsRecord.RecordType.A); @@ -750,8 +759,7 @@ public void testCreateDnsRecordSuccess() throws Exception { @Test public void testDeleteDnsRecordSuccess() throws Exception { - org.apache.cloudstack.api.command.user.dns.DeleteDnsRecordCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.DeleteDnsRecordCmd.class); + DeleteDnsRecordCmd cmd = mock(DeleteDnsRecordCmd.class); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(cmd.getName()).thenReturn("www"); when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); @@ -771,8 +779,7 @@ public void testConfigure() throws Exception { @Test public void testAddDnsServerSuccess() throws Exception { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); @@ -785,11 +792,10 @@ public void testAddDnsServerSuccess() throws Exception { @Test public void testListDnsServers() { - org.apache.cloudstack.api.command.user.dns.ListDnsServersCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.ListDnsServersCmd.class); + ListDnsServersCmd cmd = mock(ListDnsServersCmd.class); when(domainDao.getDomainParentIds(anyLong())).thenReturn(Collections.emptySet()); List servers = Collections.singletonList(serverVO); - com.cloud.utils.Pair, Integer> searchPair = new com.cloud.utils.Pair<>(servers, 1); + Pair, Integer> searchPair = new Pair<>(servers, 1); when(dnsServerDao.searchDnsServer(any(), anyLong(), any(), any(), any(), any())).thenReturn(searchPair); DnsServerJoinVO joinVO = mock(DnsServerJoinVO.class); @@ -803,8 +809,7 @@ public void testListDnsServers() { @Test public void testUpdateDnsServer() throws Exception { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.getName()).thenReturn("updated-name"); when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); @@ -816,13 +821,12 @@ public void testUpdateDnsServer() throws Exception { @Test public void testListDnsZones() { - org.apache.cloudstack.api.command.user.dns.ListDnsZonesCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.ListDnsZonesCmd.class); + ListDnsZonesCmd cmd = mock(ListDnsZonesCmd.class); when(cmd.getId()).thenReturn(null); when(cmd.getDnsServerId()).thenReturn(null); when(dnsServerDao.listDnsServerIdsByAccountId(anyLong())).thenReturn(Collections.emptyList()); List zones = Collections.singletonList(zoneVO); - com.cloud.utils.Pair, Integer> searchPair = new com.cloud.utils.Pair<>(zones, 1); + Pair, Integer> searchPair = new Pair<>(zones, 1); when(dnsZoneDao.searchZones(any(), anyLong(), any(), any(), any(), any())).thenReturn(searchPair); DnsZoneJoinVO joinVO = mock(DnsZoneJoinVO.class); @@ -834,8 +838,7 @@ public void testListDnsZones() { @Test(expected = InvalidParameterValueException.class) public void testAddDnsServerAlreadyExists() { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(serverVO); manager.addDnsServer(cmd); @@ -843,8 +846,7 @@ public void testAddDnsServerAlreadyExists() { @Test public void testAddDnsServerTrimsUrlBeforeDuplicateCheckAndPersistence() throws Exception { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(cmd.getUrl()).thenReturn(" http://192.0.2.1:8081 "); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); @@ -859,24 +861,21 @@ public void testAddDnsServerTrimsUrlBeforeDuplicateCheckAndPersistence() throws @Test(expected = InvalidParameterValueException.class) public void testAddDnsServerRejectsLoopbackUrl() { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(cmd.getUrl()).thenReturn("http://127.0.0.1:8081"); manager.addDnsServer(cmd); } @Test(expected = InvalidParameterValueException.class) public void testAddDnsServerRejectsUrlWithoutScheme() { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(cmd.getUrl()).thenReturn("192.0.2.1:8081"); manager.addDnsServer(cmd); } @Test public void testAddDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(dnsServerDao.findByUrlAndAccount(anyString(), anyLong())).thenReturn(null); @@ -890,8 +889,7 @@ public void testAddDnsServerAllowsPrivateAddressForRootAdmin() throws Exception @Test(expected = InvalidParameterValueException.class) public void testAddDnsServerPublicWithoutSuffixRejected() { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.isPublic()).thenReturn(true); @@ -901,8 +899,7 @@ public void testAddDnsServerPublicWithoutSuffixRejected() { @Test(expected = InvalidParameterValueException.class) public void testUpdateDnsServerPublicWithoutSuffixRejected() { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.isPublic()).thenReturn(true); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(true); @@ -912,8 +909,7 @@ public void testUpdateDnsServerPublicWithoutSuffixRejected() { @Test(expected = CloudRuntimeException.class) public void testAddDnsServerValidationFailure() throws Exception { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(cmd.getProvider()).thenReturn(DnsProviderType.PowerDNS); when(cmd.getNameServers()).thenReturn(Collections.emptyList()); @@ -924,16 +920,14 @@ public void testAddDnsServerValidationFailure() throws Exception { @Test(expected = PermissionDeniedException.class) public void testAddDnsServerNormalUser() throws Exception { - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.AddDnsServerCmd.class); + AddDnsServerCmd cmd = mock(AddDnsServerCmd.class); when(accountMgr.isRootAdmin(callerMock.getId())).thenReturn(false); manager.addDnsServer(cmd); } @Test(expected = InvalidParameterValueException.class) public void testUpdateDnsServerUrlDuplicate() { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); DnsServerVO existingServer = mock(DnsServerVO.class); @@ -948,8 +942,7 @@ public void testUpdateDnsServerUrlDuplicate() { @Test(expected = InvalidParameterValueException.class) public void testUpdateDnsServerRejectsLoopbackUrl() { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.getUrl()).thenReturn("http://127.0.0.1:8081"); when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); @@ -960,8 +953,7 @@ public void testUpdateDnsServerRejectsLoopbackUrl() { @Test public void testUpdateDnsServerAllowsPrivateAddressForRootAdmin() throws Exception { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.getUrl()).thenReturn("http://192.168.1.1:8081"); when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); @@ -978,8 +970,7 @@ public void testUpdateDnsServerAllowsPrivateAddressForRootAdmin() throws Excepti @Test public void testUpdateDnsServerTreatsWhitespaceOnlyUrlChangeAsUnchanged() throws Exception { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); Integer unchangedPort = serverVO.getPort(); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.getUrl()).thenReturn(" http://192.0.2.1:8081 "); @@ -996,8 +987,7 @@ public void testUpdateDnsServerTreatsWhitespaceOnlyUrlChangeAsUnchanged() throws @Test public void testUpdateDnsServerUrlValid() throws Exception { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.getUrl()).thenReturn("http://192.0.2.1:8081"); when(dnsServerDao.findById(SERVER_ID)).thenReturn(serverVO); @@ -1015,8 +1005,7 @@ public void testUpdateDnsServerUrlValid() throws Exception { @Test(expected = InvalidParameterValueException.class) public void testUpdateDnsServerValidationException() throws Exception { - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.UpdateDnsServerCmd.class); + UpdateDnsServerCmd cmd = mock(UpdateDnsServerCmd.class); when(cmd.getId()).thenReturn(SERVER_ID); when(cmd.getDnsApiKey()).thenReturn("new-api-key"); @@ -1033,10 +1022,10 @@ public void testUpdateDnsServerValidationException() throws Exception { @Test public void testVmLifecycleSubscriberStateUnchanged() { DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.OLD_STATE, com.cloud.vm.VirtualMachine.State.Running); - event.put(org.apache.cloudstack.api.ApiConstants.NEW_STATE, com.cloud.vm.VirtualMachine.State.Running); - event.put(org.apache.cloudstack.api.ApiConstants.INSTANCE_ID, 10L); + Map event = new HashMap<>(); + event.put(ApiConstants.OLD_STATE, VirtualMachine.State.Running); + event.put(ApiConstants.NEW_STATE, VirtualMachine.State.Running); + event.put(ApiConstants.INSTANCE_ID, 10L); subscriber.onPublishMessage("sender", "subject", event); verify(vmInstanceDao, never()).findByIdIncludingRemoved(anyLong()); @@ -1045,10 +1034,10 @@ public void testVmLifecycleSubscriberStateUnchanged() { @Test public void testVmLifecycleSubscriberRunning() { DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.OLD_STATE, com.cloud.vm.VirtualMachine.State.Starting); - event.put(org.apache.cloudstack.api.ApiConstants.NEW_STATE, com.cloud.vm.VirtualMachine.State.Running); - event.put(org.apache.cloudstack.api.ApiConstants.INSTANCE_ID, 12L); + Map event = new HashMap<>(); + event.put(ApiConstants.OLD_STATE, VirtualMachine.State.Starting); + event.put(ApiConstants.NEW_STATE, VirtualMachine.State.Running); + event.put(ApiConstants.INSTANCE_ID, 12L); // Expect handleVmEvent to be called, which accesses // vmInstanceDao.findByIdIncludingRemoved @@ -1061,10 +1050,10 @@ public void testVmLifecycleSubscriberRunning() { @Test public void testVmLifecycleSubscriberDestroyed() { DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.OLD_STATE, com.cloud.vm.VirtualMachine.State.Running); - event.put(org.apache.cloudstack.api.ApiConstants.NEW_STATE, VirtualMachine.State.Destroyed); - event.put(org.apache.cloudstack.api.ApiConstants.INSTANCE_ID, 15L); + Map event = new HashMap<>(); + event.put(ApiConstants.OLD_STATE, VirtualMachine.State.Running); + event.put(ApiConstants.NEW_STATE, VirtualMachine.State.Destroyed); + event.put(ApiConstants.INSTANCE_ID, 15L); when(nicDnsJoinDao.listIncludingRemovedByVmId(15L)).thenReturn(null); subscriber.onPublishMessage("sender", "subject", event); verify(nicDnsJoinDao, times(1)).listIncludingRemovedByVmId(15L); @@ -1073,10 +1062,10 @@ public void testVmLifecycleSubscriberDestroyed() { @Test public void testVmLifecycleSubscriberUnsupportedState() { DnsProviderManagerImpl.VmLifecycleSubscriber subscriber = manager.new VmLifecycleSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.OLD_STATE, com.cloud.vm.VirtualMachine.State.Running); - event.put(org.apache.cloudstack.api.ApiConstants.NEW_STATE, com.cloud.vm.VirtualMachine.State.Starting); - event.put(org.apache.cloudstack.api.ApiConstants.INSTANCE_ID, 20L); + Map event = new HashMap<>(); + event.put(ApiConstants.OLD_STATE, VirtualMachine.State.Running); + event.put(ApiConstants.NEW_STATE, VirtualMachine.State.Starting); + event.put(ApiConstants.INSTANCE_ID, 20L); subscriber.onPublishMessage("sender", "subject", event); verify(vmInstanceDao, never()).findByIdIncludingRemoved(anyLong()); @@ -1094,10 +1083,10 @@ public void testVmLifecycleSubscriberException() { @Test public void testNicLifecycleSubscriberCreate() { DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.EVENT_TYPE, com.cloud.event.EventTypes.EVENT_NIC_CREATE); - event.put(org.apache.cloudstack.api.ApiConstants.NIC_ID, 100L); - event.put(org.apache.cloudstack.api.ApiConstants.INSTANCE_ID, 200L); + Map event = new HashMap<>(); + event.put(ApiConstants.EVENT_TYPE, EventTypes.EVENT_NIC_CREATE); + event.put(ApiConstants.NIC_ID, 100L); + event.put(ApiConstants.INSTANCE_ID, 200L); when(vmInstanceDao.findById(200L)).thenReturn(null); // Short circuits handleNicEvent @@ -1108,10 +1097,10 @@ public void testNicLifecycleSubscriberCreate() { @Test public void testNicLifecycleSubscriberDelete() { DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.EVENT_TYPE, com.cloud.event.EventTypes.EVENT_NIC_DELETE); - event.put(org.apache.cloudstack.api.ApiConstants.NIC_ID, 101L); - event.put(org.apache.cloudstack.api.ApiConstants.INSTANCE_ID, 201L); + Map event = new HashMap<>(); + event.put(ApiConstants.EVENT_TYPE, EventTypes.EVENT_NIC_DELETE); + event.put(ApiConstants.NIC_ID, 101L); + event.put(ApiConstants.INSTANCE_ID, 201L); when(nicDnsJoinDao.findByIdIncludingRemoved(101L)).thenReturn(null); subscriber.onPublishMessage("sender", "subject", event); verify(nicDnsJoinDao, times(1)).findByIdIncludingRemoved(101L); @@ -1120,8 +1109,8 @@ public void testNicLifecycleSubscriberDelete() { @Test public void testNicLifecycleSubscriberMissingData() { DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.EVENT_TYPE, com.cloud.event.EventTypes.EVENT_NIC_CREATE); + Map event = new HashMap<>(); + event.put(ApiConstants.EVENT_TYPE, EventTypes.EVENT_NIC_CREATE); // Missing NIC_ID and INSTANCE_ID subscriber.onPublishMessage("sender", "subject", event); @@ -1131,10 +1120,10 @@ public void testNicLifecycleSubscriberMissingData() { @Test public void testNicLifecycleSubscriberUnsupportedEvent() { DnsProviderManagerImpl.NicLifecycleSubscriber subscriber = manager.new NicLifecycleSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.EVENT_TYPE, "unsupported-event"); - event.put(org.apache.cloudstack.api.ApiConstants.NIC_ID, 102L); - event.put(org.apache.cloudstack.api.ApiConstants.INSTANCE_ID, 202L); + Map event = new HashMap<>(); + event.put(ApiConstants.EVENT_TYPE, "unsupported-event"); + event.put(ApiConstants.NIC_ID, 102L); + event.put(ApiConstants.INSTANCE_ID, 202L); subscriber.onPublishMessage("sender", "subject", event); verify(vmInstanceDao, never()).findById(anyLong()); @@ -1168,8 +1157,7 @@ public void testPrepareDnsRecordUrlTrimsSubdomain() { @Test public void testCreateDnsRecordAlreadyExistsThrowsCloudRuntimeException() throws Exception { - org.apache.cloudstack.api.command.user.dns.CreateDnsRecordCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.CreateDnsRecordCmd.class); + CreateDnsRecordCmd cmd = mock(CreateDnsRecordCmd.class); when(cmd.getName()).thenReturn("www"); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(cmd.getType()).thenReturn(DnsRecord.RecordType.A); @@ -1188,8 +1176,7 @@ public void testCreateDnsRecordAlreadyExistsThrowsCloudRuntimeException() throws @Test public void testDeleteDnsRecordProviderReturnsNullReturnsFalse() throws Exception { - org.apache.cloudstack.api.command.user.dns.DeleteDnsRecordCmd cmd = mock( - org.apache.cloudstack.api.command.user.dns.DeleteDnsRecordCmd.class); + DeleteDnsRecordCmd cmd = mock(DeleteDnsRecordCmd.class); when(cmd.getDnsZoneId()).thenReturn(ZONE_ID); when(cmd.getName()).thenReturn("www"); when(dnsZoneDao.findById(ZONE_ID)).thenReturn(zoneVO); @@ -1234,7 +1221,7 @@ public void testSyncDnsRecordsStateOnlyIpv4AddsAAndDeletesAAAA() throws Exceptio @Test public void testHandleVmCreateEventFoundButNoActiveNics() throws DnsProviderException { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(vmInstanceDao.findById(30L)).thenReturn(instanceMock); when(nicDnsJoinDao.listActiveByVmId(30L)).thenReturn(Collections.emptyList()); @@ -1246,8 +1233,7 @@ public void testHandleVmCreateEventFoundButNoActiveNics() throws DnsProviderExce @Test public void testHandleVmDestroyEventNicWithNullDnsUrlIsSkipped() throws DnsProviderException { - NicDnsJoinVO nicMock = - mock(NicDnsJoinVO.class); + NicDnsJoinVO nicMock = mock(NicDnsJoinVO.class); when(nicMock.getNicDnsName()).thenReturn(null); when(nicDnsJoinDao.listIncludingRemovedByVmId(31L)) .thenReturn(Collections.singletonList(nicMock)); @@ -1259,8 +1245,7 @@ public void testHandleVmDestroyEventNicWithNullDnsUrlIsSkipped() throws DnsProvi @Test public void testHandleVmDestroyEventWithValidDnsUrlTriggersCleanup() throws Exception { - NicDnsJoinVO nicMock = - mock(NicDnsJoinVO.class); + NicDnsJoinVO nicMock = mock(NicDnsJoinVO.class); when(nicMock.getNicDnsName()).thenReturn("myvm.example.com"); when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); when(nicDnsJoinDao.listIncludingRemovedByVmId(32L)) @@ -1271,12 +1256,12 @@ public void testHandleVmDestroyEventWithValidDnsUrlTriggersCleanup() throws Exce when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(32L), eq(ZONE_ID), anyString())) .thenReturn(Collections.emptyList()); - try (MockedStatic txMock = - Mockito.mockStatic(com.cloud.utils.db.Transaction.class)) { - txMock.when(() -> com.cloud.utils.db.Transaction.execute( - any(com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn.class))) + try (MockedStatic txMock = + Mockito.mockStatic(Transaction.class)) { + txMock.when(() -> Transaction.execute( + any(TransactionCallbackWithExceptionNoReturn.class))) .thenAnswer(invocation -> { - com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn cb = + TransactionCallbackWithExceptionNoReturn cb = invocation.getArgument(0); try { cb.doInTransactionWithoutResult(null); @@ -1288,14 +1273,14 @@ public void testHandleVmDestroyEventWithValidDnsUrlTriggersCleanup() throws Exce manager.handleVmDestroyEvent(32L); - verify(nicDetailsDao).removeDetail(nicMock.getId(), org.apache.cloudstack.api.ApiConstants.NIC_DNS_NAME); + verify(nicDetailsDao).removeDetail(nicMock.getId(), ApiConstants.NIC_DNS_NAME); verify(dnsProviderMock, times(2)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); } } @Test public void testHandleNicPlugVmNotRunningExitsEarly() throws DnsProviderException { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(instanceMock.getState()).thenReturn(VirtualMachine.State.Destroyed); when(vmInstanceDao.findById(33L)).thenReturn(instanceMock); manager.handleNicPlug(33L, 500L); @@ -1305,8 +1290,7 @@ public void testHandleNicPlugVmNotRunningExitsEarly() throws DnsProviderExceptio @Test public void testHandleNicUnplugNicHasValidDnsUrlTriggersSyncCleanup() throws Exception { - NicDnsJoinVO nicMock = - mock(NicDnsJoinVO.class); + NicDnsJoinVO nicMock = mock(NicDnsJoinVO.class); when(nicMock.getNicDnsName()).thenReturn("myvm.example.com"); when(nicMock.getDnsZoneId()).thenReturn(ZONE_ID); when(nicDnsJoinDao.findByIdIncludingRemoved(600L)).thenReturn(nicMock); @@ -1316,12 +1300,12 @@ public void testHandleNicUnplugNicHasValidDnsUrlTriggersSyncCleanup() throws Exc when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(34L), eq(ZONE_ID), anyString())) .thenReturn(Collections.emptyList()); - try (MockedStatic txMock = - Mockito.mockStatic(com.cloud.utils.db.Transaction.class)) { - txMock.when(() -> com.cloud.utils.db.Transaction.execute( - any(com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn.class))) + try (MockedStatic txMock = + Mockito.mockStatic(Transaction.class)) { + txMock.when(() -> Transaction.execute( + any(TransactionCallbackWithExceptionNoReturn.class))) .thenAnswer(invocation -> { - com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn cb = + TransactionCallbackWithExceptionNoReturn cb = invocation.getArgument(0); try { cb.doInTransactionWithoutResult(null); @@ -1333,14 +1317,14 @@ public void testHandleNicUnplugNicHasValidDnsUrlTriggersSyncCleanup() throws Exc manager.handleNicUnplug(34L, 600L); - verify(nicDetailsDao).removeDetail(600L, org.apache.cloudstack.api.ApiConstants.NIC_DNS_NAME); + verify(nicDetailsDao).removeDetail(600L, ApiConstants.NIC_DNS_NAME); verify(dnsProviderMock, times(2)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); } } @Test public void testHandleVmHostnameChangedVmFoundButNoActiveNicsExitsEarly() throws DnsProviderException { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(vmInstanceDao.findById(35L)).thenReturn(instanceMock); when(nicDnsJoinDao.listActiveByVmId(35L)).thenReturn(Collections.emptyList()); @@ -1352,14 +1336,13 @@ public void testHandleVmHostnameChangedVmFoundButNoActiveNicsExitsEarly() throws @Test public void testIsDnsCollisionReturnsTrueForDifferentInstance() { - NicDnsJoinVO existing = - mock(NicDnsJoinVO.class); + NicDnsJoinVO existing = mock(NicDnsJoinVO.class); when(existing.getInstanceId()).thenReturn(99L); when(nicDnsJoinDao.findActiveByDnsRecordAndZone(ZONE_ID, "vm.example.com")).thenReturn(existing); - try (MockedStatic aeMock = - Mockito.mockStatic(com.cloud.event.ActionEventUtils.class)) { - aeMock.when(() -> com.cloud.event.ActionEventUtils.onActionEvent( + try (MockedStatic aeMock = + Mockito.mockStatic(ActionEventUtils.class)) { + aeMock.when(() -> ActionEventUtils.onActionEvent( anyLong(), anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString())) .thenReturn(1L); boolean result = (boolean) ReflectionTestUtils.invokeMethod( @@ -1389,8 +1372,8 @@ public void testIsDnsCollisionReturnsFalseWhenSameInstance() { @Test public void testHandleNicPlugRunningVmNicFoundButZoneNullExitsGracefully() throws DnsProviderException { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); - when(instanceMock.getState()).thenReturn(com.cloud.vm.VirtualMachine.State.Running); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); + when(instanceMock.getState()).thenReturn(VirtualMachine.State.Running); when(vmInstanceDao.findById(40L)).thenReturn(instanceMock); NicDnsJoinVO nicMock = @@ -1408,7 +1391,7 @@ public void testHandleNicPlugRunningVmNicFoundButZoneNullExitsGracefully() throw @Test public void testHandleVmHostnameChangedNonEmptyNicsAllZonesMissingSkipsTransactions() throws DnsProviderException { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(vmInstanceDao.findById(41L)).thenReturn(instanceMock); NicDnsJoinVO nicMock = @@ -1425,7 +1408,7 @@ public void testHandleVmHostnameChangedNonEmptyNicsAllZonesMissingSkipsTransacti @Test public void testHandleVmCreateEventNonEmptyNicsAllZonesMissingSkipsSync() throws DnsProviderException { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(vmInstanceDao.findById(42L)).thenReturn(instanceMock); NicDnsJoinVO nicMock = @@ -1452,11 +1435,10 @@ public void testVmRenameSubscriberInvalidPayloadIsSwallowed() { public void testVmRenameSubscriberMissingInstanceIdSwallowsNpe() { DnsProviderManagerImpl.VmRenameActionSubscriber subscriber = manager.new VmRenameActionSubscriber(); - java.util.Map event = new java.util.HashMap<>(); - event.put(org.apache.cloudstack.api.ApiConstants.EVENT_TYPE, - com.cloud.event.EventTypes.EVENT_VM_UPDATE); - event.put(org.apache.cloudstack.api.ApiConstants.HOST_NAME, "newvm"); - event.put(org.apache.cloudstack.api.ApiConstants.OLD_HOST_NAME, "oldvm"); + Map event = new HashMap<>(); + event.put(ApiConstants.EVENT_TYPE, EventTypes.EVENT_VM_UPDATE); + event.put(ApiConstants.HOST_NAME, "newvm"); + event.put(ApiConstants.OLD_HOST_NAME, "oldvm"); // INSTANCE_ID intentionally absent → (long) null → NullPointerException → caught internally subscriber.onPublishMessage("sender", "topic", event); verify(vmInstanceDao, never()).findById(anyLong()); @@ -1473,7 +1455,7 @@ public void testHandleVmCreateEventInstanceNullExitsEarly() throws DnsProviderEx @Test public void testHandleVmCreateEventFullSyncNoCollision() throws Exception { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(instanceMock.getHostName()).thenReturn("myvm"); when(vmInstanceDao.findById(51L)).thenReturn(instanceMock); @@ -1491,12 +1473,12 @@ public void testHandleVmCreateEventFullSyncNoCollision() throws Exception { when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(51L), eq(ZONE_ID), anyString())) .thenReturn(Collections.emptyList()); - try (MockedStatic txMock = - Mockito.mockStatic(com.cloud.utils.db.Transaction.class)) { - txMock.when(() -> com.cloud.utils.db.Transaction.execute( - any(com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn.class))) + try (MockedStatic txMock = + Mockito.mockStatic(Transaction.class)) { + txMock.when(() -> Transaction.execute( + any(TransactionCallbackWithExceptionNoReturn.class))) .thenAnswer(invocation -> { - com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn cb = + TransactionCallbackWithExceptionNoReturn cb = invocation.getArgument(0); try { cb.doInTransactionWithoutResult(null); } catch (Exception e) { throw new RuntimeException(e); } @@ -1506,14 +1488,14 @@ public void testHandleVmCreateEventFullSyncNoCollision() throws Exception { manager.handleVmCreateEvent(51L); verify(nicDetailsDao).addDetail(anyLong(), - eq(org.apache.cloudstack.api.ApiConstants.NIC_DNS_NAME), anyString(), eq(true)); + eq(ApiConstants.NIC_DNS_NAME), anyString(), eq(true)); verify(dnsProviderMock, times(2)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); } } @Test public void testHandleVmCreateEventCollisionSkipsAddDetail() throws Exception { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(instanceMock.getHostName()).thenReturn("myvm"); when(vmInstanceDao.findById(52L)).thenReturn(instanceMock); @@ -1530,17 +1512,17 @@ public void testHandleVmCreateEventCollisionSkipsAddDetail() throws Exception { when(colliding.getInstanceId()).thenReturn(999L); when(nicDnsJoinDao.findActiveByDnsRecordAndZone(eq(ZONE_ID), anyString())).thenReturn(colliding); - try (MockedStatic txMock = - Mockito.mockStatic(com.cloud.utils.db.Transaction.class); - MockedStatic aeMock = - Mockito.mockStatic(com.cloud.event.ActionEventUtils.class)) { - aeMock.when(() -> com.cloud.event.ActionEventUtils.onActionEvent( + try (MockedStatic txMock = + Mockito.mockStatic(Transaction.class); + MockedStatic aeMock = + Mockito.mockStatic(ActionEventUtils.class)) { + aeMock.when(() -> ActionEventUtils.onActionEvent( anyLong(), anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString())) .thenReturn(1L); - txMock.when(() -> com.cloud.utils.db.Transaction.execute( - any(com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn.class))) + txMock.when(() -> Transaction.execute( + any(TransactionCallbackWithExceptionNoReturn.class))) .thenAnswer(invocation -> { - com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn cb = + TransactionCallbackWithExceptionNoReturn cb = invocation.getArgument(0); try { cb.doInTransactionWithoutResult(null); } catch (Exception e) { throw new RuntimeException(e); } @@ -1565,7 +1547,7 @@ public void testHandleVmHostnameChangedInstanceNullExitsEarly() throws DnsProvid @Test public void testHandleVmHostnameChangedFqdnUnchangedSkipsNic() throws DnsProviderException { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(vmInstanceDao.findById(61L)).thenReturn(instanceMock); NicDnsJoinVO nicMock = @@ -1586,7 +1568,7 @@ public void testHandleVmHostnameChangedFqdnUnchangedSkipsNic() throws DnsProvide @Test public void testHandleVmHostnameChangedFullRenamePath() throws Exception { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(vmInstanceDao.findById(62L)).thenReturn(instanceMock); NicDnsJoinVO nicMock = @@ -1604,12 +1586,12 @@ public void testHandleVmHostnameChangedFullRenamePath() throws Exception { when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(62L), eq(ZONE_ID), anyString())) .thenReturn(Collections.emptyList()); - try (MockedStatic txMock = - Mockito.mockStatic(com.cloud.utils.db.Transaction.class)) { - txMock.when(() -> com.cloud.utils.db.Transaction.execute( - any(com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn.class))) + try (MockedStatic txMock = + Mockito.mockStatic(Transaction.class)) { + txMock.when(() -> Transaction.execute( + any(TransactionCallbackWithExceptionNoReturn.class))) .thenAnswer(invocation -> { - com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn cb = + TransactionCallbackWithExceptionNoReturn cb = invocation.getArgument(0); try { cb.doInTransactionWithoutResult(null); } catch (Exception e) { throw new RuntimeException(e); } @@ -1620,10 +1602,10 @@ public void testHandleVmHostnameChangedFullRenamePath() throws Exception { // Tx1: old URL removed from nic_details verify(nicDetailsDao).removeDetail(anyLong(), - eq(org.apache.cloudstack.api.ApiConstants.NIC_DNS_NAME)); + eq(ApiConstants.NIC_DNS_NAME)); // Tx2: new URL written to nic_details verify(nicDetailsDao).addDetail(anyLong(), - eq(org.apache.cloudstack.api.ApiConstants.NIC_DNS_NAME), anyString(), eq(true)); + eq(ApiConstants.NIC_DNS_NAME), anyString(), eq(true)); // deleteRecord called for both old-sync (A+AAAA) and new-sync (A+AAAA) = 4 total verify(dnsProviderMock, times(4)).deleteRecord(eq(serverVO), eq(zoneVO), any(DnsRecord.class)); } @@ -1631,7 +1613,7 @@ public void testHandleVmHostnameChangedFullRenamePath() throws Exception { @Test public void testHandleVmHostnameChangedCollisionOnNewUrlSkipsAddDetail() { - com.cloud.vm.VMInstanceVO instanceMock = mock(com.cloud.vm.VMInstanceVO.class); + VMInstanceVO instanceMock = mock(VMInstanceVO.class); when(vmInstanceDao.findById(63L)).thenReturn(instanceMock); NicDnsJoinVO nicMock = @@ -1651,17 +1633,17 @@ public void testHandleVmHostnameChangedCollisionOnNewUrlSkipsAddDetail() { when(nicDnsJoinDao.listActiveByVmIdZoneAndDnsRecord(eq(63L), eq(ZONE_ID), anyString())) .thenReturn(Collections.emptyList()); - try (MockedStatic txMock = - Mockito.mockStatic(com.cloud.utils.db.Transaction.class); - MockedStatic aeMock = - Mockito.mockStatic(com.cloud.event.ActionEventUtils.class)) { - aeMock.when(() -> com.cloud.event.ActionEventUtils.onActionEvent( + try (MockedStatic txMock = + Mockito.mockStatic(Transaction.class); + MockedStatic aeMock = + Mockito.mockStatic(ActionEventUtils.class)) { + aeMock.when(() -> ActionEventUtils.onActionEvent( anyLong(), anyLong(), anyLong(), anyString(), anyString(), anyLong(), anyString())) .thenReturn(1L); - txMock.when(() -> com.cloud.utils.db.Transaction.execute( - any(com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn.class))) + txMock.when(() -> Transaction.execute( + any(TransactionCallbackWithExceptionNoReturn.class))) .thenAnswer(invocation -> { - com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn cb = + TransactionCallbackWithExceptionNoReturn cb = invocation.getArgument(0); try { cb.doInTransactionWithoutResult(null); } catch (Exception e) { throw new RuntimeException(e); }