diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index 0846306f70f9..862e708291d3 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -89,7 +89,7 @@ public static Routing fromValue(String type) { } else if (type.equalsIgnoreCase("Dynamic")) { return Dynamic; } else { - throw new InvalidParameterValueException("Unexpected Routing type : " + type); + throw new InvalidParameterValueException("Unexpected Routing mode : " + type); } } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java index 7c4b733a80f5..cbb2ef3f0b19 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java @@ -296,7 +296,7 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement private String internetProtocol; @SerializedName(ApiConstants.IPV6_ROUTING) - @Param(description = "The IPv6 routing type of network offering", since = "4.17.0") + @Param(description = "The IPv6 routing mode of network offering", since = "4.17.0") private String ipv6Routing; @SerializedName(ApiConstants.IPV6_ROUTES) @@ -320,7 +320,7 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement private String ipv6Dns2; @SerializedName(ApiConstants.IPV4_ROUTING) - @Param(description = "The IPv4 routing type of network", since = "4.20.0") + @Param(description = "The IPv4 routing mode of network", since = "4.20.0") private String ipv4Routing; @SerializedName(ApiConstants.IPV4_ROUTES) diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java index 2648ba836785..b17327706dee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java @@ -165,6 +165,10 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll @Param(description = "The IPv4 routing mode of VPC", since = "4.20.0") private String ipv4Routing; + @SerializedName(ApiConstants.IPV6_ROUTING) + @Param(description = "The IPv6 routing mode of VPC", since = "4.22.1") + private String ipv6Routing; + @SerializedName(ApiConstants.IPV4_ROUTES) @Param(description = "The routes for the VPC to ease adding route in upstream router", since = "4.20.0") private Set ipv4Routes; @@ -312,6 +316,10 @@ public void addIpv4Route(Ipv4RouteResponse ipv4Route) { this.ipv4Routes.add(ipv4Route); } + public void setIpv6Routing(String ipv6Routing) { + this.ipv6Routing = ipv6Routing; + } + public void setIpv6Routes(Set ipv6Routes) { this.ipv6Routes = ipv6Routes; } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index e0dcc574ed56..caab9831f6dd 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -2820,19 +2820,15 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) Ipv4RouteResponse route = new Ipv4RouteResponse(network.getCidr(), ip.getAddress().addr()); response.addIpv4Route(route); } - - if (view == ResponseView.Full) { - List bgpPeerVOS = bgpPeerDao.listNonRevokeByNetworkId(network.getId()); - for (BgpPeerVO bgpPeerVO : bgpPeerVOS) { - BgpPeerResponse bgpPeerResponse = routedIpv4Manager.createBgpPeerResponse(bgpPeerVO); - response.addBgpPeer(bgpPeerResponse); - } - } } if (networkOfferingDao.isIpv6Supported(network.getNetworkOfferingId())) { response.setInternetProtocol(networkOfferingDao.getNetworkOfferingInternetProtocol(network.getNetworkOfferingId(), NetUtils.InternetProtocol.IPv4).toString()); - response.setIpv6Routing(Network.Routing.Static.toString()); + if (networkOffering != null && networkOffering.getRoutingMode() != null) { + response.setIpv6Routing(networkOffering.getRoutingMode().name()); + } else { + response.setIpv6Routing(Network.Routing.Static.toString()); + } response.setIpv6Routes(new LinkedHashSet<>()); if (Network.GuestType.Isolated.equals(networkOffering.getGuestType())) { List ipv6Addresses = ipv6Service.getPublicIpv6AddressesForNetwork(network); @@ -2843,6 +2839,15 @@ public NetworkResponse createNetworkResponse(ResponseView view, Network network) } } + // Add BGP peer information for full view + if (view == ResponseView.Full) { + List bgpPeerVOS = bgpPeerDao.listNonRevokeByNetworkId(network.getId()); + for (BgpPeerVO bgpPeerVO : bgpPeerVOS) { + BgpPeerResponse bgpPeerResponse = routedIpv4Manager.createBgpPeerResponse(bgpPeerVO); + response.addBgpPeer(bgpPeerResponse); + } + } + response.setObjectName("network"); return response; } @@ -3557,7 +3562,6 @@ public VpcResponse createVpcResponse(ResponseView view, Vpc vpc) { response.setTags(tagResponses); response.setHasAnnotation(annotationDao.hasAnnotations(vpc.getUuid(), AnnotationService.EntityType.VPC.name(), _accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId()))); - ipv6Service.updateIpv6RoutesForVpcResponse(vpc, response); response.setDns1(vpc.getIp4Dns1()); response.setDns2(vpc.getIp4Dns2()); response.setIpv6Dns1(vpc.getIp6Dns1()); @@ -3578,12 +3582,22 @@ public VpcResponse createVpcResponse(ResponseView view, Vpc vpc) { response.addIpv4Route(route); } } - if (view == ResponseView.Full) { - List bgpPeerVOS = bgpPeerDao.listNonRevokeByVpcId(vpc.getId()); - for (BgpPeerVO bgpPeerVO : bgpPeerVOS) { - BgpPeerResponse bgpPeerResponse = routedIpv4Manager.createBgpPeerResponse(bgpPeerVO); - response.addBgpPeer(bgpPeerResponse); - } + } + + // add IPv6 routes + ipv6Service.updateIpv6RoutesForVpcResponse(vpc, response); + if (Objects.nonNull(asNumberVO)) { + response.setIpv6Routing(Network.Routing.Dynamic.name()); + } else { + response.setIpv6Routing(Network.Routing.Static.name()); + } + + // Add BGP peer information for full view + if (view == ResponseView.Full && Objects.nonNull(asNumberVO)) { + List bgpPeerVOS = bgpPeerDao.listNonRevokeByVpcId(vpc.getId()); + for (BgpPeerVO bgpPeerVO : bgpPeerVOS) { + BgpPeerResponse bgpPeerResponse = routedIpv4Manager.createBgpPeerResponse(bgpPeerVO); + response.addBgpPeer(bgpPeerResponse); } } diff --git a/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java b/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java index c003bd05a518..fc9f5be990f4 100644 --- a/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java +++ b/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java @@ -34,6 +34,7 @@ import com.cloud.network.element.BgpServiceProvider; import com.cloud.network.element.NetworkElement; import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcOffering; import com.cloud.network.vpc.VpcOfferingVO; import com.cloud.network.vpc.VpcVO; import com.cloud.network.vpc.dao.VpcDao; @@ -396,9 +397,12 @@ public boolean applyBgpPeers(Network network, boolean continueOnError) throws Re if (!routedIpv4Manager.isDynamicRoutedNetwork(network)) { return true; } - final String gatewayProviderStr = ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Network.Service.Gateway); - if (gatewayProviderStr != null) { - NetworkElement provider = networkModel.getElementImplementingProvider(gatewayProviderStr); + NetworkOffering networkOffering = networkOfferingDao.findById(network.getNetworkOfferingId()); + final String bgpServiceProvider = NetworkOffering.NetworkMode.ROUTED.equals(networkOffering.getNetworkMode()) ? + ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Network.Service.Gateway): + ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Network.Service.SourceNat); + if (bgpServiceProvider != null) { + NetworkElement provider = networkModel.getElementImplementingProvider(bgpServiceProvider); if (provider != null && provider instanceof BgpServiceProvider) { List bgpPeers = getBgpPeersForNetwork(network); LOGGER.debug(String.format("Applying BPG Peers for network [%s]: [%s]", network, bgpPeers)); @@ -413,9 +417,12 @@ public boolean applyBgpPeers(Vpc vpc, boolean continueOnError) throws ResourceUn if (!routedIpv4Manager.isDynamicRoutedVpc(vpc)) { return true; } - final String gatewayProviderStr = vpcServiceMapDao.getProviderForServiceInVpc(vpc.getId(), Network.Service.Gateway); - if (gatewayProviderStr != null) { - NetworkElement provider = networkModel.getElementImplementingProvider(gatewayProviderStr); + VpcOffering vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId()); + final String bgpServiceProvider = NetworkOffering.NetworkMode.ROUTED.equals(vpcOffering.getNetworkMode()) ? + vpcServiceMapDao.getProviderForServiceInVpc(vpc.getId(), Network.Service.Gateway): + vpcServiceMapDao.getProviderForServiceInVpc(vpc.getId(), Network.Service.SourceNat); + if (bgpServiceProvider != null) { + NetworkElement provider = networkModel.getElementImplementingProvider(bgpServiceProvider); if (provider != null && provider instanceof BgpServiceProvider) { List bgpPeers = getBgpPeersForVpc(vpc); LOGGER.debug(String.format("Applying BPG Peers for VPC [%s]: [%s]", vpc, bgpPeers)); diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java index b915027e9ab3..0fdfd169567d 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -1467,14 +1467,29 @@ public void createBgpPeersCommands(final List bgpPeers, final } else { guestNetworks.add(network); } + Map guestNetworkOfferings = new HashMap<>(); + for (Network guestNetwork : guestNetworks) { + final NetworkOfferingVO offering = _networkOfferingDao.findByIdIncludingRemoved(guestNetwork.getNetworkOfferingId()); + guestNetworkOfferings.put(guestNetwork.getId(), offering); + } for (BgpPeer bgpPeer: bgpPeers) { Map bgpPeerDetails = bgpPeerDetailsDao.getBgpPeerDetails(bgpPeer.getId()); for (Network guestNetwork : guestNetworks) { - bgpPeerTOs.add(new BgpPeerTO(bgpPeer.getId(), bgpPeer.getIp4Address(), bgpPeer.getIp6Address(), bgpPeer.getAsNumber(), bgpPeer.getPassword(), - guestNetwork.getId(), asNumberVO.getAsNumber(), guestNetwork.getCidr(), guestNetwork.getIp6Cidr(), bgpPeerDetails)); + final NetworkOfferingVO offering = guestNetworkOfferings.get(guestNetwork.getId()); + if (NetworkOffering.NetworkMode.ROUTED.equals(offering.getNetworkMode())) { + bgpPeerTOs.add(new BgpPeerTO(bgpPeer.getId(), bgpPeer.getIp4Address(), bgpPeer.getIp6Address(), bgpPeer.getAsNumber(), bgpPeer.getPassword(), + guestNetwork.getId(), asNumberVO.getAsNumber(), guestNetwork.getCidr(), guestNetwork.getIp6Cidr(), bgpPeerDetails)); + } else if (guestNetwork.getIp6Cidr() != null && bgpPeer.getIp6Address() != null) { + bgpPeerTOs.add(new BgpPeerTO(bgpPeer.getId(), null, bgpPeer.getIp6Address(), bgpPeer.getAsNumber(), bgpPeer.getPassword(), + guestNetwork.getId(), asNumberVO.getAsNumber(), null, guestNetwork.getIp6Cidr(), bgpPeerDetails)); + } } } + if (bgpPeerTOs.isEmpty()) { + logger.debug("No BGP peers to configure for the guest network or VPC, skipping."); + return; + } final SetBgpPeersCommand cmd = new SetBgpPeersCommand(bgpPeerTOs); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); diff --git a/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java b/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java index ac12dc3a9b10..65ded8d3c110 100644 --- a/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java @@ -1013,8 +1013,9 @@ public boolean isDynamicRoutedNetwork(Network network) { @Override public boolean isDynamicRoutedNetwork(NetworkOffering networkOffering) { - return NetworkOffering.NetworkMode.ROUTED.equals(networkOffering.getNetworkMode()) - && NetworkOffering.RoutingMode.Dynamic.equals(networkOffering.getRoutingMode()); + return NetworkOffering.RoutingMode.Dynamic.equals(networkOffering.getRoutingMode()) && + (NetworkOffering.NetworkMode.ROUTED.equals(networkOffering.getNetworkMode()) || + NetUtils.InternetProtocol.DualStack.equals(networkOfferingDao.getNetworkOfferingInternetProtocol(networkOffering.getId()))); } @Override @@ -1030,8 +1031,9 @@ public boolean isDynamicRoutedVpc(Vpc vpc) { @Override public boolean isDynamicRoutedVpc(VpcOffering vpcOffering) { - return NetworkOffering.NetworkMode.ROUTED.equals(vpcOffering.getNetworkMode()) - && NetworkOffering.RoutingMode.Dynamic.equals(vpcOffering.getRoutingMode()); + return NetworkOffering.RoutingMode.Dynamic.equals(vpcOffering.getRoutingMode()) && + (NetworkOffering.NetworkMode.ROUTED.equals(vpcOffering.getNetworkMode()) || + NetUtils.InternetProtocol.DualStack.equals(vpcOfferingDao.getVpcOfferingInternetProtocol(vpcOffering.getId()))); } @Override diff --git a/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java b/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java index be31a1c6502c..fc9c1cc5ed68 100644 --- a/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java +++ b/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java @@ -25,7 +25,12 @@ import com.cloud.network.element.VirtualRouterElement; import com.cloud.network.element.VpcVirtualRouterElement; import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcOfferingVO; +import com.cloud.network.vpc.dao.VpcOfferingDao; import com.cloud.network.vpc.dao.VpcServiceMapDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; import org.apache.cloudstack.network.BgpPeerVO; @@ -46,6 +51,7 @@ import java.util.List; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; @@ -76,6 +82,18 @@ public class BGPServiceImplTest { @Mock VpcServiceMapDao vpcServiceMapDao; + @Mock + NetworkOfferingDao networkOfferingDao; + + @Mock + VpcOfferingDao vpcOfferingDao; + + @Mock + NetworkOfferingVO networkOffering; + + @Mock + VpcOfferingVO vpcOffering; + @Test public void testASNumbersOverlap() { Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(1,2,3,4), false); @@ -94,6 +112,8 @@ public void testApplyBgpPeersForIsolatedNetwork() throws ResourceUnavailableExce when(network.getVpcId()).thenReturn(null); when(routedIpv4Manager.isDynamicRoutedNetwork(network)).thenReturn(true); + when(networkOfferingDao.findById(anyLong())).thenReturn(networkOffering); + when(networkOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED); when(ntwkSrvcDao.getProviderForServiceInNetwork(networkId, Network.Service.Gateway)).thenReturn("VirtualRouter"); VirtualRouterElement virtualRouterElement = Mockito.mock(VirtualRouterElement.class); when(networkModel.getElementImplementingProvider("VirtualRouter")).thenReturn(virtualRouterElement); @@ -121,9 +141,11 @@ public void testApplyBgpPeersForVpcTier() throws ResourceUnavailableException { when(network.getDataCenterId()).thenReturn(zoneId); when(routedIpv4Manager.isDynamicRoutedNetwork(network)).thenReturn(true); - when(ntwkSrvcDao.getProviderForServiceInNetwork(networkId, Network.Service.Gateway)).thenReturn("VirtualRouter"); + when(networkOfferingDao.findById(anyLong())).thenReturn(networkOffering); + when(networkOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED); + when(ntwkSrvcDao.getProviderForServiceInNetwork(networkId, Network.Service.Gateway)).thenReturn("VPCVirtualRouter"); VirtualRouterElement virtualRouterElement = Mockito.mock(VirtualRouterElement.class); - when(networkModel.getElementImplementingProvider("VirtualRouter")).thenReturn(virtualRouterElement); + when(networkModel.getElementImplementingProvider("VPCVirtualRouter")).thenReturn(virtualRouterElement); when(bgpPeerDao.listNonRevokeByVpcId(vpcId)).thenReturn(new ArrayList<>()); @@ -153,6 +175,8 @@ public void testApplyBgpPeersForVpcWithBgpPeers() throws ResourceUnavailableExce when(vpc.getZoneId()).thenReturn(zoneId); when(routedIpv4Manager.isDynamicRoutedVpc(vpc)).thenReturn(true); + when(vpcOfferingDao.findById(anyLong())).thenReturn(vpcOffering); + when(vpcOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED); when(vpcServiceMapDao.getProviderForServiceInVpc(vpcId, Network.Service.Gateway)).thenReturn("VPCVirtualRouter"); VpcVirtualRouterElement vpcVirtualRouterElement = Mockito.mock(VpcVirtualRouterElement.class); when(networkModel.getElementImplementingProvider("VPCVirtualRouter")).thenReturn(vpcVirtualRouterElement); diff --git a/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java b/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java index bdd905841f92..9e19321085d4 100644 --- a/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java +++ b/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java @@ -65,6 +65,7 @@ import java.util.List; import java.util.Map; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @@ -226,6 +227,10 @@ public void testCreateBgpPeersCommandsForNetwork() { when(dcDao.findById(zoneId)).thenReturn(dc); when(dc.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced); + NetworkOfferingVO offering = Mockito.mock(NetworkOfferingVO.class); + when(networkOfferingDao.findByIdIncludingRemoved(anyLong())).thenReturn(offering); + when(offering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED); + commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, network); Assert.assertEquals(1, cmds.size()); @@ -254,6 +259,7 @@ public void testCreateBgpPeersCommandsForVpc() { NetworkOfferingVO offering = Mockito.mock(NetworkOfferingVO.class); when(networkOfferingDao.findByIdIncludingRemoved(networkOfferingId)).thenReturn(offering); when(offering.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Dynamic); + when(offering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED); NetworkVO network1 = Mockito.mock(NetworkVO.class); when(network1.getNetworkOfferingId()).thenReturn(networkOfferingId); NetworkVO network2 = Mockito.mock(NetworkVO.class); diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index acc7424c9f0c..b3f58f7173a3 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -71,7 +71,7 @@ export default { }, { name: 'bgp.peers', component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/BgpPeersTab.vue'))), - show: (record, route, user) => { return !record.vpcid && ['Admin'].includes(user.roletype) && record.ip4routing === 'Dynamic' } + show: (record, route, user) => { return !record.vpcid && ['Admin'].includes(user.roletype) && (record.ip4routing === 'Dynamic' || record.ip6routing === 'Dynamic') } }, { name: 'routing.firewall', component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutingFirewallRulesTab.vue'))), diff --git a/ui/src/views/infra/zone/BgpPeersTab.vue b/ui/src/views/infra/zone/BgpPeersTab.vue index e55daf02a413..431d728a5275 100644 --- a/ui/src/views/infra/zone/BgpPeersTab.vue +++ b/ui/src/views/infra/zone/BgpPeersTab.vue @@ -18,7 +18,7 @@