Skip to content

Commit 76c2127

Browse files
Pearl1594Pearl Dsilva
andauthored
FR571: Specify VR IP in shared networks (#32)
Allows admins to provide VR IP (v4 & v6) when creating shared networks, provided the Network offering provides at least one service that requires a virtual router Upstream PR apache#4503 Co-authored-by: Pearl Dsilva <pearl.dsilva@shapeblue.com>
1 parent deccea2 commit 76c2127

File tree

20 files changed

+458
-44
lines changed

20 files changed

+458
-44
lines changed

api/src/com/cloud/network/Network.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,4 +417,8 @@ public void setIp6Address(String ip6Address) {
417417
boolean isStrechedL2Network();
418418

419419
String getExternalId();
420+
421+
String getRouterIp();
422+
423+
String getRouterIpv6();
420424
}

api/src/com/cloud/network/NetworkProfile.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,14 @@ public String getExternalId() {
314314
return externalId;
315315
}
316316

317+
@Override
318+
public String getRouterIp() {
319+
return null;
320+
}
321+
322+
@Override
323+
public String getRouterIpv6() {
324+
return null;
325+
}
326+
317327
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,8 @@ public class ApiConstants {
700700
public static final String VIRTUAL_SIZE = "virtualsize";
701701
public static final String NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid";
702702
public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid";
703+
public static final String ROUTER_IP = "routerip";
704+
public static final String ROUTER_IPV6 = "routeripv6";
703705

704706
public static final String ZONE_ID_LIST = "zoneids";
705707
public static final String DESTINATION_ZONE_ID_LIST = "destzoneids";

api/src/org/apache/cloudstack/api/command/admin/network/CreateNetworkCmdByAdmin.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package org.apache.cloudstack.api.command.admin.network;
1818

19+
import org.apache.cloudstack.api.ApiArgValidator;
1920
import org.apache.log4j.Logger;
2021

2122
import org.apache.cloudstack.api.APICommand;
@@ -43,6 +44,12 @@ public class CreateNetworkCmdByAdmin extends CreateNetworkCmd {
4344
@Parameter(name=ApiConstants.BYPASS_VLAN_OVERLAP_CHECK, type=CommandType.BOOLEAN, description="when true bypasses VLAN id/range overlap check during network creation for shared networks")
4445
private Boolean bypassVlanOverlapCheck;
4546

47+
@Parameter(name = ApiConstants.ROUTER_IP, type = CommandType.STRING, description = "IPV4 address to be assigned to a router in a shared network", validations = {ApiArgValidator.NotNullOrEmpty})
48+
private String routerIp;
49+
50+
@Parameter(name = ApiConstants.ROUTER_IPV6, type = CommandType.STRING, description = "IPV6 address to be assigned to a router in a shared network", validations = {ApiArgValidator.NotNullOrEmpty})
51+
private String routerIpv6;
52+
4653
/////////////////////////////////////////////////////
4754
/////////////////// Accessors ///////////////////////
4855
/////////////////////////////////////////////////////
@@ -58,6 +65,14 @@ public Boolean getBypassVlanOverlapCheck() {
5865
return false;
5966
}
6067

68+
public String getRouterIp() {
69+
return routerIp;
70+
}
71+
72+
public String getRouterIpv6() {
73+
return routerIpv6;
74+
}
75+
6176
/////////////////////////////////////////////////////
6277
/////////////// API Implementation///////////////////
6378
/////////////////////////////////////////////////////

engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC
179179

180180
Network createGuestNetwork(long networkOfferingId, String name, String displayText, String gateway, String cidr, String vlanId, boolean bypassVlanOverlapCheck, String networkDomain, Account owner,
181181
Long domainId, PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String ip6Gateway, String ip6Cidr,
182-
Boolean displayNetworkEnabled, String isolatedPvlan, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException;
182+
Boolean displayNetworkEnabled, String isolatedPvlan, String externalId, String routerIp, String routerIpv6) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException;
183183

184184
UserDataServiceProvider getPasswordResetProvider(Network network);
185185

engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.apache.cloudstack.engine.orchestration;
1818

1919

20+
import static org.apache.commons.lang.StringUtils.isNotBlank;
21+
2022
import java.net.URI;
2123
import java.util.ArrayList;
2224
import java.util.Arrays;
@@ -39,6 +41,7 @@
3941
import javax.naming.ConfigurationException;
4042

4143
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
44+
import org.apache.cloudstack.api.ApiConstants;
4245
import org.apache.cloudstack.context.CallContext;
4346
import org.apache.cloudstack.engine.cloud.entity.api.db.VMNetworkMapVO;
4447
import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
@@ -123,6 +126,7 @@
123126
import com.cloud.network.dao.NetworkAccountDao;
124127
import com.cloud.network.dao.NetworkAccountVO;
125128
import com.cloud.network.dao.NetworkDao;
129+
import com.cloud.network.dao.NetworkDetailsDao;
126130
import com.cloud.network.dao.NetworkDomainDao;
127131
import com.cloud.network.dao.NetworkDomainVO;
128132
import com.cloud.network.dao.NetworkServiceMapDao;
@@ -286,6 +290,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
286290
RemoteAccessVpnDao _remoteAccessVpnDao;
287291
@Inject
288292
VpcVirtualNetworkApplianceService _routerService;
293+
@Inject
294+
NetworkDetailsDao networkDetailsDao;
289295

290296
List<NetworkGuru> networkGurus;
291297

@@ -621,6 +627,15 @@ protected NetworkOrchestrator() {
621627
setStateMachine();
622628
}
623629

630+
private void updateRouterIpInNetworkDetails(Long networkId, String routerIp, String routerIpv6) {
631+
if (isNotBlank(routerIp)) {
632+
networkDetailsDao.addDetail(networkId, ApiConstants.ROUTER_IP, routerIp, true);
633+
}
634+
if (isNotBlank(routerIpv6)) {
635+
networkDetailsDao.addDetail(networkId, ApiConstants.ROUTER_IPV6, routerIpv6, true);
636+
}
637+
}
638+
624639
@Override
625640
public List<? extends Network> setupNetwork(final Account owner, final NetworkOffering offering, final DeploymentPlan plan, final String name, final String displayText, final boolean isDefault)
626641
throws ConcurrentOperationException {
@@ -706,6 +721,8 @@ public void doInTransactionWithoutResult(final TransactionStatus status) {
706721
if (domainId != null && aclType == ACLType.Domain) {
707722
_networksDao.addDomainToNetwork(id, domainId, subdomainAccess == null ? true : subdomainAccess);
708723
}
724+
725+
updateRouterIpInNetworkDetails(networkPersisted.getId(), network.getRouterIp(), network.getRouterIpv6());
709726
}
710727
});
711728
}
@@ -2084,7 +2101,7 @@ public void expungeNics(final VirtualMachineProfile vm) {
20842101
public Network createGuestNetwork(final long networkOfferingId, final String name, final String displayText, final String gateway, final String cidr, String vlanId,
20852102
boolean bypassVlanOverlapCheck, String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk,
20862103
final long zoneId, final ACLType aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway, final String ip6Cidr,
2087-
final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, String externalId) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
2104+
final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, String externalId, String routerIp, String routerIpv6) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException {
20882105

20892106
final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId);
20902107
// this method supports only guest network creation
@@ -2339,6 +2356,14 @@ public Network doInTransaction(final TransactionStatus status) {
23392356
userNetwork.setExternalId(externalId);
23402357
}
23412358

2359+
if (isNotBlank(routerIp)) {
2360+
userNetwork.setRouterIp(routerIp);
2361+
}
2362+
2363+
if (isNotBlank(routerIpv6)) {
2364+
userNetwork.setRouterIpv6(routerIpv6);
2365+
}
2366+
23422367
if (vlanIdFinal != null) {
23432368
if (isolatedPvlan == null) {
23442369
URI uri = null;

engine/schema/src/com/cloud/network/dao/NetworkVO.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ public class NetworkVO implements Network {
181181
@Transient
182182
boolean rollingRestart = false;
183183

184+
@Transient
185+
String routerIp;
186+
187+
@Transient
188+
String routerIpv6;
189+
184190
public NetworkVO() {
185191
uuid = UUID.randomUUID().toString();
186192
}
@@ -661,4 +667,20 @@ public boolean isRollingRestart() {
661667
public void setRollingRestart(boolean rollingRestart) {
662668
this.rollingRestart = rollingRestart;
663669
}
670+
671+
public String getRouterIp() {
672+
return routerIp;
673+
}
674+
675+
public void setRouterIp(String routerIp) {
676+
this.routerIp = routerIp;
677+
}
678+
679+
public String getRouterIpv6() {
680+
return routerIpv6;
681+
}
682+
683+
public void setRouterIpv6(String routerIpv6) {
684+
this.routerIpv6 = routerIpv6;
685+
}
664686
}

server/src/com/cloud/network/IpAddressManagerImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1681,7 +1681,7 @@ public Ternary<Boolean, List<NetworkOfferingVO>, Network> doInTransaction(Transa
16811681
s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId()
16821682
+ " as a part of createVlanIpRange process");
16831683
guestNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName()
1684-
+ "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null, null);
1684+
+ "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null, null, null, null);
16851685
if (guestNetwork == null) {
16861686
s_logger.warn("Failed to create default Virtual network for the account " + accountId + "in zone " + zoneId);
16871687
throw new CloudRuntimeException("Failed to create a Guest Isolated Networks with SourceNAT "

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

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// under the License.
1717
package com.cloud.network;
1818

19+
import static org.apache.commons.lang.StringUtils.isNotBlank;
20+
1921
import java.net.Inet6Address;
2022
import java.net.InetAddress;
2123
import java.net.URI;
@@ -995,6 +997,46 @@ private void checkSharedNetworkCidrOverlap(Long zoneId, long physicalNetworkId,
995997
}
996998
}
997999

1000+
private void validateRouterIps(String routerIp, String routerIpv6, String startIp, String endIp, String gateway,
1001+
String netmask, String startIpv6, String endIpv6, String ip6Cidr) {
1002+
if (isNotBlank(routerIp)) {
1003+
if (startIp != null && endIp == null) {
1004+
endIp = startIp;
1005+
}
1006+
if (!NetUtils.isValidIp4(routerIp)) {
1007+
throw new CloudRuntimeException("Router IPv4 IP provided is of incorrect format");
1008+
}
1009+
if (isNotBlank(startIp) && isNotBlank(endIp)) {
1010+
if (!NetUtils.isIpInRange(routerIp, startIp, endIp)) {
1011+
throw new CloudRuntimeException("Router IPv4 IP provided is not within the specified range: " + startIp + " - " + endIp);
1012+
}
1013+
} else {
1014+
String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask);
1015+
if (!NetUtils.isIpWithInCidrRange(routerIp, cidr)) {
1016+
throw new CloudRuntimeException("Router IP provided in not within the network range");
1017+
}
1018+
}
1019+
}
1020+
if (isNotBlank(routerIpv6)) {
1021+
if (startIpv6 != null && endIpv6 == null) {
1022+
endIpv6 = startIpv6;
1023+
}
1024+
if (!NetUtils.isValidIp6(routerIpv6)) {
1025+
throw new CloudRuntimeException("Router IPv6 address provided is of incorrect format");
1026+
}
1027+
if (isNotBlank(startIpv6) && isNotBlank(endIpv6)) {
1028+
String ipv6Range = startIpv6 + "-" + endIpv6;
1029+
if (!NetUtils.isIp6InRange(routerIpv6, ipv6Range)) {
1030+
throw new CloudRuntimeException("Router IPv6 address provided is not within the specified range: " + startIpv6 + " - " + endIpv6);
1031+
}
1032+
} else {
1033+
if (!NetUtils.isIp6InNetwork(routerIpv6, ip6Cidr)) {
1034+
throw new CloudRuntimeException("Router IPv6 address provided is not with the network range");
1035+
}
1036+
}
1037+
}
1038+
}
1039+
9981040
@Override
9991041
@DB
10001042
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_CREATE, eventDescription = "creating network")
@@ -1007,11 +1049,13 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac
10071049
String networkDomain = cmd.getNetworkDomain();
10081050
String vlanId = null;
10091051
boolean bypassVlanOverlapCheck = false;
1052+
String routerIp = null;
1053+
String routerIpv6 = null;
10101054
if (cmd instanceof CreateNetworkCmdByAdmin) {
10111055
vlanId = ((CreateNetworkCmdByAdmin)cmd).getVlan();
1012-
}
1013-
if (cmd instanceof CreateNetworkCmdByAdmin) {
10141056
bypassVlanOverlapCheck = ((CreateNetworkCmdByAdmin)cmd).getBypassVlanOverlapCheck();
1057+
routerIp = ((CreateNetworkCmdByAdmin)cmd).getRouterIp();
1058+
routerIpv6 = ((CreateNetworkCmdByAdmin)cmd).getRouterIpv6();
10151059
}
10161060

10171061
String name = cmd.getNetworkName();
@@ -1105,6 +1149,15 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac
11051149
throw new InvalidParameterValueException("Only Admins can create network with guest type " + GuestType.Shared);
11061150
}
11071151

1152+
if (ntwkOff.getGuestType() != GuestType.Shared && (isNotBlank(routerIp) || isNotBlank(routerIpv6))) {
1153+
throw new InvalidParameterValueException("Router IP can be specified only for Shared networks");
1154+
}
1155+
1156+
if (ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.isProviderForNetworkOffering(Provider.VirtualRouter, networkOfferingId)
1157+
&& (isNotBlank(routerIp) || isNotBlank(routerIpv6))) {
1158+
throw new InvalidParameterValueException("Virtual Router is not a supported provider for the Shared network, hence router ip should not be provided");
1159+
}
1160+
11081161
// Check if the network is domain specific
11091162
if (aclType == ACLType.Domain) {
11101163
// only Admin can create domain with aclType=Domain
@@ -1216,6 +1269,8 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac
12161269
}
12171270
}
12181271

1272+
validateRouterIps(routerIp, routerIpv6, startIP, endIP, gateway, netmask, startIPv6, endIPv6, ip6Cidr);
1273+
12191274
if (isolatedPvlan != null && (zone.getNetworkType() != NetworkType.Advanced || ntwkOff.getGuestType() != Network.GuestType.Shared)) {
12201275
throw new InvalidParameterValueException("Can only support create Private VLAN network with advance shared network!");
12211276
}
@@ -1293,7 +1348,7 @@ && areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) {
12931348

12941349
Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zoneId, domainId,
12951350
isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, isolatedPvlan, ntwkOff, pNtwk, aclType, owner, cidr,
1296-
createVlan, externalId);
1351+
createVlan, externalId, routerIp, routerIpv6);
12971352

12981353
// if the network offering has persistent set to true, implement the network
12991354
if (ntwkOff.getIsPersistent()) {
@@ -1326,7 +1381,7 @@ private Network commitNetwork(final Long networkOfferingId, final String gateway
13261381
final String vlanId, final Boolean bypassVlanOverlapCheck, final String name, final String displayText, final Account caller, final Long physicalNetworkId, final Long zoneId, final Long domainId,
13271382
final boolean isDomainSpecific, final Boolean subdomainAccessFinal, final Long vpcId, final String startIPv6, final String endIPv6, final String ip6Gateway,
13281383
final String ip6Cidr, final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final NetworkOfferingVO ntwkOff, final PhysicalNetwork pNtwk,
1329-
final ACLType aclType, final Account ownerFinal, final String cidr, final boolean createVlan, final String externalId) throws InsufficientCapacityException, ResourceAllocationException {
1384+
final ACLType aclType, final Account ownerFinal, final String cidr, final boolean createVlan, final String externalId, String routerIp, String routerIpv6) throws InsufficientCapacityException, ResourceAllocationException {
13301385
try {
13311386
Network network = Transaction.execute(new TransactionCallbackWithException<Network, Exception>() {
13321387
@Override
@@ -1381,7 +1436,7 @@ public Network doInTransaction(TransactionStatus status) throws InsufficientCapa
13811436
}
13821437

13831438
network = _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, bypassVlanOverlapCheck, networkDomain, owner, sharedDomainId, pNtwk, zoneId,
1384-
aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, externalId);
1439+
aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, externalId, routerIp, routerIpv6);
13851440
}
13861441

13871442
if (_accountMgr.isRootAdmin(caller.getId()) && createVlan && network != null) {
@@ -4363,7 +4418,7 @@ public Network doInTransaction(TransactionStatus status) throws ResourceAllocati
43634418
if (privateNetwork == null) {
43644419
//create Guest network
43654420
privateNetwork = _networkMgr.createGuestNetwork(ntwkOffFinal.getId(), networkName, displayText, gateway, cidr, uriString, false, null, owner, null, pNtwk,
4366-
pNtwk.getDataCenterId(), ACLType.Account, null, vpcId, null, null, true, null, null);
4421+
pNtwk.getDataCenterId(), ACLType.Account, null, vpcId, null, null, true, null, null, null , null);
43674422
if (privateNetwork != null) {
43684423
s_logger.debug("Successfully created guest network " + privateNetwork);
43694424
} else {

server/src/com/cloud/network/guru/DirectNetworkGuru.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use
211211
if (userSpecified.getBroadcastDomainType() != null) {
212212
config.setBroadcastDomainType(userSpecified.getBroadcastDomainType());
213213
}
214+
215+
if (userSpecified.getRouterIp() != null) {
216+
config.setRouterIp(userSpecified.getRouterIp());
217+
}
218+
if (userSpecified.getRouterIpv6() != null) {
219+
config.setRouterIpv6(userSpecified.getRouterIpv6());
220+
}
214221
}
215222

216223
boolean isSecurityGroupEnabled = _networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Service.SecurityGroup);

0 commit comments

Comments
 (0)