Skip to content

Commit ea3db2f

Browse files
SakshamsMurali Reddy
authored andcommitted
CLOUDSTACK-705 IP Address reservation for Isolated Guest Networks
CloudStack uses Guest CIDR for dhcp-range for the Guest VMs. The entire CIDR is used by CloudStack for assigning IPs to Guest VMs. IP Address Reservation will allow part of address space to be used fornon CloudStack hosts/physical servers also, by restricting the address space of CloudStack Guest VMs. Reservation can be configured using update Network API by specifying guestvmCidr as an additional parameter. Reservation will be applicable for Isolated Guest Networks including VPC. reservediprange in the response will return the IP range that can be used for non Cloudstack hosts. Tested manually the following scenarios: Applying reservation when there are running VMs inside the guest_vm_cidr. Applying reservation when there are running VMs outside the guest_vm_cidr.(not allowed) Applying reservation when external device like Netscaler is configured in the guest_cidr. Applying reservation in VPC tiers. Applying reservation outside the range of guest_cidr.(not allowed)
1 parent 79fee52 commit ea3db2f

16 files changed

Lines changed: 211 additions & 33 deletions

File tree

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,12 +283,21 @@ public void setIp6Address(String ip6Address) {
283283

284284
String getGateway();
285285

286+
// "cidr" is the Cloudstack managed address space, all CloudStack managed vms get IP address from "cidr",
287+
// In general "cidr" also serves as the network CIDR
288+
// But in case IP reservation is configured for a Guest network, "networkcidr" is the Effective network CIDR for that network,
289+
// "cidr" will still continue to be the effective address space for CloudStack managed vms in that Guest network
286290
String getCidr();
287291

292+
// "networkcidr" is the network CIDR of the guest network which uses IP reservation.
293+
// It is the summation of "cidr" and the reservedIPrange(the address space used for non CloudStack purposes).
294+
// For networks not configured with IP reservation, "networkcidr" is always null
295+
String getNetworkCidr();
296+
288297
String getIp6Gateway();
289-
298+
290299
String getIp6Cidr();
291-
300+
292301
long getDataCenterId();
293302

294303
long getNetworkOfferingId();

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class NetworkProfile implements Network {
3838
private TrafficType trafficType;
3939
private String gateway;
4040
private String cidr;
41+
private String networkCidr;
4142
private String ip6Gateway;
4243
private String ip6Cidr;
4344
private long networkOfferingId;
@@ -65,6 +66,7 @@ public NetworkProfile(Network network) {
6566
this.trafficType = network.getTrafficType();
6667
this.gateway = network.getGateway();
6768
this.cidr = network.getCidr();
69+
this.networkCidr = network.getNetworkCidr();
6870
this.ip6Gateway = network.getIp6Gateway();
6971
this.ip6Cidr = network.getIp6Cidr();
7072
this.networkOfferingId = network.getNetworkOfferingId();
@@ -162,6 +164,11 @@ public String getCidr() {
162164
return cidr;
163165
}
164166

167+
@Override
168+
public String getNetworkCidr() {
169+
return networkCidr;
170+
}
171+
165172
@Override
166173
public long getNetworkOfferingId() {
167174
return networkOfferingId;

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,8 @@ boolean restartNetwork(RestartNetworkCmd cmd, boolean cleanup) throws Concurrent
6666

6767
IpAddress getIp(long id);
6868

69-
7069
Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount, User callerUser,
71-
String domainSuffix, Long networkOfferingId, Boolean changeCidr);
72-
70+
String domainSuffix, Long networkOfferingId, Boolean changeCidr, String guestVmCidr);
7371

7472
PhysicalNetwork createPhysicalNetwork(Long zoneId, String vnetRange, String networkSpeed,
7573
List<String> isolationMethods, String broadcastDomainRange, Long domainId, List<String> tags, String name);

api/src/com/cloud/network/vpc/VpcService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,5 +246,5 @@ IpAddress associateIPToVpc(long ipId, long vpcId) throws ResourceAllocationExcep
246246
InsufficientAddressCapacityException, ConcurrentOperationException;
247247

248248
public Network updateVpcGuestNetwork(long networkId, String name, String displayText, Account callerAccount,
249-
User callerUser, String domainSuffix, Long ntwkOffId, Boolean changeCidr);
249+
User callerUser, String domainSuffix, Long ntwkOffId, Boolean changeCidr, String guestVmCidr);
250250
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,9 @@ public class ApiConstants {
445445
public static final String VM_SNAPSHOT_DISK_IDS = "vmsnapshotdiskids";
446446
public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory";
447447
public static final String IMAGE_STORE_UUID = "imagestoreuuid";
448+
public static final String GUEST_VM_CIDR = "guestvmcidr";
449+
public static final String NETWORK_CIDR = "networkcidr";
450+
public static final String RESERVED_IP_RANGE = "reservediprange";
448451

449452
public enum HostDetails {
450453
all, capacity, events, stats, min;

api/src/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ public class UpdateNetworkCmd extends BaseAsyncCmd {
6464
description="network offering ID")
6565
private Long networkOfferingId;
6666

67+
@Parameter(name=ApiConstants.GUEST_VM_CIDR, type=CommandType.STRING, description="CIDR for Guest VMs,Cloudstack allocates IPs to Guest VMs only from this CIDR")
68+
private String guestVmCidr;
69+
6770
/////////////////////////////////////////////////////
6871
/////////////////// Accessors ///////////////////////
6972
/////////////////////////////////////////////////////
@@ -94,6 +97,10 @@ public Boolean getChangeCidr() {
9497
}
9598
return false;
9699
}
100+
101+
private String getGuestVmCidr() {
102+
return guestVmCidr;
103+
}
97104
/////////////////////////////////////////////////////
98105
/////////////// API Implementation///////////////////
99106
/////////////////////////////////////////////////////
@@ -125,10 +132,10 @@ public void execute() throws InsufficientCapacityException, ConcurrentOperationE
125132
Network result = null;
126133
if (network.getVpcId() != null) {
127134
result = _vpcService.updateVpcGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount,
128-
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr());
135+
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr(), getGuestVmCidr());
129136
} else {
130137
result = _networkService.updateGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount,
131-
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr());
138+
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr(), getGuestVmCidr());
132139
}
133140

134141
if (result != null) {

api/src/org/apache/cloudstack/api/response/NetworkResponse.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,15 @@ public class NetworkResponse extends BaseResponse implements ControlledEntityRes
5252
@SerializedName(ApiConstants.NETMASK) @Param(description="the network's netmask")
5353
private String netmask;
5454

55-
@SerializedName(ApiConstants.CIDR) @Param(description="the cidr the network")
55+
@SerializedName(ApiConstants.CIDR) @Param(description="Cloudstack managed address space, all CloudStack managed VMs get IP address from CIDR")
5656
private String cidr;
5757

58+
@SerializedName(ApiConstants.NETWORK_CIDR) @Param(description="the network CIDR of the guest network configured with IP reservation. It is the summation of CIDR and RESERVED_IP_RANGE")
59+
private String networkCidr;
60+
61+
@SerializedName(ApiConstants.RESERVED_IP_RANGE) @Param(description="the network's IP range not to be used by CloudStack guest VMs and can be used for non CloudStack purposes")
62+
private String reservedIpRange;
63+
5864
@SerializedName(ApiConstants.ZONE_ID) @Param(description="zone id of the network")
5965
private String zoneId;
6066

@@ -289,6 +295,14 @@ public void setCidr(String cidr) {
289295
this.cidr = cidr;
290296
}
291297

298+
public void setNetworkCidr(String networkCidr) {
299+
this.networkCidr = networkCidr;
300+
}
301+
302+
public void setReservedIpRange(String reservedIpRange) {
303+
this.reservedIpRange = reservedIpRange;
304+
}
305+
292306
public void setRestartRequired(Boolean restartRequired) {
293307
this.restartRequired = restartRequired;
294308
}

server/src/com/cloud/api/ApiResponseHelper.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2153,13 +2153,48 @@ public NetworkResponse createNetworkResponse(Network network) {
21532153

21542154
// FIXME - either set netmask or cidr
21552155
response.setCidr(network.getCidr());
2156-
if (network.getCidr() != null) {
2156+
response.setNetworkCidr((network.getNetworkCidr()));
2157+
// If network has reservation its entire network cidr is defined by getNetworkCidr()
2158+
// if no reservation is present then getCidr() will define the entire network cidr
2159+
if (network.getNetworkCidr() != null) {
2160+
response.setNetmask(NetUtils.cidr2Netmask(network.getNetworkCidr()));
2161+
}
2162+
if (((network.getCidr()) != null) && (network.getNetworkCidr() == null)) {
21572163
response.setNetmask(NetUtils.cidr2Netmask(network.getCidr()));
21582164
}
21592165

21602166
response.setIp6Gateway(network.getIp6Gateway());
21612167
response.setIp6Cidr(network.getIp6Cidr());
21622168

2169+
// create response for reserved IP ranges that can be used for non-cloudstack purposes
2170+
String reservation = null;
2171+
if ((network.getCidr() != null) && (NetUtils.isNetworkAWithinNetworkB(network.getCidr(), network.getNetworkCidr()))) {
2172+
String[] guestVmCidrPair = network.getCidr().split("\\/");
2173+
String[] guestCidrPair = network.getNetworkCidr().split("\\/");
2174+
2175+
Long guestVmCidrSize = Long.valueOf(guestVmCidrPair[1]);
2176+
Long guestCidrSize = Long.valueOf(guestCidrPair[1]);
2177+
2178+
String[] guestVmIpRange = NetUtils.getIpRangeFromCidr(guestVmCidrPair[0], guestVmCidrSize);
2179+
String[] guestIpRange = NetUtils.getIpRangeFromCidr(guestCidrPair[0], guestCidrSize);
2180+
long startGuestIp = NetUtils.ip2Long(guestIpRange[0]);
2181+
long endGuestIp = NetUtils.ip2Long(guestIpRange[1]);
2182+
long startVmIp = NetUtils.ip2Long(guestVmIpRange[0]);
2183+
long endVmIp = NetUtils.ip2Long(guestVmIpRange[1]);
2184+
2185+
if (startVmIp == startGuestIp && endVmIp < endGuestIp -1) {
2186+
reservation = (NetUtils.long2Ip(endVmIp + 1) + "-" + NetUtils.long2Ip(endGuestIp));
2187+
}
2188+
if (endVmIp == endGuestIp && startVmIp > startGuestIp + 1) {
2189+
reservation = (NetUtils.long2Ip(startGuestIp) + "-" + NetUtils.long2Ip(startVmIp-1));
2190+
}
2191+
if(startVmIp > startGuestIp + 1 && endVmIp < endGuestIp - 1) {
2192+
reservation = (NetUtils.long2Ip(startGuestIp) + "-" + NetUtils.long2Ip(startVmIp-1) + " , " +
2193+
NetUtils.long2Ip(endVmIp + 1) + "-"+ NetUtils.long2Ip(endGuestIp));
2194+
}
2195+
}
2196+
response.setReservedIpRange(reservation);
2197+
21632198
//return vlan information only to Root admin
21642199
if (network.getBroadcastUri() != null && UserContext.current().getCaller().getType() == Account.ACCOUNT_TYPE_ADMIN) {
21652200
String broadcastUri = network.getBroadcastUri().toString();

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

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import com.cloud.event.dao.UsageEventDao;
6767
import com.cloud.exception.*;
6868
import com.cloud.network.IpAddress.State;
69+
import com.cloud.vm.Nic;
6970
import com.cloud.network.Network.Capability;
7071
import com.cloud.network.Network.GuestType;
7172
import com.cloud.network.Network.Provider;
@@ -1491,7 +1492,7 @@ private boolean checkForNonStoppedVmInNetwork(long networkId) {
14911492
@DB
14921493
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_UPDATE, eventDescription = "updating network", async = true)
14931494
public Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount,
1494-
User callerUser, String domainSuffix, Long networkOfferingId, Boolean changeCidr) {
1495+
User callerUser, String domainSuffix, Long networkOfferingId, Boolean changeCidr, String guestVmCidr) {
14951496
boolean restartNetwork = false;
14961497

14971498
// verify input parameters
@@ -1552,8 +1553,6 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
15521553
ex.addProxyObject(networkOffering, networkOfferingId, "networkOfferingId");
15531554
throw ex;
15541555
}
1555-
1556-
15571556
//can't update from vpc to non-vpc network offering
15581557
boolean forVpcNew = _configMgr.isOfferingForVpc(networkOffering);
15591558
boolean vorVpcOriginal = _configMgr.isOfferingForVpc(_configMgr.getNetworkOffering(oldNetworkOfferingId));
@@ -1585,6 +1584,7 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
15851584
networkOfferingChanged = true;
15861585
}
15871586
}
1587+
15881588
Map<String, String> newSvcProviders = new HashMap<String, String>();
15891589
if (networkOfferingChanged) {
15901590
newSvcProviders = _networkMgr.finalizeServicesAndProvidersForNetwork(_configMgr.getNetworkOffering(networkOfferingId), network.getPhysicalNetworkId());
@@ -1616,6 +1616,81 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
16161616
restartNetwork = true;
16171617
}
16181618

1619+
//IP reservation checks
1620+
// allow reservation only to Isolated Guest networks
1621+
DataCenter dc = _dcDao.findById(network.getDataCenterId());
1622+
String networkCidr = network.getNetworkCidr();
1623+
1624+
if (guestVmCidr!= null ) {
1625+
if(dc.getNetworkType() == NetworkType.Basic) {
1626+
throw new InvalidParameterValueException("Guest VM CIDR can't be specified for zone with " + NetworkType.Basic + " networking");
1627+
}
1628+
if (network.getGuestType() != GuestType.Isolated) {
1629+
throw new InvalidParameterValueException("Can only allow IP Reservation in networks with guest type " + GuestType.Isolated);
1630+
}
1631+
if (networkOfferingChanged == true) {
1632+
throw new InvalidParameterValueException("Cannot specify this nework offering change and guestVmCidr at same time. Specify only one.");
1633+
}
1634+
if (!(network.getState() == Network.State.Implemented)) {
1635+
throw new InvalidParameterValueException ("The network must be in " + Network.State.Implemented + " state. IP Reservation cannot be applied in " + network.getState() + " state");
1636+
}
1637+
if (!NetUtils.isValidCIDR(guestVmCidr)) {
1638+
throw new InvalidParameterValueException ("Invalid format of Guest VM CIDR.");
1639+
}
1640+
if (!NetUtils.validateGuestCidr(guestVmCidr)) {
1641+
throw new InvalidParameterValueException ("Invalid format of Guest VM CIDR. Make sure it is RFC1918 compliant. ");
1642+
}
1643+
1644+
// If networkCidr is null it implies that there was no prior IP reservation, so the network cidr is network.getCidr()
1645+
// But in case networkCidr is a non null value (IP reservation already exists), it implies network cidr is networkCidr
1646+
if (networkCidr != null && ! NetUtils.isNetworkAWithinNetworkB(guestVmCidr, networkCidr)) {
1647+
throw new InvalidParameterValueException ("Invalid value of Guest VM CIDR. For IP Reservation, Guest VM CIDR should be a subset of network CIDR : " + networkCidr);
1648+
} else {
1649+
if (! NetUtils.isNetworkAWithinNetworkB(guestVmCidr, network.getCidr())) {
1650+
throw new InvalidParameterValueException ("Invalid value of Guest VM CIDR. For IP Reservation, Guest VM CIDR should be a subset of network CIDR : " + network.getCidr());
1651+
}
1652+
}
1653+
1654+
// This check makes sure there are no active IPs existing outside the guestVmCidr in the network
1655+
String[] guestVmCidrPair = guestVmCidr.split("\\/");
1656+
Long size = Long.valueOf(guestVmCidrPair[1]);
1657+
List<NicVO> nicsPresent = _nicDao.listByNetworkId(networkId);
1658+
1659+
String cidrIpRange[] = NetUtils.getIpRangeFromCidr(guestVmCidrPair[0], size);
1660+
s_logger.info("The start IP of the specified guest vm cidr is: " + cidrIpRange[0] +" and end IP is: " + cidrIpRange[1]);
1661+
long startIp = NetUtils.ip2Long(cidrIpRange[0]);
1662+
long endIp = NetUtils.ip2Long(cidrIpRange[1]);
1663+
long range = endIp - startIp + 1;
1664+
s_logger.info("The specified guest vm cidr has " + range + " IPs");
1665+
1666+
for (NicVO nic : nicsPresent) {
1667+
long nicIp = NetUtils.ip2Long(nic.getIp4Address());
1668+
//check if nic IP is outside the guest vm cidr
1669+
if (nicIp < startIp || nicIp > endIp) {
1670+
if(!(nic.getState() == Nic.State.Deallocating)) {
1671+
throw new InvalidParameterValueException("Active IPs like " + nic.getIp4Address() + " exist outside the Guest VM CIDR. Cannot apply reservation ");
1672+
}
1673+
}
1674+
}
1675+
1676+
// When reservation is applied for the first time, network_cidr will be null
1677+
// Populate it with the actual network cidr
1678+
if (network.getNetworkCidr() == null) {
1679+
network.setNetworkCidr(network.getCidr());
1680+
}
1681+
1682+
// Condition for IP Reservation reset : guestVmCidr and network CIDR are same
1683+
if (network.getNetworkCidr().equals(guestVmCidr)) {
1684+
s_logger.warn("Guest VM CIDR and Network CIDR both are same, reservation will reset.");
1685+
network.setNetworkCidr(null);
1686+
}
1687+
// Finally update "cidr" with the guestVmCidr
1688+
// which becomes the effective address space for CloudStack guest VMs
1689+
network.setCidr(guestVmCidr);
1690+
_networksDao.update(networkId, network);
1691+
s_logger.info("IP Reservation has been applied. The new CIDR for Guests Vms is " + guestVmCidr);
1692+
}
1693+
16191694
ReservationContext context = new ReservationContextImpl(null, null, callerUser, callerAccount);
16201695
// 1) Shutdown all the elements and cleanup all the rules. Don't allow to shutdown network in intermediate
16211696
// states - Shutdown and Implementing
@@ -1635,6 +1710,15 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
16351710
// We need to shutdown the network, since we want to re-implement the network.
16361711
s_logger.debug("Shutting down network id=" + networkId + " as a part of network update");
16371712

1713+
//check if network has reservation
1714+
if(NetUtils.isNetworkAWithinNetworkB(network.getCidr(), network.getNetworkCidr())) {
1715+
s_logger.warn ("Existing IP reservation will become ineffective for the network with id = " + networkId + " You need to reapply reservation after network reimplementation.");
1716+
//set cidr to the newtork cidr
1717+
network.setCidr(network.getNetworkCidr());
1718+
//set networkCidr to null to bring network back to no IP reservation state
1719+
network.setNetworkCidr(null);
1720+
}
1721+
16381722
if (!_networkMgr.shutdownNetwork(network.getId(), context, true)) {
16391723
s_logger.warn("Failed to shutdown the network as a part of update to network with specified id");
16401724
CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network as a part of update of specified network id");
@@ -1655,7 +1739,7 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
16551739
boolean validStateToImplement = (networkState == Network.State.Implemented || networkState == Network.State.Setup || networkState == Network.State.Allocated);
16561740
if (restartNetwork && !validStateToImplement) {
16571741
CloudRuntimeException ex = new CloudRuntimeException("Failed to implement the network elements and resources as a part of update to network with specified id; network is in wrong state: " + networkState);
1658-
ex.addProxyObject(network, networkId, "networkId");
1742+
ex.addProxyObject(network, networkId, "networkId");
16591743
throw ex;
16601744
}
16611745

@@ -1681,11 +1765,11 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
16811765
UsageEventUtils.saveUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), networkOfferingId, null, isDefault);
16821766
}
16831767
txn.commit();
1684-
} else {
1768+
} else {
16851769
network.setNetworkOfferingId(networkOfferingId);
16861770
_networksDao.update(networkId, network, _networkMgr.finalizeServicesAndProvidersForNetwork(_configMgr.getNetworkOffering(networkOfferingId), network.getPhysicalNetworkId()));
16871771
}
1688-
} else {
1772+
} else {
16891773
_networksDao.update(networkId, network);
16901774
}
16911775

@@ -1731,7 +1815,7 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
17311815
}
17321816

17331817

1734-
1818+
17351819

17361820
protected Set<Long> getAvailableIps(Network network, String requestedIp) {
17371821
String[] cidr = network.getCidr().split("/");

0 commit comments

Comments
 (0)