@Override
  public NicProfile allocate(
      Network config, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm)
      throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException {

    if (_networkModel.networkIsConfiguredForExternalNetworking(
            config.getDataCenterId(), config.getId())
        && nic != null
        && nic.getRequestedIpv4() != null) {
      throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + nic);
    }

    NicProfile profile = super.allocate(config, nic, vm);

    boolean _isEnabled = Boolean.parseBoolean(_configDao.getValue(Config.OvsTunnelNetwork.key()));
    if (_isEnabled) {
      return null;
    }

    if (_networkModel.networkIsConfiguredForExternalNetworking(
        config.getDataCenterId(), config.getId())) {
      profile.setStrategy(ReservationStrategy.Start);
      /* We won't clear IP address, because router may set gateway as it IP, and it would be updated properly later */
      // profile.setIp4Address(null);
      profile.setGateway(null);
      profile.setNetmask(null);
    }

    return profile;
  }
  // we use context.reservationId for dedup of guru & element operations.
  public boolean createNicEnv(
      Network network, NicProfile nic, DeployDestination dest, ReservationContext context) {
    String tenantNetworkUuid = _sspUuidDao.findUuidByNetwork(network);
    if (tenantNetworkUuid == null) {
      s_logger.debug("Skipping #createNicEnv() for nic on " + network.toString());
      return true;
    }

    String reservationId = context.getReservationId();
    List<SspUuidVO> tenantPortUuidVos = _sspUuidDao.listUUidVoByNicProfile(nic);
    for (SspUuidVO tenantPortUuidVo : tenantPortUuidVos) {
      if (reservationId.equals(tenantPortUuidVo.getReservationId())) {
        s_logger.info("Skipping because reservation found " + reservationId);
        return true;
      }
    }

    String tenantPortUuid = null;
    for (SspClient client :
        fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
      SspClient.TenantPort sspPort = client.createTenantPort(tenantNetworkUuid);
      if (sspPort != null) {
        tenantPortUuid = sspPort.uuid;
        nic.setReservationId(reservationId);

        SspUuidVO uuid = new SspUuidVO();
        uuid.setUuid(tenantPortUuid);
        uuid.setObjClass(SspUuidVO.objClassNicProfile);
        uuid.setObjId(nic.getId());
        uuid.setReservationId(reservationId);
        _sspUuidDao.persist(uuid);
        break;
      }
    }
    if (tenantPortUuid == null) {
      s_logger.debug("#createNicEnv() failed for nic on " + network.toString());
      return false;
    }

    for (SspClient client :
        fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
      SspClient.TenantPort sspPort =
          client.updateTenantVifBinding(tenantPortUuid, dest.getHost().getPrivateIpAddress());
      if (sspPort != null) {
        if (sspPort.vlanId != null) {
          nic.setBroadcastType(BroadcastDomainType.Vlan);
          nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(String.valueOf(sspPort.vlanId)));
        }
        return true;
      }
    }
    s_logger.error("Updating vif failed " + nic.toString());
    return false;
  }
  public boolean deleteNicEnv(Network network, NicProfile nic, ReservationContext context) {
    if (context == null) {
      s_logger.error("ReservationContext was null for " + nic + " " + network);
      return false;
    }
    String reservationId = context.getReservationId();

    SspUuidVO deleteTarget = null;
    SspUuidVO remainingTarget = null;
    List<SspUuidVO> tenantPortUuidVos = _sspUuidDao.listUUidVoByNicProfile(nic);
    for (SspUuidVO tenantPortUuidVo : tenantPortUuidVos) {
      if (reservationId.equals(tenantPortUuidVo.getReservationId())) {
        deleteTarget = tenantPortUuidVo;
      } else {
        remainingTarget = tenantPortUuidVo;
      }
    }

    if (deleteTarget != null) { // delete the target ssp uuid (tenant-port)
      String tenantPortUuid = deleteTarget.getUuid();
      boolean processed = false;
      for (SspClient client :
          fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
        SspClient.TenantPort sspPort = client.updateTenantVifBinding(tenantPortUuid, null);
        if (sspPort != null) {
          processed = true;
          break;
        }
      }
      if (!processed) {
        s_logger.warn("Ssp api nic detach failed " + nic.toString());
      }
      processed = false;
      for (SspClient client :
          fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
        if (client.deleteTenantPort(tenantPortUuid)) {
          _sspUuidDao.removeUuid(tenantPortUuid);
          processed = true;
          break;
        }
      }
      if (!processed) {
        s_logger.warn("Ssp api tenant port deletion failed " + nic.toString());
      }
      _sspUuidDao.removeUuid(tenantPortUuid);
    }
    if (remainingTarget != null) {
      NicVO nicVo = _nicDao.findById(nic.getId());
      nicVo.setReservationId(remainingTarget.getReservationId());
      _nicDao.persist(nicVo); // persist the new reservationId
    }
    return true;
  }
 public NetworkProfile(Network network) {
   this.id = network.getId();
   this.uuid = network.getUuid();
   this.broadcastUri = network.getBroadcastUri();
   this.dataCenterId = network.getDataCenterId();
   this.ownerId = network.getAccountId();
   this.state = network.getState();
   this.name = network.getName();
   this.mode = network.getMode();
   this.broadcastDomainType = network.getBroadcastDomainType();
   this.trafficType = network.getTrafficType();
   this.gateway = network.getGateway();
   this.cidr = network.getCidr();
   this.networkOfferingId = network.getNetworkOfferingId();
   this.related = network.getRelated();
   this.displayText = network.getDisplayText();
   this.reservationId = network.getReservationId();
   this.networkDomain = network.getNetworkDomain();
   this.domainId = network.getDomainId();
   this.guestType = network.getGuestType();
   this.physicalNetworkId = network.getPhysicalNetworkId();
   this.aclType = network.getAclType();
   this.restartRequired = network.isRestartRequired();
   this.specifyIpRanges = network.getSpecifyIpRanges();
   this.vpcId = network.getVpcId();
 }
  @Override
  public boolean applyLBRules(final Network network, final List<LoadBalancingRule> rules)
      throws ResourceUnavailableException {
    boolean result = true;
    if (canHandle(network, Service.Lb)) {
      if (!canHandleLbRules(rules)) {
        return false;
      }

      final List<DomainRouterVO> routers =
          _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
      if (routers == null || routers.isEmpty()) {
        s_logger.debug(
            "Virtual router elemnt doesn't need to apply firewall rules on the backend; virtual "
                + "router doesn't exist in the network "
                + network.getId());
        return true;
      }

      final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
      final NetworkTopology networkTopology = _networkTopologyContext.retrieveNetworkTopology(dcVO);

      for (final DomainRouterVO domainRouterVO : routers) {
        result = result && networkTopology.applyLoadBalancingRules(network, rules, domainRouterVO);
        if (!result) {
          s_logger.debug("Failed to apply load balancing rules in network " + network.getId());
        }
      }
    }
    return result;
  }
  @Override
  public void reserve(
      NicProfile nic,
      Network config,
      VirtualMachineProfile<? extends VirtualMachine> vm,
      DeployDestination dest,
      ReservationContext context)
      throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException {
    assert (nic.getReservationStrategy() == ReservationStrategy.Start)
        : "What can I do for nics that are not allocated at start? ";
    boolean _isEnabled = Boolean.parseBoolean(_configDao.getValue(Config.OvsTunnelNetwork.key()));
    if (_isEnabled) {
      return;
    }

    DataCenter dc = _dcDao.findById(config.getDataCenterId());

    if (_networkModel.networkIsConfiguredForExternalNetworking(
        config.getDataCenterId(), config.getId())) {
      nic.setBroadcastUri(config.getBroadcastUri());
      nic.setIsolationUri(config.getBroadcastUri());
      nic.setDns1(dc.getDns1());
      nic.setDns2(dc.getDns2());
      nic.setNetmask(NetUtils.cidr2Netmask(config.getCidr()));
      long cidrAddress = NetUtils.ip2Long(config.getCidr().split("/")[0]);
      int cidrSize = getGloballyConfiguredCidrSize();
      nic.setGateway(config.getGateway());

      if (nic.getIp4Address() == null) {
        String guestIp = _networkMgr.acquireGuestIpAddress(config, null);
        if (guestIp == null) {
          throw new InsufficientVirtualNetworkCapcityException(
              "Unable to acquire guest IP address for network " + config,
              DataCenter.class,
              dc.getId());
        }

        nic.setIp4Address(guestIp);
      } else {
        long ipMask =
            NetUtils.ip2Long(nic.getIp4Address()) & ~(0xffffffffffffffffl << (32 - cidrSize));
        nic.setIp4Address(NetUtils.long2Ip(cidrAddress | ipMask));
      }
    } else {
      super.reserve(nic, config, vm, dest, context);
    }
  }
 public NetworkType getNetworkType() {
   Network ntwk = _entityMgr.findById(Network.class, getNetworkId());
   if (ntwk != null) {
     DataCenter dc = _entityMgr.findById(DataCenter.class, ntwk.getDataCenterId());
     return dc.getNetworkType();
   }
   return null;
 }
 @Override
 public void updateNicProfile(NicProfile profile, Network network) {
   DataCenter dc = _dcDao.findById(network.getDataCenterId());
   if (profile != null) {
     profile.setDns1(dc.getDns1());
     profile.setDns2(dc.getDns2());
   }
 }
  private boolean canHandle(Network config) {
    DataCenter zone = _configMgr.getZone(config.getDataCenterId());
    if ((zone.getNetworkType() == NetworkType.Advanced
            && config.getGuestType() != Network.GuestType.Isolated)
        || (zone.getNetworkType() == NetworkType.Basic
            && config.getGuestType() != Network.GuestType.Shared)) {
      s_logger.trace("Not handling network type = " + config.getGuestType());
      return false;
    }

    return _networkManager.networkIsConfiguredForExternalNetworking(zone.getId(), config.getId());
  }
  public boolean createNetwork(
      Network network,
      NetworkOffering offering,
      DeployDestination dest,
      ReservationContext context) {
    if (_sspUuidDao.findUuidByNetwork(network) != null) {
      s_logger.info("Network already has ssp TenantNetwork uuid :" + network.toString());
      return true;
    }
    if (!canHandle(network)) {
      return false;
    }

    String tenantUuid = _sspTenantDao.findUuidByZone(network.getDataCenterId());
    if (tenantUuid == null) {
      tenantUuid = _configDao.getValueAndInitIfNotExist("ssp.tenant", "Network", null);
    }

    boolean processed = false;
    for (SspClient client :
        fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
      SspClient.TenantNetwork sspNet = client.createTenantNetwork(tenantUuid, network.getName());
      if (sspNet != null) {
        SspUuidVO uuid = new SspUuidVO();
        uuid.setUuid(sspNet.uuid);
        uuid.setObjClass(SspUuidVO.objClassNetwork);
        uuid.setObjId(network.getId());
        _sspUuidDao.persist(uuid);
        return true;
      }
      processed = true;
    }
    if (processed) {
      s_logger.error("Could not allocate an uuid for network " + network.toString());
      return false;
    } else {
      s_logger.error("Skipping #createNetwork() for " + network.toString());
      return true;
    }
  }
  @Override
  public boolean applyLoadBalancerRules(Network network, List<? extends FirewallRule> rules)
      throws ResourceUnavailableException {
    if (rules == null || rules.isEmpty()) {
      return true;
    }
    if (rules.get(0).getPurpose() != Purpose.LoadBalancing) {
      s_logger.warn("ELB: Not handling non-LB firewall rules");
      return false;
    }

    DomainRouterVO elbVm = findElbVmForLb(rules.get(0));

    if (elbVm == null) {
      s_logger.warn(
          "Unable to apply lb rules, ELB vm  doesn't exist in the network " + network.getId());
      throw new ResourceUnavailableException(
          "Unable to apply lb rules", DataCenter.class, network.getDataCenterId());
    }

    if (elbVm.getState() == State.Running) {
      // resend all rules for the public ip
      List<LoadBalancerVO> lbs = _lbDao.listByIpAddress(rules.get(0).getSourceIpAddressId());
      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);
      }
      return applyLBRules(elbVm, lbRules, network.getId());
    } else if (elbVm.getState() == State.Stopped || elbVm.getState() == State.Stopping) {
      s_logger.debug(
          "ELB VM is in "
              + elbVm.getState()
              + ", so not sending apply LoadBalancing rules commands to the backend");
      return true;
    } else {
      s_logger.warn(
          "Unable to apply loadbalancing rules, ELB VM is not in the right state "
              + elbVm.getState());
      throw new ResourceUnavailableException(
          "Unable to apply loadbalancing rules, ELB VM is not in the right state",
          VirtualRouter.class,
          elbVm.getId());
    }
  }
  @Override
  @DB
  public void deallocate(
      Network config, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm) {
    super.deallocate(config, nic, vm);

    if (Boolean.parseBoolean(_configDao.getValue(Config.OvsTunnelNetwork.key()))) {
      return;
    }

    if (_networkModel.networkIsConfiguredForExternalNetworking(
        config.getDataCenterId(), config.getId())) {
      nic.setIp4Address(null);
      nic.setGateway(null);
      nic.setNetmask(null);
      nic.setBroadcastUri(null);
      nic.setIsolationUri(null);
    }
  }
 public boolean deleteNetwork(Network network) {
   String tenantNetworkUuid = _sspUuidDao.findUuidByNetwork(network);
   if (tenantNetworkUuid != null) {
     boolean processed = false;
     for (SspClient client :
         fetchSspClients(network.getPhysicalNetworkId(), network.getDataCenterId(), true)) {
       if (client.deleteTenantNetwork(tenantNetworkUuid)) {
         _sspUuidDao.removeUuid(tenantNetworkUuid);
         processed = true;
         break;
       }
     }
     if (!processed) {
       s_logger.error("Ssp api tenant network deletion failed " + network.toString());
     }
   } else {
     s_logger.debug("Silently skipping #deleteNetwork() for " + network.toString());
   }
   return true;
 }
  @Override
  public NicProfile allocate(
      Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm)
      throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException,
          ConcurrentOperationException {

    DataCenter dc = _dcDao.findById(network.getDataCenterId());

    if (nic == null) {
      nic = new NicProfile(ReservationStrategy.Create, null, null, null, null);
    } else if (nic.getIp4Address() == null) {
      nic.setStrategy(ReservationStrategy.Start);
    } else {
      nic.setStrategy(ReservationStrategy.Create);
    }

    _networkMgr.allocateDirectIp(nic, dc, vm, network, nic.getRequestedIp());
    nic.setStrategy(ReservationStrategy.Create);

    return nic;
  }
  @Override
  public boolean shutdown(Network network, ReservationContext context, boolean cleanup)
      throws ResourceUnavailableException, ConcurrentOperationException {
    DataCenter zone = _configMgr.getZone(network.getDataCenterId());

    // don't have to implement network is Basic zone
    if (zone.getNetworkType() == NetworkType.Basic) {
      s_logger.debug("Not handling network shutdown in zone of type " + NetworkType.Basic);
      return false;
    }

    if (!canHandle(network)) {
      return false;
    }
    try {
      return _externalNetworkManager.manageGuestNetworkWithExternalFirewall(false, network);
    } catch (InsufficientCapacityException capacityException) {
      // TODO: handle out of capacity exception
      return false;
    }
  }
  @Override
  public boolean applyIps(
      final Network network,
      final List<? extends PublicIpAddress> ipAddress,
      final Set<Service> services)
      throws ResourceUnavailableException {
    boolean canHandle = true;
    for (final Service service : services) {
      // check if Ovs can handle services except SourceNat & Firewall
      if (!canHandle(network, service)
          && service != Service.SourceNat
          && service != Service.Firewall) {
        canHandle = false;
        break;
      }
    }
    boolean result = true;
    if (canHandle) {
      final List<DomainRouterVO> routers =
          _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
      if (routers == null || routers.isEmpty()) {
        s_logger.debug(
            "Virtual router element doesn't need to associate ip addresses on the backend; virtual "
                + "router doesn't exist in the network "
                + network.getId());
        return true;
      }

      final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
      final NetworkTopology networkTopology = _networkTopologyContext.retrieveNetworkTopology(dcVO);

      for (final DomainRouterVO domainRouterVO : routers) {
        result = result && networkTopology.associatePublicIP(network, ipAddress, domainRouterVO);
      }
    }
    return result;
  }
  @Override
  public boolean applyStaticNats(final Network network, final List<? extends StaticNat> rules)
      throws ResourceUnavailableException {
    if (!canHandle(network, Service.StaticNat)) {
      return false;
    }
    final List<DomainRouterVO> routers =
        _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER);
    if (routers == null || routers.isEmpty()) {
      s_logger.debug(
          "Ovs element doesn't need to apply static nat on the backend; virtual "
              + "router doesn't exist in the network "
              + network.getId());
      return true;
    }

    final DataCenterVO dcVO = _dcDao.findById(network.getDataCenterId());
    final NetworkTopology networkTopology = _networkTopologyContext.retrieveNetworkTopology(dcVO);
    boolean result = true;
    for (final DomainRouterVO domainRouterVO : routers) {
      result = result && networkTopology.applyStaticNats(network, rules, domainRouterVO);
    }
    return result;
  }
  @Override
  public NicProfile allocate(
      Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm)
      throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException,
          ConcurrentOperationException {
    if (network.getTrafficType() != TrafficType.Public) {
      return null;
    }

    if (nic == null) {
      nic = new NicProfile(ReservationStrategy.Create, null, null, null, null);
    }

    DataCenter dc = _dcDao.findById(network.getDataCenterId());
    getIp(nic, dc, vm, network);

    if (nic.getIp4Address() == null) {
      nic.setStrategy(ReservationStrategy.Start);
    } else {
      nic.setStrategy(ReservationStrategy.Create);
    }

    return nic;
  }
  @DB
  public PublicIp allocDirectIp(Account account, long guestNetworkId)
      throws InsufficientAddressCapacityException {
    Network frontEndNetwork = _networkModel.getNetwork(guestNetworkId);
    Transaction txn = Transaction.currentTxn();
    txn.start();

    PublicIp ip =
        _networkMgr.assignPublicIpAddress(
            frontEndNetwork.getDataCenterId(),
            null,
            account,
            VlanType.DirectAttached,
            frontEndNetwork.getId(),
            null,
            true);
    IPAddressVO ipvo = _ipAddressDao.findById(ip.getId());
    ipvo.setAssociatedWithNetworkId(frontEndNetwork.getId());
    _ipAddressDao.update(ipvo.getId(), ipvo);
    txn.commit();
    s_logger.info("Acquired frontend IP for ELB " + ip);

    return ip;
  }
  @Override
  public boolean implement(
      Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context)
      throws ConcurrentOperationException, ResourceUnavailableException,
          InsufficientCapacityException {
    DataCenter zone = _configMgr.getZone(network.getDataCenterId());

    if (zone.getNetworkType() == NetworkType.Basic) {
      s_logger.debug("Not handling network implement in zone of type " + NetworkType.Basic);
      return false;
    }

    if (!canHandle(network)) {
      return false;
    }

    List<CiscoVnmcControllerVO> devices =
        _ciscoVnmcDao.listByPhysicalNetwork(network.getPhysicalNetworkId());
    if (devices.isEmpty()) {
      s_logger.error("No Cisco Vnmc device on network " + network.getName());
      return false;
    }

    List<CiscoAsa1000vDeviceVO> asaList =
        _ciscoAsa1000vDao.listByPhysicalNetwork(network.getPhysicalNetworkId());
    if (asaList.isEmpty()) {
      s_logger.debug("No Cisco ASA 1000v device on network " + network.getName());
      return false;
    }

    NetworkAsa1000vMapVO asaForNetwork = _networkAsa1000vMapDao.findByNetworkId(network.getId());
    if (asaForNetwork != null) {
      s_logger.debug("Cisco ASA 1000v device already associated with network " + network.getName());
      return true;
    }

    if (!_networkModel.isProviderSupportServiceInNetwork(
        network.getId(), Service.SourceNat, Provider.CiscoVnmc)) {
      s_logger.error(
          "SourceNat service is not provided by Cisco Vnmc device on network " + network.getName());
      return false;
    }

    Transaction txn = Transaction.currentTxn();
    boolean status = false;
    try {
      txn.start();

      // ensure that there is an ASA 1000v assigned to this network
      CiscoAsa1000vDevice assignedAsa = assignAsa1000vToNetwork(network);
      if (assignedAsa == null) {
        s_logger.error("Unable to assign ASA 1000v device to network " + network.getName());
        return false;
      }

      ClusterVO asaCluster = _clusterDao.findById(assignedAsa.getClusterId());
      ClusterVSMMapVO clusterVsmMap = _clusterVsmMapDao.findByClusterId(assignedAsa.getClusterId());
      if (clusterVsmMap == null) {
        s_logger.error(
            "Vmware cluster "
                + asaCluster.getName()
                + " has no Cisco Nexus VSM device associated with it");
        return false;
      }

      CiscoNexusVSMDeviceVO vsmDevice = _vsmDeviceDao.findById(clusterVsmMap.getVsmId());
      if (vsmDevice == null) {
        s_logger.error(
            "Unable to load details of Cisco Nexus VSM device associated with cluster "
                + asaCluster.getName());
        return false;
      }

      CiscoVnmcControllerVO ciscoVnmcDevice = devices.get(0);
      HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcDevice.getHostId());
      _hostDao.loadDetails(ciscoVnmcHost);
      Account owner = context.getAccount();
      PublicIp sourceNatIp = _ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, network);
      String vlan = network.getBroadcastUri().getHost();
      long vlanId = Long.parseLong(vlan);

      List<VlanVO> vlanVOList =
          _vlanDao.listVlansByPhysicalNetworkId(network.getPhysicalNetworkId());
      List<String> publicGateways = new ArrayList<String>();
      for (VlanVO vlanVO : vlanVOList) {
        publicGateways.add(vlanVO.getVlanGateway());
      }

      // due to VNMC limitation of not allowing source NAT ip as the outside ip of firewall,
      // an additional public ip needs to acquired for assigning as firewall outside ip.
      // In case there are already additional ip addresses available (network restart) use one
      // of them such that it is not the source NAT ip
      IpAddress outsideIp = null;
      List<IPAddressVO> publicIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null);
      for (IPAddressVO ip : publicIps) {
        if (!ip.isSourceNat()) {
          outsideIp = ip;
          break;
        }
      }
      if (outsideIp == null) { // none available, acquire one
        try {
          Account caller = CallContext.current().getCallingAccount();
          long callerUserId = CallContext.current().getCallingUserId();
          outsideIp = _ipAddrMgr.allocateIp(owner, false, caller, callerUserId, zone);
        } catch (ResourceAllocationException e) {
          s_logger.error("Unable to allocate additional public Ip address. Exception details " + e);
          return false;
        }

        try {
          outsideIp =
              _ipAddrMgr.associateIPToGuestNetwork(outsideIp.getId(), network.getId(), true);
        } catch (ResourceAllocationException e) {
          s_logger.error(
              "Unable to assign allocated additional public Ip "
                  + outsideIp.getAddress().addr()
                  + " to network with vlan "
                  + vlanId
                  + ". Exception details "
                  + e);
          return false;
        }
      }

      // create logical edge firewall in VNMC
      String gatewayNetmask = NetUtils.getCidrNetmask(network.getCidr());
      // due to ASA limitation of allowing single subnet to be assigned to firewall interfaces,
      // all public ip addresses must be from same subnet, this essentially means single public
      // subnet in zone
      if (!createLogicalEdgeFirewall(
          vlanId,
          network.getGateway(),
          gatewayNetmask,
          outsideIp.getAddress().addr(),
          sourceNatIp.getNetmask(),
          publicGateways,
          ciscoVnmcHost.getId())) {
        s_logger.error(
            "Failed to create logical edge firewall in Cisco VNMC device for network "
                + network.getName());
        return false;
      }

      // create stuff in VSM for ASA device
      if (!configureNexusVsmForAsa(
          vlanId,
          network.getGateway(),
          vsmDevice.getUserName(),
          vsmDevice.getPassword(),
          vsmDevice.getipaddr(),
          assignedAsa.getInPortProfile(),
          ciscoVnmcHost.getId())) {
        s_logger.error(
            "Failed to configure Cisco Nexus VSM "
                + vsmDevice.getipaddr()
                + " for ASA device for network "
                + network.getName());
        return false;
      }

      // configure source NAT
      if (!configureSourceNat(vlanId, network.getCidr(), sourceNatIp, ciscoVnmcHost.getId())) {
        s_logger.error(
            "Failed to configure source NAT in Cisco VNMC device for network " + network.getName());
        return false;
      }

      // associate Asa 1000v instance with logical edge firewall
      if (!associateAsaWithLogicalEdgeFirewall(
          vlanId, assignedAsa.getManagementIp(), ciscoVnmcHost.getId())) {
        s_logger.error(
            "Failed to associate Cisco ASA 1000v ("
                + assignedAsa.getManagementIp()
                + ") with logical edge firewall in VNMC for network "
                + network.getName());
        return false;
      }

      status = true;
      txn.commit();
    } finally {
      if (!status) {
        txn.rollback();
        // FIXME: also undo changes in VNMC, VSM if anything failed
      }
    }

    return true;
  }
  @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;
  }
  @Override
  public Network implement(
      final Network network,
      final NetworkOffering offering,
      final DeployDestination dest,
      final ReservationContext context)
      throws InsufficientVirtualNetworkCapacityException {
    assert network.getState() == State.Implementing : "Why are we implementing " + network;

    final long dcId = dest.getDataCenter().getId();

    Long physicalNetworkId = network.getPhysicalNetworkId();

    // physical network id can be null in Guest Network in Basic zone, so locate the physical
    // network
    if (physicalNetworkId == null) {
      physicalNetworkId =
          networkModel.findPhysicalNetworkId(dcId, offering.getTags(), offering.getTrafficType());
    }

    final NetworkVO implemented =
        new NetworkVO(
            network.getTrafficType(),
            network.getMode(),
            network.getBroadcastDomainType(),
            network.getNetworkOfferingId(),
            State.Allocated,
            network.getDataCenterId(),
            physicalNetworkId,
            offering.getRedundantRouter());

    if (network.getGateway() != null) {
      implemented.setGateway(network.getGateway());
    }

    if (network.getCidr() != null) {
      implemented.setCidr(network.getCidr());
    }

    // Name is either the given name or the uuid
    String name = network.getName();
    if (name == null || name.isEmpty()) {
      name = ((NetworkVO) network).getUuid();
    }
    if (name.length() > MAX_NAME_LENGTH) {
      name = name.substring(0, MAX_NAME_LENGTH - 1);
    }

    final List<NiciraNvpDeviceVO> devices = niciraNvpDao.listByPhysicalNetwork(physicalNetworkId);
    if (devices.isEmpty()) {
      s_logger.error("No NiciraNvp Controller on physical network " + physicalNetworkId);
      return null;
    }
    final NiciraNvpDeviceVO niciraNvpDevice = devices.get(0);
    final HostVO niciraNvpHost = hostDao.findById(niciraNvpDevice.getHostId());
    hostDao.loadDetails(niciraNvpHost);
    final String transportzoneuuid = niciraNvpHost.getDetail("transportzoneuuid");
    final String transportzoneisotype = niciraNvpHost.getDetail("transportzoneisotype");

    final CreateLogicalSwitchCommand cmd =
        new CreateLogicalSwitchCommand(
            transportzoneuuid,
            transportzoneisotype,
            name,
            context.getDomain().getName() + "-" + context.getAccount().getAccountName());
    final CreateLogicalSwitchAnswer answer =
        (CreateLogicalSwitchAnswer) agentMgr.easySend(niciraNvpHost.getId(), cmd);

    if (answer == null || !answer.getResult()) {
      s_logger.error("CreateLogicalSwitchCommand failed");
      return null;
    }

    try {
      implemented.setBroadcastUri(new URI("lswitch", answer.getLogicalSwitchUuid(), null));
      implemented.setBroadcastDomainType(BroadcastDomainType.Lswitch);
      s_logger.info(
          "Implemented OK, network linked to  = " + implemented.getBroadcastUri().toString());
    } catch (final URISyntaxException e) {
      s_logger.error(
          "Unable to store logical switch id in broadcast uri, uuid = " + implemented.getUuid(), e);
      return null;
    }

    return implemented;
  }
  @Override
  public void validateFirewallRule(
      Account caller,
      IPAddressVO ipAddress,
      Integer portStart,
      Integer portEnd,
      String proto,
      Purpose purpose,
      FirewallRuleType type,
      Long networkId,
      FirewallRule.TrafficType trafficType) {
    if (portStart != null && !NetUtils.isValidPort(portStart)) {
      throw new InvalidParameterValueException("publicPort is an invalid value: " + portStart);
    }
    if (portEnd != null && !NetUtils.isValidPort(portEnd)) {
      throw new InvalidParameterValueException("Public port range is an invalid value: " + portEnd);
    }

    // start port can't be bigger than end port
    if (portStart != null && portEnd != null && portStart > portEnd) {
      throw new InvalidParameterValueException("Start port can't be bigger than end port");
    }

    if (ipAddress == null && type == FirewallRuleType.System) {
      return;
    }

    if (ipAddress != null) {
      if (ipAddress.getAssociatedWithNetworkId() == null) {
        throw new InvalidParameterValueException(
            "Unable to create firewall rule ; ip with specified id is not associated with any network");
      } else {
        networkId = ipAddress.getAssociatedWithNetworkId();
      }

      // Validate ip address
      _accountMgr.checkAccess(caller, null, true, ipAddress);
    }

    // network id either has to be passed explicitly, or implicitly as a part of ipAddress object
    if (networkId == null) {
      throw new InvalidParameterValueException(
          "Unable to retrieve network id to validate the rule");
    }

    Network network = _networkModel.getNetwork(networkId);
    assert network != null
        : "Can't create rule as network associated with public ip address is null?";

    if (trafficType == FirewallRule.TrafficType.Egress) {
      _accountMgr.checkAccess(caller, null, true, network);
    }

    // Verify that the network guru supports the protocol specified
    Map<Network.Capability, String> caps = null;

    if (purpose == Purpose.LoadBalancing) {
      if (!_elbEnabled) {
        caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Lb);
      }
    } else if (purpose == Purpose.PortForwarding) {
      caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.PortForwarding);
    } else if (purpose == Purpose.Firewall) {
      caps = _networkModel.getNetworkServiceCapabilities(network.getId(), Service.Firewall);
    }

    if (caps != null) {
      String supportedProtocols;
      String supportedTrafficTypes = null;
      if (purpose == FirewallRule.Purpose.Firewall) {
        supportedTrafficTypes = caps.get(Capability.SupportedTrafficDirection).toLowerCase();
      }

      if (purpose == FirewallRule.Purpose.Firewall
          && trafficType == FirewallRule.TrafficType.Egress) {
        supportedProtocols = caps.get(Capability.SupportedEgressProtocols).toLowerCase();
      } else {
        supportedProtocols = caps.get(Capability.SupportedProtocols).toLowerCase();
      }

      if (!supportedProtocols.contains(proto.toLowerCase())) {
        throw new InvalidParameterValueException(
            "Protocol " + proto + " is not supported in zone " + network.getDataCenterId());
      } else if (proto.equalsIgnoreCase(NetUtils.ICMP_PROTO) && purpose != Purpose.Firewall) {
        throw new InvalidParameterValueException(
            "Protocol "
                + proto
                + " is currently supported only for rules with purpose "
                + Purpose.Firewall);
      } else if (purpose == Purpose.Firewall
          && !supportedTrafficTypes.contains(trafficType.toString().toLowerCase())) {
        throw new InvalidParameterValueException(
            "Traffic Type "
                + trafficType
                + " is currently supported by Firewall in network "
                + networkId);
      }
    }
  }
  @Override
  public Network implement(
      Network config, NetworkOffering offering, DeployDestination dest, ReservationContext context)
      throws InsufficientVirtualNetworkCapcityException {
    assert (config.getState() == State.Implementing) : "Why are we implementing " + config;

    if (Boolean.parseBoolean(_configDao.getValue(Config.OvsTunnelNetwork.key()))) {
      return null;
    }

    if (!_networkModel.networkIsConfiguredForExternalNetworking(
        config.getDataCenterId(), config.getId())) {
      return super.implement(config, offering, dest, context);
    }

    DataCenter zone = dest.getDataCenter();
    NetworkVO implemented =
        new NetworkVO(
            config.getTrafficType(),
            config.getMode(),
            config.getBroadcastDomainType(),
            config.getNetworkOfferingId(),
            State.Allocated,
            config.getDataCenterId(),
            config.getPhysicalNetworkId());

    // Get a vlan tag
    int vlanTag;
    if (config.getBroadcastUri() == null) {
      String vnet =
          _dcDao.allocateVnet(
              zone.getId(),
              config.getPhysicalNetworkId(),
              config.getAccountId(),
              context.getReservationId());

      try {
        vlanTag = Integer.parseInt(vnet);
      } catch (NumberFormatException e) {
        throw new CloudRuntimeException(
            "Obtained an invalid guest vlan tag. Exception: " + e.getMessage());
      }

      implemented.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlanTag));
      ActionEventUtils.onCompletedActionEvent(
          UserContext.current().getCallerUserId(),
          config.getAccountId(),
          EventVO.LEVEL_INFO,
          EventTypes.EVENT_ZONE_VLAN_ASSIGN,
          "Assigned Zone Vlan: " + vnet + " Network Id: " + config.getId(),
          0);
    } else {
      vlanTag = Integer.parseInt(config.getBroadcastUri().getHost());
      implemented.setBroadcastUri(config.getBroadcastUri());
    }

    // Determine the new gateway and CIDR
    String[] oldCidr = config.getCidr().split("/");
    String oldCidrAddress = oldCidr[0];
    int cidrSize = Integer.parseInt(oldCidr[1]);
    long newCidrAddress = (NetUtils.ip2Long(oldCidrAddress));
    // if the implementing network is for vpc, no need to generate newcidr, use the cidr that came
    // from super cidr
    if (config.getVpcId() != null) {
      implemented.setGateway(config.getGateway());
      implemented.setCidr(config.getCidr());
      implemented.setState(State.Implemented);
    } else {
      // Determine the offset from the lowest vlan tag
      int offset = getVlanOffset(config.getPhysicalNetworkId(), vlanTag);
      cidrSize = getGloballyConfiguredCidrSize();
      // If the offset has more bits than there is room for, return null
      long bitsInOffset = 32 - Integer.numberOfLeadingZeros(offset);
      if (bitsInOffset > (cidrSize - 8)) {
        throw new CloudRuntimeException(
            "The offset "
                + offset
                + " needs "
                + bitsInOffset
                + " bits, but only have "
                + (cidrSize - 8)
                + " bits to work with.");
      }
      newCidrAddress =
          (NetUtils.ip2Long(oldCidrAddress) & 0xff000000) | (offset << (32 - cidrSize));
      implemented.setGateway(NetUtils.long2Ip(newCidrAddress + 1));
      implemented.setCidr(NetUtils.long2Ip(newCidrAddress) + "/" + cidrSize);
      implemented.setState(State.Implemented);
    }

    // Mask the Ipv4 address of all nics that use this network with the new guest VLAN offset
    List<NicVO> nicsInNetwork = _nicDao.listByNetworkId(config.getId());
    for (NicVO nic : nicsInNetwork) {
      if (nic.getIp4Address() != null) {
        long ipMask = getIpMask(nic.getIp4Address(), cidrSize);
        nic.setIp4Address(NetUtils.long2Ip(newCidrAddress | ipMask));
        _nicDao.persist(nic);
      }
    }

    // Mask the destination address of all port forwarding rules in this network with the new guest
    // VLAN offset
    List<PortForwardingRuleVO> pfRulesInNetwork = _pfRulesDao.listByNetwork(config.getId());
    for (PortForwardingRuleVO pfRule : pfRulesInNetwork) {
      if (pfRule.getDestinationIpAddress() != null) {
        long ipMask = getIpMask(pfRule.getDestinationIpAddress().addr(), cidrSize);
        String maskedDestinationIpAddress = NetUtils.long2Ip(newCidrAddress | ipMask);
        pfRule.setDestinationIpAddress(new Ip(maskedDestinationIpAddress));
        _pfRulesDao.update(pfRule.getId(), pfRule);
      }
    }

    return implemented;
  }
  @Override
  public boolean applyStaticNats(Network network, List<? extends StaticNat> rules)
      throws ResourceUnavailableException {
    if (!_networkModel.isProviderSupportServiceInNetwork(
        network.getId(), Service.StaticNat, Provider.CiscoVnmc)) {
      s_logger.error(
          "Static NAT service is not provided by Cisco Vnmc device on network "
              + network.getName());
      return false;
    }

    // Find VNMC host for physical network
    List<CiscoVnmcControllerVO> devices =
        _ciscoVnmcDao.listByPhysicalNetwork(network.getPhysicalNetworkId());
    if (devices.isEmpty()) {
      s_logger.error("No Cisco Vnmc device on network " + network.getName());
      return true;
    }

    // Find if ASA 1000v is associated with network
    NetworkAsa1000vMapVO asaForNetwork = _networkAsa1000vMapDao.findByNetworkId(network.getId());
    if (asaForNetwork == null) {
      s_logger.debug("Cisco ASA 1000v device is not associated with network " + network.getName());
      return true;
    }

    if (network.getState() == Network.State.Allocated) {
      s_logger.debug(
          "External firewall was asked to apply static NAT rules for network with ID "
              + network.getId()
              + "; this network is not implemented. Skipping backend commands.");
      return true;
    }

    CiscoVnmcControllerVO ciscoVnmcDevice = devices.get(0);
    HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcDevice.getHostId());

    List<StaticNatRuleTO> rulesTO = new ArrayList<StaticNatRuleTO>();
    for (StaticNat rule : rules) {
      IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId());
      StaticNatRuleTO ruleTO =
          new StaticNatRuleTO(
              rule.getSourceIpAddressId(),
              sourceIp.getAddress().addr(),
              null,
              null,
              rule.getDestIpAddress(),
              null,
              null,
              null,
              rule.isForRevoke(),
              false);
      rulesTO.add(ruleTO);
    }

    if (!rulesTO.isEmpty()) {
      SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rulesTO, null);
      cmd.setContextParam(
          NetworkElementCommand.GUEST_VLAN_TAG, network.getBroadcastUri().getHost());
      cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, network.getCidr());
      Answer answer = _agentMgr.easySend(ciscoVnmcHost.getId(), cmd);
      if (answer == null || !answer.getResult()) {
        String details = (answer != null) ? answer.getDetails() : "details unavailable";
        String msg =
            "Unable to apply static NAT rules to Cisco ASA 1000v appliance due to: "
                + details
                + ".";
        s_logger.error(msg);
        throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId());
      }
    }

    return true;
  }