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());
 }
  @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 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;
    if (networkIds.size() != 0) {
      final List<DomainRouterVO> routerList = _routerDao.findByNetwork(networkIds.get(0));
      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;
  }
  @Override
  public boolean finalizeCommandsOnStart(
      Commands cmds, VirtualMachineProfile<DomainRouterVO> profile) {
    DomainRouterVO elbVm = profile.getVirtualMachine();
    DataCenterVO dcVo = _dcDao.findById(elbVm.getDataCenterId());

    NicProfile controlNic = null;
    Long guestNetworkId = null;

    if (profile.getHypervisorType() == HypervisorType.VMware
        && dcVo.getNetworkType() == NetworkType.Basic) {
      // TODO this is a ugly to test hypervisor type here
      // for basic network mode, we will use the guest NIC for control NIC
      for (NicProfile nic : profile.getNics()) {
        if (nic.getTrafficType() == TrafficType.Guest && nic.getIp4Address() != null) {
          controlNic = nic;
          guestNetworkId = nic.getNetworkId();
        }
      }
    } else {
      for (NicProfile nic : profile.getNics()) {
        if (nic.getTrafficType() == TrafficType.Control && nic.getIp4Address() != null) {
          controlNic = nic;
        } else if (nic.getTrafficType() == TrafficType.Guest) {
          guestNetworkId = nic.getNetworkId();
        }
      }
    }

    if (controlNic == null) {
      s_logger.error("Control network doesn't exist for the ELB vm " + elbVm);
      return false;
    }

    cmds.addCommand(
        "checkSsh",
        new CheckSshCommand(profile.getInstanceName(), controlNic.getIp4Address(), 3922));

    // Re-apply load balancing rules
    List<LoadBalancerVO> lbs = _elbVmMapDao.listLbsForElbVm(elbVm.getId());
    List<LoadBalancingRule> lbRules = new ArrayList<LoadBalancingRule>();
    for (LoadBalancerVO lb : lbs) {
      List<LbDestination> dstList = _lbMgr.getExistingDestinations(lb.getId());
      List<LbStickinessPolicy> policyList = _lbMgr.getStickinessPolicies(lb.getId());
      List<LbHealthCheckPolicy> hcPolicyList = _lbMgr.getHealthCheckPolicies(lb.getId());
      LoadBalancingRule loadBalancing =
          new LoadBalancingRule(lb, dstList, policyList, hcPolicyList);
      lbRules.add(loadBalancing);
    }

    s_logger.debug(
        "Found "
            + lbRules.size()
            + " load balancing rule(s) to apply as a part of ELB vm "
            + elbVm
            + " start.");
    if (!lbRules.isEmpty()) {
      createApplyLoadBalancingRulesCommands(lbRules, elbVm, cmds, guestNetworkId);
    }

    return true;
  }
  @Override
  public boolean finalizeCommandsOnStart(final Commands cmds, final VirtualMachineProfile profile) {
    final DomainRouterVO domainRouterVO = _routerDao.findById(profile.getId());

    final boolean isVpc = domainRouterVO.getVpcId() != null;
    if (!isVpc) {
      return super.finalizeCommandsOnStart(cmds, profile);
    }

    if (domainRouterVO.getState() == State.Starting || domainRouterVO.getState() == State.Running) {
      // 1) FORM SSH CHECK COMMAND
      final NicProfile controlNic = getControlNic(profile);
      if (controlNic == null) {
        s_logger.error("Control network doesn't exist for the router " + domainRouterVO);
        return false;
      }

      finalizeSshAndVersionAndNetworkUsageOnStart(cmds, profile, domainRouterVO, controlNic);

      // 2) FORM PLUG NIC COMMANDS
      final List<Pair<Nic, Network>> guestNics = new ArrayList<Pair<Nic, Network>>();
      final List<Pair<Nic, Network>> publicNics = new ArrayList<Pair<Nic, Network>>();
      final Map<String, String> vlanMacAddress = new HashMap<String, String>();

      final List<? extends Nic> routerNics = _nicDao.listByVmId(profile.getId());
      for (final Nic routerNic : routerNics) {
        final Network network = _networkModel.getNetwork(routerNic.getNetworkId());
        if (network.getTrafficType() == TrafficType.Guest) {
          final Pair<Nic, Network> guestNic = new Pair<Nic, Network>(routerNic, network);
          guestNics.add(guestNic);
        } else if (network.getTrafficType() == TrafficType.Public) {
          final Pair<Nic, Network> publicNic = new Pair<Nic, Network>(routerNic, network);
          publicNics.add(publicNic);
          final String vlanTag = BroadcastDomainType.getValue(routerNic.getBroadcastUri());
          vlanMacAddress.put(vlanTag, routerNic.getMacAddress());
        }
      }

      final List<Command> usageCmds = new ArrayList<Command>();

      // 3) PREPARE PLUG NIC COMMANDS
      try {
        // add VPC router to public networks
        final List<PublicIp> sourceNat = new ArrayList<PublicIp>(1);
        for (final Pair<Nic, Network> nicNtwk : publicNics) {
          final Nic publicNic = nicNtwk.first();
          final Network publicNtwk = nicNtwk.second();
          final IPAddressVO userIp =
              _ipAddressDao.findByIpAndSourceNetworkId(
                  publicNtwk.getId(), publicNic.getIPv4Address());

          if (userIp.isSourceNat()) {
            final PublicIp publicIp =
                PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
            sourceNat.add(publicIp);

            if (domainRouterVO.getPublicIpAddress() == null) {
              final DomainRouterVO routerVO = _routerDao.findById(domainRouterVO.getId());
              routerVO.setPublicIpAddress(publicNic.getIPv4Address());
              routerVO.setPublicNetmask(publicNic.getIPv4Netmask());
              routerVO.setPublicMacAddress(publicNic.getMacAddress());
              _routerDao.update(routerVO.getId(), routerVO);
            }
          }
          final PlugNicCommand plugNicCmd =
              new PlugNicCommand(
                  _nwHelper.getNicTO(
                      domainRouterVO,
                      publicNic.getNetworkId(),
                      publicNic.getBroadcastUri().toString()),
                  domainRouterVO.getInstanceName(),
                  domainRouterVO.getType());
          cmds.addCommand(plugNicCmd);
          final VpcVO vpc = _vpcDao.findById(domainRouterVO.getVpcId());
          final NetworkUsageCommand netUsageCmd =
              new NetworkUsageCommand(
                  domainRouterVO.getPrivateIpAddress(),
                  domainRouterVO.getInstanceName(),
                  true,
                  publicNic.getIPv4Address(),
                  vpc.getCidr());
          usageCmds.add(netUsageCmd);
          UserStatisticsVO stats =
              _userStatsDao.findBy(
                  domainRouterVO.getAccountId(),
                  domainRouterVO.getDataCenterId(),
                  publicNtwk.getId(),
                  publicNic.getIPv4Address(),
                  domainRouterVO.getId(),
                  domainRouterVO.getType().toString());
          if (stats == null) {
            stats =
                new UserStatisticsVO(
                    domainRouterVO.getAccountId(),
                    domainRouterVO.getDataCenterId(),
                    publicNic.getIPv4Address(),
                    domainRouterVO.getId(),
                    domainRouterVO.getType().toString(),
                    publicNtwk.getId());
            _userStatsDao.persist(stats);
          }
        }

        // create ip assoc for source nat
        if (!sourceNat.isEmpty()) {
          _commandSetupHelper.createVpcAssociatePublicIPCommands(
              domainRouterVO, sourceNat, cmds, vlanMacAddress);
        }

        // add VPC router to guest networks
        for (final Pair<Nic, Network> nicNtwk : guestNics) {
          final Nic guestNic = nicNtwk.first();
          // plug guest nic
          final PlugNicCommand plugNicCmd =
              new PlugNicCommand(
                  _nwHelper.getNicTO(domainRouterVO, guestNic.getNetworkId(), null),
                  domainRouterVO.getInstanceName(),
                  domainRouterVO.getType());
          cmds.addCommand(plugNicCmd);
          if (!_networkModel.isPrivateGateway(guestNic.getNetworkId())) {
            // set guest network
            final VirtualMachine vm = _vmDao.findById(domainRouterVO.getId());
            final NicProfile nicProfile =
                _networkModel.getNicProfile(vm, guestNic.getNetworkId(), null);
            final SetupGuestNetworkCommand setupCmd =
                _commandSetupHelper.createSetupGuestNetworkCommand(
                    domainRouterVO, true, nicProfile);
            cmds.addCommand(setupCmd);
          } else {

            // set private network
            final PrivateIpVO ipVO =
                _privateIpDao.findByIpAndSourceNetworkId(
                    guestNic.getNetworkId(), guestNic.getIPv4Address());
            final Network network = _networkDao.findById(guestNic.getNetworkId());
            BroadcastDomainType.getValue(network.getBroadcastUri());
            final String netmask = NetUtils.getCidrNetmask(network.getCidr());
            final PrivateIpAddress ip =
                new PrivateIpAddress(
                    ipVO,
                    network.getBroadcastUri().toString(),
                    network.getGateway(),
                    netmask,
                    guestNic.getMacAddress());

            final List<PrivateIpAddress> privateIps = new ArrayList<PrivateIpAddress>(1);
            privateIps.add(ip);
            _commandSetupHelper.createVpcAssociatePrivateIPCommands(
                domainRouterVO, privateIps, cmds, true);

            final Long privateGwAclId =
                _vpcGatewayDao.getNetworkAclIdForPrivateIp(
                    ipVO.getVpcId(), ipVO.getNetworkId(), ipVO.getIpAddress());

            if (privateGwAclId != null) {
              // set network acl on private gateway
              final List<NetworkACLItemVO> networkACLs =
                  _networkACLItemDao.listByACL(privateGwAclId);
              s_logger.debug(
                  "Found "
                      + networkACLs.size()
                      + " network ACLs to apply as a part of VPC VR "
                      + domainRouterVO
                      + " start for private gateway ip = "
                      + ipVO.getIpAddress());

              _commandSetupHelper.createNetworkACLsCommands(
                  networkACLs, domainRouterVO, cmds, ipVO.getNetworkId(), true);
            }
          }
        }
      } catch (final Exception ex) {
        s_logger.warn(
            "Failed to add router " + domainRouterVO + " to network due to exception ", ex);
        return false;
      }

      // 4) RE-APPLY ALL STATIC ROUTE RULES
      final List<? extends StaticRoute> routes =
          _staticRouteDao.listByVpcId(domainRouterVO.getVpcId());
      final List<StaticRouteProfile> staticRouteProfiles =
          new ArrayList<StaticRouteProfile>(routes.size());
      final Map<Long, VpcGateway> gatewayMap = new HashMap<Long, VpcGateway>();
      for (final StaticRoute route : routes) {
        VpcGateway gateway = gatewayMap.get(route.getVpcGatewayId());
        if (gateway == null) {
          gateway = _entityMgr.findById(VpcGateway.class, route.getVpcGatewayId());
          gatewayMap.put(gateway.getId(), gateway);
        }
        staticRouteProfiles.add(new StaticRouteProfile(route, gateway));
      }

      s_logger.debug(
          "Found "
              + staticRouteProfiles.size()
              + " static routes to apply as a part of vpc route "
              + domainRouterVO
              + " start");
      if (!staticRouteProfiles.isEmpty()) {
        _commandSetupHelper.createStaticRouteCommands(staticRouteProfiles, domainRouterVO, cmds);
      }

      // 5) RE-APPLY ALL REMOTE ACCESS VPNs
      final RemoteAccessVpnVO vpn =
          _vpnDao.findByAccountAndVpc(domainRouterVO.getAccountId(), domainRouterVO.getVpcId());
      if (vpn != null) {
        _commandSetupHelper.createApplyVpnCommands(true, vpn, domainRouterVO, cmds);
      }

      // 6) REPROGRAM GUEST NETWORK
      boolean reprogramGuestNtwks = true;
      if (profile.getParameter(Param.ReProgramGuestNetworks) != null
          && (Boolean) profile.getParameter(Param.ReProgramGuestNetworks) == false) {
        reprogramGuestNtwks = false;
      }

      final VirtualRouterProvider vrProvider =
          _vrProviderDao.findById(domainRouterVO.getElementId());
      if (vrProvider == null) {
        throw new CloudRuntimeException(
            "Cannot find related virtual router provider of router: "
                + domainRouterVO.getHostName());
      }
      final Provider provider = Network.Provider.getProvider(vrProvider.getType().toString());
      if (provider == null) {
        throw new CloudRuntimeException(
            "Cannot find related provider of virtual router provider: "
                + vrProvider.getType().toString());
      }

      for (final Pair<Nic, Network> nicNtwk : guestNics) {
        final Nic guestNic = nicNtwk.first();
        final AggregationControlCommand startCmd =
            new AggregationControlCommand(
                Action.Start,
                domainRouterVO.getInstanceName(),
                controlNic.getIPv4Address(),
                _routerControlHelper.getRouterIpInNetwork(
                    guestNic.getNetworkId(), domainRouterVO.getId()));
        cmds.addCommand(startCmd);
        if (reprogramGuestNtwks) {
          finalizeIpAssocForNetwork(
              cmds, domainRouterVO, provider, guestNic.getNetworkId(), vlanMacAddress);
          finalizeNetworkRulesForNetwork(cmds, domainRouterVO, provider, guestNic.getNetworkId());
        }

        finalizeUserDataAndDhcpOnStart(cmds, domainRouterVO, provider, guestNic.getNetworkId());
        final AggregationControlCommand finishCmd =
            new AggregationControlCommand(
                Action.Finish,
                domainRouterVO.getInstanceName(),
                controlNic.getIPv4Address(),
                _routerControlHelper.getRouterIpInNetwork(
                    guestNic.getNetworkId(), domainRouterVO.getId()));
        cmds.addCommand(finishCmd);
      }

      // Add network usage commands
      cmds.addCommands(usageCmds);
    }
    return true;
  }