@DB
  protected boolean applyLoadBalancerRules(List<LoadBalancerVO> lbs)
      throws ResourceUnavailableException {
    Transaction txn = Transaction.currentTxn();
    List<LoadBalancingRule> rules = new ArrayList<LoadBalancingRule>();
    for (LoadBalancerVO lb : lbs) {
      List<LbDestination> dstList = getExistingDestinations(lb.getId());

      LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList);
      rules.add(loadBalancing);
    }

    if (!_networkMgr.applyRules(rules, false)) {
      s_logger.debug("LB rules are not completely applied");
      return false;
    }

    for (LoadBalancerVO lb : lbs) {
      txn.start();
      if (lb.getState() == FirewallRule.State.Revoke) {
        _lbDao.remove(lb.getId());
        s_logger.warn("LB " + lb.getId() + " is successfully removed");
      } else if (lb.getState() == FirewallRule.State.Add) {
        lb.setState(FirewallRule.State.Active);
        s_logger.warn("LB rule " + lb.getId() + " state is set to Active");
        _lbDao.persist(lb);
      }

      // remove LB-Vm mappings that were state to revoke
      List<LoadBalancerVMMapVO> lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lb.getId(), true);
      List<Long> instanceIds = new ArrayList<Long>();

      for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) {
        instanceIds.add(lbVmMap.getInstanceId());
      }

      if (!instanceIds.isEmpty()) {
        _lb2VmMapDao.remove(lb.getId(), instanceIds, null);
        s_logger.debug(
            "Load balancer rule id " + lb.getId() + " is removed for vms " + instanceIds);
      }

      if (_lb2VmMapDao.listByLoadBalancerId(lb.getId()).isEmpty()) {
        lb.setState(FirewallRule.State.Add);
        _lbDao.persist(lb);
        s_logger.debug(
            "LB rule "
                + lb.getId()
                + " state is set to Add as there are no more active LB-VM mappings");
      }

      txn.commit();
    }
    return true;
  }
  @Override
  @ActionEvent(
      eventType = EventTypes.EVENT_LOAD_BALANCER_UPDATE,
      eventDescription = "updating load balancer",
      async = true)
  public LoadBalancer updateLoadBalancerRule(UpdateLoadBalancerRuleCmd cmd) {
    Long lbRuleId = cmd.getId();
    String name = cmd.getLoadBalancerName();
    String description = cmd.getDescription();
    String algorithm = cmd.getAlgorithm();
    LoadBalancerVO lb = _lbDao.findById(lbRuleId);

    if (name != null) {
      lb.setName(name);
    }

    if (description != null) {
      lb.setDescription(description);
    }

    if (algorithm != null) {
      lb.setAlgorithm(algorithm);
    }

    _lbDao.update(lbRuleId, lb);

    // If algorithm is changed, have to reapply the lb config
    if (algorithm != null) {
      try {
        lb.setState(FirewallRule.State.Add);
        _lbDao.persist(lb);
        applyLoadBalancerConfig(lbRuleId);
      } catch (ResourceUnavailableException e) {
        s_logger.warn(
            "Unable to apply the load balancer config because resource is unavaliable.", e);
      }
    }

    return lb;
  }
  private boolean removeFromLoadBalancerInternal(long loadBalancerId, List<Long> instanceIds) {
    UserContext caller = UserContext.current();

    LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(loadBalancerId));
    if (loadBalancer == null) {
      throw new InvalidParameterException("Invalid load balancer value: " + loadBalancerId);
    }

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

    try {
      loadBalancer.setState(FirewallRule.State.Add);
      _lbDao.persist(loadBalancer);

      for (long instanceId : instanceIds) {
        LoadBalancerVMMapVO map =
            _lb2VmMapDao.findByLoadBalancerIdAndVmId(loadBalancerId, instanceId);
        map.setRevoke(true);
        _lb2VmMapDao.persist(map);
        s_logger.debug(
            "Set load balancer rule for revoke: rule id "
                + loadBalancerId
                + ", vmId "
                + instanceId);
      }

      if (!applyLoadBalancerConfig(loadBalancerId)) {
        s_logger.warn(
            "Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds);
        throw new CloudRuntimeException(
            "Failed to remove load balancer rule id " + loadBalancerId + " for vms " + instanceIds);
      }

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

    return true;
  }
  @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
  @ActionEvent(
      eventType = EventTypes.EVENT_ASSIGN_TO_LOAD_BALANCER_RULE,
      eventDescription = "assigning to load balancer",
      async = true)
  public boolean assignToLoadBalancer(long loadBalancerId, List<Long> instanceIds) {
    UserContext ctx = UserContext.current();
    Account caller = ctx.getCaller();

    LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
    if (loadBalancer == null) {
      throw new InvalidParameterValueException(
          "Failed to assign to load balancer "
              + loadBalancerId
              + ", the load balancer was not found.");
    }

    List<LoadBalancerVMMapVO> mappedInstances =
        _lb2VmMapDao.listByLoadBalancerId(loadBalancerId, false);
    Set<Long> mappedInstanceIds = new HashSet<Long>();
    for (LoadBalancerVMMapVO mappedInstance : mappedInstances) {
      mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId()));
    }

    List<UserVm> vmsToAdd = new ArrayList<UserVm>();

    for (Long instanceId : instanceIds) {
      if (mappedInstanceIds.contains(instanceId)) {
        throw new InvalidParameterValueException(
            "VM " + instanceId + " is already mapped to load balancer.");
      }

      UserVm vm = _vmDao.findById(instanceId);
      if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) {
        throw new InvalidParameterValueException("Invalid instance id: " + instanceId);
      }

      _rulesMgr.checkRuleAndUserVm(loadBalancer, vm, caller);

      if (vm.getAccountId() != loadBalancer.getAccountId()) {
        throw new PermissionDeniedException(
            "Cannot add virtual machines that do not belong to the same owner.");
      }

      // Let's check to make sure the vm has a nic in the same network as the load balancing rule.
      List<? extends Nic> nics = _networkMgr.getNics(vm.getId());
      Nic nicInSameNetwork = null;
      for (Nic nic : nics) {
        if (nic.getNetworkId() == loadBalancer.getNetworkId()) {
          nicInSameNetwork = nic;
          break;
        }
      }

      if (nicInSameNetwork == null) {
        throw new InvalidParameterValueException(
            "VM " + instanceId + " cannot be added because it doesn't belong in the same network.");
      }

      if (s_logger.isDebugEnabled()) {
        s_logger.debug("Adding " + vm + " to the load balancer pool");
      }
      vmsToAdd.add(vm);
    }

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

    for (UserVm vm : vmsToAdd) {
      LoadBalancerVMMapVO map = new LoadBalancerVMMapVO(loadBalancer.getId(), vm.getId(), false);
      map = _lb2VmMapDao.persist(map);
    }
    txn.commit();

    try {
      loadBalancer.setState(FirewallRule.State.Add);
      _lbDao.persist(loadBalancer);
      applyLoadBalancerConfig(loadBalancerId);
    } catch (ResourceUnavailableException e) {
      s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e);
      return false;
    }

    return true;
  }