// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package com.cloud.network.lb; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.ejb.Local; import javax.naming.ConfigurationException; import com.cloud.event.UsageEventUtils; import org.apache.cloudstack.api.command.user.loadbalancer.*; import org.apache.log4j.Logger; import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBStickinessPolicyCmd; import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRuleInstancesCmd; import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRulesCmd; import org.apache.cloudstack.api.response.ServiceResponse; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.VlanDao; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.event.dao.EventDao; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.ExternalLoadBalancerUsageManager; import com.cloud.network.IPAddressVO; import com.cloud.network.IpAddress; import com.cloud.network.LBStickinessPolicyVO; import com.cloud.network.LoadBalancerVMMapVO; import com.cloud.network.LoadBalancerVO; import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.NetworkManager; import com.cloud.network.NetworkModel; import com.cloud.network.NetworkRuleApplier; import com.cloud.network.NetworkVO; import com.cloud.network.as.AutoScalePolicy; import com.cloud.network.as.AutoScalePolicyConditionMapVO; import com.cloud.network.as.AutoScaleVmGroup; import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO; import com.cloud.network.as.AutoScaleVmGroupVO; import com.cloud.network.as.AutoScaleVmProfile; import com.cloud.network.as.Condition; import com.cloud.network.as.Counter; import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao; import com.cloud.network.as.dao.AutoScalePolicyDao; import com.cloud.network.as.dao.AutoScaleVmGroupDao; import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao; import com.cloud.network.as.dao.AutoScaleVmProfileDao; import com.cloud.network.as.dao.ConditionDao; import com.cloud.network.as.dao.CounterDao; import com.cloud.network.dao.FirewallRulesCidrsDao; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.LBStickinessPolicyDao; import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.element.LoadBalancingServiceProvider; import com.cloud.network.lb.LoadBalancingRule.LbAutoScalePolicy; import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmGroup; import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmProfile; import com.cloud.network.lb.LoadBalancingRule.LbCondition; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; import com.cloud.network.rules.FirewallManager; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.FirewallRuleType; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.LbStickinessMethod; import com.cloud.network.rules.LbStickinessMethod.LbStickinessMethodParam; import com.cloud.network.rules.LoadBalancer; import com.cloud.network.rules.RulesManager; import com.cloud.network.rules.StickinessPolicy; import com.cloud.network.vpc.VpcManager; import com.cloud.offering.NetworkOffering; import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.DomainService; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.component.Adapters; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.Nic; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @Local(value = { LoadBalancingRulesManager.class, LoadBalancingRulesService.class }) public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, LoadBalancingRulesService, NetworkRuleApplier, Manager { private static final Logger s_logger = Logger.getLogger(LoadBalancingRulesManagerImpl.class); String _name; @Inject NetworkManager _networkMgr; @Inject NetworkModel _networkModel; @Inject RulesManager _rulesMgr; @Inject AccountManager _accountMgr; @Inject IPAddressDao _ipAddressDao; @Inject LoadBalancerDao _lbDao; @Inject VlanDao _vlanDao; @Inject EventDao _eventDao; @Inject LoadBalancerVMMapDao _lb2VmMapDao; @Inject LBStickinessPolicyDao _lb2stickinesspoliciesDao; @Inject UserVmDao _vmDao; @Inject AccountDao _accountDao; @Inject DomainDao _domainDao; @Inject NicDao _nicDao; @Inject UsageEventDao _usageEventDao; @Inject FirewallRulesCidrsDao _firewallCidrsDao; @Inject FirewallManager _firewallMgr; @Inject NetworkDao _networkDao; @Inject FirewallRulesDao _firewallDao; @Inject DomainService _domainMgr; @Inject ConfigurationManager _configMgr; @Inject ExternalLoadBalancerUsageManager _externalLBUsageMgr; @Inject NetworkServiceMapDao _ntwkSrvcDao; @Inject ResourceTagDao _resourceTagDao; @Inject VpcManager _vpcMgr; @Inject VMTemplateDao _templateDao; @Inject ServiceOfferingDao _offeringsDao; @Inject CounterDao _counterDao; @Inject ConditionDao _conditionDao; @Inject AutoScaleVmProfileDao _autoScaleVmProfileDao; @Inject AutoScalePolicyDao _autoScalePolicyDao; @Inject AutoScalePolicyConditionMapDao _autoScalePolicyConditionMapDao; @Inject AutoScaleVmGroupDao _autoScaleVmGroupDao; @Inject AutoScaleVmGroupPolicyMapDao _autoScaleVmGroupPolicyMapDao; @Inject ConfigurationDao _configDao; @Inject DataCenterDao _dcDao = null; @Inject UserDao _userDao; @Inject(adapter = LoadBalancingServiceProvider.class) Adapters _lbProviders; // Will return a string. For LB Stickiness this will be a json, for autoscale this will be "," separated values @Override public String getLBCapability(long networkid, String capabilityName) { Map> serviceCapabilitiesMap = _networkModel.getNetworkCapabilities(networkid); if (serviceCapabilitiesMap != null) { for (Service service : serviceCapabilitiesMap.keySet()) { ServiceResponse serviceResponse = new ServiceResponse(); serviceResponse.setName(service.getName()); if ("Lb".equalsIgnoreCase(service.getName())) { Map serviceCapabilities = serviceCapabilitiesMap .get(service); if (serviceCapabilities != null) { for (Capability capability : serviceCapabilities .keySet()) { if (capabilityName.equals(capability.getName())) { return serviceCapabilities.get(capability); } } } } } } return null; } private LbAutoScaleVmGroup getLbAutoScaleVmGroup(AutoScaleVmGroupVO vmGroup, String currentState, LoadBalancerVO lb) { long lbNetworkId = lb.getNetworkId(); String lbName = lb.getName(); List vmGroupPolicyMapList = _autoScaleVmGroupPolicyMapDao.listByVmGroupId(vmGroup.getId()); List autoScalePolicies = new ArrayList(); for (AutoScaleVmGroupPolicyMapVO vmGroupPolicyMap : vmGroupPolicyMapList) { AutoScalePolicy autoScalePolicy = _autoScalePolicyDao.findById(vmGroupPolicyMap.getPolicyId()); List autoScalePolicyConditionMapList = _autoScalePolicyConditionMapDao.listByAll(autoScalePolicy.getId(), null); List lbConditions = new ArrayList(); for (AutoScalePolicyConditionMapVO autoScalePolicyConditionMap : autoScalePolicyConditionMapList) { Condition condition = _conditionDao.findById(autoScalePolicyConditionMap.getConditionId()); Counter counter = _counterDao.findById(condition.getCounterid()); lbConditions.add(new LbCondition(counter, condition)); } autoScalePolicies.add(new LbAutoScalePolicy(autoScalePolicy, lbConditions)); } AutoScaleVmProfile autoScaleVmProfile = _autoScaleVmProfileDao.findById(vmGroup.getProfileId()); Long autoscaleUserId = autoScaleVmProfile.getAutoScaleUserId(); User user = _userDao.findByIdIncludingRemoved(autoscaleUserId); String apiKey = user.getApiKey(); String secretKey = user.getSecretKey(); String csUrl = _configDao.getValue(Config.EndpointeUrl.key()); String zoneId = _dcDao.findById(autoScaleVmProfile.getZoneId()).getUuid(); String domainId = _domainDao.findById(autoScaleVmProfile.getDomainId()).getUuid(); String serviceOfferingId = _offeringsDao.findById(autoScaleVmProfile.getServiceOfferingId()).getUuid(); String templateId = _templateDao.findById(autoScaleVmProfile.getTemplateId()).getUuid(); String vmName = "AutoScale-LB-" + lbName; String lbNetworkUuid = null; DataCenter zone = _configMgr.getZone(vmGroup.getZoneId()); if (zone == null) { // This should never happen, but still a cautious check s_logger.warn("Unable to find zone while packaging AutoScale Vm Group, zoneid: " + vmGroup.getZoneId()); throw new InvalidParameterValueException("Unable to find zone"); } else { if (zone.getNetworkType() == NetworkType.Advanced) { NetworkVO lbNetwork = _networkDao.findById(lbNetworkId); lbNetworkUuid = lbNetwork.getUuid(); } } if (apiKey == null) { throw new InvalidParameterValueException("apiKey for user: " + user.getUsername() + " is empty. Please generate it"); } if (secretKey == null) { throw new InvalidParameterValueException("secretKey for user: " + user.getUsername() + " is empty. Please generate it"); } if (csUrl == null || csUrl.contains("localhost")) { throw new InvalidParameterValueException("Global setting endpointe.url has to be set to the Management Server's API end point"); } LbAutoScaleVmProfile lbAutoScaleVmProfile = new LbAutoScaleVmProfile(autoScaleVmProfile, apiKey, secretKey, csUrl, zoneId, domainId, serviceOfferingId, templateId, vmName, lbNetworkUuid); return new LbAutoScaleVmGroup(vmGroup, autoScalePolicies, lbAutoScaleVmProfile, currentState); } private boolean applyAutoScaleConfig(LoadBalancerVO lb, AutoScaleVmGroupVO vmGroup, String currentState) throws ResourceUnavailableException { LbAutoScaleVmGroup lbAutoScaleVmGroup = getLbAutoScaleVmGroup(vmGroup, currentState, lb); /* Regular config like destinations need not be packed for applying autoscale config as of today.*/ LoadBalancingRule rule = new LoadBalancingRule(lb, null, null); rule.setAutoScaleVmGroup(lbAutoScaleVmGroup); if (!isRollBackAllowedForProvider(lb)) { // this is for Netscaler type of devices. if their is failure the db entries will be rollbacked. return false; } List rules = Arrays.asList(rule); if (!_networkMgr.applyRules(rules, FirewallRule.Purpose.LoadBalancing, this, false)) { s_logger.debug("LB rules' autoscale config are not completely applied"); return false; } return true; } @Override @DB public boolean configureLbAutoScaleVmGroup(long vmGroupid, String currentState) throws ResourceUnavailableException { AutoScaleVmGroupVO vmGroup = _autoScaleVmGroupDao.findById(vmGroupid); boolean success = false; LoadBalancerVO loadBalancer = _lbDao.findById(vmGroup.getLoadBalancerId()); FirewallRule.State backupState = loadBalancer.getState(); if (vmGroup.getState().equals(AutoScaleVmGroup.State_New)) { loadBalancer.setState(FirewallRule.State.Add); _lbDao.persist(loadBalancer); } else if (loadBalancer.getState() == FirewallRule.State.Active && vmGroup.getState().equals(AutoScaleVmGroup.State_Revoke)) { loadBalancer.setState(FirewallRule.State.Add); _lbDao.persist(loadBalancer); } try { success = applyAutoScaleConfig(loadBalancer, vmGroup, currentState); } catch (ResourceUnavailableException e) { s_logger.warn("Unable to configure AutoScaleVmGroup to the lb rule: " + loadBalancer.getId() + " because resource is unavaliable:", e); if (isRollBackAllowedForProvider(loadBalancer)) { loadBalancer.setState(backupState); _lbDao.persist(loadBalancer); s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " lb state rolback while creating AutoscaleVmGroup"); } throw e; } finally { if (!success) { s_logger.warn("Failed to configure LB Auto Scale Vm Group with Id:" + vmGroupid); } } if (success) { if (vmGroup.getState().equals(AutoScaleVmGroup.State_New)) { Transaction.currentTxn().start(); loadBalancer.setState(FirewallRule.State.Active); s_logger.debug("LB rule " + loadBalancer.getId() + " state is set to Active"); _lbDao.persist(loadBalancer); vmGroup.setState(AutoScaleVmGroup.State_Enabled); _autoScaleVmGroupDao.persist(vmGroup); s_logger.debug("LB Auto Scale Vm Group with Id: " + vmGroupid + " is set to Enabled state."); Transaction.currentTxn().commit(); } s_logger.info("Successfully configured LB Autoscale Vm Group with Id: " + vmGroupid); } return success; } private boolean genericValidator(CreateLBStickinessPolicyCmd cmd) throws InvalidParameterValueException { LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); /* Validation : check for valid Method name and params */ List stickinessMethodList = getStickinessMethods(loadBalancer .getNetworkId()); boolean methodMatch = false; if (stickinessMethodList == null) { throw new InvalidParameterValueException("Failed: No Stickiness method available for LB rule:" + cmd.getLbRuleId()); } for (LbStickinessMethod method : stickinessMethodList) { if (method.getMethodName().equalsIgnoreCase(cmd.getStickinessMethodName())) { methodMatch = true; Map apiParamList = cmd.getparamList(); List methodParamList = method.getParamList(); Map tempParamList = new HashMap(); /* * validation-1: check for any extra params that are not * required by the policymethod(capability), FIXME: make the * below loop simple without using raw data type */ if (apiParamList != null) { Collection userGroupCollection = apiParamList.values(); Iterator iter = userGroupCollection.iterator(); while (iter.hasNext()) { HashMap paramKVpair = (HashMap) iter.next(); String paramName = paramKVpair.get("name"); String paramValue = paramKVpair.get("value"); tempParamList.put(paramName, paramValue); Boolean found = false; for (LbStickinessMethodParam param : methodParamList) { if (param.getParamName().equalsIgnoreCase(paramName)) { if ((param.getIsflag() == false) && (paramValue == null)) { throw new InvalidParameterValueException("Failed : Value expected for the Param :" + param.getParamName()); } found = true; break; } } if (!found) { throw new InvalidParameterValueException("Failed : Stickiness policy does not support param name :" + paramName); } } } /* validation-2: check for mandatory params */ for (LbStickinessMethodParam param : methodParamList) { if (param.getRequired()) { if (tempParamList.get(param.getParamName()) == null) { throw new InvalidParameterValueException("Failed : Missing Manadatory Param :" + param.getParamName()); } } } /* Successfully completed the Validation */ break; } } if (methodMatch == false) { throw new InvalidParameterValueException("Failed to match Stickiness method name for LB rule:" + cmd.getLbRuleId()); } /* Validation : check for the multiple policies to the rule id */ List stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId(), false); if (stickinessPolicies.size() > 0) { throw new InvalidParameterValueException("Failed to create Stickiness policy: Already policy attached " + cmd.getLbRuleId()); } return true; } @SuppressWarnings("rawtypes") @Override @DB @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_CREATE, eventDescription = "create lb stickinesspolicy to load balancer", create = true) public StickinessPolicy createLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd) throws NetworkRuleConflictException { UserContext caller = UserContext.current(); /* Validation : check corresponding load balancer rule exist */ LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); if (loadBalancer == null) { throw new InvalidParameterValueException("Failed: LB rule id: " + cmd.getLbRuleId() + " not present "); } _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); if (loadBalancer.getState() == FirewallRule.State.Revoke) { throw new InvalidParameterValueException("Failed: LB rule id: " + cmd.getLbRuleId() + " is in deleting state: "); } /* Generic validations */ if (!genericValidator(cmd)) { throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation Failed " + cmd.getLbRuleId()); } /* Specific validations using network element validator for specific validations */ LBStickinessPolicyVO lbpolicy = new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription()); List policyList = new ArrayList(); policyList.add(new LbStickinessPolicy(cmd.getStickinessMethodName(), lbpolicy.getParams())); LoadBalancingRule lbRule = new LoadBalancingRule(loadBalancer, getExistingDestinations(lbpolicy.getId()), policyList); if (!validateRule(lbRule)) { throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation Failed " + cmd.getLbRuleId()); } /* Finally Insert into DB */ LBStickinessPolicyVO policy = new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription()); policy = _lb2stickinesspoliciesDao.persist(policy); return policy; } private boolean validateRule(LoadBalancingRule lbRule) { Network network = _networkDao.findById(lbRule.getNetworkId()); Purpose purpose = lbRule.getPurpose(); if (purpose != Purpose.LoadBalancing) { s_logger.debug("Unable to validate network rules for purpose: " + purpose.toString()); return false; } for (LoadBalancingServiceProvider ne : _lbProviders) { boolean validated = ne.validateLBRule(network, lbRule); if (!validated) return false; } return true; } @Override @DB @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_CREATE, eventDescription = "Apply Stickinesspolicy to load balancer ", async = true) public boolean applyLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd) { boolean success = true; LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId()); if (loadBalancer == null) { throw new InvalidParameterException("Invalid Load balancer Id:" + cmd.getLbRuleId()); } FirewallRule.State backupState = loadBalancer.getState(); loadBalancer.setState(FirewallRule.State.Add); _lbDao.persist(loadBalancer); try { applyLoadBalancerConfig(cmd.getLbRuleId()); } catch (ResourceUnavailableException e) { s_logger.warn("Unable to apply Stickiness policy to the lb rule: " + cmd.getLbRuleId() + " because resource is unavaliable:", e); if (isRollBackAllowedForProvider(loadBalancer)) { loadBalancer.setState(backupState); _lbDao.persist(loadBalancer); s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " lb state rolback while creating sticky policy"); } deleteLBStickinessPolicy(cmd.getEntityId(), false); success = false; } return success; } @Override @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_DELETE, eventDescription = "revoking LB Stickiness policy ", async = true) public boolean deleteLBStickinessPolicy(long stickinessPolicyId, boolean apply) { boolean success = true; UserContext caller = UserContext.current(); LBStickinessPolicyVO stickinessPolicy = _lb2stickinesspoliciesDao.findById(stickinessPolicyId); if (stickinessPolicy == null) { throw new InvalidParameterException("Invalid Stickiness policy id value: " + stickinessPolicyId); } LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(stickinessPolicy.getLoadBalancerId())); if (loadBalancer == null) { throw new InvalidParameterException("Invalid Load balancer : " + stickinessPolicy.getLoadBalancerId() + " for Stickiness policy id: " + stickinessPolicyId); } long loadBalancerId = loadBalancer.getId(); FirewallRule.State backupState = loadBalancer.getState(); _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); if (apply) { if (loadBalancer.getState() == FirewallRule.State.Active) { loadBalancer.setState(FirewallRule.State.Add); _lbDao.persist(loadBalancer); } boolean backupStickyState = stickinessPolicy.isRevoke(); stickinessPolicy.setRevoke(true); _lb2stickinesspoliciesDao.persist(stickinessPolicy); s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", stickinesspolicyID " + stickinessPolicyId); try { if (!applyLoadBalancerConfig(loadBalancerId)) { s_logger.warn("Failed to remove load balancer rule id " + loadBalancerId + " for stickinesspolicyID " + stickinessPolicyId); throw new CloudRuntimeException("Failed to remove load balancer rule id " + loadBalancerId + " for stickinesspolicyID " + stickinessPolicyId); } } catch (ResourceUnavailableException e) { if (isRollBackAllowedForProvider(loadBalancer)) { stickinessPolicy.setRevoke(backupStickyState); _lb2stickinesspoliciesDao.persist(stickinessPolicy); loadBalancer.setState(backupState); _lbDao.persist(loadBalancer); s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while deleting sticky policy: " + stickinessPolicyId); } s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); success = false; } } else { _lb2stickinesspoliciesDao.remove(stickinessPolicy.getLoadBalancerId()); } return success; } private boolean isRollBackAllowedForProvider(LoadBalancerVO loadBalancer) { Network network = _networkDao.findById(loadBalancer.getNetworkId()); List provider = _networkMgr.getProvidersForServiceInNetwork(network, Service.Lb); if (provider == null || provider.size() == 0) { return false; } if (provider.get(0) == Provider.Netscaler || provider.get(0) == Provider.F5BigIp) { return true; } return false; } @Override @DB @ActionEvent(eventType = EventTypes.EVENT_ASSIGN_TO_LOAD_BALANCER_RULE, eventDescription = "assigning to load balancer", async = true) public boolean assignToLoadBalancer(long loadBalancerId, List instanceIds) { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); if (loadBalancer == null) { throw new InvalidParameterValueException("Failed to assign to load balancer " + loadBalancerId + ", the load balancer was not found."); } List mappedInstances = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId, false); Set mappedInstanceIds = new HashSet(); for (LoadBalancerVMMapVO mappedInstance : mappedInstances) { mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId())); } List vmsToAdd = new ArrayList(); for (Long instanceId : instanceIds) { if (mappedInstanceIds.contains(instanceId)) { throw new InvalidParameterValueException("VM " + instanceId + " is already mapped to load balancer."); } UserVm vm = _vmDao.findById(instanceId); if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) { InvalidParameterValueException ex = new InvalidParameterValueException("Invalid instance id specified"); ex.addProxyObject(vm, instanceId, "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 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 " + instanceId + " cannot be added because it doesn't belong in the same network."); ex.addProxyObject(vm, instanceId, "instanceId"); throw ex; } if (s_logger.isDebugEnabled()) { s_logger.debug("Adding " + vm + " to the load balancer pool"); } vmsToAdd.add(vm); } Transaction txn = Transaction.currentTxn(); txn.start(); for (UserVm vm : vmsToAdd) { LoadBalancerVMMapVO map = new LoadBalancerVMMapVO(loadBalancer.getId(), vm.getId(), false); map = _lb2VmMapDao.persist(map); } txn.commit(); 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) { if (isRollBackAllowedForProvider(loadBalancer)) { List vmInstanceIds = new ArrayList(); txn = Transaction.currentTxn(); txn.start(); for (UserVm vm : vmsToAdd) { vmInstanceIds.add(vm.getId()); } txn.commit(); 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); } s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); } if (!success) { CloudRuntimeException ex = new CloudRuntimeException("Failed to add specified loadbalancerruleid for vms " + instanceIds); ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); // TBD: Also pack in the instanceIds in the exception using the right VO object or table name. throw ex; } return success; } @Override @ActionEvent(eventType = EventTypes.EVENT_REMOVE_FROM_LOAD_BALANCER_RULE, eventDescription = "removing from load balancer", async = true) public boolean removeFromLoadBalancer(long loadBalancerId, List instanceIds) { return removeFromLoadBalancerInternal(loadBalancerId, instanceIds, true); } private boolean removeFromLoadBalancerInternal(long loadBalancerId, List instanceIds, boolean rollBack) { UserContext caller = UserContext.current(); LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(loadBalancerId)); if (loadBalancer == null) { throw new InvalidParameterException("Invalid load balancer value: " + loadBalancerId); } _accountMgr.checkAccess(caller.getCaller(), null, true, loadBalancer); boolean success = false; FirewallRule.State backupState = loadBalancer.getState(); try { loadBalancer.setState(FirewallRule.State.Add); _lbDao.persist(loadBalancer); for (long instanceId : instanceIds) { LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmId(loadBalancerId, instanceId); map.setRevoke(true); _lb2VmMapDao.persist(map); s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + instanceId); } 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 and only need to remove the rules in DB _lb2VmMapDao.remove(loadBalancer.getId(), instanceIds, null); return true; } if (!applyLoadBalancerConfig(loadBalancerId)) { s_logger.warn("Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds); CloudRuntimeException ex = new CloudRuntimeException("Failed to remove specified load balancer rule id for vms " + instanceIds); ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); throw ex; } success = true; } catch (ResourceUnavailableException e) { if (rollBack && isRollBackAllowedForProvider(loadBalancer)) { for (long instanceId : instanceIds) { LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmId(loadBalancerId, instanceId); map.setRevoke(false); _lb2VmMapDao.persist(map); s_logger.debug("LB Rollback rule id: " + loadBalancerId + ",while removing vmId " + instanceId); } loadBalancer.setState(backupState); _lbDao.persist(loadBalancer); s_logger.debug("LB Rollback rule id: " + loadBalancerId + " while removing vm instances"); } s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); } if (!success) { CloudRuntimeException ex = new CloudRuntimeException("Failed to remove specified load balancer rule id for vms " + instanceIds); ex.addProxyObject(loadBalancer, loadBalancerId, "loadBalancerId"); throw ex; } return success; } @Override public boolean removeVmFromLoadBalancers(long instanceId) { boolean success = true; List maps = _lb2VmMapDao.listByInstanceId(instanceId); if (maps == null || maps.isEmpty()) { return true; } Map> lbsToReconfigure = new HashMap>(); // first set all existing lb mappings with Revoke state for (LoadBalancerVMMapVO map : maps) { long lbId = map.getLoadBalancerId(); List instances = lbsToReconfigure.get(lbId); if (instances == null) { instances = new ArrayList(); } instances.add(map.getInstanceId()); lbsToReconfigure.put(lbId, instances); map.setRevoke(true); _lb2VmMapDao.persist(map); s_logger.debug("Set load balancer rule for revoke: rule id " + map.getLoadBalancerId() + ", vmId " + instanceId); } // Reapply all lbs that had the vm assigned if (lbsToReconfigure != null) { for (Map.Entry> lb : lbsToReconfigure.entrySet()) { if (!removeFromLoadBalancerInternal(lb.getKey(), lb.getValue(), false)) { success = false; } } } return success; } @Override @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_DELETE, eventDescription = "deleting load balancer", async = true) public boolean deleteLoadBalancerRule(long loadBalancerId, boolean apply) { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); LoadBalancerVO rule = _lbDao.findById(loadBalancerId); if (rule == null) { throw new InvalidParameterValueException("Unable to find load balancer rule " + loadBalancerId); } _accountMgr.checkAccess(caller, null, true, rule); boolean result = deleteLoadBalancerRule(loadBalancerId, apply, caller, ctx.getCallerUserId(), true); if (!result) { throw new CloudRuntimeException("Unable to remove load balancer rule " + loadBalancerId); } return result; } @DB public boolean deleteLoadBalancerRule(long loadBalancerId, boolean apply, Account caller, long callerUserId, boolean rollBack) { LoadBalancerVO lb = _lbDao.findById(loadBalancerId); Transaction txn = Transaction.currentTxn(); boolean generateUsageEvent = false; boolean success = true; FirewallRule.State backupState = lb.getState(); txn.start(); if (lb.getState() == FirewallRule.State.Staged) { if (s_logger.isDebugEnabled()) { s_logger.debug("Found a rule that is still in stage state so just removing it: " + lb); } generateUsageEvent = true; } else if (lb.getState() == FirewallRule.State.Add || lb.getState() == FirewallRule.State.Active) { lb.setState(FirewallRule.State.Revoke); _lbDao.persist(lb); generateUsageEvent = true; } List backupMaps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); List maps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); if (maps != null) { for (LoadBalancerVMMapVO map : maps) { map.setRevoke(true); _lb2VmMapDao.persist(map); s_logger.debug("Set load balancer rule for revoke: rule id " + loadBalancerId + ", vmId " + map.getInstanceId()); } } if (generateUsageEvent) { // Generate usage event right after all rules were marked for revoke UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), 0, lb.getId(), null, LoadBalancingRule.class.getName(), lb.getUuid()); } txn.commit(); // gather external network usage stats for this lb rule NetworkVO network = _networkDao.findById(lb.getNetworkId()); if (network != null) { if (_networkModel.networkIsConfiguredForExternalNetworking(network.getDataCenterId(), network.getId())) { _externalLBUsageMgr.updateExternalLoadBalancerNetworkUsageStats(loadBalancerId); } } if (apply) { try { if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) { // Get the associated VmGroup AutoScaleVmGroupVO vmGroup = _autoScaleVmGroupDao.listByAll(loadBalancerId, null).get(0); if (!applyAutoScaleConfig(lb, vmGroup,vmGroup.getState())) { s_logger.warn("Unable to apply the autoscale config"); return false; } } else { if (!applyLoadBalancerConfig(loadBalancerId)) { s_logger.warn("Unable to apply the load balancer config"); return false; } } } catch (ResourceUnavailableException e) { if (rollBack && isRollBackAllowedForProvider(lb)) { if (backupMaps != null) { for (LoadBalancerVMMapVO map : backupMaps) { _lb2VmMapDao.persist(map); s_logger.debug("LB Rollback rule id: " + loadBalancerId + ", vmId " + map.getInstanceId()); } } lb.setState(backupState); _lbDao.persist(lb); s_logger.debug("LB Rollback rule id: " + loadBalancerId + " while deleting LB rule."); } else { s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); } return false; } } FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(lb.getId()); if (relatedRule != null) { s_logger.warn("Unable to remove firewall rule id=" + lb.getId() + " as it has related firewall rule id=" + relatedRule.getId() + "; leaving it in Revoke state"); success = false; } else { _firewallMgr.removeRule(lb); } // FIXME: breaking the dependency on ELB manager. This breaks functionality of ELB using virtual router // Bug CS-15411 opened to document this //_elbMgr.handleDeleteLoadBalancerRule(lb, callerUserId, caller); if (success) { s_logger.debug("Load balancer with id " + lb.getId() + " is removed successfully"); } return success; } @Override @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_CREATE, eventDescription = "creating load balancer") public LoadBalancer createLoadBalancerRule(CreateLoadBalancerRuleCmd lb, boolean openFirewall) throws NetworkRuleConflictException, InsufficientAddressCapacityException { Account lbOwner = _accountMgr.getAccount(lb.getEntityOwnerId()); int defPortStart = lb.getDefaultPortStart(); int defPortEnd = lb.getDefaultPortEnd(); if (!NetUtils.isValidPort(defPortEnd)) { throw new InvalidParameterValueException("privatePort is an invalid value: " + defPortEnd); } if (defPortStart > defPortEnd) { throw new InvalidParameterValueException("private port range is invalid: " + defPortStart + "-" + defPortEnd); } if ((lb.getAlgorithm() == null) || !NetUtils.isValidAlgorithm(lb.getAlgorithm())) { throw new InvalidParameterValueException("Invalid algorithm: " + lb.getAlgorithm()); } Long ipAddrId = lb.getSourceIpAddressId(); IPAddressVO ipVO = null; if (ipAddrId != null) { ipVO = _ipAddressDao.findById(ipAddrId); } Network network = _networkModel.getNetwork(lb.getNetworkId()); // FIXME: breaking the dependency on ELB manager. This breaks functionality of ELB using virtual router // Bug CS-15411 opened to document this //LoadBalancer result = _elbMgr.handleCreateLoadBalancerRule(lb, lbOwner, lb.getNetworkId()); LoadBalancer result = null; if (result == null) { IpAddress systemIp = null; NetworkOffering off = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); if (off.getElasticLb() && ipVO == null && network.getVpcId() == null) { systemIp = _networkMgr.assignSystemIp(lb.getNetworkId(), lbOwner, true, false); lb.setSourceIpAddressId(systemIp.getId()); ipVO = _ipAddressDao.findById(systemIp.getId()); } // Validate ip address if (ipVO == null) { throw new InvalidParameterValueException("Unable to create load balance rule; can't find/allocate source IP"); } else if (ipVO.isOneToOneNat()) { throw new NetworkRuleConflictException("Can't do load balance on ip address: " + ipVO.getAddress()); } boolean performedIpAssoc = false; try { if (ipVO.getAssociatedWithNetworkId() == null) { boolean assignToVpcNtwk = network.getVpcId() != null && ipVO.getVpcId() != null && ipVO.getVpcId().longValue() == network.getVpcId(); if (assignToVpcNtwk) { //set networkId just for verification purposes _networkModel.checkIpForService(ipVO, Service.Lb, lb.getNetworkId()); s_logger.debug("The ip is not associated with the VPC network id="+ lb.getNetworkId() + " so assigning"); ipVO = _networkMgr.associateIPToGuestNetwork(ipAddrId, lb.getNetworkId(), false); performedIpAssoc = true; } } else { _networkModel.checkIpForService(ipVO, Service.Lb, null); } if (ipVO.getAssociatedWithNetworkId() == null) { throw new InvalidParameterValueException("Ip address " + ipVO + " is not assigned to the network " + network); } if (lb.getSourceIpAddressId() == null) { throw new CloudRuntimeException("No ip address is defined to assign the LB to"); } result = createLoadBalancer(lb, openFirewall); } catch (Exception ex) { s_logger.warn("Failed to create load balancer due to ", ex); if (ex instanceof NetworkRuleConflictException) { throw (NetworkRuleConflictException) ex; } } finally { if (result == null && systemIp != null) { s_logger.debug("Releasing system IP address " + systemIp + " as corresponding lb rule failed to create"); _networkMgr.handleSystemIpRelease(systemIp); } // release ip address if ipassoc was perfored if (performedIpAssoc) { ipVO = _ipAddressDao.findById(ipVO.getId()); _vpcMgr.unassignIPFromVpcNetwork(ipVO.getId(), lb.getNetworkId()); } } } if (result == null) { throw new CloudRuntimeException("Failed to create load balancer rule: " + lb.getName()); } return result; } @Override @DB public LoadBalancer createLoadBalancer(CreateLoadBalancerRuleCmd lb, boolean openFirewall) throws NetworkRuleConflictException { UserContext caller = UserContext.current(); int srcPortStart = lb.getSourcePortStart(); int defPortStart = lb.getDefaultPortStart(); int srcPortEnd = lb.getSourcePortEnd(); long sourceIpId = lb.getSourceIpAddressId(); IPAddressVO ipAddr = _ipAddressDao.findById(sourceIpId); // make sure ip address exists if (ipAddr == null || !ipAddr.readyToUse()) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id specified"); ex.addProxyObject(ipAddr, sourceIpId, "sourceIpId"); throw ex; } else if (ipAddr.isOneToOneNat()) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule; specified sourceip id has static nat enabled"); ex.addProxyObject(ipAddr, sourceIpId, "sourceIpId"); throw ex; } Long networkId = ipAddr.getAssociatedWithNetworkId(); if (networkId == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule ; specified sourceip id is not associated with any network"); ex.addProxyObject(ipAddr, sourceIpId, "sourceIpId"); throw ex; } _firewallMgr.validateFirewallRule(caller.getCaller(), ipAddr, srcPortStart, srcPortEnd, lb.getProtocol(), Purpose.LoadBalancing, FirewallRuleType.User, networkId, null); NetworkVO network = _networkDao.findById(networkId); _accountMgr.checkAccess(caller.getCaller(), null, true, ipAddr); // verify that lb service is supported by the network if (!_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Lb)) { InvalidParameterValueException ex = new InvalidParameterValueException("LB service is not supported in specified network id"); ex.addProxyObject(network, networkId, "networkId"); throw ex; } Transaction txn = Transaction.currentTxn(); txn.start(); LoadBalancerVO newRule = new LoadBalancerVO(lb.getXid(), lb.getName(), lb.getDescription(), lb.getSourceIpAddressId(), lb.getSourcePortEnd(), lb.getDefaultPortStart(), lb.getAlgorithm(), network.getId(), ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId()); // verify rule is supported by Lb provider of the network LoadBalancingRule loadBalancing = new LoadBalancingRule(newRule, new ArrayList(), new ArrayList()); if (!validateRule(loadBalancing)) { throw new InvalidParameterValueException("LB service provider cannot support this rule"); } newRule = _lbDao.persist(newRule); if (openFirewall) { _firewallMgr.createRuleForAllCidrs(sourceIpId, caller.getCaller(), lb.getSourcePortStart(), lb.getSourcePortEnd(), lb.getProtocol(), null, null, newRule.getId(), networkId); } boolean success = true; try { _firewallMgr.detectRulesConflict(newRule); if (!_firewallDao.setStateToAdd(newRule)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRule); } s_logger.debug("Load balancer " + newRule.getId() + " for Ip address id=" + sourceIpId + ", public port " + srcPortStart + ", private port " + defPortStart + " is added successfully."); UserContext.current().setEventDetails("Load balancer Id: " + newRule.getId()); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(), ipAddr.getDataCenterId(), newRule.getId(), null, LoadBalancingRule.class.getName(), newRule.getUuid()); txn.commit(); return newRule; } catch (Exception e) { success = false; if (e instanceof NetworkRuleConflictException) { throw (NetworkRuleConflictException) e; } throw new CloudRuntimeException("Unable to add rule for ip address id=" + newRule.getSourceIpAddressId(), e); } finally { if (!success && newRule != null) { txn.start(); _firewallMgr.revokeRelatedFirewallRule(newRule.getId(), false); removeLBRule(newRule); txn.commit(); } } } @Override public boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException { LoadBalancerVO lb = _lbDao.findById(lbRuleId); List lbs; if (isRollBackAllowedForProvider(lb)) { // this is for Netscalar type of devices. if their is failure the db entries will be rollbacked. lbs = Arrays.asList(lb); } else { // get all rules in transition state lbs = _lbDao.listInTransitionStateByNetworkId(lb.getNetworkId()); } return applyLoadBalancerRules(lbs, true); } @Override public boolean applyLoadBalancersForNetwork(long networkId) throws ResourceUnavailableException { List lbs = _lbDao.listByNetworkId(networkId); if (lbs != null) { return applyLoadBalancerRules(lbs, true); } else { s_logger.info("Network id=" + networkId + " doesn't have load balancer rules, nothing to apply"); return true; } } @Override public boolean applyRules(Network network, Purpose purpose, List rules) throws ResourceUnavailableException { assert(purpose == Purpose.LoadBalancing): "LB Manager asked to handle non-LB rules"; boolean handled = false; for (LoadBalancingServiceProvider lbElement: _lbProviders) { handled = lbElement.applyLBRules(network, (List) rules); if (handled) break; } return handled; } @DB protected boolean applyLoadBalancerRules(List lbs, boolean updateRulesInDB) throws ResourceUnavailableException { Transaction txn = Transaction.currentTxn(); List rules = new ArrayList(); for (LoadBalancerVO lb : lbs) { List dstList = getExistingDestinations(lb.getId()); List policyList = getStickinessPolicies(lb.getId()); LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList); rules.add(loadBalancing); } if (!_networkMgr.applyRules(rules, FirewallRule.Purpose.LoadBalancing, this, false)) { s_logger.debug("LB rules are not completely applied"); return false; } if (updateRulesInDB) { for (LoadBalancerVO lb : lbs) { boolean checkForReleaseElasticIp = false; txn.start(); if (lb.getState() == FirewallRule.State.Revoke) { removeLBRule(lb); s_logger.debug("LB " + lb.getId() + " is successfully removed"); checkForReleaseElasticIp = true; } else if (lb.getState() == FirewallRule.State.Add) { lb.setState(FirewallRule.State.Active); s_logger.debug("LB rule " + lb.getId() + " state is set to Active"); _lbDao.persist(lb); } // remove LB-Vm mappings that were state to revoke List lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lb.getId(), true); List instanceIds = new ArrayList(); for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { instanceIds.add(lbVmMap.getInstanceId()); } if (!instanceIds.isEmpty()) { _lb2VmMapDao.remove(lb.getId(), instanceIds, null); s_logger.debug("Load balancer rule id " + lb.getId() + " is removed for vms " + instanceIds); } if (_lb2VmMapDao.listByLoadBalancerId(lb.getId()).isEmpty()) { lb.setState(FirewallRule.State.Add); _lbDao.persist(lb); s_logger.debug("LB rule " + lb.getId() + " state is set to Add as there are no more active LB-VM mappings"); } // remove LB-Stickiness policy mapping that were state to revoke List stickinesspolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lb.getId(), true); if (!stickinesspolicies.isEmpty()) { _lb2stickinesspoliciesDao.remove(lb.getId(), true); s_logger.debug("Load balancer rule id " + lb.getId() + " is removed stickiness policies"); } txn.commit(); if (checkForReleaseElasticIp) { boolean success = true; long count = _firewallDao.countRulesByIpId(lb.getSourceIpAddressId()); if (count == 0) { try { success = handleSystemLBIpRelease(lb); } catch (Exception ex) { s_logger.warn("Failed to release system ip as a part of lb rule " + lb + " deletion due to exception ", ex); success = false; } finally { if (!success) { s_logger.warn("Failed to release system ip as a part of lb rule " + lb + " deletion"); } } } } // if the rule is the last one for the ip address assigned to VPC, unassign it from the network IpAddress ip = _ipAddressDao.findById(lb.getSourceIpAddressId()); _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), lb.getNetworkId()); } } return true; } protected boolean handleSystemLBIpRelease(LoadBalancerVO lb) { IpAddress ip = _ipAddressDao.findById(lb.getSourceIpAddressId()); boolean success = true; if (ip.getSystem()) { s_logger.debug("Releasing system ip address " + lb.getSourceIpAddressId() + " as a part of delete lb rule"); if (!_networkMgr.disassociatePublicIpAddress(lb.getSourceIpAddressId(), UserContext.current().getCallerUserId(), UserContext.current().getCaller())) { s_logger.warn("Unable to release system ip address id=" + lb.getSourceIpAddressId() + " as a part of delete lb rule"); success = false; } else { s_logger.warn("Successfully released system ip address id=" + lb.getSourceIpAddressId() + " as a part of delete lb rule"); } } return success; } @Override public boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId) { List rules = _firewallDao.listByIpAndPurposeAndNotRevoked(ipId, Purpose.LoadBalancing); if (rules != null) s_logger.debug("Found " + rules.size() + " lb rules to cleanup"); for (FirewallRule rule : rules) { boolean result = deleteLoadBalancerRule(rule.getId(), true, caller, callerUserId, false); if (result == false) { s_logger.warn("Unable to remove load balancer rule " + rule.getId()); return false; } } return true; } @Override public boolean removeAllLoadBalanacersForNetwork(long networkId, Account caller, long callerUserId) { List rules = _firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.LoadBalancing); if (rules != null) s_logger.debug("Found " + rules.size() + " lb rules to cleanup"); for (FirewallRule rule : rules) { boolean result = deleteLoadBalancerRule(rule.getId(), true, caller, callerUserId, false); if (result == false) { s_logger.warn("Unable to remove load balancer rule " + rule.getId()); return false; } } return true; } @Override public List getStickinessPolicies(long lbId) { List stickinessPolicies = new ArrayList(); List sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lbId); for (LBStickinessPolicyVO sDbPolicy : sDbpolicies) { LbStickinessPolicy sPolicy = new LbStickinessPolicy(sDbPolicy.getMethodName(), sDbPolicy.getParams(), sDbPolicy.isRevoke()); stickinessPolicies.add(sPolicy); } return stickinessPolicies; } @Override public List getExistingDestinations(long lbId) { List dstList = new ArrayList(); List lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lbId); LoadBalancerVO lb = _lbDao.findById(lbId); String dstIp = null; for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) { UserVm vm = _vmDao.findById(lbVmMap.getInstanceId()); Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vm.getId()); dstIp = nic.getIp4Address(); LbDestination lbDst = new LbDestination(lb.getDefaultPortStart(), lb.getDefaultPortEnd(), dstIp, lbVmMap.isRevoke()); dstList.add(lbDst); } return dstList; } @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; return true; } @Override public boolean start() { return true; } @Override public boolean stop() { return true; } @Override public String getName() { return _name; } @Override @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_UPDATE, eventDescription = "updating load balancer", async = true) public LoadBalancer updateLoadBalancerRule(UpdateLoadBalancerRuleCmd cmd) { Account caller = UserContext.current().getCaller(); Long lbRuleId = cmd.getId(); String name = cmd.getLoadBalancerName(); String description = cmd.getDescription(); String algorithm = cmd.getAlgorithm(); LoadBalancerVO lb = _lbDao.findById(lbRuleId); LoadBalancerVO lbBackup = _lbDao.findById(lbRuleId); if (lb == null) { throw new InvalidParameterValueException("Unable to find lb rule by id=" + lbRuleId); } // check permissions _accountMgr.checkAccess(caller, null, true, lb); if (name != null) { lb.setName(name); } if (description != null) { lb.setDescription(description); } if (algorithm != null) { lb.setAlgorithm(algorithm); } boolean success = _lbDao.update(lbRuleId, lb); // If algorithm is changed, have to reapply the lb config if (algorithm != null) { try { lb.setState(FirewallRule.State.Add); _lbDao.persist(lb); applyLoadBalancerConfig(lbRuleId); } catch (ResourceUnavailableException e) { if (isRollBackAllowedForProvider(lb)) { /* NOTE : We use lb object to update db instead of lbBackup object since db layer will fail to update if there is no change in the object. */ if (lbBackup.getName() != null) { lb.setName(lbBackup.getName()); } if (lbBackup.getDescription() != null) { lb.setDescription(lbBackup.getDescription()); } if (lbBackup.getAlgorithm() != null) { lb.setAlgorithm(lbBackup.getAlgorithm()); } lb.setState(lbBackup.getState()); _lbDao.update(lb.getId(), lb); _lbDao.persist(lb); s_logger.debug("LB Rollback rule id: " + lbRuleId + " while updating LB rule."); } s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); success = false; } } if (!success) { throw new CloudRuntimeException("Failed to update load balancer rule: " + lbRuleId); } return lb; } @Override public List listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException { Account caller = UserContext.current().getCaller(); Long loadBalancerId = cmd.getId(); Boolean applied = cmd.isApplied(); if (applied == null) { applied = Boolean.TRUE; } LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); if (loadBalancer == null) { return null; } _accountMgr.checkAccess(caller, null, true, loadBalancer); List loadBalancerInstances = new ArrayList(); List vmLoadBalancerMappings = null; vmLoadBalancerMappings = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId); List appliedInstanceIdList = new ArrayList(); if ((vmLoadBalancerMappings != null) && !vmLoadBalancerMappings.isEmpty()) { for (LoadBalancerVMMapVO vmLoadBalancerMapping : vmLoadBalancerMappings) { appliedInstanceIdList.add(vmLoadBalancerMapping.getInstanceId()); } } IPAddressVO addr = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId()); List userVms = _vmDao.listVirtualNetworkInstancesByAcctAndZone(loadBalancer.getAccountId(), addr.getDataCenterId(), loadBalancer.getNetworkId()); for (UserVmVO userVm : userVms) { // if the VM is destroyed, being expunged, in an error state, or in an unknown state, skip it switch (userVm.getState()) { case Destroyed: case Expunging: case Error: case Unknown: continue; } boolean isApplied = appliedInstanceIdList.contains(userVm.getId()); if ((isApplied && applied) || (!isApplied && !applied)) { loadBalancerInstances.add(userVm); } } return loadBalancerInstances; } @Override public List getStickinessMethods(long networkid) { String capability = getLBCapability(networkid, Capability.SupportedStickinessMethods.getName()); if (capability == null) { return null; } Gson gson = new Gson(); java.lang.reflect.Type listType = new TypeToken>() { }.getType(); List result = gson.fromJson(capability, listType); return result; } @Override public List searchForLBStickinessPolicies(ListLBStickinessPoliciesCmd cmd) throws PermissionDeniedException { Account caller = UserContext.current().getCaller(); Long loadBalancerId = cmd.getLbRuleId(); LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId); if (loadBalancer == null) { return null; } _accountMgr.checkAccess(caller, null, true, loadBalancer); List sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId()); return sDbpolicies; } @Override public Pair, Integer> searchForLoadBalancers(ListLoadBalancerRulesCmd cmd) { Long ipId = cmd.getPublicIpId(); Long zoneId = cmd.getZoneId(); Long id = cmd.getId(); String name = cmd.getLoadBalancerRuleName(); String keyword = cmd.getKeyword(); Long instanceId = cmd.getVirtualMachineId(); Map tags = cmd.getTags(); Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); Boolean isRecursive = domainIdRecursiveListProject.second(); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); Filter searchFilter = new Filter(LoadBalancerVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); SearchBuilder sb = _lbDao.createSearchBuilder(); _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); sb.and("sourceIpAddress", sb.entity().getSourceIpAddressId(), SearchCriteria.Op.EQ); if (instanceId != null) { SearchBuilder lbVMSearch = _lb2VmMapDao.createSearchBuilder(); lbVMSearch.and("instanceId", lbVMSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); sb.join("lbVMSearch", lbVMSearch, sb.entity().getId(), lbVMSearch.entity().getLoadBalancerId(), JoinBuilder.JoinType.INNER); } if (zoneId != null) { SearchBuilder ipSearch = _ipAddressDao.createSearchBuilder(); ipSearch.and("zoneId", ipSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); sb.join("ipSearch", ipSearch, sb.entity().getSourceIpAddressId(), ipSearch.entity().getId(), JoinBuilder.JoinType.INNER); } if (tags != null && !tags.isEmpty()) { SearchBuilder tagSearch = _resourceTagDao.createSearchBuilder(); for (int count = 0; count < tags.size(); count++) { tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ); tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ); tagSearch.cp(); } tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); sb.groupBy(sb.entity().getId()); sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); } SearchCriteria sc = sb.create(); _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); if (keyword != null) { SearchCriteria ssc = _lbDao.createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } if (name != null) { sc.setParameters("name", "%" + name + "%"); } if (id != null) { sc.setParameters("id", id); } if (ipId != null) { sc.setParameters("sourceIpAddress", ipId); } if (instanceId != null) { sc.setJoinParameters("lbVMSearch", "instanceId", instanceId); } if (zoneId != null) { sc.setJoinParameters("ipSearch", "zoneId", zoneId); } if (tags != null && !tags.isEmpty()) { int count = 0; sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.LoadBalancer.toString()); for (String key : tags.keySet()) { sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key); sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key)); count++; } } Pair, Integer> result = _lbDao.searchAndCount(sc, searchFilter); return new Pair, Integer>(result.first(), result.second()); } @Override public List listByNetworkId(long networkId) { List lbs = _lbDao.listByNetworkId(networkId); List lbRules = new ArrayList(); for (LoadBalancerVO lb : lbs) { List dstList = getExistingDestinations(lb.getId()); List policyList = this.getStickinessPolicies(lb.getId()); LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList); lbRules.add(loadBalancing); } return lbRules; } @Override public LoadBalancerVO findById(long lbId) { return _lbDao.findById(lbId); } protected void removeLBRule(LoadBalancerVO rule) { //remove the rule _lbDao.remove(rule.getId()); } }