diff --git a/api/src/com/cloud/api/commands/AssociateIPAddrCmd.java b/api/src/com/cloud/api/commands/AssociateIPAddrCmd.java index fe5a2838be1..1e44db06aeb 100644 --- a/api/src/com/cloud/api/commands/AssociateIPAddrCmd.java +++ b/api/src/com/cloud/api/commands/AssociateIPAddrCmd.java @@ -189,5 +189,13 @@ public class AssociateIPAddrCmd extends BaseAsyncCreateCmd { @Override public AsyncJob.Type getInstanceType() { return AsyncJob.Type.IpAddress; + } + + public AssociateIPAddrCmd(String accountName, Long domainId, Long zoneId, Long networkId) { + super(); + this.accountName = accountName; + this.domainId = domainId; + this.zoneId = zoneId; + this.networkId = networkId; } } diff --git a/api/src/com/cloud/api/commands/CreateLoadBalancerRuleCmd.java b/api/src/com/cloud/api/commands/CreateLoadBalancerRuleCmd.java index 45bd5d7ed46..78fd2dc823f 100644 --- a/api/src/com/cloud/api/commands/CreateLoadBalancerRuleCmd.java +++ b/api/src/com/cloud/api/commands/CreateLoadBalancerRuleCmd.java @@ -18,6 +18,8 @@ package com.cloud.api.commands; +import java.util.List; + import org.apache.log4j.Logger; import com.cloud.api.ApiConstants; @@ -26,10 +28,16 @@ import com.cloud.api.Implementation; import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.api.response.LoadBalancerResponse; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.IpAddress; import com.cloud.network.rules.LoadBalancer; +import com.cloud.user.Account; +import com.cloud.user.UserContext; import com.cloud.utils.net.NetUtils; @Implementation(description="Creates a load balancer rule", responseObject=LoadBalancerResponse.class) @@ -54,13 +62,21 @@ public class CreateLoadBalancerRuleCmd extends BaseCmd implements LoadBalancer @Parameter(name=ApiConstants.PRIVATE_PORT, type=CommandType.INTEGER, required=true, description="the private port of the private ip address/virtual machine where the network traffic will be load balanced to") private Integer privatePort; - @Parameter(name=ApiConstants.PUBLIC_IP_ID, type=CommandType.LONG, required=true, description="public ip address id from where the network traffic will be load balanced from") + @Parameter(name=ApiConstants.PUBLIC_IP_ID, type=CommandType.LONG, required=false, description="public ip address id from where the network traffic will be load balanced from") private Long publicIpId; + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=false, description="public ip address id from where the network traffic will be load balanced from") + private Long zoneId; @Parameter(name=ApiConstants.PUBLIC_PORT, type=CommandType.INTEGER, required=true, description="the public port from where the network traffic will be load balanced from") private Integer publicPort; + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="the account associated with the load balancer. Must be used with the domainId parameter.") + private String accountName; + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="the domain ID associated with the load balancer") + private Long domainId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -86,7 +102,7 @@ public class CreateLoadBalancerRuleCmd extends BaseCmd implements LoadBalancer public Long getPublicIpId() { IpAddress ipAddr = _networkService.getIp(publicIpId); if (ipAddr == null || !ipAddr.readyToUse()) { - throw new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id" + ipAddr.getId()); + throw new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id " + ipAddr.getId()); } return publicIpId; @@ -109,10 +125,51 @@ public class CreateLoadBalancerRuleCmd extends BaseCmd implements LoadBalancer return s_name; } + protected LoadBalancer findExistingLB() { + List lbs = _lbService.searchForLoadBalancers(new ListLoadBalancerRulesCmd(getAccountName(), getDomainId(), null, getName(), publicIpId, null, getZoneId()) ); + if (lbs != null && lbs.size() > 0) { + return lbs.get(0); + } + return null; + } + + protected void allocateIp() throws ResourceAllocationException, ResourceUnavailableException { + AssociateIPAddrCmd allocIpCmd = new AssociateIPAddrCmd(getAccountName(), getDomainId(), getZoneId(), null); + try { + IpAddress ip = _networkService.allocateIP(allocIpCmd); + if (ip != null) { + this.setPublicIpId(ip.getId()); + allocIpCmd.setEntityId(ip.getId()); + } else { + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to allocate ip address"); + } + //UserContext.current().setEventDetails("Ip Id: "+ ip.getId()); + //IpAddress result = _networkService.associateIP(allocIpCmd); + } catch (ConcurrentOperationException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, ex.getMessage()); + } catch (InsufficientAddressCapacityException ex) { + s_logger.info(ex); + s_logger.trace(ex); + throw new ServerApiException(BaseCmd.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); + } + } + @Override - public void execute() { + public void execute() throws ResourceAllocationException, ResourceUnavailableException { LoadBalancer result = null; try { + if (publicIpId == null) { + if (getZoneId() == null ) { + throw new InvalidParameterValueException("Either zone id or public ip id needs to be specified"); + } + LoadBalancer existing = findExistingLB(); + if (existing == null) { + allocateIp(); + } else { + this.setPublicIpId(existing.getSourceIpAddressId()); + } + } result = _lbService.createLoadBalancerRule(this); } catch (NetworkRuleConflictException e) { s_logger.warn("Exception: ", e); @@ -171,12 +228,33 @@ public class CreateLoadBalancerRuleCmd extends BaseCmd implements LoadBalancer @Override public long getAccountId() { - return _networkService.getIp(getPublicIpId()).getAccountId(); + if (publicIpId != null) + return _networkService.getIp(getPublicIpId()).getAccountId(); + Account account = UserContext.current().getCaller(); + if ((account == null) ) { + if ((domainId != null) && (accountName != null)) { + Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); + if (userAccount != null) { + return userAccount.getId(); + } + } + } + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; } @Override public long getDomainId() { - return _networkService.getIp(getPublicIpId()).getDomainId(); + if (publicIpId != null) + return _networkService.getIp(getPublicIpId()).getDomainId(); + if (domainId != null) { + return domainId; + } + return UserContext.current().getCaller().getDomainId(); } @Override @@ -192,5 +270,18 @@ public class CreateLoadBalancerRuleCmd extends BaseCmd implements LoadBalancer @Override public long getEntityOwnerId() { return getAccountId(); - } + } + + public String getAccountName() { + return accountName; + } + + public Long getZoneId() { + return zoneId; + } + + public void setPublicIpId(Long publicIpId) { + this.publicIpId = publicIpId; + } + } diff --git a/api/src/com/cloud/api/commands/ListLoadBalancerRulesCmd.java b/api/src/com/cloud/api/commands/ListLoadBalancerRulesCmd.java index 9bded66ae8b..1ceda4854ac 100644 --- a/api/src/com/cloud/api/commands/ListLoadBalancerRulesCmd.java +++ b/api/src/com/cloud/api/commands/ListLoadBalancerRulesCmd.java @@ -119,4 +119,15 @@ public class ListLoadBalancerRulesCmd extends BaseListCmd { response.setResponseName(getCommandName()); this.setResponseObject(response); } + + public ListLoadBalancerRulesCmd(String accountName, Long domainId, Long id, String loadBalancerRuleName, Long publicIpId, Long virtualMachineId, Long zoneId) { + super(); + this.accountName = accountName; + this.domainId = domainId; + this.id = id; + this.loadBalancerRuleName = loadBalancerRuleName; + this.publicIpId = publicIpId; + this.virtualMachineId = virtualMachineId; + this.zoneId = zoneId; + } } diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index 3287369ce53..9630d48e732 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -93,6 +93,8 @@ public interface Network extends ControlledEntity { public static final Provider F5BigIp = new Provider("F5BigIp"); public static final Provider ExternalDhcpServer = new Provider("ExternalDhcpServer"); public static final Provider ExternalGateWay = new Provider("ExternalGateWay"); + public static final Provider ElasticLoadBalancerVm = new Provider("ElasticLoadBalancerVm"); + public static final Provider None = new Provider("None"); private String name; diff --git a/api/src/com/cloud/network/router/VirtualRouter.java b/api/src/com/cloud/network/router/VirtualRouter.java index 32009620c88..624bd928e4c 100755 --- a/api/src/com/cloud/network/router/VirtualRouter.java +++ b/api/src/com/cloud/network/router/VirtualRouter.java @@ -26,7 +26,7 @@ import com.cloud.vm.VirtualMachine; public interface VirtualRouter extends VirtualMachine { public enum Role { DHCP_FIREWALL_LB_PASSWD_USERDATA, - DHCP_USERDATA + DHCP_USERDATA, LB } Role getRole(); -} +} \ No newline at end of file diff --git a/api/src/com/cloud/network/rules/LoadBalancer.java b/api/src/com/cloud/network/rules/LoadBalancer.java index 5cee5b7cfe6..40fdc7e34c2 100644 --- a/api/src/com/cloud/network/rules/LoadBalancer.java +++ b/api/src/com/cloud/network/rules/LoadBalancer.java @@ -32,5 +32,5 @@ public interface LoadBalancer extends FirewallRule { int getDefaultPortEnd(); String getAlgorithm(); - + } diff --git a/api/src/com/cloud/vm/VirtualMachine.java b/api/src/com/cloud/vm/VirtualMachine.java index 82b09762016..69fec11a219 100755 --- a/api/src/com/cloud/vm/VirtualMachine.java +++ b/api/src/com/cloud/vm/VirtualMachine.java @@ -151,6 +151,8 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, StateObject DomainRouter, ConsoleProxy, SecondaryStorageVm, + ElasticIpVm, + ElasticLoadBalancerVm, /* * UserBareMetal is only used for selecting VirtualMachineGuru, there is no diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in index 7d3e1772bca..b83b8df2beb 100755 --- a/client/tomcatconf/components.xml.in +++ b/client/tomcatconf/components.xml.in @@ -89,6 +89,8 @@ + + diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index be2c399bf51..b2259a0e226 100644 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -79,6 +79,7 @@ import com.cloud.network.dao.LoadBalancerVMMapDaoImpl; import com.cloud.network.dao.NetworkDaoImpl; import com.cloud.network.dao.NetworkDomainDaoImpl; import com.cloud.network.dao.NetworkRuleConfigDaoImpl; +import com.cloud.network.lb.ElasticLoadBalancerManagerImpl; import com.cloud.network.lb.LoadBalancingRulesManagerImpl; import com.cloud.network.ovs.OvsNetworkManagerImpl; import com.cloud.network.ovs.OvsTunnelManagerImpl; @@ -315,6 +316,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com ComponentInfo info = addManager("ConsoleProxyManager", ConsoleProxyManagerImpl.class); info.addParameter("consoleproxy.sslEnabled", "true"); addManager("ClusteredAgentManager", ClusteredAgentManagerImpl.class); + addManager("ElasticLoadBalancerManager", ElasticLoadBalancerManagerImpl.class); } @Override diff --git a/server/src/com/cloud/dc/DataCenterVO.java b/server/src/com/cloud/dc/DataCenterVO.java index 301f90b7f60..d8900b9460d 100644 --- a/server/src/com/cloud/dc/DataCenterVO.java +++ b/server/src/com/cloud/dc/DataCenterVO.java @@ -205,6 +205,7 @@ public class DataCenterVO implements DataCenter { dhcpProvider = Provider.DhcpServer.getName(); dnsProvider = Provider.DhcpServer.getName(); userDataProvider = Provider.DhcpServer.getName(); + loadBalancerProvider = Provider.ElasticLoadBalancerVm.getName(); } this.zoneToken = zoneToken; diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 5a64dcba743..6d188b79c7e 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -559,6 +559,16 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag if (zone.getNetworkType() != NetworkType.Basic && network.getAccountId() != ipOwner.getId()) { throw new InvalidParameterValueException("The owner of the network is not the same as owner of the IP"); } + VlanType vlanType = VlanType.VirtualNetwork; + boolean assign = false; + //For basic zone, if there isn't a public network outside of the guest network, specify the vlan type to be direct attached + if (zone.getNetworkType() == NetworkType.Basic) { + if (network.getTrafficType() == TrafficType.Guest){ + vlanType = VlanType.DirectAttached; + assign = true; + } + + } PublicIp ip = null; @@ -601,7 +611,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } } - ip = fetchNewPublicIp(zoneId, null, null, ipOwner, VlanType.VirtualNetwork, network.getId(), isSourceNat, false, null); + ip = fetchNewPublicIp(zoneId, null, null, ipOwner, vlanType, network.getId(), isSourceNat, assign, null); if (ip == null) { throw new InsufficientAddressCapacityException("Unable to find available public IP addresses", DataCenter.class, zoneId); @@ -754,8 +764,8 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag defaultGuestNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultGuestNetworkOffering); NetworkOfferingVO defaultGuestDirectNetworkOffering = new NetworkOfferingVO(NetworkOffering.DefaultDirectNetworkOffering, "Direct", TrafficType.Guest, false, true, null, null, null, true, Availability.Optional, - // services - all true except for firewall/lb/vpn and gateway services - true, true, true, false, false, false, false, GuestIpType.Direct); + // services - all true except for firewall/vpn and gateway services + true, true, true, false, false, true, false, GuestIpType.Direct); defaultGuestDirectNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultGuestDirectNetworkOffering); AccountsUsingNetworkSearch = _accountDao.createSearchBuilder(); diff --git a/server/src/com/cloud/network/element/ElasticLoadBalancerElement.java b/server/src/com/cloud/network/element/ElasticLoadBalancerElement.java new file mode 100644 index 00000000000..551a78a9de5 --- /dev/null +++ b/server/src/com/cloud/network/element/ElasticLoadBalancerElement.java @@ -0,0 +1,144 @@ +/** + * 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 . + * + */ +package com.cloud.network.element; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; + +import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.DataCenter; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.NetworkManager; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.lb.ElasticLoadBalancerManager; +import com.cloud.network.rules.FirewallRule; +import com.cloud.offering.NetworkOffering; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.component.Inject; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + + +@Local(value=NetworkElement.class) +public class ElasticLoadBalancerElement extends AdapterBase implements NetworkElement{ + private static final Logger s_logger = Logger.getLogger(ElasticLoadBalancerElement.class); + private static final Map> capabilities = setCapabilities(); + @Inject ConfigurationManager _configMgr; + @Inject NetworkManager _networkManager; + @Inject ElasticLoadBalancerManager _lbMgr; + + private boolean canHandle(Network config) { + DataCenter zone = _configMgr.getZone(config.getDataCenterId()); + if (config.getGuestType() != Network.GuestIpType.Virtual || config.getTrafficType() != TrafficType.Guest) { + s_logger.trace("Not handling network with guest Type " + config.getGuestType() + " and traffic type " + config.getTrafficType()); + return false; + } + + return (_networkManager.zoneIsConfiguredForExternalNetworking(zone.getId()) && + zone.getLoadBalancerProvider() != null && zone.getLoadBalancerProvider().equals(Network.Provider.F5BigIp.getName())); + } + + @Override + public Provider getProvider() { + return Provider.ElasticLoadBalancerVm; + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + private static Map> setCapabilities() { + Map> capabilities = new HashMap>(); + + Map lbCapabilities = new HashMap(); + lbCapabilities.put(Capability.SupportedLBAlgorithms, "roundrobin,leastconn,source"); + lbCapabilities.put(Capability.SupportedProtocols, "tcp, udp"); + + capabilities.put(Service.Lb, lbCapabilities); + return capabilities; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, + InsufficientCapacityException { + + return true; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + + return true; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO kill all loadbalancer vms by calling the ElasticLoadBalancerManager + return false; + } + + @Override + public boolean restart(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + // TODO restart all loadbalancer vms by calling the ElasticLoadBalancerManager + return false; + } + + @Override + public boolean destroy(Network network) throws ConcurrentOperationException, ResourceUnavailableException { + // TODO kill all loadbalancer vms by calling the ElasticLoadBalancerManager + return false; + } + + @Override + public boolean applyIps(Network network, List ipAddress) throws ResourceUnavailableException { + return true; + } + + @Override + public boolean applyRules(Network network, List rules) throws ResourceUnavailableException { + if (!canHandle(network)) { + return false; + } + + return _lbMgr.applyLoadBalancerRules(network, rules); + } +} diff --git a/server/src/com/cloud/network/lb/ElasticLoadBalancerManager.java b/server/src/com/cloud/network/lb/ElasticLoadBalancerManager.java new file mode 100644 index 00000000000..4c26d33c87e --- /dev/null +++ b/server/src/com/cloud/network/lb/ElasticLoadBalancerManager.java @@ -0,0 +1,39 @@ +/** + * 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 . + * + */ +package com.cloud.network.lb; + +import java.util.List; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.rules.FirewallRule; +import com.cloud.user.Account; + +public interface ElasticLoadBalancerManager { + public static final int DEFAULT_ELB_VM_RAMSIZE = 512; // 512 MB + public static final int DEFAULT_ELB_VM_CPU_MHZ = 500; // 500 MHz + public long deployLoadBalancerVM(Long networkId, Long accountId); + + public boolean applyLoadBalancerRules(Network network, + List rules) + throws ResourceUnavailableException;; + +} diff --git a/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java b/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java new file mode 100644 index 00000000000..4b9c1520c7b --- /dev/null +++ b/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java @@ -0,0 +1,428 @@ +/** + * 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 . + * + */ +package com.cloud.network.lb; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +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.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.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.DataCenter; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.deploy.DataCenterDeployment; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.StorageUnavailableException; +import com.cloud.network.IPAddressVO; +import com.cloud.network.LoadBalancerVO; +import com.cloud.network.Network; +import com.cloud.network.NetworkManager; +import com.cloud.network.NetworkVO; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.LoadBalancerDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.router.VirtualNetworkApplianceManager; +import com.cloud.network.router.VirtualRouter; +import com.cloud.network.router.VirtualRouter.Role; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.org.Cluster; +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.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.component.Inject; +import com.cloud.utils.component.Manager; +import com.cloud.utils.db.DB; +import com.cloud.vm.DomainRouterVO; +import com.cloud.vm.NicProfile; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; +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 { + 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; + + + String _name; + String _instance; + + Account _systemAcct; + ServiceOfferingVO _elasticLbVmOffering; + + int _elasticLbVmRamSize; + int _elasticLbvmCpuMHz; + + + + + public long deployLoadBalancerVM(Long networkId, Long accountId) { /* ELB_TODO :need to remove hardcoded network id,account id,pod id , cluster id */ + NetworkVO network = _networkDao.findById(networkId); + + DataCenter dc = _dcDao.findById(network.getDataCenterId()); + + Map params = new HashMap( + 1); + params.put(VirtualMachineProfile.Param.RestartNetwork, true); + Account owner = _accountService.getActiveAccount("system", new Long(1)); + DeployDestination dest = new DeployDestination(dc, null, null, null); + + s_logger.debug("About to deploy elastic LB vm if necessary"); + + try { + VirtualRouter elbVm = deployELBVm(network, dest, owner, params); + + s_logger.debug("ELB vm = " + elbVm); + if (elbVm == null) { + throw new InvalidParameterValueException("No VM with id '" + + elbVm + "' found."); + } + DomainRouterVO elbRouterVm = _routerDao.findById(elbVm.getId()); + String publicIp = elbRouterVm.getGuestIpAddress(); + IPAddressVO ipvo = _ipAddressDao.findByIpAndSourceNetworkId(networkId, publicIp); + ipvo.setAssociatedWithNetworkId(networkId); + _ipAddressDao.update(ipvo.getId(), ipvo); + return ipvo.getId(); + } catch (Throwable t) { + String errorMsg = "Error while deploying Loadbalancer VM: " + t; + s_logger.warn(errorMsg); + return 0; + } + + } + + private boolean sendCommandsToRouter(final DomainRouterVO router, + Commands cmds) throws AgentUnavailableException { + Answer[] answers = null; + try { + answers = _agentMgr.send(router.getHostId(), cmds); + } catch (OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + throw new AgentUnavailableException( + "Unable to send commands to virtual router ", + router.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 rules, DomainRouterVO router, Commands cmds) { + + String elbIp = ""; + + 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(); + + elbIp = _networkMgr.getIp(rule.getSourceIpAddressId()).getAddress() + .addr(); + int srcPort = rule.getSourcePortStart(); + List destinations = rule.getDestinations(); + LoadBalancerTO lb = new LoadBalancerTO(elbIp, srcPort, protocol, + algorithm, revoked, false, destinations); + lbs[i++] = lb; + } + + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lbs); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, + router.getPrivateIpAddress()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, + router.getInstanceName()); + cmds.addCommand(cmd); + + } + + protected boolean applyLBRules(DomainRouterVO router, + List rules) throws ResourceUnavailableException { + Commands cmds = new Commands(OnError.Continue); + createApplyLoadBalancingRulesCommands(rules, router, cmds); + // Send commands to router + return sendCommandsToRouter(router, cmds); + } + + public boolean applyLoadBalancerRules(Network network, + List rules) + throws ResourceUnavailableException { + DomainRouterVO router = _routerDao.findByNetwork(network.getId());/* ELB_TODO :may have multiple LB's , need to get the correct LB based on network id and account id */ + + if (router == null) { + s_logger.warn("Unable to apply lb rules, virtual router doesn't exist in the network " + + network.getId()); + throw new ResourceUnavailableException("Unable to apply lb rules", + DataCenter.class, network.getDataCenterId()); + } + + if (router.getState() == State.Running) { + if (rules != null && !rules.isEmpty()) { + if (rules.get(0).getPurpose() == Purpose.LoadBalancing) { + // for elastic load balancer we have to resend all lb rules + // belonging to same sourceIpAddressId for the network + List lbs = _loadBalancerDao + .listByNetworkId(network.getId()); + List lbRules = new ArrayList(); + for (LoadBalancerVO lb : lbs) { + if (lb.getSourceIpAddressId() == rules.get(0) + .getSourceIpAddressId()) { + List dstList = _lbMgr + .getExistingDestinations(lb.getId()); + LoadBalancingRule loadBalancing = new LoadBalancingRule( + lb, dstList); + lbRules.add(loadBalancing); + } + } + + return applyLBRules(router, lbRules); + } else { + s_logger.warn("Unable to apply rules of purpose: " + + rules.get(0).getPurpose()); + return false; + } + } else { + return true; + } + } else if (router.getState() == State.Stopped + || router.getState() == State.Stopping) { + s_logger.debug("Router is in " + + router.getState() + + ", so not sending apply firewall rules commands to the backend"); + return true; + } else { + s_logger.warn("Unable to apply firewall rules, virtual router is not in the right state " + + router.getState()); + throw new ResourceUnavailableException( + "Unable to apply firewall rules, virtual router is not in the right state", + VirtualRouter.class, router.getId()); + } + } + + @Override + public boolean configure(String name, Map params) + throws ConfigurationException { + _name = name; + final Map configs = _configDao.getConfiguration("AgentManager", params); + _systemAcct = _accountService.getSystemAccount(); + _instance = configs.get("instance.name"); + if (_instance == null) { + _instance = "VM"; + } + boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); + + _elasticLbVmRamSize = NumbersUtil.parseInt(configs.get("elastic.lb.vm.ram.size"), DEFAULT_ELB_VM_RAMSIZE); + _elasticLbvmCpuMHz = NumbersUtil.parseInt(configs.get("elastic.lb.vm.cpu.mhz"), DEFAULT_ELB_VM_CPU_MHZ); + _elasticLbVmOffering = new ServiceOfferingVO("System Offering For Elastic LB VM", 1, _elasticLbVmRamSize, _elasticLbvmCpuMHz, 0, 0, true, null, useLocalStorage, true, null, true, VirtualMachine.Type.ElasticLoadBalancerVm, true); + _elasticLbVmOffering.setUniqueName("Cloud.Com-ElasticLBVm"); + _elasticLbVmOffering = _serviceOfferingDao.persistSystemServiceOffering(_elasticLbVmOffering); + + + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + + @DB + public DomainRouterVO deployELBVm(Network guestNetwork, DeployDestination dest, Account owner, Map 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 configuration: " + guestNetworkId); + } + + try { + + NetworkOffering offering = _networkOfferingDao.findByIdIncludingRemoved(guestNetwork.getNetworkOfferingId()); + if (offering.isSystemOnly() || guestNetwork.getIsShared()) { + owner = _accountService.getSystemAccount(); + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Starting a elastic ip 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 router = null; + + //router = _routerDao.findByNetworkAndPodAndRole(guestNetwork.getId(), podId, Role.LB); + plan = new DataCenterDeployment(dcId, null, null, null, null); + + // } else { + // s_logger.debug("Not deploying elastic ip vm"); + // return null; + // } + + if (router == null) { + long id = _routerDao.getNextInSequence(Long.class, "id"); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Creating the elastic LB vm " + id); + } + + List offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork); + NetworkOfferingVO controlOffering = offerings.get(0); + NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false, false).get(0); + + List> networks = new ArrayList>(2); + NicProfile guestNic = new NicProfile(); + networks.add(new Pair((NetworkVO) guestNetwork, guestNic)); + networks.add(new Pair(controlConfig, null)); + + VMTemplateVO template = _templateDao.findSystemVMTemplate(dcId); + + + router = new DomainRouterVO(id, _elasticLbVmOffering.getId(), VirtualMachineName.getRouterName(id, _instance), template.getId(), template.getHypervisorType(), template.getGuestOSId(), + owner.getDomainId(), owner.getId(), guestNetwork.getId(), _elasticLbVmOffering.getOfferHA()); + router.setRole(Role.LB); + router = _itMgr.allocate(router, template, _elasticLbVmOffering, networks, plan, null, owner); + } + + State state = router.getState(); + if (state != State.Running) { + router = this.start(router, _accountService.getSystemUser(), _accountService.getSystemAccount(), params); + } + + + return router; + } finally { + _networkDao.releaseFromLockTable(guestNetworkId); + } + } + + private DomainRouterVO start(DomainRouterVO router, User user, Account caller, Map params) throws StorageUnavailableException, InsufficientCapacityException, + ConcurrentOperationException, ResourceUnavailableException { + s_logger.debug("Starting router " + router); + if (_itMgr.start(router, params, user, caller) != null) { + return _routerDao.findById(router.getId()); + } else { + return null; + } + } + +} diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 760a06fe657..d69adb008fa 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -50,12 +50,15 @@ import com.cloud.network.IPAddressVO; import com.cloud.network.LoadBalancerVMMapVO; import com.cloud.network.LoadBalancerVO; import com.cloud.network.Network; +import com.cloud.network.Network.GuestIpType; import com.cloud.network.Network.Service; import com.cloud.network.NetworkManager; +import com.cloud.network.NetworkVO; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.LoadBalancerVMMapDao; +import com.cloud.network.dao.NetworkDao; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; @@ -118,6 +121,10 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, NicDao _nicDao; @Inject UsageEventDao _usageEventDao; + @Inject + ElasticLoadBalancerManager _elbMgr; + @Inject + NetworkDao _networkDao; @Override @DB @@ -346,9 +353,25 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, UserContext caller = UserContext.current(); long ipId = lb.getSourceIpAddressId(); - - // make sure ip address exists IPAddressVO ipAddr = _ipAddressDao.findById(ipId); + Long networkId= ipAddr.getSourceNetworkId(); + NetworkVO network=_networkDao.findById(networkId); + + if (network.getGuestType() == GuestIpType.Direct) { + LoadBalancerVO lbvo; + Account account = caller.getCaller(); + lbvo = _lbDao.findByAccountAndName(account.getId(), lb.getName()); + if (lbvo == null) { + _elbMgr.deployLoadBalancerVM(networkId, account.getId()); + IPAddressVO ipvo = _ipAddressDao.findById(ipId); + ipvo.setAssociatedWithNetworkId(networkId); + _ipAddressDao.update(ipvo.getId(), ipvo); + ipAddr.setAssociatedWithNetworkId(networkId); + } else { + + } + } + // make sure ip address exists if (ipAddr == null || !ipAddr.readyToUse()) { throw new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id" + ipId); } @@ -380,7 +403,7 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, throw new InvalidParameterValueException("Invalid algorithm: " + lb.getAlgorithm()); } - Long networkId = ipAddr.getAssociatedWithNetworkId(); + networkId = ipAddr.getAssociatedWithNetworkId(); if (networkId == null) { throw new InvalidParameterValueException("Unable to create load balancer rule ; ip id=" + ipId + " is not associated with any network"); @@ -389,9 +412,8 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, _accountMgr.checkAccess(caller.getCaller(), ipAddr); // verify that lb service is supported by the network - Network network = _networkMgr.getNetwork(networkId); if (!_networkMgr.isServiceSupported(network.getNetworkOfferingId(), Service.Lb)) { - throw new InvalidParameterValueException("LB service is not supported in network id=" + networkId); + throw new InvalidParameterValueException("LB service is not supported in network id= " + networkId); } LoadBalancerVO newRule = new LoadBalancerVO(lb.getXid(), lb.getName(), lb.getDescription(), lb.getSourceIpAddressId(), lb.getSourcePortEnd(), lb.getDefaultPortStart(), lb.getAlgorithm(), diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 89ba2ed7f90..ef74240961c 100644 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -809,7 +809,7 @@ public class ConfigurationServerImpl implements ConfigurationServer { true, false, null, null, null, true, Availability.Required, true, true, true, //services - all true except for lb/vpn and gateway - false, true, false, false, GuestIpType.Direct); + false, true, true, false, GuestIpType.Direct); guestNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(guestNetworkOffering); @@ -829,7 +829,7 @@ public class ConfigurationServerImpl implements ConfigurationServer { false, true, null, null, null, true, Availability.Optional, true, true, true, //services - all true except for firewall/lb/vpn and gateway - false, false, false, false, GuestIpType.Direct); + false, false, true, false, GuestIpType.Direct); defaultGuestNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultGuestDirectNetworkOffering); }