Skip to content

Commit

Permalink
CLOUDSTACK-705 IP Address reservation for Isolated Guest Networks
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
Sakshams authored and Murali Reddy committed Feb 22, 2013
1 parent 79fee52 commit ea3db2f
Show file tree
Hide file tree
Showing 16 changed files with 211 additions and 33 deletions.
13 changes: 11 additions & 2 deletions api/src/com/cloud/network/Network.java
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,21 @@ public void setIp6Address(String ip6Address) {

String getGateway();

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

// "networkcidr" is the network CIDR of the guest network which uses IP reservation.
// It is the summation of "cidr" and the reservedIPrange(the address space used for non CloudStack purposes).
// For networks not configured with IP reservation, "networkcidr" is always null
String getNetworkCidr();

String getIp6Gateway();

String getIp6Cidr();

long getDataCenterId();

long getNetworkOfferingId();
Expand Down
7 changes: 7 additions & 0 deletions api/src/com/cloud/network/NetworkProfile.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class NetworkProfile implements Network {
private TrafficType trafficType;
private String gateway;
private String cidr;
private String networkCidr;
private String ip6Gateway;
private String ip6Cidr;
private long networkOfferingId;
Expand Down Expand Up @@ -65,6 +66,7 @@ public NetworkProfile(Network network) {
this.trafficType = network.getTrafficType();
this.gateway = network.getGateway();
this.cidr = network.getCidr();
this.networkCidr = network.getNetworkCidr();
this.ip6Gateway = network.getIp6Gateway();
this.ip6Cidr = network.getIp6Cidr();
this.networkOfferingId = network.getNetworkOfferingId();
Expand Down Expand Up @@ -162,6 +164,11 @@ public String getCidr() {
return cidr;
}

@Override
public String getNetworkCidr() {
return networkCidr;
}

@Override
public long getNetworkOfferingId() {
return networkOfferingId;
Expand Down
4 changes: 1 addition & 3 deletions api/src/com/cloud/network/NetworkService.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,8 @@ boolean restartNetwork(RestartNetworkCmd cmd, boolean cleanup) throws Concurrent

IpAddress getIp(long id);


Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount, User callerUser,
String domainSuffix, Long networkOfferingId, Boolean changeCidr);

String domainSuffix, Long networkOfferingId, Boolean changeCidr, String guestVmCidr);

PhysicalNetwork createPhysicalNetwork(Long zoneId, String vnetRange, String networkSpeed,
List<String> isolationMethods, String broadcastDomainRange, Long domainId, List<String> tags, String name);
Expand Down
2 changes: 1 addition & 1 deletion api/src/com/cloud/network/vpc/VpcService.java
Original file line number Diff line number Diff line change
Expand Up @@ -246,5 +246,5 @@ IpAddress associateIPToVpc(long ipId, long vpcId) throws ResourceAllocationExcep
InsufficientAddressCapacityException, ConcurrentOperationException;

public Network updateVpcGuestNetwork(long networkId, String name, String displayText, Account callerAccount,
User callerUser, String domainSuffix, Long ntwkOffId, Boolean changeCidr);
User callerUser, String domainSuffix, Long ntwkOffId, Boolean changeCidr, String guestVmCidr);
}
3 changes: 3 additions & 0 deletions api/src/org/apache/cloudstack/api/ApiConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@ public class ApiConstants {
public static final String VM_SNAPSHOT_DISK_IDS = "vmsnapshotdiskids";
public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory";
public static final String IMAGE_STORE_UUID = "imagestoreuuid";
public static final String GUEST_VM_CIDR = "guestvmcidr";
public static final String NETWORK_CIDR = "networkcidr";
public static final String RESERVED_IP_RANGE = "reservediprange";

public enum HostDetails {
all, capacity, events, stats, min;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ public class UpdateNetworkCmd extends BaseAsyncCmd {
description="network offering ID")
private Long networkOfferingId;

@Parameter(name=ApiConstants.GUEST_VM_CIDR, type=CommandType.STRING, description="CIDR for Guest VMs,Cloudstack allocates IPs to Guest VMs only from this CIDR")
private String guestVmCidr;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -94,6 +97,10 @@ public Boolean getChangeCidr() {
}
return false;
}

private String getGuestVmCidr() {
return guestVmCidr;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -125,10 +132,10 @@ public void execute() throws InsufficientCapacityException, ConcurrentOperationE
Network result = null;
if (network.getVpcId() != null) {
result = _vpcService.updateVpcGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount,
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr());
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr(), getGuestVmCidr());
} else {
result = _networkService.updateGuestNetwork(getId(), getNetworkName(), getDisplayText(), callerAccount,
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr());
callerUser, getNetworkDomain(), getNetworkOfferingId(), getChangeCidr(), getGuestVmCidr());
}

if (result != null) {
Expand Down
16 changes: 15 additions & 1 deletion api/src/org/apache/cloudstack/api/response/NetworkResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,15 @@ public class NetworkResponse extends BaseResponse implements ControlledEntityRes
@SerializedName(ApiConstants.NETMASK) @Param(description="the network's netmask")
private String netmask;

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

@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")
private String networkCidr;

@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")
private String reservedIpRange;

@SerializedName(ApiConstants.ZONE_ID) @Param(description="zone id of the network")
private String zoneId;

Expand Down Expand Up @@ -289,6 +295,14 @@ public void setCidr(String cidr) {
this.cidr = cidr;
}

public void setNetworkCidr(String networkCidr) {
this.networkCidr = networkCidr;
}

public void setReservedIpRange(String reservedIpRange) {
this.reservedIpRange = reservedIpRange;
}

public void setRestartRequired(Boolean restartRequired) {
this.restartRequired = restartRequired;
}
Expand Down
37 changes: 36 additions & 1 deletion server/src/com/cloud/api/ApiResponseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2153,13 +2153,48 @@ public NetworkResponse createNetworkResponse(Network network) {

// FIXME - either set netmask or cidr
response.setCidr(network.getCidr());
if (network.getCidr() != null) {
response.setNetworkCidr((network.getNetworkCidr()));
// If network has reservation its entire network cidr is defined by getNetworkCidr()
// if no reservation is present then getCidr() will define the entire network cidr
if (network.getNetworkCidr() != null) {
response.setNetmask(NetUtils.cidr2Netmask(network.getNetworkCidr()));
}
if (((network.getCidr()) != null) && (network.getNetworkCidr() == null)) {
response.setNetmask(NetUtils.cidr2Netmask(network.getCidr()));
}

response.setIp6Gateway(network.getIp6Gateway());
response.setIp6Cidr(network.getIp6Cidr());

// create response for reserved IP ranges that can be used for non-cloudstack purposes
String reservation = null;
if ((network.getCidr() != null) && (NetUtils.isNetworkAWithinNetworkB(network.getCidr(), network.getNetworkCidr()))) {
String[] guestVmCidrPair = network.getCidr().split("\\/");
String[] guestCidrPair = network.getNetworkCidr().split("\\/");

Long guestVmCidrSize = Long.valueOf(guestVmCidrPair[1]);
Long guestCidrSize = Long.valueOf(guestCidrPair[1]);

String[] guestVmIpRange = NetUtils.getIpRangeFromCidr(guestVmCidrPair[0], guestVmCidrSize);
String[] guestIpRange = NetUtils.getIpRangeFromCidr(guestCidrPair[0], guestCidrSize);
long startGuestIp = NetUtils.ip2Long(guestIpRange[0]);
long endGuestIp = NetUtils.ip2Long(guestIpRange[1]);
long startVmIp = NetUtils.ip2Long(guestVmIpRange[0]);
long endVmIp = NetUtils.ip2Long(guestVmIpRange[1]);

if (startVmIp == startGuestIp && endVmIp < endGuestIp -1) {
reservation = (NetUtils.long2Ip(endVmIp + 1) + "-" + NetUtils.long2Ip(endGuestIp));
}
if (endVmIp == endGuestIp && startVmIp > startGuestIp + 1) {
reservation = (NetUtils.long2Ip(startGuestIp) + "-" + NetUtils.long2Ip(startVmIp-1));
}
if(startVmIp > startGuestIp + 1 && endVmIp < endGuestIp - 1) {
reservation = (NetUtils.long2Ip(startGuestIp) + "-" + NetUtils.long2Ip(startVmIp-1) + " , " +
NetUtils.long2Ip(endVmIp + 1) + "-"+ NetUtils.long2Ip(endGuestIp));
}
}
response.setReservedIpRange(reservation);

//return vlan information only to Root admin
if (network.getBroadcastUri() != null && UserContext.current().getCaller().getType() == Account.ACCOUNT_TYPE_ADMIN) {
String broadcastUri = network.getBroadcastUri().toString();
Expand Down
98 changes: 91 additions & 7 deletions server/src/com/cloud/network/NetworkServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import com.cloud.event.dao.UsageEventDao;
import com.cloud.exception.*;
import com.cloud.network.IpAddress.State;
import com.cloud.vm.Nic;
import com.cloud.network.Network.Capability;
import com.cloud.network.Network.GuestType;
import com.cloud.network.Network.Provider;
Expand Down Expand Up @@ -1491,7 +1492,7 @@ private boolean checkForNonStoppedVmInNetwork(long networkId) {
@DB
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_UPDATE, eventDescription = "updating network", async = true)
public Network updateGuestNetwork(long networkId, String name, String displayText, Account callerAccount,
User callerUser, String domainSuffix, Long networkOfferingId, Boolean changeCidr) {
User callerUser, String domainSuffix, Long networkOfferingId, Boolean changeCidr, String guestVmCidr) {
boolean restartNetwork = false;

// verify input parameters
Expand Down Expand Up @@ -1552,8 +1553,6 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
ex.addProxyObject(networkOffering, networkOfferingId, "networkOfferingId");
throw ex;
}


//can't update from vpc to non-vpc network offering
boolean forVpcNew = _configMgr.isOfferingForVpc(networkOffering);
boolean vorVpcOriginal = _configMgr.isOfferingForVpc(_configMgr.getNetworkOffering(oldNetworkOfferingId));
Expand Down Expand Up @@ -1585,6 +1584,7 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
networkOfferingChanged = true;
}
}

Map<String, String> newSvcProviders = new HashMap<String, String>();
if (networkOfferingChanged) {
newSvcProviders = _networkMgr.finalizeServicesAndProvidersForNetwork(_configMgr.getNetworkOffering(networkOfferingId), network.getPhysicalNetworkId());
Expand Down Expand Up @@ -1616,6 +1616,81 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
restartNetwork = true;
}

//IP reservation checks
// allow reservation only to Isolated Guest networks
DataCenter dc = _dcDao.findById(network.getDataCenterId());
String networkCidr = network.getNetworkCidr();

if (guestVmCidr!= null ) {
if(dc.getNetworkType() == NetworkType.Basic) {
throw new InvalidParameterValueException("Guest VM CIDR can't be specified for zone with " + NetworkType.Basic + " networking");
}
if (network.getGuestType() != GuestType.Isolated) {
throw new InvalidParameterValueException("Can only allow IP Reservation in networks with guest type " + GuestType.Isolated);
}
if (networkOfferingChanged == true) {
throw new InvalidParameterValueException("Cannot specify this nework offering change and guestVmCidr at same time. Specify only one.");
}
if (!(network.getState() == Network.State.Implemented)) {
throw new InvalidParameterValueException ("The network must be in " + Network.State.Implemented + " state. IP Reservation cannot be applied in " + network.getState() + " state");
}
if (!NetUtils.isValidCIDR(guestVmCidr)) {
throw new InvalidParameterValueException ("Invalid format of Guest VM CIDR.");
}
if (!NetUtils.validateGuestCidr(guestVmCidr)) {
throw new InvalidParameterValueException ("Invalid format of Guest VM CIDR. Make sure it is RFC1918 compliant. ");
}

// If networkCidr is null it implies that there was no prior IP reservation, so the network cidr is network.getCidr()
// But in case networkCidr is a non null value (IP reservation already exists), it implies network cidr is networkCidr
if (networkCidr != null && ! NetUtils.isNetworkAWithinNetworkB(guestVmCidr, networkCidr)) {
throw new InvalidParameterValueException ("Invalid value of Guest VM CIDR. For IP Reservation, Guest VM CIDR should be a subset of network CIDR : " + networkCidr);
} else {
if (! NetUtils.isNetworkAWithinNetworkB(guestVmCidr, network.getCidr())) {
throw new InvalidParameterValueException ("Invalid value of Guest VM CIDR. For IP Reservation, Guest VM CIDR should be a subset of network CIDR : " + network.getCidr());
}
}

// This check makes sure there are no active IPs existing outside the guestVmCidr in the network
String[] guestVmCidrPair = guestVmCidr.split("\\/");
Long size = Long.valueOf(guestVmCidrPair[1]);
List<NicVO> nicsPresent = _nicDao.listByNetworkId(networkId);

String cidrIpRange[] = NetUtils.getIpRangeFromCidr(guestVmCidrPair[0], size);
s_logger.info("The start IP of the specified guest vm cidr is: " + cidrIpRange[0] +" and end IP is: " + cidrIpRange[1]);
long startIp = NetUtils.ip2Long(cidrIpRange[0]);
long endIp = NetUtils.ip2Long(cidrIpRange[1]);
long range = endIp - startIp + 1;
s_logger.info("The specified guest vm cidr has " + range + " IPs");

for (NicVO nic : nicsPresent) {
long nicIp = NetUtils.ip2Long(nic.getIp4Address());
//check if nic IP is outside the guest vm cidr
if (nicIp < startIp || nicIp > endIp) {
if(!(nic.getState() == Nic.State.Deallocating)) {
throw new InvalidParameterValueException("Active IPs like " + nic.getIp4Address() + " exist outside the Guest VM CIDR. Cannot apply reservation ");
}
}
}

// When reservation is applied for the first time, network_cidr will be null
// Populate it with the actual network cidr
if (network.getNetworkCidr() == null) {
network.setNetworkCidr(network.getCidr());
}

// Condition for IP Reservation reset : guestVmCidr and network CIDR are same
if (network.getNetworkCidr().equals(guestVmCidr)) {
s_logger.warn("Guest VM CIDR and Network CIDR both are same, reservation will reset.");
network.setNetworkCidr(null);
}
// Finally update "cidr" with the guestVmCidr
// which becomes the effective address space for CloudStack guest VMs
network.setCidr(guestVmCidr);
_networksDao.update(networkId, network);
s_logger.info("IP Reservation has been applied. The new CIDR for Guests Vms is " + guestVmCidr);
}

ReservationContext context = new ReservationContextImpl(null, null, callerUser, callerAccount);
// 1) Shutdown all the elements and cleanup all the rules. Don't allow to shutdown network in intermediate
// states - Shutdown and Implementing
Expand All @@ -1635,6 +1710,15 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
// We need to shutdown the network, since we want to re-implement the network.
s_logger.debug("Shutting down network id=" + networkId + " as a part of network update");

//check if network has reservation
if(NetUtils.isNetworkAWithinNetworkB(network.getCidr(), network.getNetworkCidr())) {
s_logger.warn ("Existing IP reservation will become ineffective for the network with id = " + networkId + " You need to reapply reservation after network reimplementation.");
//set cidr to the newtork cidr
network.setCidr(network.getNetworkCidr());
//set networkCidr to null to bring network back to no IP reservation state
network.setNetworkCidr(null);
}

if (!_networkMgr.shutdownNetwork(network.getId(), context, true)) {
s_logger.warn("Failed to shutdown the network as a part of update to network with specified id");
CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network as a part of update of specified network id");
Expand All @@ -1655,7 +1739,7 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
boolean validStateToImplement = (networkState == Network.State.Implemented || networkState == Network.State.Setup || networkState == Network.State.Allocated);
if (restartNetwork && !validStateToImplement) {
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);
ex.addProxyObject(network, networkId, "networkId");
ex.addProxyObject(network, networkId, "networkId");
throw ex;
}

Expand All @@ -1681,11 +1765,11 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
UsageEventUtils.saveUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), networkOfferingId, null, isDefault);
}
txn.commit();
} else {
} else {
network.setNetworkOfferingId(networkOfferingId);
_networksDao.update(networkId, network, _networkMgr.finalizeServicesAndProvidersForNetwork(_configMgr.getNetworkOffering(networkOfferingId), network.getPhysicalNetworkId()));
}
} else {
} else {
_networksDao.update(networkId, network);
}

Expand Down Expand Up @@ -1731,7 +1815,7 @@ public Network updateGuestNetwork(long networkId, String name, String displayTex
}




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

0 comments on commit ea3db2f

Please sign in to comment.