@Override
  public boolean removeVpcRouterFromGuestNetwork(final VirtualRouter router, final Network network)
      throws ConcurrentOperationException, ResourceUnavailableException {
    if (network.getTrafficType() != TrafficType.Guest) {
      s_logger.warn("Network " + network + " is not of type " + TrafficType.Guest);
      return false;
    }

    boolean result = true;
    try {
      // Check if router is a part of the Guest network
      if (!_networkModel.isVmPartOfNetwork(router.getId(), network.getId())) {
        s_logger.debug("Router " + router + " is not a part of the Guest network " + network);
        return result;
      }

      result =
          setupVpcGuestNetwork(
              network, router, false, _networkModel.getNicProfile(router, network.getId(), null));
      if (!result) {
        s_logger.warn("Failed to destroy guest network config " + network + " on router " + router);
        return false;
      }

      result = result && _itMgr.removeVmFromNetwork(router, network, null);
    } finally {
      if (result) {
        _routerDao.removeRouterFromGuestNetwork(router.getId(), network.getId());
      }
    }

    return result;
  }
  @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 boolean destroyPrivateGateway(final PrivateGateway gateway, final VirtualRouter router)
      throws ConcurrentOperationException, ResourceUnavailableException {

    if (!_networkModel.isVmPartOfNetwork(router.getId(), gateway.getNetworkId())) {
      s_logger.debug(
          "Router doesn't have nic for gateway " + gateway + " so no need to removed it");
      return true;
    }

    final Network privateNetwork = _networkModel.getNetwork(gateway.getNetworkId());

    s_logger.debug("Releasing private ip for gateway " + gateway + " from " + router);
    boolean result =
        setupVpcPrivateNetwork(
            router, false, _networkModel.getNicProfile(router, privateNetwork.getId(), null));
    if (!result) {
      s_logger.warn("Failed to release private ip for gateway " + gateway + " on router " + router);
      return false;
    }

    // revoke network acl on the private gateway.
    if (!_networkACLMgr.revokeACLItemsForPrivateGw(gateway)) {
      s_logger.debug("Failed to delete network acl items on " + gateway + " from router " + router);
      return false;
    }

    s_logger.debug(
        "Removing router "
            + router
            + " from private network "
            + privateNetwork
            + " as a part of delete private gateway");
    result = result && _itMgr.removeVmFromNetwork(router, privateNetwork, null);
    s_logger.debug("Private gateawy " + gateway + " is removed from router " + router);
    return result;
  }
  @Override
  @DB
  public NicProfile createPrivateNicProfileForGateway(
      final VpcGateway privateGateway, final VirtualRouter router) {
    final Network privateNetwork = _networkModel.getNetwork(privateGateway.getNetworkId());

    PrivateIpVO ipVO =
        _privateIpDao.allocateIpAddress(
            privateNetwork.getDataCenterId(),
            privateNetwork.getId(),
            privateGateway.getIp4Address());

    final Long vpcId = privateGateway.getVpcId();
    final Vpc activeVpc = _vpcMgr.getActiveVpc(vpcId);
    if (activeVpc.isRedundant() && ipVO == null) {
      ipVO = _privateIpDao.findByIpAndVpcId(vpcId, privateGateway.getIp4Address());
    }

    Nic privateNic = null;

    if (ipVO != null) {
      privateNic =
          _nicDao.findByIp4AddressAndNetworkId(ipVO.getIpAddress(), privateNetwork.getId());
    }

    NicProfile privateNicProfile = new NicProfile();

    if (privateNic != null) {
      privateNicProfile =
          new NicProfile(
              privateNic,
              privateNetwork,
              privateNic.getBroadcastUri(),
              privateNic.getIsolationUri(),
              _networkModel.getNetworkRate(privateNetwork.getId(), router.getId()),
              _networkModel.isSecurityGroupSupportedInNetwork(privateNetwork),
              _networkModel.getNetworkTag(router.getHypervisorType(), privateNetwork));

      if (router.getIsRedundantRouter()) {
        String newMacAddress =
            NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress()));
        privateNicProfile.setMacAddress(newMacAddress);
      }
    } else {
      final String netmask = NetUtils.getCidrNetmask(privateNetwork.getCidr());
      final PrivateIpAddress ip =
          new PrivateIpAddress(
              ipVO,
              privateNetwork.getBroadcastUri().toString(),
              privateNetwork.getGateway(),
              netmask,
              NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(ipVO.getMacAddress())));

      final URI netUri = BroadcastDomainType.fromString(ip.getBroadcastUri());
      privateNicProfile.setIPv4Address(ip.getIpAddress());
      privateNicProfile.setIPv4Gateway(ip.getGateway());
      privateNicProfile.setIPv4Netmask(ip.getNetmask());
      privateNicProfile.setIsolationUri(netUri);
      privateNicProfile.setBroadcastUri(netUri);
      // can we solve this in setBroadcastUri()???
      // or more plugable construct is desirable
      privateNicProfile.setBroadcastType(BroadcastDomainType.getSchemeValue(netUri));
      privateNicProfile.setFormat(AddressFormat.Ip4);
      privateNicProfile.setReservationId(String.valueOf(ip.getBroadcastUri()));
      privateNicProfile.setMacAddress(ip.getMacAddress());
    }

    return privateNicProfile;
  }
  protected Pair<Map<String, PublicIpAddress>, Map<String, PublicIpAddress>>
      getNicsToChangeOnRouter(
          final List<? extends PublicIpAddress> publicIps, final VirtualRouter router) {
    // 1) check which nics need to be plugged/unplugged and plug/unplug them

    final Map<String, PublicIpAddress> nicsToPlug = new HashMap<String, PublicIpAddress>();
    final Map<String, PublicIpAddress> nicsToUnplug = new HashMap<String, PublicIpAddress>();

    // find out nics to unplug
    for (final PublicIpAddress ip : publicIps) {
      final long publicNtwkId = ip.getNetworkId();

      // if ip is not associated to any network, and there are no firewall
      // rules, release it on the backend
      if (!_vpcMgr.isIpAllocatedToVpc(ip)) {
        ip.setState(IpAddress.State.Releasing);
      }

      if (ip.getState() == IpAddress.State.Releasing) {
        final Nic nic =
            _nicDao.findByIp4AddressAndNetworkIdAndInstanceId(
                publicNtwkId, router.getId(), ip.getAddress().addr());
        if (nic != null) {
          nicsToUnplug.put(ip.getVlanTag(), ip);
          s_logger.debug(
              "Need to unplug the nic for ip="
                  + ip
                  + "; vlan="
                  + ip.getVlanTag()
                  + " in public network id ="
                  + publicNtwkId);
        }
      }
    }

    // find out nics to plug
    for (final PublicIpAddress ip : publicIps) {
      final URI broadcastUri = BroadcastDomainType.Vlan.toUri(ip.getVlanTag());
      final long publicNtwkId = ip.getNetworkId();

      // if ip is not associated to any network, and there are no firewall
      // rules, release it on the backend
      if (!_vpcMgr.isIpAllocatedToVpc(ip)) {
        ip.setState(IpAddress.State.Releasing);
      }

      if (ip.getState() == IpAddress.State.Allocated
          || ip.getState() == IpAddress.State.Allocating) {
        // nic has to be plugged only when there are no nics for this
        // vlan tag exist on VR
        final Nic nic =
            _nicDao.findByNetworkIdInstanceIdAndBroadcastUri(
                publicNtwkId, router.getId(), broadcastUri.toString());

        if (nic == null && nicsToPlug.get(ip.getVlanTag()) == null) {
          nicsToPlug.put(ip.getVlanTag(), ip);
          s_logger.debug(
              "Need to plug the nic for ip="
                  + ip
                  + "; vlan="
                  + ip.getVlanTag()
                  + " in public network id ="
                  + publicNtwkId);
        } else {
          final PublicIpAddress nicToUnplug = nicsToUnplug.get(ip.getVlanTag());
          if (nicToUnplug != null) {
            final NicVO nicVO =
                _nicDao.findByIp4AddressAndNetworkIdAndInstanceId(
                    publicNtwkId, router.getId(), nicToUnplug.getAddress().addr());
            nicVO.setIPv4Address(ip.getAddress().addr());
            _nicDao.update(nicVO.getId(), nicVO);
            s_logger.debug(
                "Updated the nic " + nicVO + " with the new ip address " + ip.getAddress().addr());
            nicsToUnplug.remove(ip.getVlanTag());
          }
        }
      }
    }

    final Pair<Map<String, PublicIpAddress>, Map<String, PublicIpAddress>> nicsToChange =
        new Pair<Map<String, PublicIpAddress>, Map<String, PublicIpAddress>>(
            nicsToPlug, nicsToUnplug);
    return nicsToChange;
  }
  @Override
  public boolean addVpcRouterToGuestNetwork(
      final VirtualRouter router,
      final Network network,
      final Map<VirtualMachineProfile.Param, Object> params)
      throws ConcurrentOperationException, ResourceUnavailableException,
          InsufficientCapacityException {
    if (network.getTrafficType() != TrafficType.Guest) {
      s_logger.warn("Network " + network + " is not of type " + TrafficType.Guest);
      return false;
    }

    // Add router to the Guest network
    boolean result = true;
    try {

      // 1) add nic to the router
      _routerDao.addRouterToGuestNetwork(router, network);

      final NicProfile guestNic = _itMgr.addVmToNetwork(router, network, null);
      // 2) setup guest network
      if (guestNic != null) {
        result = setupVpcGuestNetwork(network, router, true, guestNic);
      } else {
        s_logger.warn("Failed to add router " + router + " to guest network " + network);
        result = false;
      }
      // 3) apply networking rules
      if (result
          && params.get(Param.ReProgramGuestNetworks) != null
          && (Boolean) params.get(Param.ReProgramGuestNetworks) == true) {
        sendNetworkRulesToRouter(router.getId(), network.getId());
      }
    } catch (final Exception ex) {
      s_logger.warn("Failed to add router " + router + " to network " + network + " due to ", ex);
      result = false;
    } finally {
      if (!result) {
        s_logger.debug(
            "Removing the router " + router + " from network " + network + " as a part of cleanup");
        if (removeVpcRouterFromGuestNetwork(router, network)) {
          s_logger.debug(
              "Removed the router "
                  + router
                  + " from network "
                  + network
                  + " as a part of cleanup");
        } else {
          s_logger.warn(
              "Failed to remove the router "
                  + router
                  + " from network "
                  + network
                  + " as a part of cleanup");
        }
      } else {
        s_logger.debug("Succesfully added router " + router + " to guest network " + network);
      }
    }

    return result;
  }