mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-03 04:12:31 +01:00
Summary of changes: Database changes will be rollbacked while applying the LB rule to the Netscaler device.
- Database changes will be rollbacked to previous state during the following Lb API's:
1) assignVM to LB rule
2) remove VM from LB rule
3) updateLb rule
4) deleteLb rule
5) create/attach sticky policy to Lb rule
6) delete sticky policy from Lb rule
- Database changes of the Lb rule will be not be rolledback during:
1) Removing IP
2) removing VM
1259 lines
55 KiB
Java
Executable File
1259 lines
55 KiB
Java
Executable File
/**
|
|
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
|
|
*
|
|
* This software is licensed under the GNU General Public License v3 or later.
|
|
*
|
|
* It is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or any later version.
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
package com.cloud.network.lb;
|
|
|
|
import java.security.InvalidParameterException;
|
|
import java.util.ArrayList;
|
|
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 org.apache.log4j.Logger;
|
|
|
|
import com.cloud.api.commands.CreateLBStickinessPolicyCmd;
|
|
import com.cloud.api.commands.CreateLoadBalancerRuleCmd;
|
|
import com.cloud.api.commands.ListLBStickinessPoliciesCmd;
|
|
import com.cloud.api.commands.ListLoadBalancerRuleInstancesCmd;
|
|
import com.cloud.api.commands.ListLoadBalancerRulesCmd;
|
|
import com.cloud.api.commands.UpdateLoadBalancerRuleCmd;
|
|
import com.cloud.api.response.ServiceResponse;
|
|
import com.cloud.configuration.ConfigurationManager;
|
|
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.UsageEventVO;
|
|
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.ExternalLoadBalancerDeviceManager;
|
|
import com.cloud.network.dao.NetworkServiceMapDao;
|
|
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.NetworkVO;
|
|
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.element.NetworkElement;
|
|
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.offering.NetworkOffering;
|
|
import com.cloud.projects.Project.ListProjectResourcesCriteria;
|
|
import com.cloud.user.Account;
|
|
import com.cloud.user.AccountManager;
|
|
import com.cloud.user.DomainService;
|
|
import com.cloud.user.UserContext;
|
|
import com.cloud.user.dao.AccountDao;
|
|
import com.cloud.uservm.UserVm;
|
|
import com.cloud.utils.Ternary;
|
|
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<Type> implements LoadBalancingRulesManager, LoadBalancingRulesService, Manager {
|
|
private static final Logger s_logger = Logger.getLogger(LoadBalancingRulesManagerImpl.class);
|
|
|
|
String _name;
|
|
|
|
@Inject
|
|
NetworkManager _networkMgr;
|
|
@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
|
|
ElasticLoadBalancerManager _elbMgr;
|
|
@Inject
|
|
NetworkDao _networkDao;
|
|
@Inject
|
|
FirewallRulesDao _firewallDao;
|
|
@Inject
|
|
DomainService _domainMgr;
|
|
@Inject
|
|
ConfigurationManager _configMgr;
|
|
@Inject
|
|
ExternalLoadBalancerDeviceManager _externalLBMgr;
|
|
@Inject
|
|
NetworkServiceMapDao _ntwkSrvcDao;
|
|
|
|
private String getLBStickinessCapability(long networkid) {
|
|
Map<Service, Map<Capability, String>> serviceCapabilitiesMap = _networkMgr.getNetworkCapabilities(networkid);
|
|
if (serviceCapabilitiesMap != null) {
|
|
for (Service service : serviceCapabilitiesMap.keySet()) {
|
|
ServiceResponse serviceResponse = new ServiceResponse();
|
|
serviceResponse.setName(service.getName());
|
|
if ("Lb".equalsIgnoreCase(service.getName())) {
|
|
Map<Capability, String> serviceCapabilities = serviceCapabilitiesMap
|
|
.get(service);
|
|
if (serviceCapabilities != null) {
|
|
for (Capability capability : serviceCapabilities
|
|
.keySet()) {
|
|
if (Capability.SupportedStickinessMethods.getName()
|
|
.equals(capability.getName())) {
|
|
return serviceCapabilities.get(capability);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private boolean genericValidator(CreateLBStickinessPolicyCmd cmd) throws InvalidParameterValueException {
|
|
LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId());
|
|
/* Validation : check for valid Method name and params */
|
|
List<LbStickinessMethod> 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<LbStickinessMethodParam> methodParamList = method.getParamList();
|
|
Map<String, String> tempParamList = new HashMap<String, String>();
|
|
|
|
/*
|
|
* 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<String, String> 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<LBStickinessPolicyVO> 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<LbStickinessPolicy> policyList = new ArrayList<LbStickinessPolicy>();
|
|
policyList.add(new LbStickinessPolicy(cmd.getStickinessMethodName(), lbpolicy.getParams()));
|
|
LoadBalancingRule lbRule = new LoadBalancingRule(loadBalancer, getExistingDestinations(lbpolicy.getId()), policyList);
|
|
if (!_networkMgr.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;
|
|
}
|
|
|
|
@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());
|
|
Provider provider = Network.Provider.Netscaler;
|
|
return _ntwkSrvcDao.canProviderSupportServiceInNetwork(network.getId(), Service.Lb, provider);
|
|
}
|
|
@Override
|
|
@DB
|
|
@ActionEvent(eventType = EventTypes.EVENT_ASSIGN_TO_LOAD_BALANCER_RULE, eventDescription = "assigning to load balancer", async = true)
|
|
public boolean assignToLoadBalancer(long loadBalancerId, List<Long> 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<LoadBalancerVMMapVO> mappedInstances = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId, false);
|
|
Set<Long> mappedInstanceIds = new HashSet<Long>();
|
|
for (LoadBalancerVMMapVO mappedInstance : mappedInstances) {
|
|
mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId()));
|
|
}
|
|
|
|
List<UserVm> vmsToAdd = new ArrayList<UserVm>();
|
|
|
|
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) {
|
|
throw new InvalidParameterValueException("Invalid instance id: " + instanceId);
|
|
}
|
|
|
|
_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 = _networkMgr.getNics(vm.getId());
|
|
Nic nicInSameNetwork = null;
|
|
for (Nic nic : nics) {
|
|
if (nic.getNetworkId() == loadBalancer.getNetworkId()) {
|
|
nicInSameNetwork = nic;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nicInSameNetwork == null) {
|
|
throw new InvalidParameterValueException("VM " + instanceId + " cannot be added because it doesn't belong in the same network.");
|
|
}
|
|
|
|
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();
|
|
|
|
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<Long> vmInstanceIds = new ArrayList<Long>();
|
|
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){
|
|
throw new CloudRuntimeException("Failed to add load balancer rule id " + loadBalancerId + " for vms " + instanceIds);
|
|
}
|
|
|
|
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<Long> instanceIds) {
|
|
return removeFromLoadBalancerInternal(loadBalancerId, instanceIds, true);
|
|
}
|
|
|
|
private boolean removeFromLoadBalancerInternal(long loadBalancerId, List<Long> 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 (!applyLoadBalancerConfig(loadBalancerId)) {
|
|
s_logger.warn("Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds);
|
|
throw new CloudRuntimeException("Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds);
|
|
}
|
|
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){
|
|
throw new CloudRuntimeException("Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
@Override
|
|
public boolean removeVmFromLoadBalancers(long instanceId) {
|
|
boolean success = true;
|
|
List<LoadBalancerVMMapVO> maps = _lb2VmMapDao.listByInstanceId(instanceId);
|
|
if (maps == null || maps.isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
Map<Long, List<Long>> lbsToReconfigure = new HashMap<Long, List<Long>>();
|
|
|
|
// first set all existing lb mappings with Revoke state
|
|
for (LoadBalancerVMMapVO map : maps) {
|
|
long lbId = map.getLoadBalancerId();
|
|
List<Long> instances = lbsToReconfigure.get(lbId);
|
|
if (instances == null) {
|
|
instances = new ArrayList<Long>();
|
|
}
|
|
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<Long, List<Long>> 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<LoadBalancerVMMapVO> backupMaps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId);
|
|
List<LoadBalancerVMMapVO> 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
|
|
UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), 0, lb.getId(), null);
|
|
_usageEventDao.persist(usageEvent);
|
|
}
|
|
|
|
txn.commit();
|
|
|
|
// gather external network usage stats for this lb rule
|
|
NetworkVO network = _networkDao.findById(lb.getNetworkId());
|
|
if (network != null) {
|
|
if (_networkMgr.networkIsConfiguredForExternalNetworking(network.getDataCenterId(), network.getId())) {
|
|
_externalLBMgr.updateExternalLoadBalancerNetworkUsageStats(loadBalancerId);
|
|
}
|
|
}
|
|
|
|
if (apply) {
|
|
try {
|
|
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 {
|
|
_firewallDao.remove(lb.getId());
|
|
}
|
|
|
|
_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 ipAddressVo = null;
|
|
if (ipAddrId != null) {
|
|
ipAddressVo = _ipAddressDao.findById(ipAddrId);
|
|
|
|
// Validate ip address
|
|
if (ipAddressVo == null) {
|
|
throw new InvalidParameterValueException("Unable to create load balance rule; ip id=" + ipAddrId + " doesn't exist in the system");
|
|
} else if (ipAddressVo.isOneToOneNat()) {
|
|
throw new NetworkRuleConflictException("Can't do load balance on ip address: " + ipAddressVo.getAddress());
|
|
}
|
|
|
|
_networkMgr.checkIpForService(ipAddressVo, Service.Lb);
|
|
}
|
|
|
|
LoadBalancer result = _elbMgr.handleCreateLoadBalancerRule(lb, lbOwner, lb.getNetworkId());
|
|
if (result == null) {
|
|
IpAddress ip = null;
|
|
Network guestNetwork = _networkMgr.getNetwork(lb.getNetworkId());
|
|
NetworkOffering off = _configMgr.getNetworkOffering(guestNetwork.getNetworkOfferingId());
|
|
if (off.getElasticLb() && ipAddressVo == null) {
|
|
ip = _networkMgr.assignElasticIp(lb.getNetworkId(), lbOwner, true, false);
|
|
lb.setSourceIpAddressId(ip.getId());
|
|
}
|
|
try {
|
|
result = createLoadBalancer(lb, openFirewall);
|
|
} catch (Exception ex) {
|
|
s_logger.warn("Failed to create load balancer due to ", ex);
|
|
} finally {
|
|
if (result == null && ip != null) {
|
|
s_logger.debug("Releasing elastic IP address " + ip + " as corresponding lb rule failed to create");
|
|
_networkMgr.handleElasticIpRelease(ip);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result == null) {
|
|
throw new CloudRuntimeException("Failed to create load balancer rule: " + lb.getName());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@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);
|
|
Long networkId = ipAddr.getSourceNetworkId();
|
|
// make sure ip address exists
|
|
if (ipAddr == null || !ipAddr.readyToUse()) {
|
|
throw new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id" + sourceIpId);
|
|
} else if (ipAddr.isOneToOneNat()) {
|
|
throw new InvalidParameterValueException("Unable to create load balancer rule; ip id=" + sourceIpId + " has static nat enabled");
|
|
}
|
|
|
|
_firewallMgr.validateFirewallRule(caller.getCaller(), ipAddr, srcPortStart, srcPortEnd, lb.getProtocol(), Purpose.LoadBalancing, FirewallRuleType.User);
|
|
|
|
networkId = ipAddr.getAssociatedWithNetworkId();
|
|
if (networkId == null) {
|
|
throw new InvalidParameterValueException("Unable to create load balancer rule ; ip id=" + sourceIpId + " is not associated with any network");
|
|
|
|
}
|
|
NetworkVO network = _networkDao.findById(networkId);
|
|
|
|
_accountMgr.checkAccess(caller.getCaller(), null, true, ipAddr);
|
|
|
|
// verify that lb service is supported by the network
|
|
if (!_networkMgr.areServicesSupportedInNetwork(network.getId(), Service.Lb)) {
|
|
throw new InvalidParameterValueException("LB service is not supported in network id= " + networkId);
|
|
|
|
}
|
|
|
|
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());
|
|
|
|
newRule = _lbDao.persist(newRule);
|
|
|
|
if (openFirewall) {
|
|
_firewallMgr.createRuleForAllCidrs(sourceIpId, caller.getCaller(), lb.getSourcePortStart(), lb.getSourcePortEnd(), lb.getProtocol(), null, null, newRule.getId());
|
|
}
|
|
|
|
boolean success = true;
|
|
|
|
try {
|
|
_firewallMgr.detectRulesConflict(newRule, ipAddr);
|
|
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());
|
|
UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(), ipAddr.getDataCenterId(), newRule.getId(), null);
|
|
_usageEventDao.persist(usageEvent);
|
|
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);
|
|
_lbDao.remove(newRule.getId());
|
|
|
|
txn.commit();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException {
|
|
LoadBalancerVO lb = _lbDao.findById(lbRuleId);
|
|
// get all rules in transition state
|
|
List<LoadBalancerVO> lbs = _lbDao.listInTransitionStateByNetworkId(lb.getNetworkId());
|
|
return applyLoadBalancerRules(lbs, true);
|
|
}
|
|
|
|
@Override
|
|
public boolean applyLoadBalancersForNetwork(long networkId) throws ResourceUnavailableException {
|
|
List<LoadBalancerVO> 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;
|
|
}
|
|
}
|
|
|
|
@DB
|
|
protected boolean applyLoadBalancerRules(List<LoadBalancerVO> lbs, boolean updateRulesInDB) throws ResourceUnavailableException {
|
|
Transaction txn = Transaction.currentTxn();
|
|
List<LoadBalancingRule> rules = new ArrayList<LoadBalancingRule>();
|
|
for (LoadBalancerVO lb : lbs) {
|
|
List<LbDestination> dstList = getExistingDestinations(lb.getId());
|
|
List<LbStickinessPolicy> policyList = getStickinessPolicies(lb.getId());
|
|
|
|
LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList);
|
|
rules.add(loadBalancing);
|
|
}
|
|
|
|
if (!_networkMgr.applyRules(rules, 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) {
|
|
_lbDao.remove(lb.getId());
|
|
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<LoadBalancerVMMapVO> lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lb.getId(), true);
|
|
List<Long> instanceIds = new ArrayList<Long>();
|
|
|
|
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<LBStickinessPolicyVO> 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 = handleElasticLBIpRelease(lb);
|
|
} catch (Exception ex) {
|
|
s_logger.warn("Failed to release elastic ip as a part of lb rule " + lb + " deletion due to exception ", ex);
|
|
success = false;
|
|
} finally {
|
|
if (!success) {
|
|
s_logger.warn("Failed to release elastic ip as a part of lb rule " + lb + " deletion");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected boolean handleElasticLBIpRelease(LoadBalancerVO lb) {
|
|
IpAddress ip = _ipAddressDao.findById(lb.getSourceIpAddressId());
|
|
boolean success = true;
|
|
if (ip.getElastic()) {
|
|
s_logger.debug("Releasing elastic ip address " + lb.getSourceIpAddressId() + " as a part of delete lb rule");
|
|
if (!_networkMgr.releasePublicIpAddress(lb.getSourceIpAddressId(), UserContext.current().getCallerUserId(), UserContext.current().getCaller())) {
|
|
s_logger.warn("Unable to release elastic ip address id=" + lb.getSourceIpAddressId() + " as a part of delete lb rule");
|
|
success = false;
|
|
} else {
|
|
s_logger.warn("Successfully released elastic 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<FirewallRuleVO> 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<FirewallRuleVO> 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<LbStickinessPolicy> getStickinessPolicies(long lbId) {
|
|
List<LbStickinessPolicy> stickinessPolicies = new ArrayList<LbStickinessPolicy>();
|
|
List<LBStickinessPolicyVO> 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<LbDestination> getExistingDestinations(long lbId) {
|
|
List<LbDestination> dstList = new ArrayList<LbDestination>();
|
|
List<LoadBalancerVMMapVO> 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<String, Object> 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<UserVmVO> 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<UserVmVO> loadBalancerInstances = new ArrayList<UserVmVO>();
|
|
List<LoadBalancerVMMapVO> vmLoadBalancerMappings = null;
|
|
|
|
vmLoadBalancerMappings = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId);
|
|
|
|
List<Long> appliedInstanceIdList = new ArrayList<Long>();
|
|
if ((vmLoadBalancerMappings != null) && !vmLoadBalancerMappings.isEmpty()) {
|
|
for (LoadBalancerVMMapVO vmLoadBalancerMapping : vmLoadBalancerMappings) {
|
|
appliedInstanceIdList.add(vmLoadBalancerMapping.getInstanceId());
|
|
}
|
|
}
|
|
|
|
IPAddressVO addr = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
|
|
List<UserVmVO> 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<LbStickinessMethod> getStickinessMethods(long networkid)
|
|
{
|
|
String capability = getLBStickinessCapability(networkid);
|
|
if (capability == null)
|
|
return null;
|
|
Gson gson = new Gson();
|
|
java.lang.reflect.Type listType = new TypeToken<List<LbStickinessMethod>>() {
|
|
}.getType();
|
|
List<LbStickinessMethod> result = gson.fromJson(capability, listType);
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public List<LBStickinessPolicyVO> 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<LBStickinessPolicyVO> sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId());
|
|
|
|
return sDbpolicies;
|
|
}
|
|
|
|
@Override
|
|
public List<LoadBalancerVO> 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();
|
|
|
|
Account caller = UserContext.current().getCaller();
|
|
List<Long> permittedAccounts = new ArrayList<Long>();
|
|
|
|
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
|
|
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll());
|
|
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<LoadBalancerVO> 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<LoadBalancerVMMapVO> 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<IPAddressVO> 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);
|
|
}
|
|
|
|
SearchCriteria<LoadBalancerVO> sc = sb.create();
|
|
_accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
|
|
|
|
if (keyword != null) {
|
|
SearchCriteria<LoadBalancerVO> 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);
|
|
}
|
|
|
|
return _lbDao.search(sc, searchFilter);
|
|
}
|
|
|
|
@Override
|
|
public List<LoadBalancingRule> listByNetworkId(long networkId) {
|
|
List<LoadBalancerVO> lbs = _lbDao.listByNetworkId(networkId);
|
|
List<LoadBalancingRule> lbRules = new ArrayList<LoadBalancingRule>();
|
|
for (LoadBalancerVO lb : lbs) {
|
|
List<LbDestination> dstList = getExistingDestinations(lb.getId());
|
|
List<LbStickinessPolicy> 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);
|
|
}
|
|
|
|
}
|