@Override
  @ActionEvent(
      eventType = EventTypes.EVENT_FIREWALL_OPEN,
      eventDescription = "creating firewall rule",
      create = true)
  public FirewallRule createIngressFirewallRule(FirewallRule rule)
      throws NetworkRuleConflictException {
    Account caller = CallContext.current().getCallingAccount();
    Long sourceIpAddressId = rule.getSourceIpAddressId();

    return createFirewallRule(
        sourceIpAddressId,
        caller,
        rule.getXid(),
        rule.getSourcePortStart(),
        rule.getSourcePortEnd(),
        rule.getProtocol(),
        rule.getSourceCidrList(),
        rule.getIcmpCode(),
        rule.getIcmpType(),
        null,
        rule.getType(),
        rule.getNetworkId(),
        rule.getTrafficType());
  }
  @Override
  @ActionEvent(
      eventType = EventTypes.EVENT_FIREWALL_OPEN,
      eventDescription = "creating firewall rule",
      create = true)
  public FirewallRule createEgressFirewallRule(FirewallRule rule)
      throws NetworkRuleConflictException {
    Account caller = CallContext.current().getCallingAccount();

    Network network = _networkDao.findById(rule.getNetworkId());
    if (network.getGuestType() == Network.GuestType.Shared) {
      throw new InvalidParameterValueException(
          "Egress firewall rules are not supported for " + network.getGuestType() + "  networks");
    }

    return createFirewallRule(
        null,
        caller,
        rule.getXid(),
        rule.getSourcePortStart(),
        rule.getSourcePortEnd(),
        rule.getProtocol(),
        rule.getSourceCidrList(),
        rule.getIcmpCode(),
        rule.getIcmpType(),
        null,
        rule.getType(),
        rule.getNetworkId(),
        rule.getTrafficType());
  }
  @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");
    }
  }