throws ResourceAllocationException, ResourceUnavailableException,
InsufficientAddressCapacityException, ConcurrentOperationException {
Account caller = UserContext.current().getCaller();
Account owner = null;
IPAddressVO ipToAssoc = _ipAddressDao.findById(ipId);
if (ipToAssoc != null) {
Network network = _networksDao.findById(networkId);
if (network == null) {
throw new InvalidParameterValueException("Invalid network id is given");
}
DataCenter zone = _configMgr.getZone(network.getDataCenterId());
if (zone.getNetworkType() == NetworkType.Advanced) {
if (network.getGuestType() == Network.GuestType.Shared) {
if (isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) {
_accountMgr.checkAccess(UserContext.current().getCaller(), AccessType.UseNetwork, false, network);
} else {
throw new InvalidParameterValueException("IP can be associated with guest network of 'shared' type only if " +
"network services Source Nat, Static Nat, Port Forwarding, Load balancing, firewall are enabled in the network");
}
}
} else {
_accountMgr.checkAccess(caller, null, true, ipToAssoc);
}
owner = _accountMgr.getAccount(ipToAssoc.getAllocatedToAccountId());
} else {
s_logger.debug("Unable to find ip address by id: " + ipId);
return null;
}
if (ipToAssoc.getAssociatedWithNetworkId() != null) {
s_logger.debug("IP " + ipToAssoc + " is already assocaited with network id" + networkId);
return ipToAssoc;
}
Network network = _networksDao.findById(networkId);
if (network != null) {
_accountMgr.checkAccess(owner, AccessType.UseNetwork, false, network);
} else {
s_logger.debug("Unable to find ip address by id: " + ipId);
return null;
}
DataCenter zone = _configMgr.getZone(network.getDataCenterId());
// allow associating IP addresses to guest network only
if (network.getTrafficType() != TrafficType.Guest) {
throw new InvalidParameterValueException("Ip address can be associated to the network with trafficType " + TrafficType.Guest);
}
// Check that network belongs to IP owner - skip this check
// - if zone is basic zone as there is just one guest network,
// - if shared network in Advanced zone
// - and it belongs to the system
if (network.getAccountId() != owner.getId()) {
if (zone.getNetworkType() != NetworkType.Basic && !(zone.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Shared)) {
throw new InvalidParameterValueException("The owner of the network is not the same as owner of the IP");
}
}
// In Advance zone only allow to do IP assoc
// - for Isolated networks with source nat service enabled
// - for shared networks with source nat service enabled
if (zone.getNetworkType() == NetworkType.Advanced &&
!(_networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat))) {
throw new InvalidParameterValueException("In zone of type " + NetworkType.Advanced +
" ip address can be associated only to the network of guest type " + GuestType.Isolated + " with the "
+ Service.SourceNat.getName() + " enabled");
}
NetworkOffering offering = _networkOfferingDao.findById(network.getNetworkOfferingId());
boolean sharedSourceNat = offering.getSharedSourceNat();
boolean isSourceNat = false;
if (!sharedSourceNat) {
if (getExistingSourceNatInNetwork(owner.getId(), networkId) == null) {
if (network.getGuestType() == GuestType.Isolated && network.getVpcId() == null &&
!ipToAssoc.isPortable()) {
isSourceNat = true;
}
}
}
s_logger.debug("Associating ip " + ipToAssoc + " to network " + network);
IPAddressVO ip = _ipAddressDao.findById(ipId);
//update ip address with networkId
ip.setAssociatedWithNetworkId(networkId);
ip.setSourceNat(isSourceNat);
_ipAddressDao.update(ipId, ip);
boolean success = false;
try {
success = applyIpAssociations(network, false, false);
if (success) {
s_logger.debug("Successfully associated ip address " + ip.getAddress().addr() + " to network " + network);
} else {
s_logger.warn("Failed to associate ip address " + ip.getAddress().addr() + " to network " + network);
}
return ip;
} finally {
if (!success && releaseOnFailure) {
if (ip != null) {
try {
s_logger.warn("Failed to associate ip address, so releasing ip from the database " + ip);
_ipAddressDao.markAsUnavailable(ip.getId());
if (!applyIpAssociations(network, true, false)) {
// if fail to apply ip assciations again, unassign ip address without updating resource
// count and generating usage event as there is no need to keep it in the db
_ipAddressDao.unassignIpAddress(ip.getId());
}
} catch (Exception e) {
s_logger.warn("Unable to disassociate ip address for recovery", e);
}
}