blob: 9a5c5a7c6a99c762e6b2d4673ca24bf9e73f489e [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.network.lb;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO;
import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.GetDomRVersionAnswer;
import com.cloud.agent.api.GetDomRVersionCmd;
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.configuration.Config;
import com.cloud.configuration.ConfigurationManagerImpl;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.deploy.DataCenterDeployment;
import com.cloud.deploy.DeployDestination;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.exception.InvalidParameterValueException;
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.IpAddressManager;
import com.cloud.network.Network;
import com.cloud.network.Network.Provider;
import com.cloud.network.Network.Service;
import com.cloud.network.NetworkModel;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.PhysicalNetworkServiceProvider;
import com.cloud.network.VirtualRouterProvider;
import com.cloud.network.VirtualRouterProvider.Type;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
import com.cloud.network.dao.VirtualRouterProviderDao;
import com.cloud.network.lb.LoadBalancingRule;
import com.cloud.network.lb.LoadBalancingRule.LbDestination;
import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy;
import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy;
import com.cloud.network.lb.LoadBalancingRulesManager;
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.offering.NetworkOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.resource.ResourceManager;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Storage;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.Ip;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.DomainRouterVO;
import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile;
import com.cloud.vm.NicVO;
import com.cloud.vm.ReservationContext;
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;
import com.cloud.vm.dao.NicDao;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.Hyperv;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.KVM;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.LXC;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.VMware;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.XenServer;
public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements InternalLoadBalancerVMManager, InternalLoadBalancerVMService, VirtualMachineGuru {
static final private String InternalLbVmNamePrefix = "b";
private String _instance;
private String _mgmtHost;
private String _mgmtCidr;
private long _internalLbVmOfferingId = 0L;
@Inject
IpAddressManager _ipAddrMgr;
@Inject
VirtualMachineManager _itMgr;
@Inject
DomainRouterDao _internalLbVmDao;
@Inject
ConfigurationDao _configDao;
@Inject
AgentManager _agentMgr;
@Inject
DataCenterDao _dcDao;
@Inject
VirtualRouterProviderDao _vrProviderDao;
@Inject
ApplicationLoadBalancerRuleDao _lbDao;
@Inject
NetworkModel _ntwkModel;
@Inject
LoadBalancingRulesManager _lbMgr;
@Inject
NicDao _nicDao;
@Inject
AccountManager _accountMgr;
@Inject
NetworkDao _networkDao;
@Inject
NetworkOrchestrationService _ntwkMgr;
@Inject
ServiceOfferingDao _serviceOfferingDao;
@Inject
PhysicalNetworkServiceProviderDao _physicalProviderDao;
@Inject
NetworkOfferingDao _networkOfferingDao;
@Inject
VMTemplateDao _templateDao;
@Inject
ResourceManager _resourceMgr;
@Inject
UserDao _userDao;
@Override
public boolean finalizeVirtualMachineProfile(final VirtualMachineProfile profile, final DeployDestination dest, final ReservationContext context) {
//Internal LB vm starts up with 2 Nics
//Nic #1 - Guest Nic with IP address that would act as the LB entry point
//Nic #2 - Control/Management Nic
final StringBuilder buf = profile.getBootArgsBuilder();
buf.append(" template=domP");
buf.append(" name=").append(profile.getHostName());
if (Boolean.valueOf(_configDao.getValue("system.vm.random.password"))) {
buf.append(" vmpassword=").append(_configDao.getValue("system.vm.password"));
}
NicProfile controlNic = null;
Network guestNetwork = null;
for (final NicProfile nic : profile.getNics()) {
final int deviceId = nic.getDeviceId();
buf.append(" eth").append(deviceId).append("ip=").append(nic.getIPv4Address());
buf.append(" eth").append(deviceId).append("mask=").append(nic.getIPv4Netmask());
if (nic.isDefaultNic()) {
buf.append(" gateway=").append(nic.getIPv4Gateway());
buf.append(" dns1=").append(nic.getIPv4Gateway());
}
if (nic.getTrafficType() == TrafficType.Guest) {
guestNetwork = _ntwkModel.getNetwork(nic.getNetworkId());
} else if (nic.getTrafficType() == TrafficType.Management) {
buf.append(" localgw=").append(dest.getPod().getGateway());
} else if (nic.getTrafficType() == TrafficType.Control) {
controlNic = nic;
// Internal LB control command is sent over management server in VMware
if (dest.getHost().getHypervisorType() == VMware) {
if (logger.isInfoEnabled()) {
logger.info("Check if we need to add management server explicit route to Internal LB. pod cidr: " + dest.getPod().getCidrAddress() + "/" +
dest.getPod().getCidrSize() + ", pod gateway: " + dest.getPod().getGateway() + ", management host: " + _mgmtHost);
}
if (logger.isInfoEnabled()) {
logger.info("Add management server explicit route to Internal LB.");
}
buf.append(" mgmtcidr=").append(_mgmtCidr);
buf.append(" localgw=").append(dest.getPod().getGateway());
}
}
String msPublicKey = _configDao.getValue("ssh.publickey");
buf.append(" authorized_key=").append(VirtualMachineGuru.getEncodedMsPublicKey(msPublicKey));
}
if (controlNic == null) {
throw new CloudRuntimeException("Didn't start a control port");
}
if (guestNetwork != null) {
final String domain = guestNetwork.getNetworkDomain();
if (domain != null) {
buf.append(" domain=" + domain);
}
}
final String type = "ilbvm";
buf.append(" type=" + type);
if (logger.isDebugEnabled()) {
logger.debug("Boot Args for " + profile + ": " + buf.toString());
}
return true;
}
@Override
public boolean finalizeDeployment(final Commands cmds, final VirtualMachineProfile profile, final DeployDestination dest, final ReservationContext context)
throws ResourceUnavailableException {
final DomainRouterVO internalLbVm = _internalLbVmDao.findById(profile.getId());
final List<NicProfile> nics = profile.getNics();
for (final NicProfile nic : nics) {
if (nic.getTrafficType() == TrafficType.Control) {
internalLbVm.setPrivateIpAddress(nic.getIPv4Address());
internalLbVm.setPrivateMacAddress(nic.getMacAddress());
}
}
_internalLbVmDao.update(internalLbVm.getId(), internalLbVm);
finalizeCommandsOnStart(cmds, profile);
return true;
}
@Override
public boolean finalizeStart(final VirtualMachineProfile profile, final long hostId, final Commands cmds, final ReservationContext context) {
DomainRouterVO internalLbVm = _internalLbVmDao.findById(profile.getId());
boolean result = true;
Answer answer = cmds.getAnswer("checkSsh");
if (answer != null && answer instanceof CheckSshAnswer) {
final CheckSshAnswer sshAnswer = (CheckSshAnswer)answer;
if (sshAnswer == null || !sshAnswer.getResult()) {
logger.warn("Unable to ssh to the internal LB VM: " + sshAnswer.getDetails());
result = false;
}
} else {
result = false;
}
if (result == false) {
return result;
}
//Get guest network info
final List<Network> guestNetworks = new ArrayList<Network>();
final List<? extends Nic> internalLbVmNics = _nicDao.listByVmId(profile.getId());
for (final Nic internalLbVmNic : internalLbVmNics) {
final Network network = _ntwkModel.getNetwork(internalLbVmNic.getNetworkId());
if (network.getTrafficType() == TrafficType.Guest) {
guestNetworks.add(network);
}
}
answer = cmds.getAnswer("getDomRVersion");
if (answer != null && answer instanceof GetDomRVersionAnswer) {
final GetDomRVersionAnswer versionAnswer = (GetDomRVersionAnswer)answer;
if (answer == null || !answer.getResult()) {
logger.warn("Unable to get the template/scripts version of internal LB VM " + internalLbVm.getInstanceName() + " due to: " + versionAnswer.getDetails());
result = false;
} else {
internalLbVm.setTemplateVersion(versionAnswer.getTemplateVersion());
internalLbVm.setScriptsVersion(versionAnswer.getScriptsVersion());
internalLbVm = _internalLbVmDao.persist(internalLbVm, guestNetworks);
}
} else {
result = false;
}
return result;
}
@Override
public boolean finalizeCommandsOnStart(final Commands cmds, final VirtualMachineProfile profile) {
final DomainRouterVO internalLbVm = _internalLbVmDao.findById(profile.getId());
final NicProfile controlNic = getNicProfileByTrafficType(profile, TrafficType.Control);
if (controlNic == null) {
logger.error("Control network doesn't exist for the internal LB vm " + internalLbVm);
return false;
}
finalizeSshAndVersionOnStart(cmds, profile, internalLbVm, controlNic);
// restart network if restartNetwork = false is not specified in profile parameters
boolean reprogramGuestNtwk = true;
if (profile.getParameter(Param.ReProgramGuestNetworks) != null && (Boolean)profile.getParameter(Param.ReProgramGuestNetworks) == false) {
reprogramGuestNtwk = false;
}
final VirtualRouterProvider lbProvider = _vrProviderDao.findById(internalLbVm.getElementId());
if (lbProvider == null) {
throw new CloudRuntimeException("Cannot find related element " + Type.InternalLbVm + " of vm: " + internalLbVm.getHostName());
}
final Provider provider = Network.Provider.getProvider(lbProvider.getType().toString());
if (provider == null) {
throw new CloudRuntimeException("Cannot find related provider of provider: " + lbProvider.getType().toString());
}
if (reprogramGuestNtwk) {
final NicProfile guestNic = getNicProfileByTrafficType(profile, TrafficType.Guest);
finalizeLbRulesForIp(cmds, internalLbVm, provider, new Ip(guestNic.getIPv4Address()), guestNic.getNetworkId());
}
return true;
}
@Override
public void finalizeStop(final VirtualMachineProfile profile, final Answer answer) {
}
@Override
public void finalizeExpunge(final VirtualMachine vm) {
}
@Override
public void prepareStop(final VirtualMachineProfile profile) {
}
@Override
public void finalizeUnmanage(VirtualMachine vm) {
}
@Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
final Map<String, String> configs = _configDao.getConfiguration("AgentManager", params);
_instance = configs.get("instance.name");
if (_instance == null) {
_instance = "DEFAULT";
}
_mgmtHost = configs.get("host");
_mgmtCidr = _configDao.getValue(Config.ManagementNetwork.key());
final String offUUID = configs.get(Config.InternalLbVmServiceOfferingId.key());
if (offUUID != null && !offUUID.isEmpty()) {
//get the id by offering UUID
final ServiceOfferingVO off = _serviceOfferingDao.findByUuid(offUUID);
if (off != null) {
_internalLbVmOfferingId = off.getId();
} else {
logger.warn("Invalid offering UUID is passed in " + Config.InternalLbVmServiceOfferingId.key() + "; the default offering will be used instead");
}
}
//if offering wasn't set, try to get the default one
if (_internalLbVmOfferingId == 0L) {
List<ServiceOfferingVO> offerings = _serviceOfferingDao.createSystemServiceOfferings("System Offering For Internal LB VM",
ServiceOffering.internalLbVmDefaultOffUniqueName, 1, InternalLoadBalancerVMManager.DEFAULT_INTERNALLB_VM_RAMSIZE,
InternalLoadBalancerVMManager.DEFAULT_INTERNALLB_VM_CPU_MHZ, null, null, true, null,
Storage.ProvisioningType.THIN, true, null, true, VirtualMachine.Type.InternalLoadBalancerVm, true);
if (offerings == null || offerings.size() < 2) {
String msg = "Data integrity problem : System Offering For Internal LB VM has been removed?";
logger.error(msg);
throw new ConfigurationException(msg);
}
}
_itMgr.registerGuru(VirtualMachine.Type.InternalLoadBalancerVm, this);
if (logger.isInfoEnabled()) {
logger.info(getName() + " has been configured");
}
return true;
}
@Override
public String getName() {
return _name;
}
protected NicProfile getNicProfileByTrafficType(final VirtualMachineProfile profile, final TrafficType trafficType) {
for (final NicProfile nic : profile.getNics()) {
if (nic.getTrafficType() == trafficType && nic.getIPv4Address() != null) {
return nic;
}
}
return null;
}
protected void finalizeSshAndVersionOnStart(final Commands cmds, final VirtualMachineProfile profile, final DomainRouterVO router, final NicProfile controlNic) {
cmds.addCommand("checkSsh", new CheckSshCommand(profile.getInstanceName(), controlNic.getIPv4Address(), 3922));
// Update internal lb vm template/scripts version
final GetDomRVersionCmd command = new GetDomRVersionCmd();
command.setAccessDetail(NetworkElementCommand.ROUTER_IP, controlNic.getIPv4Address());
command.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
cmds.addCommand("getDomRVersion", command);
}
protected void finalizeLbRulesForIp(final Commands cmds, final DomainRouterVO internalLbVm, final Provider provider, final Ip sourceIp, final long guestNtwkId) {
logger.debug("Resending load balancing rules as a part of start for " + internalLbVm);
final List<ApplicationLoadBalancerRuleVO> lbs = _lbDao.listBySrcIpSrcNtwkId(sourceIp, guestNtwkId);
final List<LoadBalancingRule> lbRules = new ArrayList<LoadBalancingRule>();
if (_ntwkModel.isProviderSupportServiceInNetwork(guestNtwkId, Service.Lb, provider)) {
// Re-apply load balancing rules
for (final ApplicationLoadBalancerRuleVO lb : lbs) {
final List<LbDestination> dstList = _lbMgr.getExistingDestinations(lb.getId());
final List<LbStickinessPolicy> policyList = _lbMgr.getStickinessPolicies(lb.getId());
final List<LbHealthCheckPolicy> hcPolicyList = _lbMgr.getHealthCheckPolicies(lb.getId());
final LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList, hcPolicyList, sourceIp);
lbRules.add(loadBalancing);
}
}
logger.debug("Found " + lbRules.size() + " load balancing rule(s) to apply as a part of Intenrnal LB vm" + internalLbVm + " start.");
if (!lbRules.isEmpty()) {
createApplyLoadBalancingRulesCommands(lbRules, internalLbVm, cmds, guestNtwkId);
}
}
private void createApplyLoadBalancingRulesCommands(final List<LoadBalancingRule> rules, final VirtualRouter internalLbVm, final Commands cmds, final long guestNetworkId) {
final LoadBalancerTO[] lbs = new LoadBalancerTO[rules.size()];
int i = 0;
final boolean inline = false;
for (final LoadBalancingRule rule : rules) {
final boolean revoked = rule.getState().equals(FirewallRule.State.Revoke);
final String protocol = rule.getProtocol();
final String algorithm = rule.getAlgorithm();
final String uuid = rule.getUuid();
final String srcIp = rule.getSourceIp().addr();
final int srcPort = rule.getSourcePortStart();
final List<LbDestination> destinations = rule.getDestinations();
final List<LbStickinessPolicy> stickinessPolicies = rule.getStickinessPolicies();
final LoadBalancerTO lb = new LoadBalancerTO(uuid, srcIp, srcPort, protocol, algorithm, revoked, false, inline, destinations, stickinessPolicies);
lb.setCidrList(rule.getCidrList());
lbs[i++] = lb;
}
final Network guestNetwork = _ntwkModel.getNetwork(guestNetworkId);
final Nic guestNic = _nicDao.findByNtwkIdAndInstanceId(guestNetwork.getId(), internalLbVm.getId());
final NicProfile guestNicProfile =
new NicProfile(guestNic, guestNetwork, guestNic.getBroadcastUri(), guestNic.getIsolationUri(), _ntwkModel.getNetworkRate(guestNetwork.getId(),
internalLbVm.getId()), _ntwkModel.isSecurityGroupSupportedInNetwork(guestNetwork), _ntwkModel.getNetworkTag(internalLbVm.getHypervisorType(),
guestNetwork));
final NetworkOffering offering = _networkOfferingDao.findById(guestNetwork.getNetworkOfferingId());
String maxconn = null;
if (offering.getConcurrentConnections() == null) {
maxconn = _configDao.getValue(Config.NetworkLBHaproxyMaxConn.key());
} else {
maxconn = offering.getConcurrentConnections().toString();
}
final LoadBalancerConfigCommand cmd =
new LoadBalancerConfigCommand(lbs, guestNic.getIPv4Address(), guestNic.getIPv4Address(), internalLbVm.getPrivateIpAddress(), _itMgr.toNicTO(guestNicProfile,
internalLbVm.getHypervisorType()), internalLbVm.getVpcId(), maxconn, offering.isKeepAliveEnabled());
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());
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, getInternalLbControlIp(internalLbVm.getId()));
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, guestNic.getIPv4Address());
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, internalLbVm.getInstanceName());
final DataCenterVO dcVo = _dcDao.findById(internalLbVm.getDataCenterId());
cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString());
cmds.addCommand(cmd);
}
protected String getInternalLbControlIp(final long internalLbVmId) {
String controlIpAddress = null;
final List<NicVO> nics = _nicDao.listByVmId(internalLbVmId);
for (final NicVO nic : nics) {
final Network ntwk = _ntwkModel.getNetwork(nic.getNetworkId());
if (ntwk.getTrafficType() == TrafficType.Control) {
controlIpAddress = nic.getIPv4Address();
}
}
if (controlIpAddress == null) {
logger.warn("Unable to find Internal LB control ip in its attached NICs!. Internal LB vm: " + internalLbVmId);
final DomainRouterVO internalLbVm = _internalLbVmDao.findById(internalLbVmId);
return internalLbVm.getPrivateIpAddress();
}
return controlIpAddress;
}
@Override
public boolean destroyInternalLbVm(final long vmId, final Account caller, final Long callerUserId) throws ResourceUnavailableException, ConcurrentOperationException {
if (logger.isDebugEnabled()) {
logger.debug("Attempting to destroy Internal LB vm " + vmId);
}
final DomainRouterVO internalLbVm = _internalLbVmDao.findById(vmId);
if (internalLbVm == null) {
return true;
}
_accountMgr.checkAccess(caller, null, true, internalLbVm);
_itMgr.expunge(internalLbVm.getUuid());
_internalLbVmDao.remove(internalLbVm.getId());
return true;
}
@Override
public VirtualRouter stopInternalLbVm(final long vmId, final boolean forced, final Account caller, final long callerUserId) throws ConcurrentOperationException, ResourceUnavailableException {
final DomainRouterVO internalLbVm = _internalLbVmDao.findById(vmId);
if (internalLbVm == null || internalLbVm.getRole() != Role.INTERNAL_LB_VM) {
throw new InvalidParameterValueException("Can't find internal lb vm by id specified");
}
//check permissions
_accountMgr.checkAccess(caller, null, true, internalLbVm);
return stopInternalLbVm(internalLbVm, forced, caller, callerUserId);
}
protected VirtualRouter stopInternalLbVm(final DomainRouterVO internalLbVm, final boolean forced, final Account caller, final long callerUserId) throws ResourceUnavailableException,
ConcurrentOperationException {
logger.debug("Stopping internal lb vm " + internalLbVm);
try {
_itMgr.advanceStop(internalLbVm.getUuid(), forced);
return _internalLbVmDao.findById(internalLbVm.getId());
} catch (final OperationTimedoutException e) {
throw new CloudRuntimeException("Unable to stop " + internalLbVm, e);
}
}
@Override
public List<DomainRouterVO> deployInternalLbVm(final Network guestNetwork, final Ip requestedGuestIp, final DeployDestination dest, final Account owner, final Map<Param, Object> params)
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
final List<DomainRouterVO> internalLbVms = findOrDeployInternalLbVm(guestNetwork, requestedGuestIp, dest, owner, params);
return startInternalLbVms(params, internalLbVms);
}
protected List<DomainRouterVO> startInternalLbVms(final Map<Param, Object> params, final List<DomainRouterVO> internalLbVms) throws StorageUnavailableException,
InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
List<DomainRouterVO> runningInternalLbVms = null;
if (internalLbVms != null) {
runningInternalLbVms = new ArrayList<DomainRouterVO>();
} else {
logger.debug("Have no internal lb vms to start");
return null;
}
for (DomainRouterVO internalLbVm : internalLbVms) {
if (internalLbVm.getState() != VirtualMachine.State.Running) {
internalLbVm = startInternalLbVm(internalLbVm, _accountMgr.getSystemAccount(), User.UID_SYSTEM, params);
}
if (internalLbVm != null) {
runningInternalLbVms.add(internalLbVm);
}
}
return runningInternalLbVms;
}
@DB
protected List<DomainRouterVO> findOrDeployInternalLbVm(final Network guestNetwork, final Ip requestedGuestIp, final DeployDestination dest, final Account owner, final Map<Param, Object> params)
throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
List<DomainRouterVO> internalLbVms = new ArrayList<DomainRouterVO>();
final Network lock = _networkDao.acquireInLockTable(guestNetwork.getId(), NetworkOrchestrationService.NetworkLockTimeout.value());
if (lock == null) {
throw new ConcurrentOperationException("Unable to lock network " + guestNetwork.getId());
}
if (logger.isDebugEnabled()) {
logger.debug("Lock is acquired for network id " + lock.getId() + " as a part of internal lb startup in " + dest);
}
final long internalLbProviderId = getInternalLbProviderId(guestNetwork);
try {
assert guestNetwork.getState() == Network.State.Implemented || guestNetwork.getState() == Network.State.Setup ||
guestNetwork.getState() == Network.State.Implementing : "Network is not yet fully implemented: " + guestNetwork;
assert guestNetwork.getTrafficType() == TrafficType.Guest;
//deploy internal lb vm
final Pair<DeploymentPlan, List<DomainRouterVO>> planAndInternalLbVms = getDeploymentPlanAndInternalLbVms(dest, guestNetwork.getId(), requestedGuestIp);
internalLbVms = planAndInternalLbVms.second();
final DeploymentPlan plan = planAndInternalLbVms.first();
if (internalLbVms.size() > 0) {
logger.debug("Found " + internalLbVms.size() + " internal lb vms for the requested IP " + requestedGuestIp.addr());
return internalLbVms;
}
final LinkedHashMap<Network, List<? extends NicProfile>> networks = createInternalLbVmNetworks(guestNetwork, plan, requestedGuestIp);
long internalLbVmOfferingId = _internalLbVmOfferingId;
if (internalLbVmOfferingId == 0L) {
ServiceOfferingVO serviceOffering = _serviceOfferingDao.findDefaultSystemOffering(ServiceOffering.internalLbVmDefaultOffUniqueName, ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dest.getDataCenter().getId()));
internalLbVmOfferingId = serviceOffering.getId();
}
//Pass startVm=false as we are holding the network lock that needs to be released at the end of vm allocation
final DomainRouterVO internalLbVm =
deployInternalLbVm(owner, dest, plan, params, internalLbProviderId, internalLbVmOfferingId, guestNetwork.getVpcId(), networks, false);
if (internalLbVm != null) {
_internalLbVmDao.addRouterToGuestNetwork(internalLbVm, guestNetwork);
internalLbVms.add(internalLbVm);
}
} finally {
if (lock != null) {
_networkDao.releaseFromLockTable(lock.getId());
if (logger.isDebugEnabled()) {
logger.debug("Lock is released for network id " + lock.getId() + " as a part of internal lb vm startup in " + dest);
}
}
}
return internalLbVms;
}
protected long getInternalLbProviderId(final Network guestNetwork) {
final Type type = Type.InternalLbVm;
final long physicalNetworkId = _ntwkModel.getPhysicalNetworkId(guestNetwork);
final PhysicalNetworkServiceProvider provider = _physicalProviderDao.findByServiceProvider(physicalNetworkId, type.toString());
if (provider == null) {
throw new CloudRuntimeException("Cannot find service provider " + type.toString() + " in physical network " + physicalNetworkId);
}
final VirtualRouterProvider internalLbProvider = _vrProviderDao.findByNspIdAndType(provider.getId(), type);
if (internalLbProvider == null) {
throw new CloudRuntimeException("Cannot find provider " + type.toString() + " as service provider " + provider.getId());
}
return internalLbProvider.getId();
}
protected LinkedHashMap<Network, List<? extends NicProfile>> createInternalLbVmNetworks(final Network guestNetwork, final DeploymentPlan plan, final Ip guestIp) throws ConcurrentOperationException,
InsufficientAddressCapacityException {
//Form networks
final LinkedHashMap<Network, List<? extends NicProfile>> networks = new LinkedHashMap<Network, List<? extends NicProfile>>(3);
//1) Guest network - default
if (guestNetwork != null) {
logger.debug("Adding nic for Internal LB in Guest network " + guestNetwork);
final NicProfile guestNic = new NicProfile();
if (guestIp != null) {
guestNic.setIPv4Address(guestIp.addr());
} else {
guestNic.setIPv4Address(_ipAddrMgr.acquireGuestIpAddress(guestNetwork, null));
}
guestNic.setIPv4Gateway(guestNetwork.getGateway());
guestNic.setBroadcastUri(guestNetwork.getBroadcastUri());
guestNic.setBroadcastType(guestNetwork.getBroadcastDomainType());
guestNic.setIsolationUri(guestNetwork.getBroadcastUri());
guestNic.setMode(guestNetwork.getMode());
final String gatewayCidr = guestNetwork.getCidr();
guestNic.setIPv4Netmask(NetUtils.getCidrNetmask(gatewayCidr));
guestNic.setDefaultNic(true);
networks.put(guestNetwork, new ArrayList<NicProfile>(Arrays.asList(guestNic)));
}
//2) Control network
logger.debug("Adding nic for Internal LB vm in Control network ");
final List<? extends NetworkOffering> offerings = _ntwkModel.getSystemAccountNetworkOfferings(NetworkOffering.SystemControlNetwork);
final NetworkOffering controlOffering = offerings.get(0);
final Network controlConfig = _ntwkMgr.setupNetwork(_accountMgr.getSystemAccount(), controlOffering, plan, null, null, false).get(0);
networks.put(controlConfig, new ArrayList<NicProfile>());
return networks;
}
protected Pair<DeploymentPlan, List<DomainRouterVO>> getDeploymentPlanAndInternalLbVms(final DeployDestination dest, final long guestNetworkId, final Ip requestedGuestIp) {
final long dcId = dest.getDataCenter().getId();
final DeploymentPlan plan = new DataCenterDeployment(dcId);
final List<DomainRouterVO> internalLbVms = findInternalLbVms(guestNetworkId, requestedGuestIp);
return new Pair<DeploymentPlan, List<DomainRouterVO>>(plan, internalLbVms);
}
@Override
public List<DomainRouterVO> findInternalLbVms(final long guestNetworkId, final Ip requestedGuestIp) {
final List<DomainRouterVO> internalLbVms = _internalLbVmDao.listByNetworkAndRole(guestNetworkId, Role.INTERNAL_LB_VM);
if (requestedGuestIp != null && !internalLbVms.isEmpty()) {
final Iterator<DomainRouterVO> it = internalLbVms.iterator();
while (it.hasNext()) {
final DomainRouterVO vm = it.next();
final Nic nic = _nicDao.findByNtwkIdAndInstanceId(guestNetworkId, vm.getId());
if (!nic.getIPv4Address().equalsIgnoreCase(requestedGuestIp.addr())) {
it.remove();
}
}
}
return internalLbVms;
}
protected DomainRouterVO deployInternalLbVm(final Account owner, final DeployDestination dest, final DeploymentPlan plan, final Map<Param, Object> params, final long internalLbProviderId,
final long svcOffId, final Long vpcId, final LinkedHashMap<Network, List<? extends NicProfile>> networks, final boolean startVm) throws ConcurrentOperationException,
InsufficientAddressCapacityException, InsufficientServerCapacityException, InsufficientCapacityException, StorageUnavailableException,
ResourceUnavailableException {
final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(svcOffId);
// Internal lb is the network element, we don't know the hypervisor type yet.
// Try to allocate the internal lb twice using diff hypervisors, and when failed both times, throw the exception up
final List<HypervisorType> hypervisors = getHypervisors(dest, plan, null);
int allocateRetry = 0;
int startRetry = 0;
DomainRouterVO internalLbVm = null;
for (final Iterator<HypervisorType> iter = hypervisors.iterator(); iter.hasNext();) {
final HypervisorType hType = iter.next();
try {
final long id = _internalLbVmDao.getNextInSequence(Long.class, "id");
if (logger.isDebugEnabled()) {
logger.debug("Creating the internal lb vm " + id + " in datacenter " + dest.getDataCenter() + " with hypervisor type " + hType);
}
String templateName = null;
if (hType.equals(XenServer)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateXen.valueIn(dest.getDataCenter().getId());
} else if (hType.equals(KVM)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateKvm.valueIn(dest.getDataCenter().getId());
} else if (hType.equals(VMware)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateVmware.valueIn(dest.getDataCenter().getId());
} else if (hType.equals(Hyperv)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateHyperV.valueIn(dest.getDataCenter().getId());
} else if (hType.equals(LXC)) {
templateName = VirtualNetworkApplianceManager.RouterTemplateLxc.valueIn(dest.getDataCenter().getId());
}
final VMTemplateVO template = _templateDao.findRoutingTemplate(hType, templateName);
if (template == null) {
logger.debug(hType + " won't support system vm, skip it");
continue;
}
long userId = CallContext.current().getCallingUserId();
if (CallContext.current().getCallingAccount().getId() != owner.getId()) {
List<UserVO> userVOs = _userDao.listByAccount(owner.getAccountId());
if (!userVOs.isEmpty()) {
userId = userVOs.get(0).getId();
}
}
internalLbVm =
new DomainRouterVO(id, routerOffering.getId(), internalLbProviderId, VirtualMachineName.getSystemVmName(id, _instance, InternalLbVmNamePrefix),
template.getId(), template.getHypervisorType(), template.getGuestOSId(), owner.getDomainId(), owner.getId(), userId, false, RedundantState.UNKNOWN, false, false, VirtualMachine.Type.InternalLoadBalancerVm, vpcId);
internalLbVm.setRole(Role.INTERNAL_LB_VM);
internalLbVm = _internalLbVmDao.persist(internalLbVm);
_itMgr.allocate(internalLbVm.getInstanceName(), template, routerOffering, networks, plan, null);
internalLbVm = _internalLbVmDao.findById(internalLbVm.getId());
} catch (final InsufficientCapacityException ex) {
if (allocateRetry < 2 && iter.hasNext()) {
logger.debug("Failed to allocate the Internal lb vm with hypervisor type " + hType + ", retrying one more time");
continue;
} else {
throw ex;
}
} finally {
allocateRetry++;
}
if (startVm) {
try {
internalLbVm = startInternalLbVm(internalLbVm, _accountMgr.getSystemAccount(), User.UID_SYSTEM, params);
break;
} catch (final InsufficientCapacityException ex) {
if (startRetry < 2 && iter.hasNext()) {
logger.debug("Failed to start the Internal lb vm " + internalLbVm + " with hypervisor type " + hType + ", " +
"destroying it and recreating one more time");
// destroy the internal lb vm
destroyInternalLbVm(internalLbVm.getId(), _accountMgr.getSystemAccount(), User.UID_SYSTEM);
continue;
} else {
throw ex;
}
} finally {
startRetry++;
}
} else {
//return stopped internal lb vm
return internalLbVm;
}
}
return internalLbVm;
}
protected DomainRouterVO startInternalLbVm(DomainRouterVO internalLbVm, final Account caller, final long callerUserId, final Map<Param, Object> params)
throws StorageUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
logger.debug("Starting Internal LB VM " + internalLbVm);
_itMgr.start(internalLbVm.getUuid(), params, null, null);
if (internalLbVm.isStopPending()) {
logger.info("Clear the stop pending flag of Internal LB VM " + internalLbVm.getHostName() + " after start router successfully!");
internalLbVm.setStopPending(false);
internalLbVm = _internalLbVmDao.persist(internalLbVm);
}
return _internalLbVmDao.findById(internalLbVm.getId());
}
protected List<HypervisorType> getHypervisors(final DeployDestination dest, final DeploymentPlan plan, final List<HypervisorType> supportedHypervisors)
throws InsufficientServerCapacityException {
List<HypervisorType> hypervisors = new ArrayList<HypervisorType>();
final HypervisorType defaults = _resourceMgr.getDefaultHypervisor(dest.getDataCenter().getId());
if (defaults != HypervisorType.None) {
hypervisors.add(defaults);
} else {
//if there is no default hypervisor, get it from the cluster
hypervisors = _resourceMgr.getSupportedHypervisorTypes(dest.getDataCenter().getId(), true, plan.getPodId());
}
//keep only elements defined in supported hypervisors
final StringBuilder hTypesStr = new StringBuilder();
if (supportedHypervisors != null && !supportedHypervisors.isEmpty()) {
hypervisors.retainAll(supportedHypervisors);
for (final HypervisorType hType : supportedHypervisors) {
hTypesStr.append(hType).append(" ");
}
}
if (hypervisors.isEmpty()) {
throw new InsufficientServerCapacityException("Unable to create internal lb vm, " + "there are no clusters in the zone ", DataCenter.class,
dest.getDataCenter().getId());
}
return hypervisors;
}
@Override
public boolean applyLoadBalancingRules(final Network network, final List<LoadBalancingRule> rules, final List<? extends VirtualRouter> internalLbVms)
throws ResourceUnavailableException {
if (rules == null || rules.isEmpty()) {
logger.debug("No lb rules to be applied for network " + network);
return true;
}
logger.info("lb rules to be applied for network ");
//only one internal lb vm is supported per ip address at this time
if (internalLbVms == null || internalLbVms.isEmpty()) {
throw new CloudRuntimeException("Can't apply the lb rules on network " + network + " as the list of internal lb vms is empty");
}
final VirtualRouter lbVm = internalLbVms.get(0);
if (lbVm.getState() == State.Running) {
return sendLBRules(lbVm, rules, network.getId());
} else if (lbVm.getState() == State.Stopped || lbVm.getState() == State.Stopping) {
logger.debug("Internal LB VM " + lbVm.getInstanceName() + " is in " + lbVm.getState() + ", so not sending apply lb rules commands to the backend");
return true;
} else {
logger.warn("Unable to apply lb rules, Internal LB VM is not in the right state " + lbVm.getState());
throw new ResourceUnavailableException("Unable to apply lb rules; Internal LB VM is not in the right state", DataCenter.class, lbVm.getDataCenterId());
}
}
protected boolean sendLBRules(final VirtualRouter internalLbVm, final List<LoadBalancingRule> rules, final long guestNetworkId) throws ResourceUnavailableException {
final Commands cmds = new Commands(Command.OnError.Continue);
createApplyLoadBalancingRulesCommands(rules, internalLbVm, cmds, guestNetworkId);
return sendCommandsToInternalLbVm(internalLbVm, cmds);
}
protected boolean sendCommandsToInternalLbVm(final VirtualRouter internalLbVm, final Commands cmds) throws AgentUnavailableException {
Answer[] answers = null;
try {
answers = _agentMgr.send(internalLbVm.getHostId(), cmds);
} catch (final OperationTimedoutException e) {
logger.warn("Timed Out", e);
throw new AgentUnavailableException("Unable to send commands to virtual router ", internalLbVm.getHostId(), e);
}
if (answers == null) {
return false;
}
if (answers.length != cmds.size()) {
return false;
}
boolean result = true;
if (answers.length > 0) {
for (final Answer answer : answers) {
if (!answer.getResult()) {
result = false;
break;
}
}
}
return result;
}
@Override
public VirtualRouter startInternalLbVm(final long internalLbVmId, final Account caller, final long callerUserId) throws StorageUnavailableException, InsufficientCapacityException,
ConcurrentOperationException, ResourceUnavailableException {
final DomainRouterVO internalLbVm = _internalLbVmDao.findById(internalLbVmId);
if (internalLbVm == null || internalLbVm.getRole() != Role.INTERNAL_LB_VM) {
throw new InvalidParameterValueException("Can't find internal lb vm by id specified");
}
//check permissions
_accountMgr.checkAccess(caller, null, true, internalLbVm);
return startInternalLbVm(internalLbVm, caller, callerUserId, null);
}
}