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;
  }
  protected boolean shutdownRouterVM(DomainRouterVO router) {
    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Try to shutdown router VM " + router.getInstanceName() + " directly.");
    }

    Pair<Boolean, String> result;
    try {
      result =
          SshHelper.sshExecute(
              router.getPrivateIpAddress(),
              DEFAULT_DOMR_SSHPORT,
              "root",
              getSystemVMKeyFile(),
              null,
              "poweroff -f");

      if (!result.first()) {
        s_logger.debug("Unable to shutdown " + router.getInstanceName() + " directly");
        return false;
      }
    } catch (Throwable e) {
      s_logger.warn("Unable to shutdown router " + router.getInstanceName() + " directly.");
      return false;
    }
    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Shutdown router " + router.getInstanceName() + " successful.");
    }
    return true;
  }
 @Override
 public boolean processDisconnect(long agentId, Status state) {
   UserContext context = UserContext.current();
   context.setAccountId(1);
   /* Stopped VMware Host's virtual routers */
   HostVO host = _hostDao.findById(agentId);
   if (host.getHypervisorType() != HypervisorType.VMware) {
     return true;
   }
   List<DomainRouterVO> routers = _routerDao.listByHostId(agentId);
   for (DomainRouterVO router : routers) {
     try {
       State oldState = router.getState();
       _routerMgr.stopRouter(router.getId(), true);
       // In case only vCenter is disconnected, we want to shut down router directly
       if (oldState == State.Running) {
         shutdownRouterVM(router);
       }
     } catch (ResourceUnavailableException e) {
       s_logger.warn(
           "Fail to stop router " + router.getInstanceName() + " when host disconnected!", e);
     } catch (ConcurrentOperationException e) {
       s_logger.warn(
           "Fail to stop router " + router.getInstanceName() + " when host disconnected!", e);
     }
   }
   return true;
 }
  @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);
  }
  private void createApplyLoadBalancingRulesCommands(
      List<LoadBalancingRule> rules, DomainRouterVO elbVm, Commands cmds, long guestNetworkId) {

    LoadBalancerTO[] lbs = new LoadBalancerTO[rules.size()];
    int i = 0;
    for (LoadBalancingRule rule : rules) {
      boolean revoked = (rule.getState().equals(FirewallRule.State.Revoke));
      String protocol = rule.getProtocol();
      String algorithm = rule.getAlgorithm();

      String elbIp = _networkModel.getIp(rule.getSourceIpAddressId()).getAddress().addr();
      int srcPort = rule.getSourcePortStart();
      String uuid = rule.getUuid();
      List<LbDestination> destinations = rule.getDestinations();
      LoadBalancerTO lb =
          new LoadBalancerTO(
              uuid, elbIp, srcPort, protocol, algorithm, revoked, false, false, destinations);
      lbs[i++] = lb;
    }

    LoadBalancerConfigCommand cmd =
        new LoadBalancerConfigCommand(
            lbs,
            elbVm.getPublicIpAddress(),
            _nicDao.getIpAddress(guestNetworkId, elbVm.getId()),
            elbVm.getPrivateIpAddress(),
            null,
            null);
    cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, elbVm.getPrivateIpAddress());
    cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, elbVm.getInstanceName());
    // FIXME: why are we setting attributes directly? Ick!! There should be accessors and
    // the constructor should set defaults.
    cmd.lbStatsVisibility = _configDao.getValue(Config.NetworkLBHaproxyStatsVisbility.key());
    cmd.lbStatsUri = _configDao.getValue(Config.NetworkLBHaproxyStatsUri.key());
    cmd.lbStatsAuth = _configDao.getValue(Config.NetworkLBHaproxyStatsAuth.key());
    cmd.lbStatsPort = _configDao.getValue(Config.NetworkLBHaproxyStatsPort.key());

    cmds.addCommand(cmd);
  }
  @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(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;
  }