@Override
  public List<CiscoAsa1000vDeviceVO> listCiscoAsa1000vResources(ListCiscoAsa1000vResourcesCmd cmd) {
    Long physicalNetworkId = cmd.getPhysicalNetworkId();
    Long ciscoAsa1000vResourceId = cmd.getCiscoAsa1000vResourceId();
    List<CiscoAsa1000vDeviceVO> responseList = new ArrayList<CiscoAsa1000vDeviceVO>();

    if (physicalNetworkId == null && ciscoAsa1000vResourceId == null) {
      throw new InvalidParameterValueException(
          "Either physical network Id or Asa 1000v device Id must be specified");
    }

    if (ciscoAsa1000vResourceId != null) {
      CiscoAsa1000vDeviceVO ciscoAsa1000vResource =
          _ciscoAsa1000vDao.findById(ciscoAsa1000vResourceId);
      if (ciscoAsa1000vResource == null) {
        throw new InvalidParameterValueException(
            "Could not find Cisco Asa 1000v device with id: " + ciscoAsa1000vResourceId);
      }
      responseList.add(ciscoAsa1000vResource);
    } else {
      PhysicalNetworkVO physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId);
      if (physicalNetwork == null) {
        throw new InvalidParameterValueException(
            "Could not find a physical network with id: " + physicalNetworkId);
      }
      responseList = _ciscoAsa1000vDao.listByPhysicalNetwork(physicalNetworkId);
    }

    return responseList;
  }
  @Override
  public boolean deleteCiscoVnmcResource(DeleteCiscoVnmcResourceCmd cmd) {
    Long vnmcResourceId = cmd.getCiscoVnmcResourceId();
    CiscoVnmcControllerVO vnmcResource = _ciscoVnmcDao.findById(vnmcResourceId);
    if (vnmcResource == null) {
      throw new InvalidParameterValueException(
          "Could not find a Cisco VNMC appliance with id " + vnmcResourceId);
    }

    // Check if there any ASA 1000v appliances
    Long physicalNetworkId = vnmcResource.getPhysicalNetworkId();
    PhysicalNetworkVO physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId);
    if (physicalNetwork != null) {
      List<CiscoAsa1000vDeviceVO> responseList =
          _ciscoAsa1000vDao.listByPhysicalNetwork(physicalNetworkId);
      if (responseList.size() > 0) {
        throw new CloudRuntimeException(
            "Cisco VNMC appliance with id "
                + vnmcResourceId
                + " cannot be deleted as there Cisco ASA 1000v appliances using it");
      }
    }

    HostVO vnmcHost = _hostDao.findById(vnmcResource.getHostId());
    Long hostId = vnmcHost.getId();
    vnmcHost.setResourceState(ResourceState.Maintenance);
    _hostDao.update(hostId, vnmcHost);
    _resourceMgr.deleteHost(hostId, false, false);
    _ciscoVnmcDao.remove(vnmcResourceId);

    return true;
  }
  @Override
  public boolean deleteCiscoAsa1000vResource(DeleteCiscoAsa1000vResourceCmd cmd) {
    Long asaResourceId = cmd.getCiscoAsa1000vResourceId();
    CiscoAsa1000vDeviceVO asaResource = _ciscoAsa1000vDao.findById(asaResourceId);
    if (asaResource == null) {
      throw new InvalidParameterValueException(
          "Could not find a Cisco ASA 1000v appliance with id " + asaResourceId);
    }

    NetworkAsa1000vMapVO networkAsaMap =
        _networkAsa1000vMapDao.findByAsa1000vId(asaResource.getId());
    if (networkAsaMap != null) {
      throw new CloudRuntimeException(
          "Cisco ASA 1000v appliance with id "
              + asaResourceId
              + " cannot be deleted as it is associated with guest network");
    }

    _ciscoAsa1000vDao.remove(asaResourceId);

    return true;
  }
 @Override
 public CiscoAsa1000vDevice assignAsa1000vToNetwork(Network network) {
   List<CiscoAsa1000vDeviceVO> asaList =
       _ciscoAsa1000vDao.listByPhysicalNetwork(network.getPhysicalNetworkId());
   for (CiscoAsa1000vDeviceVO asa : asaList) {
     NetworkAsa1000vMapVO assignedToNetwork = _networkAsa1000vMapDao.findByAsa1000vId(asa.getId());
     if (assignedToNetwork == null) {
       NetworkAsa1000vMapVO networkAsaMap = new NetworkAsa1000vMapVO(network.getId(), asa.getId());
       _networkAsa1000vMapDao.persist(networkAsaMap);
       return asa;
     }
   }
   return null;
 }
  @Override
  public CiscoAsa1000vDevice addCiscoAsa1000vResource(AddCiscoAsa1000vResourceCmd cmd) {
    Long physicalNetworkId = cmd.getPhysicalNetworkId();
    CiscoAsa1000vDevice ciscoAsa1000vResource = null;

    PhysicalNetworkVO physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId);
    if (physicalNetwork == null) {
      throw new InvalidParameterValueException(
          "Could not find phyical network with ID: " + physicalNetworkId);
    }

    ciscoAsa1000vResource =
        new CiscoAsa1000vDeviceVO(
            physicalNetworkId, cmd.getManagementIp(), cmd.getInPortProfile(), cmd.getClusterId());
    _ciscoAsa1000vDao.persist((CiscoAsa1000vDeviceVO) ciscoAsa1000vResource);

    return ciscoAsa1000vResource;
  }
  @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;
  }