This is an automated email from the ASF dual-hosted git repository. rohit pushed a commit to branch 4.12 in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 0649821a211399ebc459c6e8b98c38da350af3ba Merge: a2323e1 9ac32aa Author: Rohit Yadav <rohit.ya...@shapeblue.com> AuthorDate: Mon Jul 1 14:37:40 2019 +0530 Merge remote-tracking branch 'origin/4.11' into 4.12 Signed-off-by: Rohit Yadav <rohit.ya...@shapeblue.com> .../src/main/java/com/cloud/network/router/NetworkHelperImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --cc server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java index 25e2793,0000000..da07bb5 mode 100644,000000..100644 --- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java +++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java @@@ -1,881 -1,0 +1,886 @@@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.router; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import org.apache.log4j.Logger; +import org.cloud.network.router.deployment.RouterDeploymentDefinition; + +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.utils.CloudStackVersion; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.NicTO; +import com.cloud.agent.manager.Commands; +import com.cloud.alert.AlertManager; +import com.cloud.configuration.Config; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; +import com.cloud.dc.Pod; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.deploy.DataCenterDeployment; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +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.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.IpAddressManager; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.Networks.IsolationType; +import com.cloud.network.addr.PublicIp; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.UserIpv6AddressDao; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.router.VirtualRouter.RedundantState; +import com.cloud.network.router.VirtualRouter.Role; +import com.cloud.network.rules.LbStickinessMethod; +import com.cloud.network.vpn.Site2SiteVpnManager; +import com.cloud.offering.NetworkOffering; +import com.cloud.resource.ResourceManager; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VolumeDao; +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.exception.CloudRuntimeException; +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.VirtualMachine.State; +import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VirtualMachineName; +import com.cloud.vm.VirtualMachineProfile.Param; +import com.cloud.vm.dao.DomainRouterDao; +import com.cloud.vm.dao.NicDao; + +public class NetworkHelperImpl implements NetworkHelper { + + private static final Logger s_logger = Logger.getLogger(NetworkHelperImpl.class); + + protected static Account s_systemAccount; + protected static String s_vmInstanceName; + + @Inject + protected NicDao _nicDao; + @Inject + protected NetworkDao _networkDao; + @Inject + protected DomainRouterDao _routerDao; + @Inject + private AgentManager _agentMgr; + @Inject + private AlertManager _alertMgr; + @Inject + protected NetworkModel _networkModel; + @Inject + private AccountManager _accountMgr; + @Inject + private Site2SiteVpnManager _s2sVpnMgr; + @Inject + private HostDao _hostDao; + @Inject + private VolumeDao _volumeDao; + @Inject + private VMTemplateDao _templateDao; + @Inject + private ResourceManager _resourceMgr; + @Inject + private ClusterDao _clusterDao; + @Inject + protected IPAddressDao _ipAddressDao; + @Inject + private UserIpv6AddressDao _ipv6Dao; + @Inject + protected NetworkOrchestrationService _networkMgr; + @Inject + private UserDao _userDao; + @Inject + protected ServiceOfferingDao _serviceOfferingDao; + @Inject + protected VirtualMachineManager _itMgr; + @Inject + protected IpAddressManager _ipAddrMgr; + @Inject + ConfigurationDao _configDao; + + protected final Map<HypervisorType, ConfigKey<String>> hypervisorsMap = new HashMap<>(); + + @PostConstruct + protected void setupHypervisorsMap() { + hypervisorsMap.put(HypervisorType.XenServer, VirtualNetworkApplianceManager.RouterTemplateXen); + hypervisorsMap.put(HypervisorType.KVM, VirtualNetworkApplianceManager.RouterTemplateKvm); + hypervisorsMap.put(HypervisorType.VMware, VirtualNetworkApplianceManager.RouterTemplateVmware); + hypervisorsMap.put(HypervisorType.Hyperv, VirtualNetworkApplianceManager.RouterTemplateHyperV); + hypervisorsMap.put(HypervisorType.LXC, VirtualNetworkApplianceManager.RouterTemplateLxc); + hypervisorsMap.put(HypervisorType.Ovm3, VirtualNetworkApplianceManager.RouterTemplateOvm3); + } + + @Override + public boolean sendCommandsToRouter(final VirtualRouter router, final Commands cmds) throws AgentUnavailableException, ResourceUnavailableException { + if (!checkRouterVersion(router)) { + s_logger.debug("Router requires upgrade. Unable to send command to router:" + router.getId() + ", router template version : " + router.getTemplateVersion() + + ", minimal required version : " + NetworkOrchestrationService.MinVRVersion.valueIn(router.getDataCenterId())); + throw new ResourceUnavailableException("Unable to send command. Router requires upgrade", VirtualRouter.class, router.getId()); + } + Answer[] answers = null; + try { + answers = _agentMgr.send(router.getHostId(), cmds); + } catch (final OperationTimedoutException e) { + s_logger.warn("Timed Out", e); + throw new AgentUnavailableException("Unable to send commands to virtual router ", router.getHostId(), e); + } + + if (answers == null || answers.length != cmds.size()) { + return false; + } + + // FIXME: Have to return state for individual command in the future + boolean result = true; + for (final Answer answer : answers) { + if (!answer.getResult()) { + result = false; + break; + } + } + return result; + } + + @Override + public void handleSingleWorkingRedundantRouter(final List<? extends VirtualRouter> connectedRouters, final List<? extends VirtualRouter> disconnectedRouters, + final String reason) throws ResourceUnavailableException { + if (connectedRouters.isEmpty() || disconnectedRouters.isEmpty()) { + return; + } + + for (final VirtualRouter virtualRouter : connectedRouters) { + if (!virtualRouter.getIsRedundantRouter()) { + throw new ResourceUnavailableException("Who is calling this with non-redundant router or non-domain router?", DataCenter.class, virtualRouter.getDataCenterId()); + } + } + + for (final VirtualRouter virtualRouter : disconnectedRouters) { + if (!virtualRouter.getIsRedundantRouter()) { + throw new ResourceUnavailableException("Who is calling this with non-redundant router or non-domain router?", DataCenter.class, virtualRouter.getDataCenterId()); + } + } + + final DomainRouterVO connectedRouter = (DomainRouterVO) connectedRouters.get(0); + DomainRouterVO disconnectedRouter = (DomainRouterVO) disconnectedRouters.get(0); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("About to stop the router " + disconnectedRouter.getInstanceName() + " due to: " + reason); + } + final String title = "Virtual router " + disconnectedRouter.getInstanceName() + " would be stopped after connecting back, due to " + reason; + final String context = "Virtual router (name: " + disconnectedRouter.getInstanceName() + ", id: " + disconnectedRouter.getId() + + ") would be stopped after connecting back, due to: " + reason; + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER, disconnectedRouter.getDataCenterId(), disconnectedRouter.getPodIdToDeployIn(), title, context); + disconnectedRouter.setStopPending(true); + disconnectedRouter = _routerDao.persist(disconnectedRouter); + } + + @Override + public NicTO getNicTO(final VirtualRouter router, final Long networkId, final String broadcastUri) { + final NicProfile nicProfile = _networkModel.getNicProfile(router, networkId, broadcastUri); + + return _itMgr.toNicTO(nicProfile, router.getHypervisorType()); + } + + @Override + public VirtualRouter destroyRouter(final long routerId, final Account caller, final Long callerUserId) throws ResourceUnavailableException, ConcurrentOperationException { + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Attempting to destroy router " + routerId); + } + + final DomainRouterVO router = _routerDao.findById(routerId); + if (router == null) { + return null; + } + + _accountMgr.checkAccess(caller, null, true, router); + + _itMgr.expunge(router.getUuid()); + _routerDao.remove(router.getId()); + return router; + } + + @Override + public boolean checkRouterVersion(final VirtualRouter router) { + if (!VirtualNetworkApplianceManagerImpl.routerVersionCheckEnabled.value()) { + // Router version check is disabled. + return true; + } + if (router.getTemplateVersion() == null) { + return false; + } + final long dcid = router.getDataCenterId(); + String routerVersion = CloudStackVersion.trimRouterVersion(router.getTemplateVersion()); + return CloudStackVersion.compare(routerVersion, NetworkOrchestrationService.MinVRVersion.valueIn(dcid)) >= 0; + } + + protected DomainRouterVO start(DomainRouterVO router, final User user, final Account caller, final Map<Param, Object> params, final DeploymentPlan planToDeploy) + throws StorageUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { + s_logger.debug("Starting router " + router); + try { + _itMgr.advanceStart(router.getUuid(), params, planToDeploy, null); + } catch (final OperationTimedoutException e) { + throw new ResourceUnavailableException("Starting router " + router + " failed! " + e.toString(), DataCenter.class, router.getDataCenterId()); + } + if (router.isStopPending()) { + s_logger.info("Clear the stop pending flag of router " + router.getHostName() + " after start router successfully!"); + router.setStopPending(false); + router = _routerDao.persist(router); + } + // We don't want the failure of VPN Connection affect the status of + // router, so we try to make connection + // only after router start successfully + final Long vpcId = router.getVpcId(); + if (vpcId != null) { + _s2sVpnMgr.reconnectDisconnectedVpnByVpc(vpcId); + } + return _routerDao.findById(router.getId()); + } + + protected DomainRouterVO waitRouter(final DomainRouterVO router) { + DomainRouterVO vm = _routerDao.findById(router.getId()); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router " + router.getInstanceName() + " is not fully up yet, we will wait"); + } + while (vm.getState() == State.Starting) { + try { + Thread.sleep(1000); + } catch (final InterruptedException e) { + } + + // reload to get the latest state info + vm = _routerDao.findById(router.getId()); + } + + if (vm.getState() == State.Running) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Router " + router.getInstanceName() + " is now fully up"); + } + + return router; + } + + s_logger.warn("Router " + router.getInstanceName() + " failed to start. current state: " + vm.getState()); + return null; + } + + @Override + public List<DomainRouterVO> startRouters(final RouterDeploymentDefinition routerDeploymentDefinition) throws StorageUnavailableException, InsufficientCapacityException, + ConcurrentOperationException, ResourceUnavailableException { + + final List<DomainRouterVO> runningRouters = new ArrayList<DomainRouterVO>(); + + for (DomainRouterVO router : routerDeploymentDefinition.getRouters()) { + boolean skip = false; + final State state = router.getState(); + if (router.getHostId() != null && state != State.Running) { + final HostVO host = _hostDao.findById(router.getHostId()); + if (host == null || host.getState() != Status.Up) { + skip = true; + } + } + if (!skip) { + if (state != State.Running) { + router = startVirtualRouter(router, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount(), routerDeploymentDefinition.getParams()); + } + if (router != null) { + runningRouters.add(router); + } + } + } + return runningRouters; + } + + @Override + public DomainRouterVO startVirtualRouter(final DomainRouterVO router, final User user, final Account caller, final Map<Param, Object> params) + throws StorageUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { + + if (router.getRole() != Role.VIRTUAL_ROUTER || !router.getIsRedundantRouter()) { + return start(router, user, caller, params, null); + } + + if (router.getState() == State.Running) { + s_logger.debug("Redundant router " + router.getInstanceName() + " is already running!"); + return router; + } + + // + // If another thread has already requested a VR start, there is a + // transition period for VR to transit from + // Starting to Running, there exist a race conditioning window here + // We will wait until VR is up or fail + if (router.getState() == State.Starting) { + return waitRouter(router); + } + + final DataCenterDeployment plan = new DataCenterDeployment(0, null, null, null, null, null); + DomainRouterVO result = null; + assert router.getIsRedundantRouter(); + final List<Long> networkIds = _routerDao.getRouterNetworks(router.getId()); + + DomainRouterVO routerToBeAvoid = null; ++ List<DomainRouterVO> routerList = null; + if (networkIds.size() != 0) { - final List<DomainRouterVO> routerList = _routerDao.findByNetwork(networkIds.get(0)); ++ routerList = _routerDao.findByNetwork(networkIds.get(0)); ++ } else if (router.getVpcId() != null) { ++ routerList = _routerDao.listByVpcId(router.getVpcId()); ++ } ++ if (routerList != null) { + for (final DomainRouterVO rrouter : routerList) { + if (rrouter.getHostId() != null && rrouter.getIsRedundantRouter() && rrouter.getState() == State.Running) { + if (routerToBeAvoid != null) { + throw new ResourceUnavailableException("Try to start router " + router.getInstanceName() + "(" + router.getId() + ")" + + ", but there are already two redundant routers with IP " + router.getPublicIpAddress() + ", they are " + rrouter.getInstanceName() + "(" + + rrouter.getId() + ") and " + routerToBeAvoid.getInstanceName() + "(" + routerToBeAvoid.getId() + ")", DataCenter.class, + rrouter.getDataCenterId()); + } + routerToBeAvoid = rrouter; + } + } + } + if (routerToBeAvoid == null) { + return start(router, user, caller, params, null); + } + // We would try best to deploy the router to another place + final int retryIndex = 5; + final ExcludeList[] avoids = new ExcludeList[5]; + avoids[0] = new ExcludeList(); + avoids[0].addPod(routerToBeAvoid.getPodIdToDeployIn()); + avoids[1] = new ExcludeList(); + avoids[1].addCluster(_hostDao.findById(routerToBeAvoid.getHostId()).getClusterId()); + avoids[2] = new ExcludeList(); + final List<VolumeVO> volumes = _volumeDao.findByInstanceAndType(routerToBeAvoid.getId(), Volume.Type.ROOT); + if (volumes != null && volumes.size() != 0) { + avoids[2].addPool(volumes.get(0).getPoolId()); + } + avoids[2].addHost(routerToBeAvoid.getHostId()); + avoids[3] = new ExcludeList(); + avoids[3].addHost(routerToBeAvoid.getHostId()); + avoids[4] = new ExcludeList(); + + for (int i = 0; i < retryIndex; i++) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Try to deploy redundant virtual router:" + router.getHostName() + ", for " + i + " time"); + } + plan.setAvoids(avoids[i]); + try { + result = start(router, user, caller, params, plan); + } catch (final InsufficientServerCapacityException ex) { + result = null; + } + if (result != null) { + break; + } + } + return result; + } + + protected String retrieveTemplateName(final HypervisorType hType, final long datacenterId) { + String templateName = null; + + if (hType == HypervisorType.BareMetal) { + final ConfigKey<String> hypervisorConfigKey = hypervisorsMap.get(HypervisorType.VMware); + templateName = hypervisorConfigKey.valueIn(datacenterId); + } else { + // Returning NULL is fine because the simulator will need it when + // being used instead of a real hypervisor. + // The hypervisorsMap contains only real hypervisors. + final ConfigKey<String> hypervisorConfigKey = hypervisorsMap.get(hType); + + if (hypervisorConfigKey != null) { + templateName = hypervisorConfigKey.valueIn(datacenterId); + } + } + + return templateName; + } + + @Override + public DomainRouterVO deployRouter(final RouterDeploymentDefinition routerDeploymentDefinition, final boolean startRouter) + throws InsufficientAddressCapacityException, InsufficientServerCapacityException, InsufficientCapacityException, StorageUnavailableException, ResourceUnavailableException { + + final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(routerDeploymentDefinition.getServiceOfferingId()); + final Account owner = routerDeploymentDefinition.getOwner(); + + // Router is the network element, we don't know the hypervisor type yet. + // Try to allocate the domR twice using diff hypervisors, and when + // failed both times, throw the exception up + final List<HypervisorType> hypervisors = getHypervisors(routerDeploymentDefinition); + + int allocateRetry = 0; + int startRetry = 0; + DomainRouterVO router = null; + for (final Iterator<HypervisorType> iter = hypervisors.iterator(); iter.hasNext();) { + final HypervisorType hType = iter.next(); + try { + final long id = _routerDao.getNextInSequence(Long.class, "id"); + if (s_logger.isDebugEnabled()) { + s_logger.debug(String.format("Allocating the VR with id=%s in datacenter %s with the hypervisor type %s", id, routerDeploymentDefinition.getDest() + .getDataCenter(), hType)); + } + + final String templateName = retrieveTemplateName(hType, routerDeploymentDefinition.getDest().getDataCenter().getId()); + final VMTemplateVO template = _templateDao.findRoutingTemplate(hType, templateName); + + if (template == null) { + s_logger.debug(hType + " won't support system vm, skip it"); + continue; + } + + final boolean offerHA = routerOffering.isOfferHA(); + + // routerDeploymentDefinition.getVpc().getId() ==> do not use + // VPC because it is not a VPC offering. + final Long vpcId = routerDeploymentDefinition.getVpc() != null ? routerDeploymentDefinition.getVpc().getId() : null; + + long userId = CallContext.current().getCallingUserId(); + if (CallContext.current().getCallingAccount().getId() != owner.getId()) { + final List<UserVO> userVOs = _userDao.listByAccount(owner.getAccountId()); + if (!userVOs.isEmpty()) { + userId = userVOs.get(0).getId(); + } + } + + router = new DomainRouterVO(id, routerOffering.getId(), routerDeploymentDefinition.getVirtualProvider().getId(), VirtualMachineName.getRouterName(id, + s_vmInstanceName), template.getId(), template.getHypervisorType(), template.getGuestOSId(), owner.getDomainId(), owner.getId(), + userId, routerDeploymentDefinition.isRedundant(), RedundantState.UNKNOWN, offerHA, false, vpcId); + + router.setDynamicallyScalable(template.isDynamicallyScalable()); + router.setRole(Role.VIRTUAL_ROUTER); + router = _routerDao.persist(router); + + reallocateRouterNetworks(routerDeploymentDefinition, router, template, null); + router = _routerDao.findById(router.getId()); + } catch (final InsufficientCapacityException ex) { + if (allocateRetry < 2 && iter.hasNext()) { + s_logger.debug("Failed to allocate the VR with hypervisor type " + hType + ", retrying one more time"); + continue; + } else { + throw ex; + } + } finally { + allocateRetry++; + } + + if (startRouter) { + try { + router = startVirtualRouter(router, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount(), routerDeploymentDefinition.getParams()); + break; + } catch (final InsufficientCapacityException ex) { + if (startRetry < 2 && iter.hasNext()) { + s_logger.debug("Failed to start the VR " + router + " with hypervisor type " + hType + ", " + "destroying it and recreating one more time"); + // destroy the router + destroyRouter(router.getId(), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM), User.UID_SYSTEM); + continue; + } else { + throw ex; + } + } finally { + startRetry++; + } + } else { + // return stopped router + return router; + } + } + + return router; + } + + protected void filterSupportedHypervisors(final List<HypervisorType> hypervisors) { + // For non vpc we keep them all assuming all types in the list are + // supported + } + + protected String getNoHypervisorsErrMsgDetails() { + return ""; + } + + protected List<HypervisorType> getHypervisors(final RouterDeploymentDefinition routerDeploymentDefinition) throws InsufficientServerCapacityException { + final DeployDestination dest = routerDeploymentDefinition.getDest(); + List<HypervisorType> hypervisors = new ArrayList<HypervisorType>(); + if (dest.getCluster() != null) { + if (dest.getCluster().getHypervisorType() == HypervisorType.Ovm) { + hypervisors.add(getClusterToStartDomainRouterForOvm(dest.getCluster().getPodId())); + } else { + hypervisors.add(dest.getCluster().getHypervisorType()); + } + } else { + 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, routerDeploymentDefinition.getPlan().getPodId()); + } + } + + filterSupportedHypervisors(hypervisors); + + if (hypervisors.isEmpty()) { + if (routerDeploymentDefinition.getPodId() != null) { + throw new InsufficientServerCapacityException("Unable to create virtual router, there are no clusters in the pod." + getNoHypervisorsErrMsgDetails(), Pod.class, + routerDeploymentDefinition.getPodId()); + } + throw new InsufficientServerCapacityException("Unable to create virtual router, there are no clusters in the zone." + getNoHypervisorsErrMsgDetails(), + DataCenter.class, dest.getDataCenter().getId()); + } + return hypervisors; + } + + /* + * Ovm won't support any system. So we have to choose a partner cluster in + * the same pod to start domain router for us + */ + protected HypervisorType getClusterToStartDomainRouterForOvm(final long podId) { + final List<ClusterVO> clusters = _clusterDao.listByPodId(podId); + for (final ClusterVO cv : clusters) { + if (cv.getHypervisorType() == HypervisorType.Ovm || cv.getHypervisorType() == HypervisorType.BareMetal) { + continue; + } + + final List<HostVO> hosts = _resourceMgr.listAllHostsInCluster(cv.getId()); + if (hosts == null || hosts.isEmpty()) { + continue; + } + + for (final HostVO h : hosts) { + if (h.getState() == Status.Up) { + s_logger.debug("Pick up host that has hypervisor type " + h.getHypervisorType() + " in cluster " + cv.getId() + " to start domain router for OVM"); + return h.getHypervisorType(); + } + } + } + + final String errMsg = new StringBuilder("Cannot find an available cluster in Pod ").append(podId) + .append(" to start domain router for Ovm. \n Ovm won't support any system vm including domain router, ") + .append("please make sure you have a cluster with hypervisor type of any of xenserver/KVM/Vmware in the same pod") + .append(" with Ovm cluster. And there is at least one host in UP status in that cluster.").toString(); + throw new CloudRuntimeException(errMsg); + } + + protected LinkedHashMap<Network, List<? extends NicProfile>> configureControlNic(final RouterDeploymentDefinition routerDeploymentDefinition) { + final LinkedHashMap<Network, List<? extends NicProfile>> controlConfig = new LinkedHashMap<Network, List<? extends NicProfile>>(3); + + s_logger.debug("Adding nic for Virtual Router in Control network "); + final List<? extends NetworkOffering> offerings = _networkModel.getSystemAccountNetworkOfferings(NetworkOffering.SystemControlNetwork); + final NetworkOffering controlOffering = offerings.get(0); + final Network controlNic = _networkMgr.setupNetwork(s_systemAccount, controlOffering, routerDeploymentDefinition.getPlan(), null, null, false).get(0); + + controlConfig.put(controlNic, new ArrayList<NicProfile>()); + + return controlConfig; + } + + protected LinkedHashMap<Network, List<? extends NicProfile>> configurePublicNic(final RouterDeploymentDefinition routerDeploymentDefinition, final boolean hasGuestNic) { + final LinkedHashMap<Network, List<? extends NicProfile>> publicConfig = new LinkedHashMap<Network, List<? extends NicProfile>>(3); + + if (routerDeploymentDefinition.isPublicNetwork()) { + s_logger.debug("Adding nic for Virtual Router in Public network "); + // if source nat service is supported by the network, get the source + // nat ip address + final NicProfile defaultNic = new NicProfile(); + defaultNic.setDefaultNic(true); + final PublicIp sourceNatIp = routerDeploymentDefinition.getSourceNatIP(); + defaultNic.setIPv4Address(sourceNatIp.getAddress().addr()); + defaultNic.setIPv4Gateway(sourceNatIp.getGateway()); + defaultNic.setIPv4Netmask(sourceNatIp.getNetmask()); + defaultNic.setMacAddress(sourceNatIp.getMacAddress()); + // get broadcast from public network + final Network pubNet = _networkDao.findById(sourceNatIp.getNetworkId()); + if (pubNet.getBroadcastDomainType() == BroadcastDomainType.Vxlan) { + defaultNic.setBroadcastType(BroadcastDomainType.Vxlan); + defaultNic.setBroadcastUri(BroadcastDomainType.Vxlan.toUri(sourceNatIp.getVlanTag())); + defaultNic.setIsolationUri(BroadcastDomainType.Vxlan.toUri(sourceNatIp.getVlanTag())); + } else { + defaultNic.setBroadcastType(BroadcastDomainType.Vlan); + defaultNic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag())); + defaultNic.setIsolationUri(IsolationType.Vlan.toUri(sourceNatIp.getVlanTag())); + } + + //If guest nic has already been added we will have 2 devices in the list. + if (hasGuestNic) { + defaultNic.setDeviceId(2); + } + + final NetworkOffering publicOffering = _networkModel.getSystemAccountNetworkOfferings(NetworkOffering.SystemPublicNetwork).get(0); + final List<? extends Network> publicNetworks = _networkMgr.setupNetwork(s_systemAccount, publicOffering, routerDeploymentDefinition.getPlan(), null, null, false); + final String publicIp = defaultNic.getIPv4Address(); + // We want to use the identical MAC address for RvR on public + // interface if possible + final NicVO peerNic = _nicDao.findByIp4AddressAndNetworkId(publicIp, publicNetworks.get(0).getId()); + if (peerNic != null) { + s_logger.info("Use same MAC as previous RvR, the MAC is " + peerNic.getMacAddress()); + defaultNic.setMacAddress(peerNic.getMacAddress()); + } + publicConfig.put(publicNetworks.get(0), new ArrayList<NicProfile>(Arrays.asList(defaultNic))); + } + + return publicConfig; + } + + @Override + public LinkedHashMap<Network, List<? extends NicProfile>> configureDefaultNics(final RouterDeploymentDefinition routerDeploymentDefinition) throws ConcurrentOperationException, InsufficientAddressCapacityException { + + final LinkedHashMap<Network, List<? extends NicProfile>> networks = new LinkedHashMap<Network, List<? extends NicProfile>>(3); + + // 1) Guest Network + final LinkedHashMap<Network, List<? extends NicProfile>> guestNic = configureGuestNic(routerDeploymentDefinition); + networks.putAll(guestNic); + + // 2) Control network + final LinkedHashMap<Network, List<? extends NicProfile>> controlNic = configureControlNic(routerDeploymentDefinition); + networks.putAll(controlNic); + + // 3) Public network + final LinkedHashMap<Network, List<? extends NicProfile>> publicNic = configurePublicNic(routerDeploymentDefinition, networks.size() > 1); + networks.putAll(publicNic); + + return networks; + } + + @Override + public LinkedHashMap<Network, List<? extends NicProfile>> configureGuestNic(final RouterDeploymentDefinition routerDeploymentDefinition) + throws ConcurrentOperationException, InsufficientAddressCapacityException { + + // Form networks + final LinkedHashMap<Network, List<? extends NicProfile>> networks = new LinkedHashMap<Network, List<? extends NicProfile>>(3); + // 1) Guest network + final Network guestNetwork = routerDeploymentDefinition.getGuestNetwork(); + + if (guestNetwork != null) { + s_logger.debug("Adding nic for Virtual Router in Guest network " + guestNetwork); + String defaultNetworkStartIp = null, defaultNetworkStartIpv6 = null; + if (!routerDeploymentDefinition.isPublicNetwork()) { + final Nic placeholder = _networkModel.getPlaceholderNicForRouter(guestNetwork, routerDeploymentDefinition.getPodId()); + if (guestNetwork.getCidr() != null) { + if (placeholder != null && placeholder.getIPv4Address() != null) { + s_logger.debug("Requesting ipv4 address " + placeholder.getIPv4Address() + " stored in placeholder nic for the network " + + guestNetwork); + defaultNetworkStartIp = placeholder.getIPv4Address(); + } else { + final String startIp = _networkModel.getStartIpAddress(guestNetwork.getId()); + if (startIp != null + && _ipAddressDao.findByIpAndSourceNetworkId(guestNetwork.getId(), startIp).getAllocatedTime() == null) { + defaultNetworkStartIp = startIp; + } else if (s_logger.isDebugEnabled()) { + s_logger.debug("First ipv4 " + startIp + " in network id=" + guestNetwork.getId() + + " is already allocated, can't use it for domain router; will get random ip address from the range"); + } + } + } + + if (guestNetwork.getIp6Cidr() != null) { + if (placeholder != null && placeholder.getIPv6Address() != null) { + s_logger.debug("Requesting ipv6 address " + placeholder.getIPv6Address() + " stored in placeholder nic for the network " + + guestNetwork); + defaultNetworkStartIpv6 = placeholder.getIPv6Address(); + } else { + final String startIpv6 = _networkModel.getStartIpv6Address(guestNetwork.getId()); + if (startIpv6 != null && _ipv6Dao.findByNetworkIdAndIp(guestNetwork.getId(), startIpv6) == null) { + defaultNetworkStartIpv6 = startIpv6; + } else if (s_logger.isDebugEnabled()) { + s_logger.debug("First ipv6 " + startIpv6 + " in network id=" + guestNetwork.getId() + + " is already allocated, can't use it for domain router; will get random ipv6 address from the range"); + } + } + } + } + + final NicProfile gatewayNic = new NicProfile(defaultNetworkStartIp, defaultNetworkStartIpv6); + if (routerDeploymentDefinition.isPublicNetwork()) { + if (routerDeploymentDefinition.isRedundant()) { + gatewayNic.setIPv4Address(_ipAddrMgr.acquireGuestIpAddress(guestNetwork, null)); + } else { + gatewayNic.setIPv4Address(guestNetwork.getGateway()); + } + gatewayNic.setBroadcastUri(guestNetwork.getBroadcastUri()); + gatewayNic.setBroadcastType(guestNetwork.getBroadcastDomainType()); + gatewayNic.setIsolationUri(guestNetwork.getBroadcastUri()); + gatewayNic.setMode(guestNetwork.getMode()); + final String gatewayCidr = _networkModel.getValidNetworkCidr(guestNetwork); + gatewayNic.setIPv4Netmask(NetUtils.getCidrNetmask(gatewayCidr)); + } else { + gatewayNic.setDefaultNic(true); + } + + networks.put(guestNetwork, new ArrayList<NicProfile>(Arrays.asList(gatewayNic))); + } + return networks; + } + + @Override + public void reallocateRouterNetworks(final RouterDeploymentDefinition routerDeploymentDefinition, final VirtualRouter router, final VMTemplateVO template, final HypervisorType hType) + throws ConcurrentOperationException, InsufficientCapacityException { + final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(routerDeploymentDefinition.getServiceOfferingId()); + + final LinkedHashMap<Network, List<? extends NicProfile>> networks = configureDefaultNics(routerDeploymentDefinition); + + _itMgr.allocate(router.getInstanceName(), template, routerOffering, networks, routerDeploymentDefinition.getPlan(), hType); + } + + public static void setSystemAccount(final Account systemAccount) { + s_systemAccount = systemAccount; + } + + public static void setVMInstanceName(final String vmInstanceName) { + s_vmInstanceName = vmInstanceName; + } + @Override + public boolean validateHAProxyLBRule(final LoadBalancingRule rule) { + final String timeEndChar = "dhms"; + int haproxy_stats_port = Integer.parseInt(_configDao.getValue(Config.NetworkLBHaproxyStatsPort.key())); + if (rule.getSourcePortStart() == haproxy_stats_port) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Can't create LB on port "+ haproxy_stats_port +", haproxy is listening for LB stats on this port"); + } + return false; + } + + for (final LoadBalancingRule.LbStickinessPolicy stickinessPolicy : rule.getStickinessPolicies()) { + final List<Pair<String, String>> paramsList = stickinessPolicy.getParams(); + + if (LbStickinessMethod.StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) { + + } else if (LbStickinessMethod.StickinessMethodType.SourceBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) { + String tablesize = "200k"; // optional + String expire = "30m"; // optional + + /* overwrite default values with the stick parameters */ + for (final Pair<String, String> paramKV : paramsList) { + final String key = paramKV.first(); + final String value = paramKV.second(); + if ("tablesize".equalsIgnoreCase(key)) { + tablesize = value; + } + if ("expire".equalsIgnoreCase(key)) { + expire = value; + } + } + if (expire != null && !containsOnlyNumbers(expire, timeEndChar)) { + throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: expire is not in timeformat: " + expire); + } + if (tablesize != null && !containsOnlyNumbers(tablesize, "kmg")) { + throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: tablesize is not in size format: " + tablesize); + + } + } else if (LbStickinessMethod.StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) { + String length = null; // optional + String holdTime = null; // optional + + for (final Pair<String, String> paramKV : paramsList) { + final String key = paramKV.first(); + final String value = paramKV.second(); + if ("length".equalsIgnoreCase(key)) { + length = value; + } + if ("holdtime".equalsIgnoreCase(key)) { + holdTime = value; + } + } + + if (length != null && !containsOnlyNumbers(length, null)) { + throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: length is not a number: " + length); + } + if (holdTime != null && !containsOnlyNumbers(holdTime, timeEndChar) && !containsOnlyNumbers(holdTime, null)) { + throw new InvalidParameterValueException("Failed LB in validation rule id: " + rule.getId() + " Cause: holdtime is not in timeformat: " + holdTime); + } + } + } + return true; + } + + /* + * This function detects numbers like 12 ,32h ,42m .. etc,. 1) plain number + * like 12 2) time or tablesize like 12h, 34m, 45k, 54m , here last + * character is non-digit but from known characters . + */ + private static boolean containsOnlyNumbers(final String str, final String endChar) { + if (str == null) { + return false; + } + + String number = str; + if (endChar != null) { + boolean matchedEndChar = false; + if (str.length() < 2) { + return false; // at least one numeric and one char. example: + } + // 3h + final char strEnd = str.toCharArray()[str.length() - 1]; + for (final char c : endChar.toCharArray()) { + if (strEnd == c) { + number = str.substring(0, str.length() - 1); + matchedEndChar = true; + break; + } + } + if (!matchedEndChar) { + return false; + } + } + try { + Integer.parseInt(number); + } catch (final NumberFormatException e) { + return false; + } + return true; + } +}