public void releaseIp(long ipId, long userId, Account caller) {
   s_logger.info("ELB: Release public IP for loadbalancing " + ipId);
   IPAddressVO ipvo = _ipAddressDao.findById(ipId);
   ipvo.setAssociatedWithNetworkId(null);
   _ipAddressDao.update(ipvo.getId(), ipvo);
   _networkMgr.disassociatePublicIpAddress(ipId, userId, caller);
   _ipAddressDao.unassignIpAddress(ipId);
 }
  @Override
  public List<UserVmVO> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd)
      throws PermissionDeniedException {
    Account caller = UserContext.current().getCaller();
    Long loadBalancerId = cmd.getId();
    Boolean applied = cmd.isApplied();

    if (applied == null) {
      applied = Boolean.TRUE;
    }

    LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
    if (loadBalancer == null) {
      return null;
    }

    _accountMgr.checkAccess(caller, null, loadBalancer);

    List<UserVmVO> loadBalancerInstances = new ArrayList<UserVmVO>();
    List<LoadBalancerVMMapVO> vmLoadBalancerMappings = null;

    vmLoadBalancerMappings = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId);

    List<Long> appliedInstanceIdList = new ArrayList<Long>();
    if ((vmLoadBalancerMappings != null) && !vmLoadBalancerMappings.isEmpty()) {
      for (LoadBalancerVMMapVO vmLoadBalancerMapping : vmLoadBalancerMappings) {
        appliedInstanceIdList.add(vmLoadBalancerMapping.getInstanceId());
      }
    }

    IPAddressVO addr = _ipAddressDao.findById(loadBalancer.getSourceIpAddressId());
    List<UserVmVO> userVms =
        _vmDao.listVirtualNetworkInstancesByAcctAndZone(
            loadBalancer.getAccountId(), addr.getDataCenterId(), loadBalancer.getNetworkId());

    for (UserVmVO userVm : userVms) {
      // if the VM is destroyed, being expunged, in an error state, or in an unknown state, skip it
      switch (userVm.getState()) {
        case Destroyed:
        case Expunging:
        case Error:
        case Unknown:
          continue;
      }

      boolean isApplied = appliedInstanceIdList.contains(userVm.getId());
      if ((isApplied && applied) || (!isApplied && !applied)) {
        loadBalancerInstances.add(userVm);
      }
    }

    return loadBalancerInstances;
  }
  @Override
  public boolean applyRules(
      List<? extends FirewallRule> rules, boolean continueOnError, boolean updateRulesInDB)
      throws ResourceUnavailableException {
    boolean success = true;
    if (rules == null || rules.size() == 0) {
      s_logger.debug("There are no rules to forward to the network elements");
      return true;
    }
    Purpose purpose = rules.get(0).getPurpose();
    if (!_ipAddrMgr.applyRules(rules, purpose, this, continueOnError)) {
      s_logger.warn("Rules are not completely applied");
      return false;
    } else {
      if (updateRulesInDB) {
        for (FirewallRule rule : rules) {
          if (rule.getState() == FirewallRule.State.Revoke) {
            FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(rule.getId());
            if (relatedRule != null) {
              s_logger.warn(
                  "Can't remove the firewall rule id="
                      + rule.getId()
                      + " as it has related firewall rule id="
                      + relatedRule.getId()
                      + "; leaving it in Revoke state");
              success = false;
            } else {
              removeRule(rule);
              if (rule.getSourceIpAddressId() != null) {
                // if the rule is the last one for the ip address assigned to VPC, unassign it from
                // the network
                IpAddress ip = _ipAddressDao.findById(rule.getSourceIpAddressId());
                _vpcMgr.unassignIPFromVpcNetwork(ip.getId(), rule.getNetworkId());
              }
            }
          } else if (rule.getState() == FirewallRule.State.Add) {
            FirewallRuleVO ruleVO = _firewallDao.findById(rule.getId());
            ruleVO.setState(FirewallRule.State.Active);
            _firewallDao.update(ruleVO.getId(), ruleVO);
          }
        }
      }
    }

    return success;
  }
  @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
  @DB
  public RemoteAccessVpn createRemoteAccessVpn(
      final long publicIpId, String ipRange, boolean openFirewall, final Boolean forDisplay)
      throws NetworkRuleConflictException {
    CallContext ctx = CallContext.current();
    final Account caller = ctx.getCallingAccount();

    Long networkId = null;

    // make sure ip address exists
    final PublicIpAddress ipAddr = _networkMgr.getPublicIpAddress(publicIpId);
    if (ipAddr == null) {
      throw new InvalidParameterValueException(
          "Unable to create remote access vpn, invalid public IP address id" + publicIpId);
    }

    _accountMgr.checkAccess(caller, null, true, ipAddr);

    if (!ipAddr.readyToUse()) {
      throw new InvalidParameterValueException(
          "The Ip address is not ready to be used yet: " + ipAddr.getAddress());
    }

    IPAddressVO ipAddress = _ipAddressDao.findById(publicIpId);

    networkId = ipAddress.getAssociatedWithNetworkId();
    if (networkId != null) {
      _networkMgr.checkIpForService(ipAddress, Service.Vpn, null);
    }

    final Long vpcId = ipAddress.getVpcId();
    /* IP Address used for VPC must be the source NAT IP of whole VPC */
    if (vpcId != null && ipAddress.isSourceNat()) {
      assert networkId == null;
      // No firewall setting for VPC, it would be open internally
      openFirewall = false;
    }

    final boolean openFirewallFinal = openFirewall;

    if (networkId == null && vpcId == null) {
      throw new InvalidParameterValueException(
          "Unable to create remote access vpn for the ipAddress: "
              + ipAddr.getAddress().addr()
              + " as ip is not associated with any network or VPC");
    }

    RemoteAccessVpnVO vpnVO = _remoteAccessVpnDao.findByPublicIpAddress(publicIpId);

    if (vpnVO != null) {
      // if vpn is in Added state, return it to the api
      if (vpnVO.getState() == RemoteAccessVpn.State.Added) {
        return vpnVO;
      }
      throw new InvalidParameterValueException(
          "A Remote Access VPN already exists for this public Ip address");
    }

    if (ipRange == null) {
      ipRange = RemoteAccessVpnClientIpRange.valueIn(ipAddr.getAccountId());
    }
    final String[] range = ipRange.split("-");
    if (range.length != 2) {
      throw new InvalidParameterValueException("Invalid ip range");
    }
    if (!NetUtils.isValidIp(range[0]) || !NetUtils.isValidIp(range[1])) {
      throw new InvalidParameterValueException("Invalid ip in range specification " + ipRange);
    }
    if (!NetUtils.validIpRange(range[0], range[1])) {
      throw new InvalidParameterValueException("Invalid ip range " + ipRange);
    }

    Pair<String, Integer> cidr = null;

    // TODO: assumes one virtual network / domr per account per zone
    if (networkId != null) {
      vpnVO = _remoteAccessVpnDao.findByAccountAndNetwork(ipAddr.getAccountId(), networkId);
      if (vpnVO != null) {
        // if vpn is in Added state, return it to the api
        if (vpnVO.getState() == RemoteAccessVpn.State.Added) {
          return vpnVO;
        }
        throw new InvalidParameterValueException(
            "A Remote Access VPN already exists for this account");
      }
      // Verify that vpn service is enabled for the network
      Network network = _networkMgr.getNetwork(networkId);
      if (!_networkMgr.areServicesSupportedInNetwork(network.getId(), Service.Vpn)) {
        throw new InvalidParameterValueException(
            "Vpn service is not supported in network id=" + ipAddr.getAssociatedWithNetworkId());
      }
      cidr = NetUtils.getCidr(network.getCidr());
    } else { // Don't need to check VPC because there is only one IP(source NAT IP) available for
             // VPN
      Vpc vpc = _vpcDao.findById(vpcId);
      cidr = NetUtils.getCidr(vpc.getCidr());
    }

    // FIXME: This check won't work for the case where the guest ip range
    // changes depending on the vlan allocated.
    String[] guestIpRange = NetUtils.getIpRangeFromCidr(cidr.first(), cidr.second());
    if (NetUtils.ipRangesOverlap(range[0], range[1], guestIpRange[0], guestIpRange[1])) {
      throw new InvalidParameterValueException(
          "Invalid ip range: "
              + ipRange
              + " overlaps with guest ip range "
              + guestIpRange[0]
              + "-"
              + guestIpRange[1]);
    }
    // TODO: check sufficient range
    // TODO: check overlap with private and public ip ranges in datacenter

    long startIp = NetUtils.ip2Long(range[0]);
    final String newIpRange = NetUtils.long2Ip(++startIp) + "-" + range[1];
    final String sharedSecret = PasswordGenerator.generatePresharedKey(_pskLength);

    return Transaction.execute(
        new TransactionCallbackWithException<RemoteAccessVpn, NetworkRuleConflictException>() {
          @Override
          public RemoteAccessVpn doInTransaction(TransactionStatus status)
              throws NetworkRuleConflictException {
            if (vpcId == null) {
              _rulesMgr.reservePorts(
                  ipAddr,
                  NetUtils.UDP_PROTO,
                  Purpose.Vpn,
                  openFirewallFinal,
                  caller,
                  NetUtils.VPN_PORT,
                  NetUtils.VPN_L2TP_PORT,
                  NetUtils.VPN_NATT_PORT);
            }
            RemoteAccessVpnVO vpnVO =
                new RemoteAccessVpnVO(
                    ipAddr.getAccountId(),
                    ipAddr.getDomainId(),
                    ipAddr.getAssociatedWithNetworkId(),
                    publicIpId,
                    vpcId,
                    range[0],
                    newIpRange,
                    sharedSecret);

            if (forDisplay != null) {
              vpnVO.setDisplay(forDisplay);
            }
            return _remoteAccessVpnDao.persist(vpnVO);
          }
        });
  }
  @DB
  public LoadBalancer createLoadBalancer(CreateLoadBalancerRuleCmd lb, boolean openFirewall)
      throws NetworkRuleConflictException {
    long ipId = lb.getSourceIpAddressId();
    UserContext caller = UserContext.current();
    int srcPortStart = lb.getSourcePortStart();
    int defPortStart = lb.getDefaultPortStart();
    int srcPortEnd = lb.getSourcePortEnd();

    IPAddressVO ipAddr = _ipAddressDao.findById(lb.getSourceIpAddressId());
    Long networkId = ipAddr.getSourceNetworkId();
    // make sure ip address exists
    if (ipAddr == null || !ipAddr.readyToUse()) {
      throw new InvalidParameterValueException(
          "Unable to create load balancer rule, invalid IP address id" + ipId);
    }

    _firewallMgr.validateFirewallRule(
        caller.getCaller(),
        ipAddr,
        srcPortStart,
        srcPortEnd,
        lb.getProtocol(),
        Purpose.LoadBalancing);

    networkId = ipAddr.getAssociatedWithNetworkId();
    if (networkId == null) {
      throw new InvalidParameterValueException(
          "Unable to create load balancer rule ; ip id="
              + ipId
              + " is not associated with any network");
    }
    NetworkVO network = _networkDao.findById(networkId);

    _accountMgr.checkAccess(caller.getCaller(), null, ipAddr);

    // verify that lb service is supported by the network
    if (!_networkMgr.isServiceSupported(network.getNetworkOfferingId(), Service.Lb)) {
      throw new InvalidParameterValueException(
          "LB service is not supported in network id= " + networkId);
    }

    Transaction txn = Transaction.currentTxn();
    txn.start();

    LoadBalancerVO newRule =
        new LoadBalancerVO(
            lb.getXid(),
            lb.getName(),
            lb.getDescription(),
            lb.getSourceIpAddressId(),
            lb.getSourcePortEnd(),
            lb.getDefaultPortStart(),
            lb.getAlgorithm(),
            network.getId(),
            ipAddr.getAccountId(),
            ipAddr.getDomainId());

    newRule = _lbDao.persist(newRule);

    if (openFirewall) {
      _firewallMgr.createRuleForAllCidrs(
          ipId,
          caller.getCaller(),
          lb.getSourcePortStart(),
          lb.getSourcePortEnd(),
          lb.getProtocol(),
          null,
          null,
          newRule.getId());
    }

    boolean success = true;

    try {
      _firewallMgr.detectRulesConflict(newRule, ipAddr);
      if (!_firewallDao.setStateToAdd(newRule)) {
        throw new CloudRuntimeException("Unable to update the state to add for " + newRule);
      }
      s_logger.debug(
          "Load balancer "
              + newRule.getId()
              + " for Ip address id="
              + ipId
              + ", public port "
              + srcPortStart
              + ", private port "
              + defPortStart
              + " is added successfully.");
      UserContext.current().setEventDetails("Load balancer Id: " + newRule.getId());
      UsageEventVO usageEvent =
          new UsageEventVO(
              EventTypes.EVENT_LOAD_BALANCER_CREATE,
              ipAddr.getAllocatedToAccountId(),
              ipAddr.getDataCenterId(),
              newRule.getId(),
              null);
      _usageEventDao.persist(usageEvent);
      txn.commit();

      return newRule;
    } catch (Exception e) {
      success = false;
      if (e instanceof NetworkRuleConflictException) {
        throw (NetworkRuleConflictException) e;
      }
      throw new CloudRuntimeException(
          "Unable to add rule for ip address id=" + newRule.getSourceIpAddressId(), e);
    } finally {
      if (!success && newRule != null) {

        txn.start();
        _firewallMgr.revokeRelatedFirewallRule(newRule.getId(), false);
        _lbDao.remove(newRule.getId());

        txn.commit();
      }
    }
  }
  @Override
  @DB
  public LoadBalancer handleCreateLoadBalancerRule(
      CreateLoadBalancerRuleCmd lb, Account account, long networkId)
      throws InsufficientAddressCapacityException, NetworkRuleConflictException {
    // this part of code is executed when the LB provider is Elastic Load Balancer vm
    if (!_networkModel.isProviderSupportServiceInNetwork(
        lb.getNetworkId(), Service.Lb, Provider.ElasticLoadBalancerVm)) {
      return null;
    }

    Long ipId = lb.getSourceIpAddressId();
    if (ipId != null) {
      return null;
    }
    boolean newIp = false;
    account = _accountDao.acquireInLockTable(account.getId());
    if (account == null) {
      s_logger.warn("ELB: CreateLoadBalancer: Failed to acquire lock on account");
      throw new CloudRuntimeException("Failed to acquire lock on account");
    }
    try {
      List<LoadBalancerVO> existingLbs =
          findExistingLoadBalancers(
              lb.getName(),
              lb.getSourceIpAddressId(),
              lb.getAccountId(),
              lb.getDomainId(),
              lb.getSourcePortStart());
      if (existingLbs == null) {
        existingLbs =
            findExistingLoadBalancers(
                lb.getName(), lb.getSourceIpAddressId(), lb.getAccountId(), lb.getDomainId(), null);
        if (existingLbs == null) {
          if (lb.getSourceIpAddressId() != null) {
            existingLbs =
                findExistingLoadBalancers(
                    lb.getName(), null, lb.getAccountId(), lb.getDomainId(), null);
            if (existingLbs != null) {
              throw new InvalidParameterValueException(
                  "Supplied LB name "
                      + lb.getName()
                      + " is not associated with IP "
                      + lb.getSourceIpAddressId());
            }
          } else {
            s_logger.debug(
                "Could not find any existing frontend ips for this account for this LB rule, acquiring a new frontent IP for ELB");
            PublicIp ip = allocDirectIp(account, networkId);
            ipId = ip.getId();
            newIp = true;
          }
        } else {
          ipId = existingLbs.get(0).getSourceIpAddressId();
          s_logger.debug(
              "ELB: Found existing frontend ip for this account for this LB rule " + ipId);
        }
      } else {
        s_logger.warn("ELB: Found existing load balancers matching requested new LB");
        throw new NetworkRuleConflictException(
            "ELB: Found existing load balancers matching requested new LB");
      }

      Network network = _networkModel.getNetwork(networkId);
      IPAddressVO ipAddr = _ipAddressDao.findById(ipId);

      LoadBalancer result = null;
      try {
        lb.setSourceIpAddressId(ipId);
        result = _lbMgr.createLoadBalancer(lb, false);
      } catch (NetworkRuleConflictException e) {
        s_logger.warn("Failed to create LB rule, not continuing with ELB deployment");
        if (newIp) {
          releaseIp(ipId, UserContext.current().getCallerUserId(), account);
        }
        throw e;
      }

      DomainRouterVO elbVm = null;

      if (existingLbs == null) {
        elbVm = findELBVmWithCapacity(network, ipAddr);
        if (elbVm == null) {
          elbVm = deployLoadBalancerVM(networkId, ipAddr, account.getId());
          if (elbVm == null) {
            s_logger.warn(
                "Failed to deploy a new ELB vm for ip "
                    + ipAddr
                    + " in network "
                    + network
                    + "lb name="
                    + lb.getName());
            if (newIp) releaseIp(ipId, UserContext.current().getCallerUserId(), account);
          }
        }

      } else {
        ElasticLbVmMapVO elbVmMap = _elbVmMapDao.findOneByIp(ipId);
        if (elbVmMap != null) {
          elbVm = _routerDao.findById(elbVmMap.getElbVmId());
        }
      }

      if (elbVm == null) {
        s_logger.warn("No ELB VM can be found or deployed");
        s_logger.warn("Deleting LB since we failed to deploy ELB VM");
        _lbDao.remove(result.getId());
        return null;
      }

      ElasticLbVmMapVO mapping = new ElasticLbVmMapVO(ipId, elbVm.getId(), result.getId());
      _elbVmMapDao.persist(mapping);
      return result;

    } finally {
      if (account != null) {
        _accountDao.releaseFromLockTable(account.getId());
      }
    }
  }
  @Override
  public RemoteAccessVpn createRemoteAccessVpn(
      long publicIpId, String ipRange, boolean openFirewall, long networkId)
      throws NetworkRuleConflictException {
    UserContext ctx = UserContext.current();
    Account caller = ctx.getCaller();

    // make sure ip address exists
    PublicIpAddress ipAddr = _networkMgr.getPublicIpAddress(publicIpId);
    if (ipAddr == null) {
      throw new InvalidParameterValueException(
          "Unable to create remote access vpn, invalid public IP address id" + publicIpId);
    }

    _accountMgr.checkAccess(caller, null, true, ipAddr);

    if (!ipAddr.readyToUse()) {
      throw new InvalidParameterValueException(
          "The Ip address is not ready to be used yet: " + ipAddr.getAddress());
    }

    IPAddressVO ipAddress = _ipAddressDao.findById(publicIpId);
    _networkMgr.checkIpForService(ipAddress, Service.Vpn, null);

    RemoteAccessVpnVO vpnVO = _remoteAccessVpnDao.findByPublicIpAddress(publicIpId);

    if (vpnVO != null) {
      // if vpn is in Added state, return it to the api
      if (vpnVO.getState() == RemoteAccessVpn.State.Added) {
        return vpnVO;
      }
      throw new InvalidParameterValueException(
          "A Remote Access VPN already exists for this public Ip address");
    }

    // TODO: assumes one virtual network / domr per account per zone
    vpnVO = _remoteAccessVpnDao.findByAccountAndNetwork(ipAddr.getAccountId(), networkId);
    if (vpnVO != null) {
      // if vpn is in Added state, return it to the api
      if (vpnVO.getState() == RemoteAccessVpn.State.Added) {
        return vpnVO;
      }
      throw new InvalidParameterValueException(
          "A Remote Access VPN already exists for this account");
    }

    // Verify that vpn service is enabled for the network
    Network network = _networkMgr.getNetwork(networkId);
    if (!_networkMgr.areServicesSupportedInNetwork(network.getId(), Service.Vpn)) {
      throw new InvalidParameterValueException(
          "Vpn service is not supported in network id=" + ipAddr.getAssociatedWithNetworkId());
    }

    if (ipRange == null) {
      ipRange = _clientIpRange;
    }
    String[] range = ipRange.split("-");
    if (range.length != 2) {
      throw new InvalidParameterValueException("Invalid ip range");
    }
    if (!NetUtils.isValidIp(range[0]) || !NetUtils.isValidIp(range[1])) {
      throw new InvalidParameterValueException("Invalid ip in range specification " + ipRange);
    }
    if (!NetUtils.validIpRange(range[0], range[1])) {
      throw new InvalidParameterValueException("Invalid ip range " + ipRange);
    }

    Pair<String, Integer> cidr = NetUtils.getCidr(network.getCidr());

    // FIXME: This check won't work for the case where the guest ip range
    // changes depending on the vlan allocated.
    String[] guestIpRange = NetUtils.getIpRangeFromCidr(cidr.first(), cidr.second());
    if (NetUtils.ipRangesOverlap(range[0], range[1], guestIpRange[0], guestIpRange[1])) {
      throw new InvalidParameterValueException(
          "Invalid ip range: "
              + ipRange
              + " overlaps with guest ip range "
              + guestIpRange[0]
              + "-"
              + guestIpRange[1]);
    }
    // TODO: check sufficient range
    // TODO: check overlap with private and public ip ranges in datacenter

    long startIp = NetUtils.ip2Long(range[0]);
    String newIpRange = NetUtils.long2Ip(++startIp) + "-" + range[1];
    String sharedSecret = PasswordGenerator.generatePresharedKey(_pskLength);
    _rulesMgr.reservePorts(
        ipAddr,
        NetUtils.UDP_PROTO,
        Purpose.Vpn,
        openFirewall,
        caller,
        NetUtils.VPN_PORT,
        NetUtils.VPN_L2TP_PORT,
        NetUtils.VPN_NATT_PORT);
    vpnVO =
        new RemoteAccessVpnVO(
            ipAddr.getAccountId(),
            ipAddr.getDomainId(),
            ipAddr.getAssociatedWithNetworkId(),
            publicIpId,
            range[0],
            newIpRange,
            sharedSecret);
    return _remoteAccessVpnDao.persist(vpnVO);
  }
  @Override
  public Pair<List<? extends FirewallRule>, Integer> listFirewallRules(ListFirewallRulesCmd cmd) {
    Long ipId = cmd.getIpAddressId();
    Long id = cmd.getId();
    Long networkId = null;
    Map<String, String> tags = cmd.getTags();
    FirewallRule.TrafficType trafficType = cmd.getTrafficType();

    Account caller = CallContext.current().getCallingAccount();
    List<Long> permittedAccounts = new ArrayList<Long>();

    if (ipId != null) {
      IPAddressVO ipAddressVO = _ipAddressDao.findById(ipId);
      if (ipAddressVO == null || !ipAddressVO.readyToUse()) {
        throw new InvalidParameterValueException(
            "Ip address id=" + ipId + " not ready for firewall rules yet");
      }
      _accountMgr.checkAccess(caller, null, true, ipAddressVO);
    }

    Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject =
        new Ternary<Long, Boolean, ListProjectResourcesCriteria>(
            cmd.getDomainId(), cmd.isRecursive(), null);
    _accountMgr.buildACLSearchParameters(
        caller,
        id,
        cmd.getAccountName(),
        cmd.getProjectId(),
        permittedAccounts,
        domainIdRecursiveListProject,
        cmd.listAll(),
        false);
    Long domainId = domainIdRecursiveListProject.first();
    Boolean isRecursive = domainIdRecursiveListProject.second();
    ListProjectResourcesCriteria listProjectResourcesCriteria =
        domainIdRecursiveListProject.third();

    Filter filter =
        new Filter(FirewallRuleVO.class, "id", false, cmd.getStartIndex(), cmd.getPageSizeVal());
    SearchBuilder<FirewallRuleVO> sb = _firewallDao.createSearchBuilder();
    _accountMgr.buildACLSearchBuilder(
        sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);

    sb.and("id", sb.entity().getId(), Op.EQ);
    sb.and("trafficType", sb.entity().getTrafficType(), Op.EQ);
    if (cmd instanceof ListEgressFirewallRulesCmd) {
      networkId = ((ListEgressFirewallRulesCmd) cmd).getNetworkId();
      sb.and("networkId", sb.entity().getNetworkId(), Op.EQ);
    } else {
      sb.and("ip", sb.entity().getSourceIpAddressId(), Op.EQ);
    }
    sb.and("purpose", sb.entity().getPurpose(), Op.EQ);

    if (tags != null && !tags.isEmpty()) {
      SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder();
      for (int count = 0; count < tags.size(); count++) {
        tagSearch
            .or()
            .op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ);
        tagSearch.and(
            "value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ);
        tagSearch.cp();
      }
      tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
      sb.groupBy(sb.entity().getId());
      sb.join(
          "tagSearch",
          tagSearch,
          sb.entity().getId(),
          tagSearch.entity().getResourceId(),
          JoinBuilder.JoinType.INNER);
    }

    SearchCriteria<FirewallRuleVO> sc = sb.create();
    _accountMgr.buildACLSearchCriteria(
        sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);

    if (id != null) {
      sc.setParameters("id", id);
    }

    if (tags != null && !tags.isEmpty()) {
      int count = 0;
      sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.FirewallRule.toString());
      for (String key : tags.keySet()) {
        sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key);
        sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key));
        count++;
      }
    }

    if (ipId != null) {
      sc.setParameters("ip", ipId);
    } else if (cmd instanceof ListEgressFirewallRulesCmd) {
      if (networkId != null) {
        sc.setParameters("networkId", networkId);
      }
    }

    sc.setParameters("purpose", Purpose.Firewall);
    sc.setParameters("trafficType", trafficType);

    Pair<List<FirewallRuleVO>, Integer> result = _firewallDao.searchAndCount(sc, filter);
    return new Pair<List<? extends FirewallRule>, Integer>(result.first(), result.second());
  }
  @DB
  protected FirewallRule createFirewallRule(
      Long ipAddrId,
      Account caller,
      String xId,
      Integer portStart,
      Integer portEnd,
      String protocol,
      List<String> sourceCidrList,
      Integer icmpCode,
      Integer icmpType,
      Long relatedRuleId,
      FirewallRule.FirewallRuleType type,
      Long networkId,
      FirewallRule.TrafficType trafficType)
      throws NetworkRuleConflictException {

    IPAddressVO ipAddress = null;
    if (ipAddrId != null) {
      // this for ingress firewall rule, for egress id is null
      ipAddress = _ipAddressDao.findById(ipAddrId);
      // Validate ip address
      if (ipAddress == null && type == FirewallRule.FirewallRuleType.User) {
        throw new InvalidParameterValueException(
            "Unable to create firewall rule; " + "couldn't locate IP address by id in the system");
      }
      _networkModel.checkIpForService(ipAddress, Service.Firewall, null);
    }

    validateFirewallRule(
        caller,
        ipAddress,
        portStart,
        portEnd,
        protocol,
        Purpose.Firewall,
        type,
        networkId,
        trafficType);

    // icmp code and icmp type can't be passed in for any other protocol rather than icmp
    if (!protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (icmpCode != null || icmpType != null)) {
      throw new InvalidParameterValueException(
          "Can specify icmpCode and icmpType for ICMP protocol only");
    }

    if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (portStart != null || portEnd != null)) {
      throw new InvalidParameterValueException(
          "Can't specify start/end port when protocol is ICMP");
    }

    Long accountId = null;
    Long domainId = null;

    if (ipAddress != null) {
      // Ingress firewall rule
      accountId = ipAddress.getAllocatedToAccountId();
      domainId = ipAddress.getAllocatedInDomainId();
    } else if (networkId != null) {
      // egress firewall rule
      Network network = _networkModel.getNetwork(networkId);
      accountId = network.getAccountId();
      domainId = network.getDomainId();
    }

    Transaction txn = Transaction.currentTxn();
    txn.start();

    FirewallRuleVO newRule =
        new FirewallRuleVO(
            xId,
            ipAddrId,
            portStart,
            portEnd,
            protocol.toLowerCase(),
            networkId,
            accountId,
            domainId,
            Purpose.Firewall,
            sourceCidrList,
            icmpCode,
            icmpType,
            relatedRuleId,
            trafficType);
    newRule.setType(type);
    newRule = _firewallDao.persist(newRule);

    if (type == FirewallRuleType.User) detectRulesConflict(newRule);

    if (!_firewallDao.setStateToAdd(newRule)) {
      throw new CloudRuntimeException("Unable to update the state to add for " + newRule);
    }
    CallContext.current().setEventDetails("Rule Id: " + newRule.getId());

    txn.commit();

    return newRule;
  }