From f91a7308e2febb75af8c688b64e059e5d250a262 Mon Sep 17 00:00:00 2001 From: Mitch <25337396+MitchDrage@users.noreply.github.com> Date: Fri, 25 Sep 2026 12:33:33 +0000 Subject: [PATCH 1/2] Fix listPublicIpAddresses NPE on shared network router placeholder NICs (#14248) --- .../main/java/com/cloud/api/ApiDBUtils.java | 4 +++ .../java/com/cloud/api/ApiResponseHelper.java | 5 ++-- .../com/cloud/api/ApiResponseHelperTest.java | 27 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index c74f2ee536f7..afd460a5749b 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -2311,6 +2311,10 @@ public static NicVO findByIp4AddressAndNetworkId(String ip4Address, long network return s_nicDao.findByIp4AddressAndNetworkId(ip4Address, networkId); } + public static NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId) { + return s_nicDao.findNonPlaceHolderByIp4AddressAndNetworkId(ip4Address, networkId); + } + public static NicSecondaryIpVO findSecondaryIpByIp4AddressAndNetworkId(String ip4Address, long networkId) { return s_nicSecondaryIpDao.findByIp4AddressAndNetworkId(ip4Address, networkId); } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 9cf1304e292e..91526101134d 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -1235,9 +1235,9 @@ private void setVpcIdInResponse(Long vpcId, Consumer vpcUuidSetter, Cons } } - private void showVmInfoForSharedNetworks(boolean forVirtualNetworks, IpAddress ipAddr, IPAddressResponse ipResponse) { + protected void showVmInfoForSharedNetworks(boolean forVirtualNetworks, IpAddress ipAddr, IPAddressResponse ipResponse) { if (!forVirtualNetworks) { - NicVO nic = ApiDBUtils.findByIp4AddressAndNetworkId(ipAddr.getAddress().toString(), ipAddr.getNetworkId()); + NicVO nic = ApiDBUtils.findNonPlaceHolderByIp4AddressAndNetworkId(ipAddr.getAddress().toString(), ipAddr.getNetworkId()); if (nic == null) { // find in nic_secondary_ips, user vm only NicSecondaryIpVO secondaryIp = @@ -1266,6 +1266,7 @@ private void showVmInfoForSharedNetworks(boolean forVirtualNetworks, IpAddress i } } } else if (nic.getVmType() == Type.DomainRouter) { + ipResponse.setIsSystem(true); VirtualMachine vm = ApiDBUtils.findVMInstanceById(nic.getInstanceId()); if (vm != null) { ipResponse.setVirtualMachineId(vm.getUuid()); diff --git a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java index 223b0740cf27..2bf6a6d90fe3 100644 --- a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java +++ b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java @@ -42,6 +42,7 @@ import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; import org.apache.cloudstack.api.response.DirectDownloadCertificateResponse; import org.apache.cloudstack.api.response.GuestOSCategoryResponse; +import org.apache.cloudstack.api.response.IPAddressResponse; import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.api.response.ResourceIconResponse; @@ -68,6 +69,7 @@ import com.cloud.configuration.Resource; import com.cloud.domain.DomainVO; import com.cloud.host.HostVO; +import com.cloud.network.IpAddress; import com.cloud.network.PublicIpQuarantine; import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.network.as.AutoScaleVmGroupVO; @@ -96,7 +98,9 @@ import com.cloud.utils.net.Ip; import com.cloud.vm.ConsoleSessionVO; import com.cloud.vm.NicSecondaryIp; +import com.cloud.vm.NicVO; import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.response.ConsoleSessionResponse; @@ -759,4 +763,27 @@ public void createConsoleSessionResponseTestShouldReturnFullResponse() { Assert.assertEquals(expected.getVmName(), response.getVmName()); } } + + @Test + public void showVmInfoForSharedNetworksTestRouterIp() { + IpAddress ipAddress = Mockito.mock(IpAddress.class); + when(ipAddress.getAddress()).thenReturn(new Ip("10.1.1.2")); + when(ipAddress.getNetworkId()).thenReturn(1L); + NicVO routerNic = new NicVO("DirectNetworkGuru", 2L, 1L, VirtualMachine.Type.DomainRouter); + IPAddressResponse ipResponse = Mockito.mock(IPAddressResponse.class); + + try (MockedStatic ignored = Mockito.mockStatic(ApiDBUtils.class)) { + when(ApiDBUtils.findNonPlaceHolderByIp4AddressAndNetworkId("10.1.1.2", 1L)).thenReturn(routerNic); + when(ApiDBUtils.findVMInstanceById(2L)).thenReturn(vmInstanceVOMock); + when(vmInstanceVOMock.getUuid()).thenReturn("router-uuid"); + when(vmInstanceVOMock.getHostName()).thenReturn("r-2-VM"); + when(vmInstanceVOMock.getType()).thenReturn(VirtualMachine.Type.DomainRouter); + + apiResponseHelper.showVmInfoForSharedNetworks(false, ipAddress, ipResponse); + + verify(ipResponse).setIsSystem(true); + verify(ipResponse).setVirtualMachineId("router-uuid"); + verify(ipResponse).setVirtualMachineName("r-2-VM"); + } + } } From d4fcd0fa895226a7d065325dfa15de13be0cb1bf Mon Sep 17 00:00:00 2001 From: Mitch <25337396+MitchDrage@users.noreply.github.com> Date: Sun, 4 Oct 2026 05:06:13 +0000 Subject: [PATCH 2/2] Get the router's real NIC when the shared network IP lookup returns its placeholder --- .../java/com/cloud/api/ApiResponseHelper.java | 19 ++++++++++----- .../com/cloud/api/ApiResponseHelperTest.java | 24 +++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 91526101134d..3b09e7a2f184 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -1237,7 +1237,7 @@ private void setVpcIdInResponse(Long vpcId, Consumer vpcUuidSetter, Cons protected void showVmInfoForSharedNetworks(boolean forVirtualNetworks, IpAddress ipAddr, IPAddressResponse ipResponse) { if (!forVirtualNetworks) { - NicVO nic = ApiDBUtils.findNonPlaceHolderByIp4AddressAndNetworkId(ipAddr.getAddress().toString(), ipAddr.getNetworkId()); + NicVO nic = ApiDBUtils.findByIp4AddressAndNetworkId(ipAddr.getAddress().toString(), ipAddr.getNetworkId()); if (nic == null) { // find in nic_secondary_ips, user vm only NicSecondaryIpVO secondaryIp = @@ -1267,11 +1267,18 @@ protected void showVmInfoForSharedNetworks(boolean forVirtualNetworks, IpAddress } } else if (nic.getVmType() == Type.DomainRouter) { ipResponse.setIsSystem(true); - VirtualMachine vm = ApiDBUtils.findVMInstanceById(nic.getInstanceId()); - if (vm != null) { - ipResponse.setVirtualMachineId(vm.getUuid()); - ipResponse.setVirtualMachineName(vm.getHostName()); - ipResponse.setVirtualMachineType(vm.getType().toString()); + // The lookup above returns either the router's nic or its placeholder, whichever the db returns first. + NicVO routerNic = nic; + if (nic.getReservationStrategy() == Nic.ReservationStrategy.PlaceHolder) { // found a placeholder nic, get the router's real nic + routerNic = ApiDBUtils.findNonPlaceHolderByIp4AddressAndNetworkId(ipAddr.getAddress().toString(), ipAddr.getNetworkId()); + } + if (routerNic != null) { // continue if we have the router's real nic, i.e. the router exists + VirtualMachine vm = ApiDBUtils.findVMInstanceById(routerNic.getInstanceId()); + if (vm != null) { + ipResponse.setVirtualMachineId(vm.getUuid()); + ipResponse.setVirtualMachineName(vm.getHostName()); + ipResponse.setVirtualMachineType(vm.getType().toString()); + } } } else if (nic.getVmType().isUsedBySystem()) { ipResponse.setIsSystem(true); diff --git a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java index 2bf6a6d90fe3..0a9afbc224b4 100644 --- a/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java +++ b/server/src/test/java/com/cloud/api/ApiResponseHelperTest.java @@ -97,6 +97,7 @@ import com.cloud.user.dao.UserDataDao; import com.cloud.utils.net.Ip; import com.cloud.vm.ConsoleSessionVO; +import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.NicVO; import com.cloud.vm.VMInstanceVO; @@ -769,10 +770,13 @@ public void showVmInfoForSharedNetworksTestRouterIp() { IpAddress ipAddress = Mockito.mock(IpAddress.class); when(ipAddress.getAddress()).thenReturn(new Ip("10.1.1.2")); when(ipAddress.getNetworkId()).thenReturn(1L); + NicVO placeholderNic = new NicVO(null, null, 1L, VirtualMachine.Type.DomainRouter); + placeholderNic.setReservationStrategy(Nic.ReservationStrategy.PlaceHolder); NicVO routerNic = new NicVO("DirectNetworkGuru", 2L, 1L, VirtualMachine.Type.DomainRouter); IPAddressResponse ipResponse = Mockito.mock(IPAddressResponse.class); try (MockedStatic ignored = Mockito.mockStatic(ApiDBUtils.class)) { + when(ApiDBUtils.findByIp4AddressAndNetworkId("10.1.1.2", 1L)).thenReturn(placeholderNic); when(ApiDBUtils.findNonPlaceHolderByIp4AddressAndNetworkId("10.1.1.2", 1L)).thenReturn(routerNic); when(ApiDBUtils.findVMInstanceById(2L)).thenReturn(vmInstanceVOMock); when(vmInstanceVOMock.getUuid()).thenReturn("router-uuid"); @@ -786,4 +790,24 @@ public void showVmInfoForSharedNetworksTestRouterIp() { verify(ipResponse).setVirtualMachineName("r-2-VM"); } } + + @Test + public void showVmInfoForSharedNetworksTestRouterPlaceholderOnly() { + IpAddress ipAddress = Mockito.mock(IpAddress.class); + when(ipAddress.getAddress()).thenReturn(new Ip("10.1.1.2")); + when(ipAddress.getNetworkId()).thenReturn(1L); + NicVO placeholderNic = new NicVO(null, null, 1L, VirtualMachine.Type.DomainRouter); + placeholderNic.setReservationStrategy(Nic.ReservationStrategy.PlaceHolder); + IPAddressResponse ipResponse = Mockito.mock(IPAddressResponse.class); + + try (MockedStatic ignored = Mockito.mockStatic(ApiDBUtils.class)) { + when(ApiDBUtils.findNonPlaceHolderByIp4AddressAndNetworkId("10.1.1.2", 1L)).thenReturn(null); + when(ApiDBUtils.findByIp4AddressAndNetworkId("10.1.1.2", 1L)).thenReturn(placeholderNic); + + apiResponseHelper.showVmInfoForSharedNetworks(false, ipAddress, ipResponse); + + verify(ipResponse).setIsSystem(true); + verify(ipResponse, Mockito.never()).setVirtualMachineId(Mockito.anyString()); + } + } }