You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2019/07/01 09:08:17 UTC
[cloudstack] 01/01: Merge remote-tracking branch 'origin/4.11' into
4.12
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 <ro...@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 <ro...@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;
+ }
+}