@Override
  public boolean applyFirewallRules(
      List<FirewallRuleVO> rules, boolean continueOnError, Account caller) {

    if (rules.size() == 0) {
      s_logger.debug("There are no firewall rules to apply");
      return true;
    }

    for (FirewallRuleVO rule : rules) {
      // load cidrs if any
      rule.setSourceCidrList(_firewallCidrsDao.getSourceCidrs(rule.getId()));
    }

    if (caller != null) {
      _accountMgr.checkAccess(caller, null, true, rules.toArray(new FirewallRuleVO[rules.size()]));
    }

    try {
      if (!applyRules(rules, continueOnError, true)) {
        return false;
      }
    } catch (ResourceUnavailableException ex) {
      s_logger.warn("Failed to apply firewall rules due to ", ex);
      return false;
    }

    return true;
  }
  @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;
  }
  @Override
  @DB
  public void revokeRule(FirewallRuleVO rule, Account caller, long userId, boolean needUsageEvent) {
    if (caller != null) {
      _accountMgr.checkAccess(caller, null, true, rule);
    }

    Transaction txn = Transaction.currentTxn();
    boolean generateUsageEvent = false;

    txn.start();
    if (rule.getState() == State.Staged) {
      if (s_logger.isDebugEnabled()) {
        s_logger.debug("Found a rule that is still in stage state so just removing it: " + rule);
      }
      removeRule(rule);
      generateUsageEvent = true;
    } else if (rule.getState() == State.Add || rule.getState() == State.Active) {
      rule.setState(State.Revoke);
      _firewallDao.update(rule.getId(), rule);
      generateUsageEvent = true;
    }

    if (generateUsageEvent && needUsageEvent) {
      UsageEventUtils.publishUsageEvent(
          EventTypes.EVENT_NET_RULE_DELETE,
          rule.getAccountId(),
          0,
          rule.getId(),
          null,
          rule.getClass().getName(),
          rule.getUuid());
    }

    txn.commit();
  }
  @Override
  @ActionEvent(
      eventType = EventTypes.EVENT_FIREWALL_CLOSE,
      eventDescription = "revoking firewall rule",
      async = true)
  public boolean revokeAllFirewallRulesForNetwork(long networkId, long userId, Account caller)
      throws ResourceUnavailableException {
    List<FirewallRule> rules = new ArrayList<FirewallRule>();

    List<FirewallRuleVO> fwRules =
        _firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.Firewall);
    if (s_logger.isDebugEnabled()) {
      s_logger.debug("Releasing " + fwRules.size() + " firewall rules for network id=" + networkId);
    }

    for (FirewallRuleVO rule : fwRules) {
      // Mark all Firewall rules as Revoke, but don't revoke them yet - we have to revoke all rules
      // for ip, no
      // need to send them one by one
      revokeFirewallRule(rule.getId(), false, caller, Account.ACCOUNT_ID_SYSTEM);
    }

    // now send everything to the backend
    List<FirewallRuleVO> rulesToApply =
        _firewallDao.listByNetworkAndPurpose(networkId, Purpose.Firewall);
    boolean success = applyFirewallRules(rulesToApply, true, caller);

    // Now we check again in case more rules have been inserted.
    rules.addAll(_firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.Firewall));

    if (s_logger.isDebugEnabled()) {
      s_logger.debug(
          "Successfully released firewall rules for network id="
              + networkId
              + " and # of rules now = "
              + rules.size());
    }

    return success && rules.size() == 0;
  }
  @DB
  public boolean deleteLoadBalancerRule(
      long loadBalancerId, boolean apply, Account caller, long callerUserId) {
    LoadBalancerVO lb = _lbDao.findById(loadBalancerId);
    Transaction txn = Transaction.currentTxn();
    boolean generateUsageEvent = false;
    boolean success = true;

    txn.start();
    if (lb.getState() == FirewallRule.State.Staged) {
      if (s_logger.isDebugEnabled()) {
        s_logger.debug("Found a rule that is still in stage state so just removing it: " + lb);
      }
      generateUsageEvent = true;
    } else if (lb.getState() == FirewallRule.State.Add
        || lb.getState() == FirewallRule.State.Active) {
      lb.setState(FirewallRule.State.Revoke);
      _lbDao.persist(lb);
      generateUsageEvent = true;
    }

    List<LoadBalancerVMMapVO> maps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId);
    if (maps != null) {
      for (LoadBalancerVMMapVO map : maps) {
        map.setRevoke(true);
        _lb2VmMapDao.persist(map);
        s_logger.debug(
            "Set load balancer rule for revoke: rule id "
                + loadBalancerId
                + ", vmId "
                + map.getInstanceId());
      }
    }

    if (generateUsageEvent) {
      // Generate usage event right after all rules were marked for revoke
      UsageEventVO usageEvent =
          new UsageEventVO(
              EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), 0, lb.getId(), null);
      _usageEventDao.persist(usageEvent);
    }

    txn.commit();

    if (apply) {
      try {
        if (!applyLoadBalancerConfig(loadBalancerId)) {
          s_logger.warn("Unable to apply the load balancer config");
          return false;
        }
      } catch (ResourceUnavailableException e) {
        s_logger.warn(
            "Unable to apply the load balancer config because resource is unavaliable.", e);
        return false;
      }
    }

    FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(lb.getId());
    if (relatedRule != null) {
      s_logger.warn(
          "Unable to remove firewall rule id="
              + lb.getId()
              + " as it has related firewall rule id="
              + relatedRule.getId()
              + "; leaving it in Revoke state");
      success = false;
    } else {
      _firewallDao.remove(lb.getId());
    }

    _elbMgr.handleDeleteLoadBalancerRule(lb, callerUserId, caller);
    if (success) {
      s_logger.debug("Load balancer with id " + lb.getId() + " is removed successfully");
    }

    return success;
  }
  @Override
  public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflictException {
    List<FirewallRuleVO> rules;
    if (newRule.getSourceIpAddressId() != null) {
      rules = _firewallDao.listByIpAndPurposeAndNotRevoked(newRule.getSourceIpAddressId(), null);
      assert (rules.size() >= 1)
          : "For network rules, we now always first persist the rule and then check for "
              + "network conflicts so we should at least have one rule at this point.";
    } else {
      // fetches only firewall egress rules.
      rules =
          _firewallDao.listByNetworkPurposeTrafficTypeAndNotRevoked(
              newRule.getNetworkId(), Purpose.Firewall, newRule.getTrafficType());
      assert (rules.size() >= 1);
    }

    for (FirewallRuleVO rule : rules) {
      if (rule.getId() == newRule.getId()) {
        continue; // Skips my own rule.
      }

      boolean oneOfRulesIsFirewall =
          ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall)
              && ((newRule.getPurpose() != rule.getPurpose())
                  || (!newRule.getProtocol().equalsIgnoreCase(rule.getProtocol()))));

      // if both rules are firewall and their cidrs are different, we can skip port ranges
      // verification
      boolean bothRulesFirewall =
          (rule.getPurpose() == newRule.getPurpose() && rule.getPurpose() == Purpose.Firewall);
      boolean duplicatedCidrs = false;
      if (bothRulesFirewall) {
        // Verify that the rules have different cidrs
        _firewallDao.loadSourceCidrs(rule);
        _firewallDao.loadSourceCidrs((FirewallRuleVO) newRule);

        List<String> ruleCidrList = rule.getSourceCidrList();
        List<String> newRuleCidrList = newRule.getSourceCidrList();

        if (ruleCidrList == null || newRuleCidrList == null) {
          continue;
        }

        Collection<String> similar = new HashSet<String>(ruleCidrList);
        similar.retainAll(newRuleCidrList);

        if (similar.size() > 0) {
          duplicatedCidrs = true;
        }
      }

      if (!oneOfRulesIsFirewall) {
        if (rule.getPurpose() == Purpose.StaticNat && newRule.getPurpose() != Purpose.StaticNat) {
          throw new NetworkRuleConflictException(
              "There is 1 to 1 Nat rule specified for the ip address id="
                  + newRule.getSourceIpAddressId());
        } else if (rule.getPurpose() != Purpose.StaticNat
            && newRule.getPurpose() == Purpose.StaticNat) {
          throw new NetworkRuleConflictException(
              "There is already firewall rule specified for the ip address id="
                  + newRule.getSourceIpAddressId());
        }
      }

      if (rule.getNetworkId() != newRule.getNetworkId() && rule.getState() != State.Revoke) {
        throw new NetworkRuleConflictException(
            "New rule is for a different network than what's specified in rule " + rule.getXid());
      }

      if (newRule.getProtocol().equalsIgnoreCase(NetUtils.ICMP_PROTO)
          && newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())) {
        if (newRule.getIcmpCode().longValue() == rule.getIcmpCode().longValue()
            && newRule.getIcmpType().longValue() == rule.getIcmpType().longValue()
            && newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())
            && duplicatedCidrs) {
          throw new InvalidParameterValueException(
              "New rule conflicts with existing rule id=" + rule.getId());
        }
      }

      boolean notNullPorts =
          (newRule.getSourcePortStart() != null
              && newRule.getSourcePortEnd() != null
              && rule.getSourcePortStart() != null
              && rule.getSourcePortEnd() != null);
      if (!notNullPorts) {
        continue;
      } else if (!oneOfRulesIsFirewall
          && !(bothRulesFirewall && !duplicatedCidrs)
          && ((rule.getSourcePortStart().intValue() <= newRule.getSourcePortStart().intValue()
                  && rule.getSourcePortEnd().intValue() >= newRule.getSourcePortStart().intValue())
              || (rule.getSourcePortStart().intValue() <= newRule.getSourcePortEnd().intValue()
                  && rule.getSourcePortEnd().intValue() >= newRule.getSourcePortEnd().intValue())
              || (newRule.getSourcePortStart().intValue() <= rule.getSourcePortStart().intValue()
                  && newRule.getSourcePortEnd().intValue() >= rule.getSourcePortStart().intValue())
              || (newRule.getSourcePortStart().intValue() <= rule.getSourcePortEnd().intValue()
                  && newRule.getSourcePortEnd().intValue()
                      >= rule.getSourcePortEnd().intValue()))) {

        // we allow port forwarding rules with the same parameters but different protocols
        boolean allowPf =
            (rule.getPurpose() == Purpose.PortForwarding
                && newRule.getPurpose() == Purpose.PortForwarding
                && !newRule.getProtocol().equalsIgnoreCase(rule.getProtocol()));
        boolean allowStaticNat =
            (rule.getPurpose() == Purpose.StaticNat
                && newRule.getPurpose() == Purpose.StaticNat
                && !newRule.getProtocol().equalsIgnoreCase(rule.getProtocol()));

        if (!(allowPf || allowStaticNat || oneOfRulesIsFirewall)) {
          throw new NetworkRuleConflictException(
              "The range specified, "
                  + newRule.getSourcePortStart()
                  + "-"
                  + newRule.getSourcePortEnd()
                  + ", conflicts with rule "
                  + rule.getId()
                  + " which has "
                  + rule.getSourcePortStart()
                  + "-"
                  + rule.getSourcePortEnd());
        }
      }
    }

    if (s_logger.isDebugEnabled()) {
      s_logger.debug(
          "No network rule conflicts detected for "
              + newRule
              + " against "
              + (rules.size() - 1)
              + " existing rules");
    }
  }
  @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;
  }
  @Override
  @ActionEvent(
      eventType = EventTypes.EVENT_FIREWALL_CLOSE,
      eventDescription = "revoking firewall rule",
      async = true)
  public boolean revokeFirewallRulesForVm(long vmId) {
    boolean success = true;
    UserVmVO vm = _vmDao.findByIdIncludingRemoved(vmId);
    if (vm == null) {
      return false;
    }

    List<PortForwardingRuleVO> pfRules = _pfRulesDao.listByVm(vmId);
    List<FirewallRuleVO> staticNatRules = _firewallDao.listStaticNatByVmId(vm.getId());
    List<FirewallRuleVO> firewallRules = new ArrayList<FirewallRuleVO>();

    // Make a list of firewall rules to reprogram
    for (PortForwardingRuleVO pfRule : pfRules) {
      FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(pfRule.getId());
      if (relatedRule != null) {
        firewallRules.add(relatedRule);
      }
    }

    for (FirewallRuleVO staticNatRule : staticNatRules) {
      FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(staticNatRule.getId());
      if (relatedRule != null) {
        firewallRules.add(relatedRule);
      }
    }

    Set<Long> ipsToReprogram = new HashSet<Long>();

    if (firewallRules.isEmpty()) {
      s_logger.debug("No firewall rules are found for vm id=" + vmId);
      return true;
    } else {
      s_logger.debug("Found " + firewallRules.size() + " to cleanup for vm id=" + vmId);
    }

    for (FirewallRuleVO rule : firewallRules) {
      // Mark firewall rules as Revoked, but don't revoke it yet (apply=false)
      revokeFirewallRule(
          rule.getId(), false, _accountMgr.getSystemAccount(), Account.ACCOUNT_ID_SYSTEM);
      ipsToReprogram.add(rule.getSourceIpAddressId());
    }

    // apply rules for all ip addresses
    for (Long ipId : ipsToReprogram) {
      s_logger.debug(
          "Applying firewall rules for ip address id=" + ipId + " as a part of vm expunge");
      try {
        success = success && applyIngressFirewallRules(ipId, _accountMgr.getSystemAccount());
      } catch (ResourceUnavailableException ex) {
        s_logger.warn("Failed to apply port forwarding rules for ip id=" + ipId);
        success = false;
      }
    }

    return success;
  }