@ActionEvent(eventType = EventTypes.EVENT_ASSIGN_TO_LOAD_BALANCER_RULE, eventDescription = "assigning to load balancer", async = true)
public boolean assignToLoadBalancer(long loadBalancerId, List<Long> instanceIds, Map<Long, List<String>> vmIdIpMap) {
CallContext ctx = CallContext.current();
Account caller = ctx.getCallingAccount();
final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
if (loadBalancer == null) {
throw new InvalidParameterValueException("Failed to assign to load balancer " + loadBalancerId + ", the load balancer was not found.");
}
if (instanceIds == null && vmIdIpMap.isEmpty()) {
throw new InvalidParameterValueException("Both instanceids and vmidipmap can't be null");
}
// instanceIds and vmIdipmap is passed
if (instanceIds != null && !vmIdIpMap.isEmpty()) {
for(long instanceId: instanceIds) {
if (!vmIdIpMap.containsKey(instanceId)) {
vmIdIpMap.put(instanceId, null);
}
}
}
//only instanceids list passed
if (instanceIds != null && vmIdIpMap.isEmpty()){
vmIdIpMap = new HashMap<Long, List<String>>();
for (long instanceId: instanceIds){
vmIdIpMap.put(instanceId, null);
}
}
List<LoadBalancerVMMapVO> mappedInstances = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId, false);
Set<Long> mappedInstanceIds = new HashSet<Long>();
for (LoadBalancerVMMapVO mappedInstance : mappedInstances) {
mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId()));
}
Map<Long, List<String>> existingVmIdIps = new HashMap<Long, List<String>>();
// now get the ips of vm and add it to map
for (LoadBalancerVMMapVO mappedInstance : mappedInstances) {
List<String> ipsList = null;
if (existingVmIdIps.containsKey(mappedInstance.getInstanceId())) {
ipsList = existingVmIdIps.get(mappedInstance.getInstanceId());
} else {
ipsList = new ArrayList<String>();
}
ipsList.add(mappedInstance.getInstanceIp());
existingVmIdIps.put(mappedInstance.getInstanceId(), ipsList);
}
final List<UserVm> vmsToAdd = new ArrayList<UserVm>();
// check for conflict
Set<Long> passedInstanceIds = vmIdIpMap.keySet();
for (Long instanceId : passedInstanceIds) {
UserVm vm = _vmDao.findById(instanceId);
if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) {
InvalidParameterValueException ex = new InvalidParameterValueException("Invalid instance id specified");
if (vm == null) {
ex.addProxyObject(instanceId.toString(), "instanceId");
} else {
ex.addProxyObject(vm.getUuid(), "instanceId");
}
throw ex;
}
_rulesMgr.checkRuleAndUserVm(loadBalancer, vm, caller);
if (vm.getAccountId() != loadBalancer.getAccountId()) {
throw new PermissionDeniedException("Cannot add virtual machines that do not belong to the same owner.");
}
// Let's check to make sure the vm has a nic in the same network as
// the load balancing rule.
List<? extends Nic> nics = _networkModel.getNics(vm.getId());
Nic nicInSameNetwork = null;
for (Nic nic : nics) {
if (nic.getNetworkId() == loadBalancer.getNetworkId()) {
nicInSameNetwork = nic;
break;
}
}
if (nicInSameNetwork == null) {
InvalidParameterValueException ex =
new InvalidParameterValueException("VM with id specified cannot be added because it doesn't belong in the same network.");
ex.addProxyObject(vm.getUuid(), "instanceId");
throw ex;
}
String priIp = nicInSameNetwork.getIp4Address();
if (existingVmIdIps.containsKey(instanceId)) {
// now check for ip address
List<String> mappedIps = existingVmIdIps.get(instanceId);
List<String> newIps = vmIdIpMap.get(instanceId);
if (newIps == null) {
newIps = new ArrayList<String>();
newIps.add(priIp);
}
for (String newIp: newIps) {
if (mappedIps.contains(newIp)) {
throw new InvalidParameterValueException("VM " + instanceId + " with " + newIp +" is already mapped to load balancer.");
}
}
}
List<String> vmIpsList = vmIdIpMap.get(instanceId);
String vmLbIp = null;
if (vmIpsList != null) {
//check if the ips belongs to nic secondary ip
for (String ip: vmIpsList) {
// skip the primary ip from vm secondary ip comparisions
if (ip.equals(priIp)) {
continue;
}
if(_nicSecondaryIpDao.findByIp4AddressAndNicId(ip,nicInSameNetwork.getId()) == null) {
throw new InvalidParameterValueException("VM ip "+ ip + " specified does not belong to " +
"nic in network " + nicInSameNetwork.getNetworkId());
}
}
} else {
vmIpsList = new ArrayList<String>();
vmIpsList.add(priIp);
}
// when vm id is passed in instance ids and in vmidipmap
// assign for primary ip and ip passed in vmidipmap
if (instanceIds != null ) {
if (instanceIds.contains(instanceId)) {
vmIpsList.add(priIp);
}
}
vmIdIpMap.put(instanceId, vmIpsList);
if (s_logger.isDebugEnabled()) {
s_logger.debug("Adding " + vm + " to the load balancer pool");
}
vmsToAdd.add(vm);
}
final Set<Long> vmIds = vmIdIpMap.keySet();
final Map<Long, List<String>> newMap = vmIdIpMap;
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
for (Long vmId : vmIds) {
final Set<String> lbVmIps = new HashSet<String>(newMap.get(vmId));
for (String vmIp: lbVmIps) {
LoadBalancerVMMapVO map = new LoadBalancerVMMapVO(loadBalancer.getId(), vmId, vmIp, false);
map = _lb2VmMapDao.persist(map);
}
}
}
});
if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) {
// For autoscaled loadbalancer, the rules need not be applied,
// meaning the call need not reach the resource layer.
// We can consider the job done.
return true;
}
boolean success = false;
FirewallRule.State backupState = loadBalancer.getState();
try {
loadBalancer.setState(FirewallRule.State.Add);
_lbDao.persist(loadBalancer);
applyLoadBalancerConfig(loadBalancerId);
success = true;
} catch (ResourceUnavailableException e) {
s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e);
success = false;
} finally {
if (!success) {
final List<Long> vmInstanceIds = new ArrayList<Long>();
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
for (Long vmId : vmIds) {
vmInstanceIds.add(vmId);
}
}
});
if (!vmInstanceIds.isEmpty()) {
_lb2VmMapDao.remove(loadBalancer.getId(), vmInstanceIds, null);
s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while attaching VM: " + vmInstanceIds);
}
loadBalancer.setState(backupState);
_lbDao.persist(loadBalancer);
CloudRuntimeException ex = new CloudRuntimeException("Failed to add specified loadbalancerruleid for vms "
+ instanceIds);
ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId");
// TBD: Also pack in the instanceIds in the exception using the
// right VO object or table name.
throw ex;
}