@Override
 @ActionEvent(
     eventType = EventTypes.EVENT_FIREWALL_OPEN,
     eventDescription = "creating firewall rule",
     create = true)
 public boolean addSystemFirewallRules(IPAddressVO ip, Account acct) {
   List<FirewallRuleVO> systemRules = _firewallDao.listSystemRules();
   for (FirewallRuleVO rule : systemRules) {
     try {
       if (rule.getSourceCidrList() == null
           && (rule.getPurpose() == Purpose.Firewall || rule.getPurpose() == Purpose.NetworkACL)) {
         _firewallDao.loadSourceCidrs(rule);
       }
       createFirewallRule(
           ip.getId(),
           acct,
           rule.getXid(),
           rule.getSourcePortStart(),
           rule.getSourcePortEnd(),
           rule.getProtocol(),
           rule.getSourceCidrList(),
           rule.getIcmpCode(),
           rule.getIcmpType(),
           rule.getRelated(),
           FirewallRuleType.System,
           rule.getNetworkId(),
           rule.getTrafficType());
     } catch (Exception e) {
       s_logger.debug("Failed to add system wide firewall rule, due to:" + e.toString());
     }
   }
   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
 public boolean applyEgressFirewallRules(FirewallRule rule, Account caller)
     throws ResourceUnavailableException {
   List<FirewallRuleVO> rules =
       _firewallDao.listByNetworkPurposeTrafficType(
           rule.getNetworkId(), Purpose.Firewall, FirewallRule.TrafficType.Egress);
   applyDefaultEgressFirewallRule(rule.getNetworkId(), true);
   return applyFirewallRules(rules, false, caller);
 }
  protected boolean revokeFirewallRule(long ruleId, boolean apply, Account caller, long userId) {

    FirewallRuleVO rule = _firewallDao.findById(ruleId);
    if (rule == null || rule.getPurpose() != Purpose.Firewall) {
      throw new InvalidParameterValueException(
          "Unable to find " + ruleId + " having purpose " + Purpose.Firewall);
    }

    if (rule.getType() == FirewallRuleType.System
        && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
      throw new InvalidParameterValueException(
          "Only root admin can delete the system wide firewall rule");
    }

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

    revokeRule(rule, caller, userId, false);

    boolean success = false;
    Long networkId = rule.getNetworkId();

    if (apply) {
      // ingress firewall rule
      if (rule.getSourceIpAddressId() != null) {
        // feteches ingress firewall, ingress firewall rules associated with the ip
        List<FirewallRuleVO> rules =
            _firewallDao.listByIpAndPurpose(rule.getSourceIpAddressId(), Purpose.Firewall);
        return applyFirewallRules(rules, false, caller);
        // egress firewall rule
      } else if (networkId != null) {
        List<FirewallRuleVO> rules =
            _firewallDao.listByNetworkPurposeTrafficType(
                rule.getNetworkId(), Purpose.Firewall, FirewallRule.TrafficType.Egress);
        return applyFirewallRules(rules, false, caller);
      }
    } else {
      success = true;
    }

    return success;
  }
  @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;
  }
 @Override
 public boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId) {
   List<FirewallRuleVO> rules =
       _firewallDao.listByIpAndPurposeAndNotRevoked(ipId, Purpose.LoadBalancing);
   if (rules != null) s_logger.debug("Found " + rules.size() + " lb rules to cleanup");
   for (FirewallRule rule : rules) {
     boolean result = deleteLoadBalancerRule(rule.getId(), true, caller, callerUserId);
     if (result == false) {
       s_logger.warn("Unable to remove load balancer rule " + rule.getId());
       return false;
     }
   }
   return true;
 }
  @Override
  public boolean revokeRelatedFirewallRule(long ruleId, boolean apply) {
    FirewallRule fwRule = _firewallDao.findByRelatedId(ruleId);

    if (fwRule == null) {
      s_logger.trace(
          "No related firewall rule exists for rule id=" + ruleId + " so returning true here");
      return true;
    }

    s_logger.debug(
        "Revoking Firewall rule id="
            + fwRule.getId()
            + " as a part of rule delete id="
            + ruleId
            + " with apply="
            + apply);
    return revokeFirewallRule(fwRule.getId(), apply);
  }
  @Override
  @ActionEvent(
      eventType = EventTypes.EVENT_FIREWALL_OPEN,
      eventDescription = "creating firewall rule",
      create = true)
  public FirewallRule createRuleForAllCidrs(
      long ipAddrId,
      Account caller,
      Integer startPort,
      Integer endPort,
      String protocol,
      Integer icmpCode,
      Integer icmpType,
      Long relatedRuleId,
      long networkId)
      throws NetworkRuleConflictException {

    // If firwallRule for this port range already exists, return it
    List<FirewallRuleVO> rules =
        _firewallDao.listByIpPurposeAndProtocolAndNotRevoked(
            ipAddrId, startPort, endPort, protocol, Purpose.Firewall);
    if (!rules.isEmpty()) {
      return rules.get(0);
    }

    List<String> oneCidr = new ArrayList<String>();
    oneCidr.add(NetUtils.ALL_CIDRS);
    return createFirewallRule(
        ipAddrId,
        caller,
        null,
        startPort,
        endPort,
        protocol,
        oneCidr,
        icmpCode,
        icmpType,
        relatedRuleId,
        FirewallRule.FirewallRuleType.User,
        networkId,
        FirewallRule.TrafficType.Ingress);
  }
  @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
  @DB
  @ActionEvent(
      eventType = EventTypes.EVENT_REMOTE_ACCESS_VPN_DESTROY,
      eventDescription = "removing remote access vpn",
      async = true)
  public boolean destroyRemoteAccessVpnForIp(long ipId, Account caller, final boolean forceCleanup)
      throws ResourceUnavailableException {
    final RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findByPublicIpAddress(ipId);
    if (vpn == null) {
      s_logger.debug("there are no Remote access vpns for public ip address id=" + ipId);
      return true;
    }

    _accountMgr.checkAccess(caller, AccessType.OperateEntry, true, vpn);

    RemoteAccessVpn.State prevState = vpn.getState();
    vpn.setState(RemoteAccessVpn.State.Removed);
    _remoteAccessVpnDao.update(vpn.getId(), vpn);

    boolean success = false;
    try {
      for (RemoteAccessVPNServiceProvider element : _vpnServiceProviders) {
        if (element.stopVpn(vpn)) {
          success = true;
          break;
        }
      }
    } catch (ResourceUnavailableException ex) {
      vpn.setState(prevState);
      _remoteAccessVpnDao.update(vpn.getId(), vpn);
      s_logger.debug(
          "Failed to stop the vpn "
              + vpn.getId()
              + " , so reverted state to "
              + RemoteAccessVpn.State.Running);
      success = false;
    } finally {
      if (success || forceCleanup) {
        // Cleanup corresponding ports
        final List<? extends FirewallRule> vpnFwRules =
            _rulesDao.listByIpAndPurpose(ipId, Purpose.Vpn);

        boolean applyFirewall = false;
        final List<FirewallRuleVO> fwRules = new ArrayList<FirewallRuleVO>();
        // if related firewall rule is created for the first vpn port, it would be created for the 2
        // other ports as well, so need to cleanup the backend
        if (vpnFwRules.size() != 0
            && _rulesDao.findByRelatedId(vpnFwRules.get(0).getId()) != null) {
          applyFirewall = true;
        }

        if (applyFirewall) {
          Transaction.execute(
              new TransactionCallbackNoReturn() {
                @Override
                public void doInTransactionWithoutResult(TransactionStatus status) {
                  for (FirewallRule vpnFwRule : vpnFwRules) {
                    // don't apply on the backend yet; send all 3 rules in a banch
                    _firewallMgr.revokeRelatedFirewallRule(vpnFwRule.getId(), false);
                    fwRules.add(_rulesDao.findByRelatedId(vpnFwRule.getId()));
                  }

                  s_logger.debug(
                      "Marked "
                          + fwRules.size()
                          + " firewall rules as Revoked as a part of disable remote access vpn");
                }
              });

          // now apply vpn rules on the backend
          s_logger.debug(
              "Reapplying firewall rules for ip id="
                  + ipId
                  + " as a part of disable remote access vpn");
          success = _firewallMgr.applyIngressFirewallRules(ipId, caller);
        }

        if (success || forceCleanup) {
          try {
            Transaction.execute(
                new TransactionCallbackNoReturn() {
                  @Override
                  public void doInTransactionWithoutResult(TransactionStatus status) {
                    _remoteAccessVpnDao.remove(vpn.getId());
                    // Stop billing of VPN users when VPN is removed. VPN_User_ADD events will be
                    // generated when VPN is created again
                    List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId());
                    for (VpnUserVO user : vpnUsers) {
                      // VPN_USER_REMOVE event is already generated for users in Revoke state
                      if (user.getState() != VpnUser.State.Revoke) {
                        UsageEventUtils.publishUsageEvent(
                            EventTypes.EVENT_VPN_USER_REMOVE,
                            user.getAccountId(),
                            0,
                            user.getId(),
                            user.getUsername(),
                            user.getClass().getName(),
                            user.getUuid());
                      }
                    }
                    if (vpnFwRules != null) {
                      for (FirewallRule vpnFwRule : vpnFwRules) {
                        _rulesDao.remove(vpnFwRule.getId());
                        s_logger.debug(
                            "Successfully removed firewall rule with ip id="
                                + vpnFwRule.getSourceIpAddressId()
                                + " and port "
                                + vpnFwRule.getSourcePortStart().intValue()
                                + " as a part of vpn cleanup");
                      }
                    }
                  }
                });
          } catch (Exception ex) {
            s_logger.warn("Unable to release the three vpn ports from the firewall rules", ex);
          }
        }
      }
    }
    return success;
  }
  @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();
      }
    }
  }
  @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
  @DB
  public void destroyRemoteAccessVpn(long ipId, Account caller)
      throws ResourceUnavailableException {
    RemoteAccessVpnVO vpn = _remoteAccessVpnDao.findById(ipId);
    if (vpn == null) {
      s_logger.debug("vpn id=" + ipId + " does not exists ");
      return;
    }

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

    Network network = _networkMgr.getNetwork(vpn.getNetworkId());

    vpn.setState(RemoteAccessVpn.State.Removed);
    _remoteAccessVpnDao.update(vpn.getServerAddressId(), vpn);

    boolean success = false;
    try {
      for (RemoteAccessVPNServiceProvider element : _vpnServiceProviders) {
        if (element.stopVpn(network, vpn)) {
          success = true;
          break;
        }
      }
    } finally {
      if (success) {
        // Cleanup corresponding ports
        List<? extends FirewallRule> vpnFwRules = _rulesDao.listByIpAndPurpose(ipId, Purpose.Vpn);
        Transaction txn = Transaction.currentTxn();

        boolean applyFirewall = false;
        List<FirewallRuleVO> fwRules = new ArrayList<FirewallRuleVO>();
        // if related firewall rule is created for the first vpn port, it would be created for the 2
        // other ports as well, so need to cleanup the backend
        if (_rulesDao.findByRelatedId(vpnFwRules.get(0).getId()) != null) {
          applyFirewall = true;
        }

        if (applyFirewall) {
          txn.start();

          for (FirewallRule vpnFwRule : vpnFwRules) {
            // don't apply on the backend yet; send all 3 rules in a banch
            _firewallMgr.revokeRelatedFirewallRule(vpnFwRule.getId(), false);
            fwRules.add(_rulesDao.findByRelatedId(vpnFwRule.getId()));
          }

          s_logger.debug(
              "Marked "
                  + fwRules.size()
                  + " firewall rules as Revoked as a part of disable remote access vpn");

          txn.commit();

          // now apply vpn rules on the backend
          s_logger.debug(
              "Reapplying firewall rules for ip id="
                  + ipId
                  + " as a part of disable remote access vpn");
          success = _firewallMgr.applyIngressFirewallRules(ipId, caller);
        }

        if (success) {
          try {
            txn.start();
            _remoteAccessVpnDao.remove(ipId);
            // Stop billing of VPN users when VPN is removed. VPN_User_ADD events will be generated
            // when VPN is created again
            List<VpnUserVO> vpnUsers = _vpnUsersDao.listByAccount(vpn.getAccountId());
            for (VpnUserVO user : vpnUsers) {
              // VPN_USER_REMOVE event is already generated for users in Revoke state
              if (user.getState() != VpnUser.State.Revoke) {
                UsageEventUtils.publishUsageEvent(
                    EventTypes.EVENT_VPN_USER_REMOVE,
                    user.getAccountId(),
                    0,
                    user.getId(),
                    user.getUsername(),
                    user.getClass().getName(),
                    user.getUuid());
              }
            }
            if (vpnFwRules != null) {
              for (FirewallRule vpnFwRule : vpnFwRules) {
                _rulesDao.remove(vpnFwRule.getId());
                s_logger.debug(
                    "Successfully removed firewall rule with ip id="
                        + vpnFwRule.getSourceIpAddressId()
                        + " and port "
                        + vpnFwRule.getSourcePortStart()
                        + " as a part of vpn cleanup");
              }
            }
            txn.commit();
          } catch (Exception ex) {
            txn.rollback();
            s_logger.warn("Unable to release the three vpn ports from the firewall rules", ex);
          }
        }
      }
    }
  }
 @Override
 public FirewallRule getFirewallRule(long ruleId) {
   return _firewallDao.findById(ruleId);
 }
  @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;
  }
 @Override
 public boolean applyIngressFirewallRules(long ipId, Account caller)
     throws ResourceUnavailableException {
   List<FirewallRuleVO> rules = _firewallDao.listByIpAndPurpose(ipId, Purpose.Firewall);
   return applyFirewallRules(rules, false, caller);
 }
  @Override
  public void removeRule(FirewallRule rule) {

    // remove the rule
    _firewallDao.remove(rule.getId());
  }
  @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
  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");
    }
  }
  @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());
  }