// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by 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. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.network.lb; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager.OnError; import com.cloud.agent.api.Answer; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.check.CheckSshAnswer; import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.agent.manager.Commands; import com.cloud.api.commands.CreateLoadBalancerRuleCmd; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.ElasticLbVmMapVO; import com.cloud.network.IPAddressVO; import com.cloud.network.LoadBalancerVO; import com.cloud.network.Network; 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.Networks.TrafficType; import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.network.VirtualRouterProvider; import com.cloud.network.VirtualRouterProvider.VirtualRouterProviderType; import com.cloud.network.addr.PublicIp; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.VirtualRouterProviderDao; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; import com.cloud.network.lb.dao.ElasticLbVmMapDao; import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualRouter; import com.cloud.network.router.VirtualRouter.RedundantState; import com.cloud.network.router.VirtualRouter.Role; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.LoadBalancer; import com.cloud.offering.ServiceOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; 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.vm.DomainRouterVO; import com.cloud.vm.NicProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineGuru; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineName; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile.Param; import com.cloud.vm.dao.DomainRouterDao; @Local(value = { ElasticLoadBalancerManager.class }) public class ElasticLoadBalancerManagerImpl implements ElasticLoadBalancerManager, Manager, VirtualMachineGuru<DomainRouterVO> { private static final Logger s_logger = Logger .getLogger(ElasticLoadBalancerManagerImpl.class); @Inject IPAddressDao _ipAddressDao; @Inject AgentManager _agentMgr; @Inject NetworkManager _networkMgr; @Inject LoadBalancerDao _loadBalancerDao = null; @Inject LoadBalancingRulesManager _lbMgr; @Inject VirtualNetworkApplianceManager _routerMgr; @Inject DomainRouterDao _routerDao = null; @Inject protected HostPodDao _podDao = null; @Inject protected ClusterDao _clusterDao; @Inject DataCenterDao _dcDao = null; @Inject protected NetworkDao _networkDao; @Inject protected NetworkOfferingDao _networkOfferingDao; @Inject VMTemplateDao _templateDao = null; @Inject VirtualMachineManager _itMgr; @Inject ConfigurationDao _configDao; @Inject ServiceOfferingDao _serviceOfferingDao = null; @Inject AccountService _accountService; @Inject LoadBalancerDao _lbDao; @Inject VlanDao _vlanDao; @Inject PodVlanMapDao _podVlanMapDao; @Inject ElasticLbVmMapDao _elbVmMapDao; @Inject NetworkDao _networksDao; @Inject AccountDao _accountDao; @Inject PhysicalNetworkServiceProviderDao _physicalProviderDao; @Inject VirtualRouterProviderDao _vrProviderDao; String _name; String _instance; static final private String _elbVmNamePrefix = "l"; static final private String _systemVmType = "elbvm"; boolean _enabled; TrafficType _frontendTrafficType = TrafficType.Guest; Account _systemAcct; ServiceOfferingVO _elasticLbVmOffering; ScheduledExecutorService _gcThreadPool; String _mgmtCidr; String _mgmtHost; Set<Long> _gcCandidateElbVmIds = Collections.newSetFromMap(new ConcurrentHashMap<Long,Boolean>()); int _elasticLbVmRamSize; int _elasticLbvmCpuMHz; int _elasticLbvmNumCpu; private Long getPodIdForDirectIp(IPAddressVO ipAddr) { PodVlanMapVO podVlanMaps = _podVlanMapDao.listPodVlanMapsByVlan(ipAddr.getVlanId()); if (podVlanMaps == null) { return null; } else { return podVlanMaps.getPodId(); } } public DomainRouterVO deployLoadBalancerVM(Long networkId, IPAddressVO ipAddr, Long accountId) { NetworkVO network = _networkDao.findById(networkId); DataCenter dc = _dcDao.findById(network.getDataCenterId()); Long podId = getPodIdForDirectIp(ipAddr); Pod pod = podId == null?null:_podDao.findById(podId); Map<VirtualMachineProfile.Param, Object> params = new HashMap<VirtualMachineProfile.Param, Object>( 1); params.put(VirtualMachineProfile.Param.ReProgramNetwork, true); Account owner = _accountService.getActiveAccountByName("system", new Long(1)); DeployDestination dest = new DeployDestination(dc, pod, null, null); s_logger.debug("About to deploy ELB vm "); try { DomainRouterVO elbVm = deployELBVm(network, dest, owner, params); if (elbVm == null) { throw new InvalidParameterValueException("Could not deploy or find existing ELB VM"); } s_logger.debug("Deployed ELB vm = " + elbVm); return elbVm; } catch (Throwable t) { s_logger.warn("Error while deploying ELB VM: ", t); return null; } } private boolean sendCommandsToRouter(final DomainRouterVO elbVm, Commands cmds) throws AgentUnavailableException { Answer[] answers = null; try { answers = _agentMgr.send(elbVm.getHostId(), cmds); } catch (OperationTimedoutException e) { s_logger.warn("ELB: Timed Out", e); throw new AgentUnavailableException( "Unable to send commands to virtual elbVm ", elbVm.getHostId(), e); } if (answers == null) { return false; } if (answers.length != cmds.size()) { return false; } // FIXME: Have to return state for individual command in the future if (answers.length > 0) { Answer ans = answers[0]; return ans.getResult(); } return true; } private void createApplyLoadBalancingRulesCommands( List<LoadBalancingRule> rules, DomainRouterVO elbVm, Commands cmds) { LoadBalancerTO[] lbs = new LoadBalancerTO[rules.size()]; int i = 0; for (LoadBalancingRule rule : rules) { boolean revoked = (rule.getState() .equals(FirewallRule.State.Revoke)); String protocol = rule.getProtocol(); String algorithm = rule.getAlgorithm(); String elbIp = _networkMgr.getIp(rule.getSourceIpAddressId()).getAddress() .addr(); int srcPort = rule.getSourcePortStart(); List<LbDestination> destinations = rule.getDestinations(); LoadBalancerTO lb = new LoadBalancerTO(elbIp, srcPort, protocol, algorithm, revoked, false, destinations); lbs[i++] = lb; } LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lbs,elbVm.getPublicIpAddress(),elbVm.getGuestIpAddress(),elbVm.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, elbVm.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, elbVm.getInstanceName()); //FIXME: why are we setting attributes directly? Ick!! There should be accessors and //the constructor should set defaults. cmd.lbStatsVisibility = _configDao.getValue(Config.NetworkLBHaproxyStatsVisbility.key()); cmd.lbStatsUri = _configDao.getValue(Config.NetworkLBHaproxyStatsUri.key()); cmd.lbStatsAuth = _configDao.getValue(Config.NetworkLBHaproxyStatsAuth.key()); cmd.lbStatsPort = _configDao.getValue(Config.NetworkLBHaproxyStatsPort.key()); cmds.addCommand(cmd); } protected boolean applyLBRules(DomainRouterVO elbVm, List<LoadBalancingRule> rules) throws ResourceUnavailableException { Commands cmds = new Commands(OnError.Continue); createApplyLoadBalancingRulesCommands(rules, elbVm, cmds); // Send commands to elbVm return sendCommandsToRouter(elbVm, cmds); } protected DomainRouterVO findElbVmForLb(FirewallRule lb) {//TODO: use a table to lookup ElasticLbVmMapVO map = _elbVmMapDao.findOneByIp(lb.getSourceIpAddressId()); if (map == null) { return null; } DomainRouterVO elbVm = _routerDao.findById(map.getElbVmId()); return elbVm; } public boolean applyLoadBalancerRules(Network network, List<? extends FirewallRule> rules) throws ResourceUnavailableException { if (rules == null || rules.isEmpty()) { return true; } if (rules.get(0).getPurpose() != Purpose.LoadBalancing) { s_logger.warn("ELB: Not handling non-LB firewall rules"); return false; } DomainRouterVO elbVm = findElbVmForLb(rules.get(0)); if (elbVm == null) { s_logger.warn("Unable to apply lb rules, ELB vm doesn't exist in the network " + network.getId()); throw new ResourceUnavailableException("Unable to apply lb rules", DataCenter.class, network.getDataCenterId()); } if (elbVm.getState() == State.Running) { //resend all rules for the public ip List<LoadBalancerVO> lbs = _lbDao.listByIpAddress(rules.get(0).getSourceIpAddressId()); List<LoadBalancingRule> lbRules = new ArrayList<LoadBalancingRule>(); for (LoadBalancerVO lb : lbs) { List<LbDestination> dstList = _lbMgr.getExistingDestinations(lb.getId()); List<LbStickinessPolicy> policyList = _lbMgr.getStickinessPolicies(lb.getId()); LoadBalancingRule loadBalancing = new LoadBalancingRule( lb, dstList, policyList); lbRules.add(loadBalancing); } return applyLBRules(elbVm, lbRules); } else if (elbVm.getState() == State.Stopped || elbVm.getState() == State.Stopping) { s_logger.debug("ELB VM is in " + elbVm.getState() + ", so not sending apply LoadBalancing rules commands to the backend"); return true; } else { s_logger.warn("Unable to apply loadbalancing rules, ELB VM is not in the right state " + elbVm.getState()); throw new ResourceUnavailableException( "Unable to apply loadbalancing rules, ELB VM is not in the right state", VirtualRouter.class, elbVm.getId()); } } @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { _name = name; final Map<String, String> configs = _configDao.getConfiguration("AgentManager", params); _systemAcct = _accountService.getSystemAccount(); _instance = configs.get("instance.name"); if (_instance == null) { _instance = "VM"; } _mgmtCidr = _configDao.getValue(Config.ManagementNetwork.key()); _mgmtHost = _configDao.getValue(Config.ManagementHostIPAdr.key()); boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); _elasticLbVmRamSize = NumbersUtil.parseInt(configs.get(Config.ElasticLoadBalancerVmMemory.key()), DEFAULT_ELB_VM_RAMSIZE); _elasticLbvmCpuMHz = NumbersUtil.parseInt(configs.get(Config.ElasticLoadBalancerVmCpuMhz.key()), DEFAULT_ELB_VM_CPU_MHZ); _elasticLbvmNumCpu = NumbersUtil.parseInt(configs.get(Config.ElasticLoadBalancerVmNumVcpu.key()), 1); _elasticLbVmOffering = new ServiceOfferingVO("System Offering For Elastic LB VM", _elasticLbvmNumCpu, _elasticLbVmRamSize, _elasticLbvmCpuMHz, 0, 0, true, null, useLocalStorage, true, null, true, VirtualMachine.Type.ElasticLoadBalancerVm, true); _elasticLbVmOffering.setUniqueName(ServiceOffering.elbVmDefaultOffUniqueName); _elasticLbVmOffering = _serviceOfferingDao.persistSystemServiceOffering(_elasticLbVmOffering); String enabled = _configDao.getValue(Config.ElasticLoadBalancerEnabled.key()); _enabled = (enabled == null) ? false: Boolean.parseBoolean(enabled); s_logger.info("Elastic Load balancer enabled: " + _enabled); if (_enabled) { String traffType = _configDao.getValue(Config.ElasticLoadBalancerNetwork.key()); if ("guest".equalsIgnoreCase(traffType)) { _frontendTrafficType = TrafficType.Guest; } else if ("public".equalsIgnoreCase(traffType)){ _frontendTrafficType = TrafficType.Public; } else throw new ConfigurationException("ELB: Traffic type for front end of load balancer has to be guest or public; found : " + traffType); s_logger.info("ELB: Elastic Load Balancer: will balance on " + traffType ); int gcIntervalMinutes = NumbersUtil.parseInt(configs.get(Config.ElasticLoadBalancerVmGcInterval.key()), 5); if (gcIntervalMinutes < 5) gcIntervalMinutes = 5; s_logger.info("ELB: Elastic Load Balancer: scheduling GC to run every " + gcIntervalMinutes + " minutes" ); _gcThreadPool = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ELBVM-GC")); _gcThreadPool.scheduleAtFixedRate(new CleanupThread(), gcIntervalMinutes, gcIntervalMinutes, TimeUnit.MINUTES); _itMgr.registerGuru(VirtualMachine.Type.ElasticLoadBalancerVm, this); } return true; } @Override public boolean start() { return true; } @Override public boolean stop() { return true; } @Override public String getName() { return _name; } private DomainRouterVO findELBVmWithCapacity(Network guestNetwork, IPAddressVO ipAddr) { List<DomainRouterVO> unusedElbVms = _elbVmMapDao.listUnusedElbVms(); if (unusedElbVms.size() > 0) { List<DomainRouterVO> candidateVms = new ArrayList<DomainRouterVO>(); for (DomainRouterVO candidateVm: unusedElbVms) { if (candidateVm.getPodIdToDeployIn() == getPodIdForDirectIp(ipAddr)) candidateVms.add(candidateVm); } return candidateVms.size()==0?null:candidateVms.get(new Random().nextInt(candidateVms.size())); } return null; } public DomainRouterVO deployELBVm(Network guestNetwork, DeployDestination dest, Account owner, Map<Param, Object> params) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { long dcId = dest.getDataCenter().getId(); // lock guest network Long guestNetworkId = guestNetwork.getId(); guestNetwork = _networkDao.acquireInLockTable(guestNetworkId); if (guestNetwork == null) { throw new ConcurrentOperationException("Unable to acquire network lock: " + guestNetworkId); } try { if (_networkMgr.isNetworkSystem(guestNetwork) || guestNetwork.getGuestType() == Network.GuestType.Shared) { owner = _accountService.getSystemAccount(); } if (s_logger.isDebugEnabled()) { s_logger.debug("Starting a ELB vm for network configurations: " + guestNetwork + " in " + dest); } assert guestNetwork.getState() == Network.State.Implemented || guestNetwork.getState() == Network.State.Setup || guestNetwork.getState() == Network.State.Implementing : "Network is not yet fully implemented: "+ guestNetwork; DataCenterDeployment plan = null; DomainRouterVO elbVm = null; plan = new DataCenterDeployment(dcId, dest.getPod().getId(), null, null, null, null); if (elbVm == null) { long id = _routerDao.getNextInSequence(Long.class, "id"); if (s_logger.isDebugEnabled()) { s_logger.debug("Creating the ELB vm " + id); } List<NetworkOfferingVO> offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork); NetworkOfferingVO controlOffering = offerings.get(0); NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false).get(0); List<Pair<NetworkVO, NicProfile>> networks = new ArrayList<Pair<NetworkVO, NicProfile>>(2); NicProfile guestNic = new NicProfile(); guestNic.setDefaultNic(true); networks.add(new Pair<NetworkVO, NicProfile>((NetworkVO) guestNetwork, guestNic)); networks.add(new Pair<NetworkVO, NicProfile>(controlConfig, null)); VMTemplateVO template = _templateDao.findSystemVMTemplate(dcId); String typeString = "ElasticLoadBalancerVm"; Long physicalNetworkId = _networkMgr.getPhysicalNetworkId(guestNetwork); PhysicalNetworkServiceProvider provider = _physicalProviderDao.findByServiceProvider(physicalNetworkId, typeString); if (provider == null) { throw new CloudRuntimeException("Cannot find service provider " + typeString + " in physical network " + physicalNetworkId); } VirtualRouterProvider vrProvider = _vrProviderDao.findByNspIdAndType(provider.getId(), VirtualRouterProviderType.ElasticLoadBalancerVm); if (vrProvider == null) { throw new CloudRuntimeException("Cannot find virtual router provider " + typeString + " as service provider " + provider.getId()); } elbVm = new DomainRouterVO(id, _elasticLbVmOffering.getId(), vrProvider.getId(), VirtualMachineName.getSystemVmName(id, _instance, _elbVmNamePrefix), template.getId(), template.getHypervisorType(), template.getGuestOSId(), owner.getDomainId(), owner.getId(), guestNetwork.getId(), false, 0, false, RedundantState.UNKNOWN, _elasticLbVmOffering.getOfferHA(), false, VirtualMachine.Type.ElasticLoadBalancerVm); elbVm.setRole(Role.LB); elbVm = _itMgr.allocate(elbVm, template, _elasticLbVmOffering, networks, plan, null, owner); //TODO: create usage stats } State state = elbVm.getState(); if (state != State.Running) { elbVm = this.start(elbVm, _accountService.getSystemUser(), _accountService.getSystemAccount(), params); } return elbVm; } finally { _networkDao.releaseFromLockTable(guestNetworkId); } } private DomainRouterVO start(DomainRouterVO elbVm, User user, Account caller, Map<Param, Object> params) throws StorageUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { s_logger.debug("Starting ELB VM " + elbVm); if (_itMgr.start(elbVm, params, user, caller) != null) { return _routerDao.findById(elbVm.getId()); } else { return null; } } private DomainRouterVO stop(DomainRouterVO elbVm, boolean forced, User user, Account caller) throws ConcurrentOperationException, ResourceUnavailableException { s_logger.debug("Stopping ELB vm " + elbVm); try { if (_itMgr.advanceStop( elbVm, forced, user, caller)) { return _routerDao.findById(elbVm.getId()); } else { return null; } } catch (OperationTimedoutException e) { throw new CloudRuntimeException("Unable to stop " + elbVm, e); } } protected List<LoadBalancerVO> findExistingLoadBalancers(String lbName, Long ipId, Long accountId, Long domainId, Integer publicPort) { SearchBuilder<LoadBalancerVO> sb = _lbDao.createSearchBuilder(); sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ); sb.and("publicPort", sb.entity().getSourcePortStart(), SearchCriteria.Op.EQ); if (ipId != null) { sb.and("sourceIpAddress", sb.entity().getSourceIpAddressId(), SearchCriteria.Op.EQ); } if (domainId != null) { sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.EQ); } if (publicPort != null) { sb.and("publicPort", sb.entity().getSourcePortStart(), SearchCriteria.Op.EQ); } SearchCriteria<LoadBalancerVO> sc = sb.create(); sc.setParameters("name", lbName); sc.setParameters("accountId", accountId); if (ipId != null) { sc.setParameters("sourceIpAddress", ipId); } if (domainId != null) { sc.setParameters("domainId",domainId); } if (publicPort != null) { sc.setParameters("publicPort", publicPort); } List<LoadBalancerVO> lbs = _lbDao.search(sc, null); return lbs == null || lbs.size()==0 ? null: lbs; } @DB public PublicIp allocDirectIp(Account account, long guestNetworkId) throws InsufficientAddressCapacityException { Network frontEndNetwork = _networkMgr.getNetwork(guestNetworkId); Transaction txn = Transaction.currentTxn(); txn.start(); PublicIp ip = _networkMgr.assignPublicIpAddress(frontEndNetwork.getDataCenterId(), null, account, VlanType.DirectAttached, frontEndNetwork.getId(), null, true); IPAddressVO ipvo = _ipAddressDao.findById(ip.getId()); ipvo.setAssociatedWithNetworkId(frontEndNetwork.getId()); _ipAddressDao.update(ipvo.getId(), ipvo); txn.commit(); s_logger.info("Acquired frontend IP for ELB " + ip); return ip; } public void releaseIp(long ipId, long userId, Account caller) { s_logger.info("ELB: Release public IP for loadbalancing " + ipId); IPAddressVO ipvo = _ipAddressDao.findById(ipId); ipvo.setAssociatedWithNetworkId(null); _ipAddressDao.update(ipvo.getId(), ipvo); _networkMgr.releasePublicIpAddress(ipId, userId, caller); _ipAddressDao.unassignIpAddress(ipId); } @Override @DB public LoadBalancer handleCreateLoadBalancerRule(CreateLoadBalancerRuleCmd lb, Account account, long networkId) throws InsufficientAddressCapacityException, NetworkRuleConflictException { //this part of code is executed when the LB provider is Elastic Load Balancer vm if (!_networkMgr.isProviderSupportServiceInNetwork(lb.getNetworkId(), Service.Lb, Provider.ElasticLoadBalancerVm)) { return null; } Long ipId = lb.getSourceIpAddressId(); if (ipId != null) { return null; } boolean newIp = false; account = _accountDao.acquireInLockTable(account.getId()); if (account == null) { s_logger.warn("ELB: CreateLoadBalancer: Failed to acquire lock on account"); throw new CloudRuntimeException("Failed to acquire lock on account"); } try { List<LoadBalancerVO> existingLbs = findExistingLoadBalancers(lb.getName(), lb.getSourceIpAddressId(), lb.getAccountId(), lb.getDomainId(), lb.getSourcePortStart()); if (existingLbs == null ){ existingLbs = findExistingLoadBalancers(lb.getName(), lb.getSourceIpAddressId(), lb.getAccountId(), lb.getDomainId(), null); if (existingLbs == null) { if (lb.getSourceIpAddressId() != null) { existingLbs = findExistingLoadBalancers(lb.getName(), null, lb.getAccountId(), lb.getDomainId(), null); if (existingLbs != null) { throw new InvalidParameterValueException("Supplied LB name " + lb.getName() + " is not associated with IP " + lb.getSourceIpAddressId() ); } } else { s_logger.debug("Could not find any existing frontend ips for this account for this LB rule, acquiring a new frontent IP for ELB"); PublicIp ip = allocDirectIp(account, networkId); ipId = ip.getId(); newIp = true; } } else { ipId = existingLbs.get(0).getSourceIpAddressId(); s_logger.debug("ELB: Found existing frontend ip for this account for this LB rule " + ipId); } } else { s_logger.warn("ELB: Found existing load balancers matching requested new LB"); throw new NetworkRuleConflictException("ELB: Found existing load balancers matching requested new LB"); } Network network = _networkMgr.getNetwork(networkId); IPAddressVO ipAddr = _ipAddressDao.findById(ipId); LoadBalancer result = null; try { lb.setSourceIpAddressId(ipId); result = _lbMgr.createLoadBalancer(lb, false); } catch (NetworkRuleConflictException e) { s_logger.warn("Failed to create LB rule, not continuing with ELB deployment"); if (newIp) { releaseIp(ipId, UserContext.current().getCallerUserId(), account); } throw e; } DomainRouterVO elbVm = null; if (existingLbs == null) { elbVm = findELBVmWithCapacity(network, ipAddr); if (elbVm == null) { elbVm = deployLoadBalancerVM(networkId, ipAddr, account.getId()); if (elbVm == null) { s_logger.warn("Failed to deploy a new ELB vm for ip " + ipAddr + " in network " + network + "lb name=" + lb.getName()); if (newIp) releaseIp(ipId, UserContext.current().getCallerUserId(), account); } } } else { ElasticLbVmMapVO elbVmMap = _elbVmMapDao.findOneByIp(ipId); if (elbVmMap != null) { elbVm = _routerDao.findById(elbVmMap.getElbVmId()); } } if (elbVm == null) { s_logger.warn("No ELB VM can be found or deployed"); s_logger.warn("Deleting LB since we failed to deploy ELB VM"); _lbDao.remove(result.getId()); return null; } ElasticLbVmMapVO mapping = new ElasticLbVmMapVO(ipId, elbVm.getId(), result.getId()); _elbVmMapDao.persist(mapping); return result; } finally { if (account != null) { _accountDao.releaseFromLockTable(account.getId()); } } } void garbageCollectUnusedElbVms() { List<DomainRouterVO> unusedElbVms = _elbVmMapDao.listUnusedElbVms(); if (unusedElbVms != null && unusedElbVms.size() > 0) s_logger.info("Found " + unusedElbVms.size() + " unused ELB vms"); Set<Long> currentGcCandidates = new HashSet<Long>(); for (DomainRouterVO elbVm: unusedElbVms) { currentGcCandidates.add(elbVm.getId()); } _gcCandidateElbVmIds.retainAll(currentGcCandidates); currentGcCandidates.removeAll(_gcCandidateElbVmIds); User user = _accountService.getSystemUser(); for (Long elbVmId : _gcCandidateElbVmIds) { DomainRouterVO elbVm = _routerDao.findById(elbVmId); boolean gceed = false; try { s_logger.info("Attempting to stop ELB VM: " + elbVm); stop(elbVm, true, user, _systemAcct); gceed = true; } catch (ConcurrentOperationException e) { s_logger.warn("Unable to stop unused ELB vm " + elbVm + " due to ", e); } catch (ResourceUnavailableException e) { s_logger.warn("Unable to stop unused ELB vm " + elbVm + " due to ", e); continue; } if (gceed) { try { s_logger.info("Attempting to destroy ELB VM: " + elbVm); _itMgr.expunge(elbVm, user, _systemAcct); } catch (ResourceUnavailableException e) { s_logger.warn("Unable to destroy unused ELB vm " + elbVm + " due to ", e); gceed = false; } } if (!gceed) { currentGcCandidates.add(elbVm.getId()); } } _gcCandidateElbVmIds = currentGcCandidates; } public class CleanupThread implements Runnable { @Override public void run() { garbageCollectUnusedElbVms(); } CleanupThread() { } } @Override public void handleDeleteLoadBalancerRule(LoadBalancer lb, long userId, Account caller) { if (!_enabled) { return; } List<LoadBalancerVO> remainingLbs = _loadBalancerDao.listByIpAddress(lb.getSourceIpAddressId()); if (remainingLbs.size() == 0) { s_logger.debug("ELB mgr: releasing ip " + lb.getSourceIpAddressId() + " since no LB rules remain for this ip address"); releaseIp(lb.getSourceIpAddressId(), userId, caller); } } @Override public DomainRouterVO findByName(String name) { if (!VirtualMachineName.isValidSystemVmName(name, _instance, _elbVmNamePrefix)) { return null; } return _routerDao.findById(VirtualMachineName.getSystemVmId(name)); } @Override public DomainRouterVO findById(long id) { return _routerDao.findById(id); } @Override public DomainRouterVO persist(DomainRouterVO elbVm) { return _routerDao.persist(elbVm); } @Override public boolean finalizeVirtualMachineProfile(VirtualMachineProfile<DomainRouterVO> profile, DeployDestination dest, ReservationContext context) { DomainRouterVO elbVm = profile.getVirtualMachine(); NetworkVO network = _networkDao.findById(elbVm.getNetworkId()); DataCenter dc = dest.getDataCenter(); StringBuilder buf = profile.getBootArgsBuilder(); buf.append(" template=domP type=" + _systemVmType); buf.append(" name=").append(profile.getHostName()); NicProfile controlNic = null; String defaultDns1 = null; String defaultDns2 = null; for (NicProfile nic : profile.getNics()) { int deviceId = nic.getDeviceId(); buf.append(" eth").append(deviceId).append("ip=").append(nic.getIp4Address()); buf.append(" eth").append(deviceId).append("mask=").append(nic.getNetmask()); if (nic.isDefaultNic()) { buf.append(" gateway=").append(nic.getGateway()); defaultDns1 = nic.getDns1(); defaultDns2 = nic.getDns2(); } if (nic.getTrafficType() == TrafficType.Management) { buf.append(" localgw=").append(dest.getPod().getGateway()); } else if (nic.getTrafficType() == TrafficType.Control) { // control command is sent over management network in VMware if (dest.getHost().getHypervisorType() == HypervisorType.VMware) { if (s_logger.isInfoEnabled()) { s_logger.info("Check if we need to add management server explicit route to ELB vm. pod cidr: " + dest.getPod().getCidrAddress() + "/" + dest.getPod().getCidrSize() + ", pod gateway: " + dest.getPod().getGateway() + ", management host: " + _mgmtHost); } if (s_logger.isDebugEnabled()) { s_logger.debug("Added management server explicit route to ELB vm."); } // always add management explicit route, for basic networking setup buf.append(" mgmtcidr=").append(_mgmtCidr); buf.append(" localgw=").append(dest.getPod().getGateway()); if (dc.getNetworkType() == NetworkType.Basic) { // ask elb vm to setup SSH on guest network buf.append(" sshonguest=true"); } } controlNic = nic; } } String domain = network.getNetworkDomain(); if (domain != null) { buf.append(" domain=" + domain); } buf.append(" dns1=").append(defaultDns1); if (defaultDns2 != null) { buf.append(" dns2=").append(defaultDns2); } if (s_logger.isDebugEnabled()) { s_logger.debug("Boot Args for " + profile + ": " + buf.toString()); } if (controlNic == null) { throw new CloudRuntimeException("Didn't start a control port"); } return true; } @Override public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile<DomainRouterVO> profile, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException { DomainRouterVO elbVm = profile.getVirtualMachine(); List<NicProfile> nics = profile.getNics(); for (NicProfile nic : nics) { if (nic.getTrafficType() == TrafficType.Public) { elbVm.setPublicIpAddress(nic.getIp4Address()); elbVm.setPublicNetmask(nic.getNetmask()); elbVm.setPublicMacAddress(nic.getMacAddress()); } else if (nic.getTrafficType() == TrafficType.Guest) { elbVm.setGuestIpAddress(nic.getIp4Address()); } else if (nic.getTrafficType() == TrafficType.Control) { elbVm.setPrivateIpAddress(nic.getIp4Address()); elbVm.setPrivateMacAddress(nic.getMacAddress()); } } _routerDao.update(elbVm.getId(), elbVm); finalizeCommandsOnStart(cmds, profile); return true; } @Override public boolean finalizeStart(VirtualMachineProfile<DomainRouterVO> profile, long hostId, Commands cmds, ReservationContext context) { CheckSshAnswer answer = (CheckSshAnswer) cmds.getAnswer("checkSsh"); if (answer == null || !answer.getResult()) { s_logger.warn("Unable to ssh to the ELB VM: " + answer.getDetails()); return false; } return true; } @Override public boolean finalizeCommandsOnStart(Commands cmds, VirtualMachineProfile<DomainRouterVO> profile) { DomainRouterVO elbVm = profile.getVirtualMachine(); DataCenterVO dcVo = _dcDao.findById(elbVm.getDataCenterIdToDeployIn()); NicProfile controlNic = null; if(profile.getHypervisorType() == HypervisorType.VMware && dcVo.getNetworkType() == NetworkType.Basic) { // TODO this is a ugly to test hypervisor type here // for basic network mode, we will use the guest NIC for control NIC for (NicProfile nic : profile.getNics()) { if (nic.getTrafficType() == TrafficType.Guest && nic.getIp4Address() != null) { controlNic = nic; } } } else { for (NicProfile nic : profile.getNics()) { if (nic.getTrafficType() == TrafficType.Control && nic.getIp4Address() != null) { controlNic = nic; } } } if (controlNic == null) { s_logger.error("Control network doesn't exist for the ELB vm " + elbVm); return false; } cmds.addCommand("checkSsh", new CheckSshCommand(profile.getInstanceName(), controlNic.getIp4Address(), 3922)); // Re-apply load balancing rules List<LoadBalancerVO> lbs = _elbVmMapDao.listLbsForElbVm(elbVm.getId()); List<LoadBalancingRule> lbRules = new ArrayList<LoadBalancingRule>(); for (LoadBalancerVO lb : lbs) { List<LbDestination> dstList = _lbMgr.getExistingDestinations(lb.getId()); List<LbStickinessPolicy> policyList = _lbMgr.getStickinessPolicies(lb.getId()); LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList); lbRules.add(loadBalancing); } s_logger.debug("Found " + lbRules.size() + " load balancing rule(s) to apply as a part of ELB vm " + elbVm + " start."); if (!lbRules.isEmpty()) { createApplyLoadBalancingRulesCommands(lbRules, elbVm, cmds); } return true; } @Override public void finalizeStop(VirtualMachineProfile<DomainRouterVO> profile, StopAnswer answer) { if (answer != null) { VMInstanceVO vm = profile.getVirtualMachine(); DomainRouterVO elbVm = _routerDao.findById(vm.getId()); processStopOrRebootAnswer(elbVm, answer); } } public void processStopOrRebootAnswer(final DomainRouterVO elbVm, Answer answer) { //TODO: process network usage stats } @Override public void finalizeExpunge(DomainRouterVO vm) { // no-op } @Override public Long convertToId(String vmName) { if (!VirtualMachineName.isValidSystemVmName(vmName, _instance, _elbVmNamePrefix)) { return null; } return VirtualMachineName.getSystemVmId(vmName); } }