diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java index 454bbdc4390..3b900b93f14 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -99,6 +99,7 @@ import com.cloud.network.element.IpDeployingRequester; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.UserDataServiceProvider; import com.cloud.network.router.VirtualRouter; +import org.apache.cloudstack.extension.Extension; import org.apache.cloudstack.extension.ExtensionHelper; import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement; import com.cloud.network.rules.FirewallRule.Purpose; @@ -1310,34 +1311,46 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi } } - // Also include extension-backed NetworkExtension providers registered in - // physical_network_service_providers whose provider name matches a registered - // NetworkOrchestrator extension (detected via extensionHelper.isNetworkExtensionProvider). - // - // We use _pNSPDao.listBy(physNetId) to enumerate all NSP entries, then check - // each provider name against the extension registry. This avoids a separate - // pass over all physical-network/extension combinations. + // Also include extension-backed NetworkExtension providers. // resolveProvider() creates a transient Provider (not added to the static list) // for extension names that are not in the built-in registry. try { - List physNets = _physicalNetworkDao.listAll(); - if (physNets != null) { - // Use a set to avoid adding the same provider name twice (multiple phys-nets) - Set addedExtProviders = new HashSet<>(); - for (PhysicalNetworkVO physNet : physNets) { - List nsps = _pNSPDao.listBy(physNet.getId()); - if (nsps == null) continue; + List nsps = _pNSPDao.listAll(); + if (CollectionUtils.isNotEmpty(nsps)) { + Set extensionProviderNames = new HashSet<>(); + List extensions = extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator); + if (extensions != null) { + for (Extension extension : extensions) { + if (extension == null || StringUtils.isBlank(extension.getName())) { + continue; + } + extensionProviderNames.add(extension.getName().toLowerCase()); + } + } + + if (!extensionProviderNames.isEmpty()) { + // Avoid duplicate provider names across multiple physical networks. + Set addedExtProviders = new HashSet<>(); for (PhysicalNetworkServiceProviderVO nsp : nsps) { String provName = nsp.getProviderName(); - if (provName == null || addedExtProviders.contains(provName)) continue; - if (!extensionHelper.isNetworkExtensionProvider(provName)) continue; + if (StringUtils.isBlank(provName)) { + continue; + } - // Filter by service if requested: check the NSP's service flags - if (service != null && !isServiceProvidedByNsp(nsp, service)) continue; + if (addedExtProviders.contains(provName) || !extensionProviderNames.contains(provName)) { + continue; + } + + // Filter by service if requested: check the NSP's service flags. + if (service != null && !isServiceProvidedByNsp(nsp, service)) { + continue; + } // Resolve or create a transient Provider for the extension name. Provider extProvider = resolveProvider(provName); - if (extProvider == null) continue; + if (extProvider == null) { + continue; + } supportedProviders.add(extProvider); addedExtProviders.add(provName); } @@ -1355,7 +1368,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi * has its service flag set for {@code service}. * *

This is used by {@link #listSupportedNetworkServiceProviders} to filter extension-backed - * providers (looked up via {@link com.cloud.network.dao.PhysicalNetworkServiceProviderDao#listBy}) + * providers (looked up via {@link com.cloud.network.dao.PhysicalNetworkServiceProviderDao#listAll}) * without having to query each extension's capability JSON.

*/ private boolean isServiceProvidedByNsp( diff --git a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java index 9ef1f44ddcd..44a73f4fb73 100644 --- a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java +++ b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java @@ -60,7 +60,6 @@ import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; -import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.VpcVirtualRouterElement; import com.cloud.network.vpc.VpcVO; @@ -73,6 +72,7 @@ import com.cloud.utils.net.Ip; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.extension.Extension; import org.apache.cloudstack.extension.ExtensionHelper; import org.apache.cloudstack.framework.extensions.network.NetworkExtensionElement; @@ -392,13 +392,14 @@ public class NetworkModelImplTest { @Test public void listSupportedNetworkServiceProvidersIncludesExtensionBackedProviders() { - PhysicalNetworkVO physNet = mock(PhysicalNetworkVO.class); - when(physNet.getId()).thenReturn(1L); - when(physicalNetworkDao.listAll()).thenReturn(List.of(physNet)); - PhysicalNetworkServiceProviderVO nsp = mock(PhysicalNetworkServiceProviderVO.class); when(nsp.getProviderName()).thenReturn("my-ext"); - when(physicalNetworkServiceProviderDao.listBy(1L)).thenReturn(List.of(nsp)); + when(physicalNetworkServiceProviderDao.listAll()).thenReturn(List.of(nsp)); + + Extension extension = mock(Extension.class); + when(extension.getName()).thenReturn("my-ext"); + when(extensionHelper.listExtensionsByType(Extension.Type.NetworkOrchestrator)).thenReturn(List.of(extension)); + when(extensionHelper.isNetworkExtensionProvider("my-ext")).thenReturn(true); // networkElements is empty so no standard providers found @@ -409,5 +410,8 @@ public class NetworkModelImplTest { boolean found = result.stream().anyMatch(p -> "my-ext".equalsIgnoreCase(p.getName())); assertTrue("Extension-backed provider should be included", found); + + Mockito.verify(physicalNetworkServiceProviderDao, Mockito.times(1)).listAll(); + Mockito.verify(physicalNetworkServiceProviderDao, Mockito.never()).listBy(Mockito.anyLong()); } }