Skip to content

Commit 22a6604

Browse files
Routed mode: minor improvements and fixes (apache#9697)
* Routed: add global setting to enable/disable routed mode * Routed: add unit tests * Routed: add unit tests RoutedIpv4ManagerImplTest * PR9697: fix end of files lint error * PR9697: fix unit tests error with JDK17 * Routed: do not show ROUTED mode when create network/vpc offering if routed is disabled * Routed: check if routed network is enabled when create ipv4 subnet/bgp peer/ASN range/network/vpc * Routed: UI bug fix and improvements * Routed: apply available BGP peers when VR is restarted
1 parent 8f63660 commit 22a6604

File tree

26 files changed

+1902
-90
lines changed

26 files changed

+1902
-90
lines changed

api/src/main/java/com/cloud/bgp/BGPService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.cloud.network.vpc.Vpc;
2222
import com.cloud.utils.Pair;
2323
import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd;
24+
import org.apache.cloudstack.network.BgpPeer;
2425

2526
import java.util.List;
2627

@@ -36,4 +37,8 @@ public interface BGPService {
3637
boolean applyBgpPeers(Network network, boolean continueOnError) throws ResourceUnavailableException;
3738

3839
boolean applyBgpPeers(Vpc vpc, boolean continueOnError) throws ResourceUnavailableException;
40+
41+
List<? extends BgpPeer> getBgpPeersForNetwork(Network network);
42+
43+
List<? extends BgpPeer> getBgpPeersForVpc(Vpc vpc);
3944
}

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,7 @@ public class ApiConstants {
986986
public static final String ACL_NAME = "aclname";
987987
public static final String NUMBER = "number";
988988
public static final String IS_DYNAMICALLY_SCALABLE = "isdynamicallyscalable";
989+
public static final String ROUTED_MODE_ENABLED = "routedmodeenabled";
989990
public static final String ROUTING = "isrouting";
990991
public static final String ROUTING_MODE = "routingmode";
991992
public static final String MAX_CONNECTIONS = "maxconnections";

api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
157157
@Param(description = "AS Number Range")
158158
private String asnRange;
159159

160+
@SerializedName(ApiConstants.ROUTED_MODE_ENABLED)
161+
@Param(description = "true, if routed network/vpc is enabled", since = "4.20.1")
162+
private boolean routedModeEnabled = false;
163+
164+
160165
public ZoneResponse() {
161166
tags = new LinkedHashSet<ResourceTagResponse>();
162167
}
@@ -412,4 +417,12 @@ public void setAsnRange(String asnRange) {
412417
public String getAsnRange() {
413418
return asnRange;
414419
}
420+
421+
public boolean isRoutedModeEnabled() {
422+
return routedModeEnabled;
423+
}
424+
425+
public void setRoutedModeEnabled(boolean routedModeEnabled) {
426+
this.routedModeEnabled = routedModeEnabled;
427+
}
415428
}

api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@
5757

5858
public interface RoutedIpv4Manager extends PluggableService, Configurable {
5959

60+
ConfigKey<Boolean> RoutedNetworkVpcEnabled = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Boolean.class,
61+
"routed.network.vpc.enabled",
62+
"true",
63+
"If true, the Routed network and VPC are enabled in the zone.",
64+
true,
65+
ConfigKey.Scope.Zone);
66+
6067
ConfigKey<Integer> RoutedNetworkIPv4MaxCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class,
6168
"routed.network.ipv4.max.cidr.size", "30", "The maximum value of the cidr size for isolated networks in ROUTED mode",
6269
true, ConfigKey.Scope.Account);
@@ -196,4 +203,6 @@ public interface RoutedIpv4Manager extends PluggableService, Configurable {
196203
void removeBgpPeersByAccountId(long accountId);
197204

198205
void removeBgpPeersByDomainId(long domainId);
206+
207+
Boolean isRoutedNetworkVpcEnabled(long zoneId);
199208
}

server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.apache.cloudstack.api.response.ResourceTagResponse;
3535
import org.apache.cloudstack.api.response.ZoneResponse;
3636
import org.apache.cloudstack.context.CallContext;
37+
import org.apache.cloudstack.network.RoutedIpv4Manager;
3738
import org.apache.commons.collections.CollectionUtils;
3839
import org.apache.commons.lang3.ObjectUtils;
3940
import org.springframework.stereotype.Component;
@@ -141,6 +142,8 @@ public ZoneResponse newDataCenterResponse(ResponseView view, DataCenterJoinVO da
141142
String asRange = asNumberRange.stream().map(range -> range.getStartASNumber() + "-" + range.getEndASNumber()).collect(Collectors.joining(", "));
142143
zoneResponse.setAsnRange(asRange);
143144

145+
zoneResponse.setRoutedModeEnabled(RoutedIpv4Manager.RoutedNetworkVpcEnabled.valueIn(dataCenter.getId()));
146+
144147
zoneResponse.setResourceDetails(ApiDBUtils.getResourceDetails(dataCenter.getId(), ResourceObjectType.Zone));
145148
zoneResponse.setHasAnnotation(annotationDao.hasAnnotations(dataCenter.getUuid(), AnnotationService.EntityType.ZONE.name(),
146149
_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())));

server/src/main/java/com/cloud/bgp/BGPServiceImpl.java

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.cloud.dc.dao.DataCenterDao;
2525
import com.cloud.domain.Domain;
2626
import com.cloud.domain.dao.DomainDao;
27+
import com.cloud.exception.InvalidParameterValueException;
2728
import com.cloud.exception.ResourceUnavailableException;
2829
import com.cloud.network.Network;
2930
import com.cloud.network.NetworkModel;
@@ -54,6 +55,7 @@
5455
import com.cloud.utils.exception.CloudRuntimeException;
5556
import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd;
5657
import org.apache.cloudstack.context.CallContext;
58+
import org.apache.cloudstack.network.BgpPeer;
5759
import org.apache.cloudstack.network.BgpPeerVO;
5860
import org.apache.cloudstack.network.RoutedIpv4Manager;
5961
import org.apache.cloudstack.network.dao.BgpPeerDao;
@@ -116,6 +118,9 @@ public ASNumberRange createASNumberRange(long zoneId, long startASNumber, long e
116118
LOGGER.error(msg);
117119
throw new InvalidParameterException(msg);
118120
}
121+
if (!routedIpv4Manager.isRoutedNetworkVpcEnabled(zoneId)) {
122+
throw new InvalidParameterValueException("Cannot create ASN range as Routed networks and VPCs are not enabled for the zone.");
123+
}
119124
if (startASNumber > endASNumber) {
120125
String msg = "Please specify a valid AS Number range";
121126
LOGGER.error(msg);
@@ -391,19 +396,7 @@ public boolean applyBgpPeers(Network network, boolean continueOnError) throws Re
391396
if (gatewayProviderStr != null) {
392397
NetworkElement provider = networkModel.getElementImplementingProvider(gatewayProviderStr);
393398
if (provider != null && provider instanceof BgpServiceProvider) {
394-
List<BgpPeerVO> bgpPeers;
395-
if (network.getVpcId() != null) {
396-
bgpPeers = bgpPeerDao.listNonRevokeByVpcId(network.getVpcId());
397-
} else {
398-
bgpPeers = bgpPeerDao.listNonRevokeByNetworkId(network.getId());
399-
}
400-
if (CollectionUtils.isEmpty(bgpPeers)) {
401-
Account owner = accountDao.findByIdIncludingRemoved(network.getAccountId());
402-
List<Long> bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, network.getDataCenterId());
403-
bgpPeers = bgpPeerIds.stream()
404-
.map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId))
405-
.collect(Collectors.toList());
406-
}
399+
List<? extends BgpPeer> bgpPeers = getBgpPeersForNetwork(network);
407400
LOGGER.debug(String.format("Applying BPG Peers for network [%s]: [%s]", network, bgpPeers));
408401
return ((BgpServiceProvider) provider).applyBgpPeers(null, network, bgpPeers);
409402
}
@@ -420,19 +413,43 @@ public boolean applyBgpPeers(Vpc vpc, boolean continueOnError) throws ResourceUn
420413
if (gatewayProviderStr != null) {
421414
NetworkElement provider = networkModel.getElementImplementingProvider(gatewayProviderStr);
422415
if (provider != null && provider instanceof BgpServiceProvider) {
423-
List<BgpPeerVO> bgpPeers = bgpPeerDao.listNonRevokeByVpcId(vpc.getId());
424-
if (CollectionUtils.isEmpty(bgpPeers)) {
425-
Account owner = accountDao.findByIdIncludingRemoved(vpc.getAccountId());
426-
List<Long> bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, vpc.getZoneId());
427-
bgpPeers = bgpPeerIds.stream()
428-
.map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId))
429-
.collect(Collectors.toList());
430-
}
416+
List<? extends BgpPeer> bgpPeers = getBgpPeersForVpc(vpc);
431417
LOGGER.debug(String.format("Applying BPG Peers for VPC [%s]: [%s]", vpc, bgpPeers));
432418
return ((BgpServiceProvider) provider).applyBgpPeers(vpc, null, bgpPeers);
433419

434420
}
435421
}
436422
return true;
437423
}
424+
425+
@Override
426+
public List<? extends BgpPeer> getBgpPeersForNetwork(Network network) {
427+
List<BgpPeerVO> bgpPeers;
428+
if (network.getVpcId() != null) {
429+
bgpPeers = bgpPeerDao.listNonRevokeByVpcId(network.getVpcId());
430+
} else {
431+
bgpPeers = bgpPeerDao.listNonRevokeByNetworkId(network.getId());
432+
}
433+
if (CollectionUtils.isEmpty(bgpPeers)) {
434+
Account owner = accountDao.findByIdIncludingRemoved(network.getAccountId());
435+
List<Long> bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, network.getDataCenterId());
436+
bgpPeers = bgpPeerIds.stream()
437+
.map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId))
438+
.collect(Collectors.toList());
439+
}
440+
return bgpPeers;
441+
}
442+
443+
@Override
444+
public List<? extends BgpPeer> getBgpPeersForVpc(Vpc vpc) {
445+
List<BgpPeerVO> bgpPeers = bgpPeerDao.listNonRevokeByVpcId(vpc.getId());
446+
if (CollectionUtils.isEmpty(bgpPeers)) {
447+
Account owner = accountDao.findByIdIncludingRemoved(vpc.getAccountId());
448+
List<Long> bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, vpc.getZoneId());
449+
bgpPeers = bgpPeerIds.stream()
450+
.map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId))
451+
.collect(Collectors.toList());
452+
}
453+
return bgpPeers;
454+
}
438455
}

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
import org.apache.cloudstack.framework.messagebus.MessageBus;
107107
import org.apache.cloudstack.framework.messagebus.MessageSubscriber;
108108
import org.apache.cloudstack.framework.messagebus.PublishScope;
109+
import org.apache.cloudstack.network.RoutedIpv4Manager;
109110
import org.apache.cloudstack.query.QueryService;
110111
import org.apache.cloudstack.region.PortableIp;
111112
import org.apache.cloudstack.region.PortableIpDao;
@@ -6673,6 +6674,16 @@ public NetworkOfferingVO createNetworkOffering(final String name, final String d
66736674
throw new InvalidParameterValueException("networkMode should be set only for Isolated network offerings");
66746675
}
66756676
if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode)) {
6677+
if (!RoutedIpv4Manager.RoutedNetworkVpcEnabled.value()) {
6678+
throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for Routed networks", RoutedIpv4Manager.RoutedNetworkVpcEnabled.key()));
6679+
}
6680+
if (zoneIds != null) {
6681+
for (Long zoneId: zoneIds) {
6682+
if (!RoutedIpv4Manager.RoutedNetworkVpcEnabled.valueIn(zoneId)) {
6683+
throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for Routed networks in zone (ID: %s)", RoutedIpv4Manager.RoutedNetworkVpcEnabled.key(), zoneId));
6684+
}
6685+
}
6686+
}
66766687
boolean useVirtualRouterOnly = true;
66776688
for (Service service : serviceProviderMap.keySet()) {
66786689
Set<Provider> providers = serviceProviderMap.get(service);

server/src/main/java/com/cloud/network/NetworkServiceImpl.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,7 +1384,7 @@ private void checkSharedNetworkCidrOverlap(Long zoneId, long physicalNetworkId,
13841384
}
13851385
}
13861386

1387-
void validateNetworkCidrSize(Account caller, Integer cidrSize, String cidr, NetworkOffering networkOffering, long accountId) {
1387+
void validateNetworkCidrSize(Account caller, Integer cidrSize, String cidr, NetworkOffering networkOffering, long accountId, long zoneId) {
13881388
if (!GuestType.Isolated.equals(networkOffering.getGuestType())) {
13891389
if (cidrSize != null) {
13901390
throw new InvalidParameterValueException("network cidr size is only applicable on Isolated networks");
@@ -1405,11 +1405,11 @@ void validateNetworkCidrSize(Account caller, Integer cidrSize, String cidr, Netw
14051405
if (cidrSize == null) {
14061406
throw new InvalidParameterValueException("network cidr or cidr size is required for Isolated networks with ROUTED mode");
14071407
}
1408-
Integer maxCidrSize = routedIpv4Manager.RoutedNetworkIPv4MaxCidrSize.valueIn(accountId);
1408+
Integer maxCidrSize = RoutedIpv4Manager.RoutedNetworkIPv4MaxCidrSize.valueIn(accountId);
14091409
if (cidrSize > maxCidrSize) {
14101410
throw new InvalidParameterValueException("network cidr size cannot be bigger than maximum cidr size " + maxCidrSize);
14111411
}
1412-
Integer minCidrSize = routedIpv4Manager.RoutedNetworkIPv4MinCidrSize.valueIn(accountId);
1412+
Integer minCidrSize = RoutedIpv4Manager.RoutedNetworkIPv4MinCidrSize.valueIn(accountId);
14131413
if (cidrSize < minCidrSize) {
14141414
throw new InvalidParameterValueException("network cidr size cannot be smaller than minimum cidr size " + minCidrSize);
14151415
}
@@ -1651,11 +1651,16 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac
16511651
}
16521652
}
16531653

1654+
if (NetworkOffering.NetworkMode.ROUTED.equals(ntwkOff.getNetworkMode())
1655+
&& !routedIpv4Manager.isRoutedNetworkVpcEnabled(zone.getId())) {
1656+
throw new InvalidParameterValueException("Routed network is not enabled in this zone");
1657+
}
1658+
16541659
if (isNonVpcNetworkSupportingDynamicRouting(ntwkOff) && ntwkOff.isSpecifyAsNumber() && asNumber == null) {
16551660
throw new InvalidParameterValueException("AS number is required for the network but not passed.");
16561661
}
16571662

1658-
validateNetworkCidrSize(caller, networkCidrSize, cidr, ntwkOff, owner.getAccountId());
1663+
validateNetworkCidrSize(caller, networkCidrSize, cidr, ntwkOff, owner.getAccountId(), zone.getId());
16591664

16601665
validateSharedNetworkRouterIPs(gateway, startIP, endIP, netmask, routerIPv4, routerIPv6, startIPv6, endIPv6, ip6Cidr, ntwkOff);
16611666

server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,8 @@
6565
import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO;
6666
import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao;
6767
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
68-
import org.apache.cloudstack.network.BgpPeerVO;
68+
import org.apache.cloudstack.network.BgpPeer;
6969
import org.apache.cloudstack.network.RoutedIpv4Manager;
70-
import org.apache.cloudstack.network.dao.BgpPeerDao;
71-
import org.apache.cloudstack.network.dao.BgpPeerNetworkMapDao;
7270
import org.apache.cloudstack.network.topology.NetworkTopology;
7371
import org.apache.cloudstack.network.topology.NetworkTopologyContext;
7472
import org.apache.cloudstack.utils.CloudStackVersion;
@@ -114,6 +112,7 @@
114112
import com.cloud.api.query.dao.UserVmJoinDao;
115113
import com.cloud.api.query.vo.DomainRouterJoinVO;
116114
import com.cloud.api.query.vo.UserVmJoinVO;
115+
import com.cloud.bgp.BGPService;
117116
import com.cloud.cluster.ManagementServerHostVO;
118117
import com.cloud.cluster.dao.ManagementServerHostDao;
119118
import com.cloud.configuration.Config;
@@ -348,9 +347,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V
348347
@Inject
349348
RoutedIpv4Manager routedIpv4Manager;
350349
@Inject
351-
BgpPeerDao bgpPeerDao;
352-
@Inject
353-
BgpPeerNetworkMapDao bgpPeerNetworkMapDao;
350+
BGPService bgpService;
354351

355352
private int _routerRamSize;
356353
private int _routerCpuMHz;
@@ -2508,12 +2505,12 @@ protected void finalizeNetworkRulesForNetwork(final Commands cmds, final DomainR
25082505
if (guestNetwork.getVpcId() != null) {
25092506
final Vpc vpc = _vpcDao.findById(guestNetwork.getVpcId());
25102507
if (routedIpv4Manager.isDynamicRoutedVpc(vpc)) {
2511-
final List<BgpPeerVO> bgpPeers = bgpPeerDao.listNonRevokeByVpcId(guestNetwork.getVpcId());
2508+
List<? extends BgpPeer> bgpPeers = bgpService.getBgpPeersForVpc(vpc);
25122509
_commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, guestNetwork);
25132510
}
25142511
} else {
25152512
if (routedIpv4Manager.isDynamicRoutedNetwork(guestNetwork)) {
2516-
final List<BgpPeerVO> bgpPeers = bgpPeerDao.listNonRevokeByNetworkId(guestNetworkId);
2513+
List<? extends BgpPeer> bgpPeers = bgpService.getBgpPeersForNetwork(guestNetwork);
25172514
_commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, guestNetwork);
25182515
}
25192516
}

server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,18 @@ public VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd) {
500500
}
501501
networkMode = NetworkOffering.NetworkMode.valueOf(networkModeStr);
502502
}
503+
if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode)) {
504+
if (!RoutedIpv4Manager.RoutedNetworkVpcEnabled.value()) {
505+
throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for Routed VPCs", RoutedIpv4Manager.RoutedNetworkVpcEnabled.key()));
506+
}
507+
if (zoneIds != null) {
508+
for (Long zoneId: zoneIds) {
509+
if (!RoutedIpv4Manager.RoutedNetworkVpcEnabled.valueIn(zoneId)) {
510+
throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for Routed VPCs in zone (ID: %s)", RoutedIpv4Manager.RoutedNetworkVpcEnabled.key(), zoneId));
511+
}
512+
}
513+
}
514+
}
503515
boolean specifyAsNumber = cmd.getSpecifyAsNumber();
504516
String routingModeString = cmd.getRoutingMode();
505517

@@ -1161,12 +1173,17 @@ public Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwner
11611173
throw ex;
11621174
}
11631175

1176+
if (NetworkOffering.NetworkMode.ROUTED.equals(vpcOff.getNetworkMode())
1177+
&& !routedIpv4Manager.RoutedNetworkVpcEnabled.valueIn(zoneId)) {
1178+
throw new InvalidParameterValueException("Routed VPC is not enabled in this zone");
1179+
}
1180+
11641181
if (NetworkOffering.RoutingMode.Dynamic.equals(vpcOff.getRoutingMode()) && vpcOff.isSpecifyAsNumber() && asNumber == null) {
11651182
throw new InvalidParameterValueException("AS number is required for the VPC but not passed.");
11661183
}
11671184

11681185
// Validate VPC cidr/cidrsize
1169-
validateVpcCidrSize(caller, owner.getAccountId(), vpcOff, cidr, cidrSize);
1186+
validateVpcCidrSize(caller, owner.getAccountId(), vpcOff, cidr, cidrSize, zoneId);
11701187

11711188
// Validate BGP peers
11721189
if (CollectionUtils.isNotEmpty(bgpPeerIds)) {
@@ -1251,7 +1268,7 @@ public Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwner
12511268
return newVpc;
12521269
}
12531270

1254-
private void validateVpcCidrSize(Account caller, long accountId, VpcOffering vpcOffering, String cidr, Integer cidrSize) {
1271+
private void validateVpcCidrSize(Account caller, long accountId, VpcOffering vpcOffering, String cidr, Integer cidrSize, long zoneId) {
12551272
if (ObjectUtils.allNull(cidr, cidrSize)) {
12561273
throw new InvalidParameterValueException("VPC cidr or cidr size must be specified");
12571274
}

0 commit comments

Comments
 (0)