@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 = guestNetwork.getCidr();
        gatewayNic.setIPv4Netmask(NetUtils.getCidrNetmask(gatewayCidr));
      } else {
        gatewayNic.setDefaultNic(true);
      }

      networks.put(guestNetwork, new ArrayList<NicProfile>(Arrays.asList(gatewayNic)));
    }
    return networks;
  }
  @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;
  }