content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  package com.cloud.network.lb;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  import java.util.Set;
  
  import javax.inject.Inject;
  
  import com.cloud.offerings.NetworkOfferingServiceMapVO;
  import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
  import org.apache.cloudstack.acl.SecurityChecker;
  import org.apache.cloudstack.api.ApiConstants;
  import org.apache.cloudstack.api.ApiErrorCode;
  import org.apache.cloudstack.api.ServerApiException;
  import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBHealthCheckPolicyCmd;
  import org.apache.cloudstack.api.command.user.loadbalancer.CreateLBStickinessPolicyCmd;
  import org.apache.cloudstack.api.command.user.loadbalancer.ListLBHealthCheckPoliciesCmd;
  import org.apache.cloudstack.api.command.user.loadbalancer.ListLBStickinessPoliciesCmd;
  import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRuleInstancesCmd;
  import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRulesCmd;
  import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerRuleCmd;
  import org.apache.cloudstack.api.response.ServiceResponse;
  import org.apache.cloudstack.config.ApiServiceConfiguration;
  import org.apache.cloudstack.context.CallContext;
  import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
  import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
  import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO;
  import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao;
  import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.lang3.StringUtils;
  
  import com.cloud.agent.api.to.LoadBalancerTO;
  import com.cloud.configuration.ConfigurationManager;
  import com.cloud.dc.DataCenter;
  import com.cloud.dc.DataCenter.NetworkType;
  import com.cloud.dc.dao.DataCenterDao;
  import com.cloud.dc.dao.VlanDao;
  import com.cloud.domain.dao.DomainDao;
  import com.cloud.event.ActionEvent;
  import com.cloud.event.EventTypes;
  import com.cloud.event.UsageEventUtils;
  import com.cloud.event.dao.EventDao;
  import com.cloud.event.dao.UsageEventDao;
  import com.cloud.exception.InsufficientAddressCapacityException;
  import com.cloud.exception.InvalidParameterValueException;
  import com.cloud.exception.NetworkRuleConflictException;
  import com.cloud.exception.PermissionDeniedException;
  import com.cloud.exception.ResourceUnavailableException;
  import com.cloud.network.ExternalDeviceUsageManager;
  import com.cloud.network.IpAddress;
  import com.cloud.network.IpAddressManager;
  import com.cloud.network.LBHealthCheckPolicyVO;
  import com.cloud.network.Network;
  import com.cloud.network.Network.Capability;
  import com.cloud.network.Network.Provider;
  import com.cloud.network.Network.Service;
  import com.cloud.network.NetworkModel;
  import com.cloud.network.addr.PublicIp;
  import com.cloud.network.as.AutoScaleManager;
  import com.cloud.network.as.AutoScalePolicy;
  import com.cloud.network.as.AutoScalePolicyConditionMapVO;
  import com.cloud.network.as.AutoScaleVmGroup;
  import com.cloud.network.as.AutoScaleVmGroupPolicyMapVO;
  import com.cloud.network.as.AutoScaleVmGroupVO;
  import com.cloud.network.as.AutoScaleVmProfile;
  import com.cloud.network.as.Condition;
  import com.cloud.network.as.Counter;
  import com.cloud.network.as.dao.AutoScalePolicyConditionMapDao;
  import com.cloud.network.as.dao.AutoScalePolicyDao;
  import com.cloud.network.as.dao.AutoScaleVmGroupDao;
  import com.cloud.network.as.dao.AutoScaleVmGroupPolicyMapDao;
  import com.cloud.network.as.dao.AutoScaleVmGroupVmMapDao;
  import com.cloud.network.as.dao.AutoScaleVmProfileDao;
  import com.cloud.network.as.dao.ConditionDao;
  import com.cloud.network.as.dao.CounterDao;
  import com.cloud.network.dao.FirewallRulesCidrsDao;
  import com.cloud.network.dao.FirewallRulesDao;
  import com.cloud.network.dao.IPAddressDao;
  import com.cloud.network.dao.IPAddressVO;
  import com.cloud.network.dao.LBHealthCheckPolicyDao;
  import com.cloud.network.dao.LBStickinessPolicyDao;
  import com.cloud.network.dao.LBStickinessPolicyVO;
  import com.cloud.network.dao.LoadBalancerCertMapDao;
  import com.cloud.network.dao.LoadBalancerCertMapVO;
  import com.cloud.network.dao.LoadBalancerDao;
  import com.cloud.network.dao.LoadBalancerVMMapDao;
  import com.cloud.network.dao.LoadBalancerVMMapVO;
  import com.cloud.network.dao.LoadBalancerVO;
  import com.cloud.network.dao.NetworkDao;
  import com.cloud.network.dao.NetworkServiceMapDao;
  import com.cloud.network.dao.NetworkVO;
  import com.cloud.network.dao.SslCertVO;
  import com.cloud.network.element.LoadBalancingServiceProvider;
  import com.cloud.network.lb.LoadBalancingRule.LbAutoScalePolicy;
  import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmGroup;
  import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmProfile;
  import com.cloud.network.lb.LoadBalancingRule.LbCondition;
  import com.cloud.network.lb.LoadBalancingRule.LbDestination;
  import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy;
  import com.cloud.network.lb.LoadBalancingRule.LbSslCert;
  import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy;
  import com.cloud.network.rules.FirewallManager;
  import com.cloud.network.rules.FirewallRule;
  import com.cloud.network.rules.FirewallRule.FirewallRuleType;
  import com.cloud.network.rules.FirewallRule.Purpose;
  import com.cloud.network.rules.FirewallRuleVO;
  import com.cloud.network.rules.HealthCheckPolicy;
  import com.cloud.network.rules.LbStickinessMethod;
  import com.cloud.network.rules.LbStickinessMethod.LbStickinessMethodParam;
  import com.cloud.network.rules.LoadBalancer;
  import com.cloud.network.rules.LoadBalancerContainer.Scheme;
  import com.cloud.network.rules.RulesManager;
  import com.cloud.network.rules.StickinessPolicy;
  import com.cloud.network.vpc.VpcManager;
  import com.cloud.offering.NetworkOffering;
  import com.cloud.projects.Project.ListProjectResourcesCriteria;
  import com.cloud.server.ResourceTag.ResourceObjectType;
  import com.cloud.service.dao.ServiceOfferingDao;
  import com.cloud.storage.dao.VMTemplateDao;
  import com.cloud.tags.ResourceTagVO;
  import com.cloud.tags.dao.ResourceTagDao;
  import com.cloud.user.Account;
  import com.cloud.user.AccountManager;
  import com.cloud.user.DomainService;
  import com.cloud.user.User;
  import com.cloud.user.dao.AccountDao;
  import com.cloud.user.dao.UserDao;
  import com.cloud.uservm.UserVm;
  import com.cloud.utils.Pair;
  import com.cloud.utils.Ternary;
  import com.cloud.utils.component.ManagerBase;
  import com.cloud.utils.db.DB;
  import com.cloud.utils.db.EntityManager;
  import com.cloud.utils.db.Filter;
  import com.cloud.utils.db.JoinBuilder;
  import com.cloud.utils.db.SearchBuilder;
  import com.cloud.utils.db.SearchCriteria;
  import com.cloud.utils.db.Transaction;
  import com.cloud.utils.db.TransactionCallback;
  import com.cloud.utils.db.TransactionCallbackNoReturn;
  import com.cloud.utils.db.TransactionCallbackWithException;
  import com.cloud.utils.db.TransactionStatus;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.utils.net.Ip;
  import com.cloud.utils.net.NetUtils;
  import com.cloud.vm.Nic;
  import com.cloud.vm.UserVmVO;
  import com.cloud.vm.VirtualMachine.State;
  import com.cloud.vm.dao.NicDao;
  import com.cloud.vm.dao.NicSecondaryIpDao;
  import com.cloud.vm.dao.UserVmDao;
  import com.google.gson.Gson;
  import com.google.gson.reflect.TypeToken;
  
  public class LoadBalancingRulesManagerImpl<Type> extends ManagerBase implements LoadBalancingRulesManager, LoadBalancingRulesService {
  
      @Inject
      NetworkOrchestrationService _networkMgr;
      @Inject
      NetworkModel _networkModel;
      @Inject
      RulesManager _rulesMgr;
      @Inject
      AccountManager _accountMgr;
      @Inject
      IPAddressDao _ipAddressDao;
      @Inject
      LoadBalancerDao _lbDao;
      @Inject
      VlanDao _vlanDao;
      @Inject
      EventDao _eventDao;
      @Inject
      LoadBalancerVMMapDao _lb2VmMapDao;
      @Inject
      LBStickinessPolicyDao _lb2stickinesspoliciesDao;
      @Inject
      LBHealthCheckPolicyDao _lb2healthcheckDao;
      @Inject
      UserVmDao _vmDao;
      @Inject
      AccountDao _accountDao;
      @Inject
      DomainDao _domainDao;
      @Inject
      NicDao _nicDao;
      @Inject
      UsageEventDao _usageEventDao;
      @Inject
      FirewallRulesCidrsDao _firewallCidrsDao;
      @Inject
      FirewallManager _firewallMgr;
      @Inject
      NetworkDao _networkDao;
      @Inject
      NetworkOfferingServiceMapDao _networkOfferingServiceDao;
      @Inject
      FirewallRulesDao _firewallDao;
      @Inject
      DomainService _domainMgr;
      @Inject
      ConfigurationManager _configMgr;
  
      @Inject
      ExternalDeviceUsageManager _externalDeviceUsageMgr;
      @Inject
      NetworkServiceMapDao _ntwkSrvcDao;
      @Inject
      ResourceTagDao _resourceTagDao;
      @Inject
      VpcManager _vpcMgr;
      @Inject
      VMTemplateDao _templateDao;
      @Inject
      ServiceOfferingDao _offeringsDao;
      @Inject
      CounterDao _counterDao;
      @Inject
      ConditionDao _conditionDao;
      @Inject
      AutoScaleVmProfileDao _autoScaleVmProfileDao;
      @Inject
      AutoScalePolicyDao _autoScalePolicyDao;
      @Inject
      AutoScalePolicyConditionMapDao _autoScalePolicyConditionMapDao;
      @Inject
      AutoScaleVmGroupDao _autoScaleVmGroupDao;
      @Inject
      AutoScaleVmGroupPolicyMapDao _autoScaleVmGroupPolicyMapDao;
      @Inject
      AutoScaleVmGroupVmMapDao autoScaleVmGroupVmMapDao;
      @Inject
      AutoScaleManager autoScaleManager;
      @Inject
      ConfigurationDao _configDao;
      @Inject
      DataCenterDao _dcDao = null;
      @Inject
      UserDao _userDao;
      List<LoadBalancingServiceProvider> _lbProviders;
      @Inject
      ApplicationLoadBalancerRuleDao _appLbRuleDao;
      @Inject
      IpAddressManager _ipAddrMgr;
      @Inject
      EntityManager _entityMgr;
      @Inject
      LoadBalancerCertMapDao _lbCertMapDao;
  
      @Inject
      NicSecondaryIpDao _nicSecondaryIpDao;
  
      private static final int DNS_PORT = 53;
      // Will return a string. For LB Stickiness this will be a json, for
      // autoscale this will be "," separated values
      @Override
      public String getLBCapability(long networkid, String capabilityName) {
          Map<Service, Map<Capability, String>> serviceCapabilitiesMap = _networkModel.getNetworkCapabilities(networkid);
          if (serviceCapabilitiesMap != null) {
              for (Service service : serviceCapabilitiesMap.keySet()) {
                  ServiceResponse serviceResponse = new ServiceResponse();
                  serviceResponse.setName(service.getName());
                  if ("Lb".equalsIgnoreCase(service.getName())) {
                      Map<Capability, String> serviceCapabilities = serviceCapabilitiesMap.get(service);
                      if (serviceCapabilities != null) {
                          for (Capability capability : serviceCapabilities.keySet()) {
                              if (capabilityName.equals(capability.getName())) {
                                  return serviceCapabilities.get(capability);
                              }
                          }
                      }
                  }
              }
          }
          return null;
      }
  
      private LbAutoScaleVmGroup getLbAutoScaleVmGroup(AutoScaleVmGroupVO vmGroup, AutoScaleVmGroup.State currentState, LoadBalancerVO lb) {
          long lbNetworkId = lb.getNetworkId();
          String lbName = lb.getName();
          List<AutoScaleVmGroupPolicyMapVO> vmGroupPolicyMapList = _autoScaleVmGroupPolicyMapDao.listByVmGroupId(vmGroup.getId());
          List<LbAutoScalePolicy> autoScalePolicies = new ArrayList<LbAutoScalePolicy>();
          for (AutoScaleVmGroupPolicyMapVO vmGroupPolicyMap : vmGroupPolicyMapList) {
              AutoScalePolicy autoScalePolicy = _autoScalePolicyDao.findById(vmGroupPolicyMap.getPolicyId());
              List<AutoScalePolicyConditionMapVO> autoScalePolicyConditionMapList = _autoScalePolicyConditionMapDao.listByAll(autoScalePolicy.getId(), null);
              List<LbCondition> lbConditions = new ArrayList<LbCondition>();
              for (AutoScalePolicyConditionMapVO autoScalePolicyConditionMap : autoScalePolicyConditionMapList) {
                  Condition condition = _conditionDao.findById(autoScalePolicyConditionMap.getConditionId());
                  Counter counter = _counterDao.findById(condition.getCounterId());
                  lbConditions.add(new LbCondition(counter, condition));
              }
              autoScalePolicies.add(new LbAutoScalePolicy(autoScalePolicy, lbConditions));
          }
          AutoScaleVmProfile autoScaleVmProfile = _autoScaleVmProfileDao.findById(vmGroup.getProfileId());
          String zoneId = _dcDao.findById(autoScaleVmProfile.getZoneId()).getUuid();
          String domainId = _domainDao.findById(autoScaleVmProfile.getDomainId()).getUuid();
          String serviceOfferingId = _offeringsDao.findById(autoScaleVmProfile.getServiceOfferingId()).getUuid();
          String templateId = _templateDao.findById(autoScaleVmProfile.getTemplateId()).getUuid();
          String vmName = "AutoScale-LB-" + lbName;
          String lbNetworkUuid = null;
  
          DataCenter zone = _entityMgr.findById(DataCenter.class, vmGroup.getZoneId());
          if (zone == null) {
              // This should never happen, but still a cautious check
              logger.warn("Unable to find zone while packaging AutoScale Vm Group, zoneid: " + vmGroup.getZoneId());
              throw new InvalidParameterValueException("Unable to find zone");
          } else {
              if (zone.getNetworkType() == NetworkType.Advanced) {
                  NetworkVO lbNetwork = _networkDao.findById(lbNetworkId);
                  lbNetworkUuid = lbNetwork.getUuid();
              }
          }
  
          String apiKey = null;
          String secretKey = null;
          String csUrl = ApiServiceConfiguration.ApiServletPath.value();
          Network.Provider provider = getLoadBalancerServiceProvider(lb);
          if (Network.Provider.Netscaler.equals(provider)) {
              Long autoscaleUserId = autoScaleVmProfile.getAutoScaleUserId();
              if (autoscaleUserId == null) {
                  throw new InvalidParameterValueException("autoscaleUserId is required but not specified");
              }
              User user = _userDao.findById(autoscaleUserId);
              if (user == null) {
                  throw new InvalidParameterValueException("Unable to find user by id " + autoscaleUserId);
              }
              apiKey = user.getApiKey();
              secretKey = user.getSecretKey();
              if (apiKey == null) {
                  throw new InvalidParameterValueException("apiKey for user: " + user.getUsername() + " is empty. Please generate it");
              }
  
              if (secretKey == null) {
                  throw new InvalidParameterValueException("secretKey for user: " + user.getUsername() + " is empty. Please generate it");
              }
  
              if (csUrl == null || csUrl.contains("localhost")) {
                  throw new InvalidParameterValueException(String.format("Global setting %s has to be set to the Management Server's API end point", ApiServiceConfiguration.ApiServletPath.key()));
              }
          }
  
          LbAutoScaleVmProfile lbAutoScaleVmProfile =
              new LbAutoScaleVmProfile(autoScaleVmProfile, apiKey, secretKey, csUrl, zoneId, domainId, serviceOfferingId, templateId, vmName, lbNetworkUuid);
          return new LbAutoScaleVmGroup(vmGroup, autoScalePolicies, lbAutoScaleVmProfile, currentState);
      }
  
      @Override
      public LoadBalancerTO.AutoScaleVmGroupTO toAutoScaleVmGroupTO(LbAutoScaleVmGroup lbAutoScaleVmGroup) {
          List<LbAutoScalePolicy> lbAutoScalePolicies = lbAutoScaleVmGroup.getPolicies();
          List<LoadBalancerTO.AutoScalePolicyTO> autoScalePolicyTOs = new ArrayList<>(lbAutoScalePolicies.size());
          for (LbAutoScalePolicy lbAutoScalePolicy : lbAutoScalePolicies) {
              List<LbCondition> lbConditions = lbAutoScalePolicy.getConditions();
              List<LoadBalancerTO.ConditionTO> conditionTOs = new ArrayList<>(lbConditions.size());
              for (LbCondition lbCondition : lbConditions) {
                  Counter counter = lbCondition.getCounter();
                  LoadBalancerTO.CounterTO counterTO = new LoadBalancerTO.CounterTO(counter.getId(), counter.getUuid(), counter.getName(), counter.getSource(), "" + counter.getValue(), counter.getProvider());
                  Condition condition = lbCondition.getCondition();
                  LoadBalancerTO.ConditionTO conditionTO = new LoadBalancerTO.ConditionTO(condition.getId(), condition.getUuid(), condition.getThreshold(), condition.getRelationalOperator(), counterTO);
                  conditionTOs.add(conditionTO);
              }
              AutoScalePolicy autoScalePolicy = lbAutoScalePolicy.getPolicy();
              autoScalePolicyTOs.add(new LoadBalancerTO.AutoScalePolicyTO(autoScalePolicy.getId(), autoScalePolicy.getUuid(), autoScalePolicy.getDuration(), autoScalePolicy.getQuietTime(),
                      autoScalePolicy.getLastQuietTime(), autoScalePolicy.getAction(), conditionTOs, lbAutoScalePolicy.isRevoked()));
          }
          LbAutoScaleVmProfile lbAutoScaleVmProfile = lbAutoScaleVmGroup.getProfile();
          AutoScaleVmProfile autoScaleVmProfile = lbAutoScaleVmProfile.getProfile();
  
          LoadBalancerTO.AutoScaleVmProfileTO autoScaleVmProfileTO =
                  new LoadBalancerTO.AutoScaleVmProfileTO(lbAutoScaleVmProfile.getZoneId(), lbAutoScaleVmProfile.getDomainId(), lbAutoScaleVmProfile.getCsUrl(),
                          lbAutoScaleVmProfile.getAutoScaleUserApiKey(), lbAutoScaleVmProfile.getAutoScaleUserSecretKey(), lbAutoScaleVmProfile.getServiceOfferingId(),
                          lbAutoScaleVmProfile.getTemplateId(), lbAutoScaleVmProfile.getVmName(), lbAutoScaleVmProfile.getNetworkId(), autoScaleVmProfile.getOtherDeployParams(),
                          autoScaleVmProfile.getCounterParams(), autoScaleVmProfile.getExpungeVmGracePeriod());
  
          AutoScaleVmGroup autoScaleVmGroup = lbAutoScaleVmGroup.getVmGroup();
          return
                  new LoadBalancerTO.AutoScaleVmGroupTO(autoScaleVmGroup.getId(), autoScaleVmGroup.getUuid(), autoScaleVmGroup.getMinMembers(), autoScaleVmGroup.getMaxMembers(), autoScaleVmGroup.getMemberPort(),
                          autoScaleVmGroup.getInterval(), autoScalePolicyTOs, autoScaleVmProfileTO, autoScaleVmGroup.getState(), lbAutoScaleVmGroup.getCurrentState(), autoScaleVmGroup.getLoadBalancerId());
      }
  
      @Override
      public LoadBalancerTO.AutoScaleVmGroupTO toAutoScaleVmGroupTO(AutoScaleVmGroupVO vmGroup) {
          final LoadBalancerVO loadBalancer = _lbDao.findById(vmGroup.getLoadBalancerId());
          if (loadBalancer == null) {
              throw new CloudRuntimeException("Unable to find load balancer with id: " + vmGroup.getLoadBalancerId());
          }
          LbAutoScaleVmGroup lbAutoScaleVmGroup = getLbAutoScaleVmGroup(vmGroup, vmGroup.getState(), loadBalancer);
          return toAutoScaleVmGroupTO(lbAutoScaleVmGroup);
      }
  
      @Override
      public Network.Provider getLoadBalancerServiceProvider(LoadBalancerVO loadBalancer) {
          Network network = _networkDao.findById(loadBalancer.getNetworkId());
          List<Network.Provider> providers = _networkMgr.getProvidersForServiceInNetwork(network, Network.Service.Lb);
          if (CollectionUtils.isEmpty(providers)) {
              throw new CloudRuntimeException(String.format("Unable to find LB provider for network: %s ", network));
          }
          return providers.get(0);
      }
  
      private boolean applyAutoScaleConfig(LoadBalancerVO lb, AutoScaleVmGroupVO vmGroup, AutoScaleVmGroup.State currentState) throws ResourceUnavailableException {
          LbAutoScaleVmGroup lbAutoScaleVmGroup = getLbAutoScaleVmGroup(vmGroup, currentState, lb);
          /*
           * Regular config like destinations need not be packed for applying
           * autoscale config as of today.
           */
          List<LbStickinessPolicy> policyList = getStickinessPolicies(lb.getId());
          Ip sourceIp = getSourceIp(lb);
          LoadBalancingRule rule = new LoadBalancingRule(lb, null, policyList, null, sourceIp, null, lb.getLbProtocol());
          rule.setAutoScaleVmGroup(lbAutoScaleVmGroup);
  
          if (!isRollBackAllowedForProvider(lb)) {
              // this is for Netscaler type of devices. if their is failure the db
              // entries will be rollbacked.
              return false;
          }
  
          List<LoadBalancingRule> rules = Arrays.asList(rule);
  
          if (!applyLbRules(new ArrayList<>(rules), false)) {
              logger.debug("LB rules' autoscale config are not completely applied");
              return false;
          }
  
          return true;
      }
  
      private Ip getSourceIp(LoadBalancer lb) {
          Ip sourceIp = null;
          if (lb.getScheme() == Scheme.Public) {
              sourceIp = _networkModel.getPublicIpAddress(lb.getSourceIpAddressId()).getAddress();
          } else if (lb.getScheme() == Scheme.Internal) {
              ApplicationLoadBalancerRuleVO appLbRule = _appLbRuleDao.findById(lb.getId());
              sourceIp = appLbRule.getSourceIp();
          }
          return sourceIp;
      }
  
      @Override
      @DB
      public boolean configureLbAutoScaleVmGroup(final long vmGroupid, AutoScaleVmGroup.State currentState) throws ResourceUnavailableException {
          final AutoScaleVmGroupVO vmGroup = _autoScaleVmGroupDao.findById(vmGroupid);
          boolean success = false;
  
          final LoadBalancerVO loadBalancer = _lbDao.findById(vmGroup.getLoadBalancerId());
  
          FirewallRule.State backupState = loadBalancer.getState();
  
          if (vmGroup.getState().equals(AutoScaleVmGroup.State.NEW)
                  || (loadBalancer.getState() == FirewallRule.State.Active && vmGroup.getState().equals(AutoScaleVmGroup.State.REVOKE))) {
              loadBalancer.setState(FirewallRule.State.Add);
              _lbDao.persist(loadBalancer);
          }
  
          try {
              success = applyAutoScaleConfig(loadBalancer, vmGroup, currentState);
          } catch (ResourceUnavailableException e) {
              logger.warn("Unable to configure AutoScaleVmGroup to the lb rule: {} because resource is unavailable:", loadBalancer, e);
              if (isRollBackAllowedForProvider(loadBalancer)) {
                  loadBalancer.setState(backupState);
                  _lbDao.persist(loadBalancer);
                  logger.debug("LB Rollback rule: {} lb state rollback while creating AutoscaleVmGroup", loadBalancer);
              }
              throw e;
          } finally {
              if (!success) {
                  logger.warn(String.format("Failed to configure LB Auto Scale Vm Group: %s", vmGroup));
              }
          }
  
          if (success) {
              if (vmGroup.getState().equals(AutoScaleVmGroup.State.NEW)) {
                  Transaction.execute(new TransactionCallbackNoReturn() {
                      @Override
                      public void doInTransactionWithoutResult(TransactionStatus status) {
                          loadBalancer.setState(FirewallRule.State.Active);
                          logger.debug("LB rule {} state is set to Active", loadBalancer);
                          _lbDao.persist(loadBalancer);
                          vmGroup.setState(AutoScaleVmGroup.State.ENABLED);
                          _autoScaleVmGroupDao.persist(vmGroup);
                          logger.debug("LB Auto Scale Vm Group: {} is set to Enabled state.", vmGroup);
                      }
                  });
              }
              logger.info("Successfully configured LB Autoscale Vm Group: {}", vmGroup);
          }
          return success;
      }
  
      private boolean validateHealthCheck(CreateLBHealthCheckPolicyCmd cmd) {
          LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId());
          String capability = getLBCapability(loadBalancer.getNetworkId(), Capability.HealthCheckPolicy.getName());
          if (capability != null) {
              return true;
          }
          return false;
      }
  
      private boolean genericValidator(CreateLBStickinessPolicyCmd cmd) throws InvalidParameterValueException {
          LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId());
          /* Validation : check for valid Method name and params */
          List<LbStickinessMethod> stickinessMethodList = getStickinessMethods(loadBalancer.getNetworkId());
          boolean methodMatch = false;
  
          if (stickinessMethodList == null) {
              throw new InvalidParameterValueException("Failed:  No Stickiness method available for LB rule:" + cmd.getLbRuleId());
          }
          for (LbStickinessMethod method : stickinessMethodList) {
              if (method.getMethodName().equalsIgnoreCase(cmd.getStickinessMethodName())) {
                  methodMatch = true;
                  Map apiParamList = cmd.getparamList();
                  List<LbStickinessMethodParam> methodParamList = method.getParamList();
                  Map<String, String> tempParamList = new HashMap<String, String>();
  
                  /*
                   * validation-1: check for any extra params that are not
                   * required by the policymethod(capability), FIXME: make the
                   * below loop simple without using raw data type
                   */
                  if (apiParamList != null) {
                      Collection userGroupCollection = apiParamList.values();
                      Iterator iter = userGroupCollection.iterator();
                      while (iter.hasNext()) {
                          HashMap<String, String> paramKVpair = (HashMap)iter.next();
                          String paramName = paramKVpair.get("name");
                          String paramValue = paramKVpair.get("value");
  
                          tempParamList.put(paramName, paramValue);
                          Boolean found = false;
                          for (LbStickinessMethodParam param : methodParamList) {
                              if (param.getParamName().equalsIgnoreCase(paramName)) {
                                  if ((param.getIsflag() == false) && (paramValue == null)) {
                                      throw new InvalidParameterValueException("Failed : Value expected for the Param :" + param.getParamName());
                                  }
                                  found = true;
                                  break;
                              }
                          }
                          if (!found) {
                              throw new InvalidParameterValueException("Failed : Stickiness policy does not support param name :" + paramName);
                          }
                      }
                  }
  
                  /* validation-2: check for mandatory params */
                  for (LbStickinessMethodParam param : methodParamList) {
                      if (param.getRequired()) {
                          if (tempParamList.get(param.getParamName()) == null) {
                              throw new InvalidParameterValueException("Failed : Missing Manadatory Param :" + param.getParamName());
                          }
                      }
                  }
                  /* Successfully completed the Validation */
                  break;
              }
          }
          if (methodMatch == false) {
              throw new InvalidParameterValueException("Failed to match Stickiness method name for LB rule:" + cmd.getLbRuleId());
          }
  
          /* Validation : check for the multiple policies to the rule id */
          List<LBStickinessPolicyVO> stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId(), false);
          if (stickinessPolicies.size() > 1) {
              throw new InvalidParameterValueException("Failed to create Stickiness policy: Already two policies attached " + cmd.getLbRuleId());
          }
          return true;
      }
  
      @SuppressWarnings("rawtypes")
      @Override
      @DB
      @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_CREATE, eventDescription = "create lb stickinesspolicy to load balancer", create = true)
      public StickinessPolicy createLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd) throws NetworkRuleConflictException {
          CallContext caller = CallContext.current();
  
          /* Validation : check corresponding load balancer rule exist */
          LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId());
          if (loadBalancer == null) {
              throw new InvalidParameterValueException("Failed: LB rule id: " + cmd.getLbRuleId() + " not present ");
          }
  
          _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer);
          if (loadBalancer.getState() == FirewallRule.State.Revoke) {
              throw new InvalidParameterValueException(String.format("Failed: LB rule: %s is in deleting state: ", loadBalancer));
          }
  
          /* Generic validations */
          if (!genericValidator(cmd)) {
              throw new InvalidParameterValueException(String.format("Failed to create Stickiness policy: Validation Failed %s", loadBalancer));
          }
  
          /*
           * Specific validations using network element validator for specific
           * validations
           */
          LBStickinessPolicyVO lbpolicy =
              new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription());
          List<LbStickinessPolicy> policyList = new ArrayList<LbStickinessPolicy>();
          policyList.add(new LbStickinessPolicy(cmd.getStickinessMethodName(), lbpolicy.getParams()));
          Ip sourceIp = getSourceIp(loadBalancer);
          LoadBalancingRule lbRule =
              new LoadBalancingRule(loadBalancer, getExistingDestinations(lbpolicy.getId()), policyList, null, sourceIp, null, loadBalancer.getLbProtocol());
          if (!validateLbRule(lbRule)) {
              throw new InvalidParameterValueException(String.format("Failed to create Stickiness policy: Validation Failed %s", loadBalancer));
          }
  
          /* Finally Insert into DB */
          LBStickinessPolicyVO policy =
              new LBStickinessPolicyVO(loadBalancer.getId(), cmd.getLBStickinessPolicyName(), cmd.getStickinessMethodName(), cmd.getparamList(), cmd.getDescription());
          Boolean forDisplay = cmd.getDisplay();
          if (forDisplay != null) {
              policy.setDisplay(forDisplay);
          }
          policy = _lb2stickinesspoliciesDao.persist(policy);
  
          return policy;
      }
  
      @Override
      @DB
      @ActionEvent(eventType = EventTypes.EVENT_LB_HEALTHCHECKPOLICY_CREATE, eventDescription = "create load balancer health check to load balancer", create = true)
      public HealthCheckPolicy createLBHealthCheckPolicy(CreateLBHealthCheckPolicyCmd cmd) {
          CallContext caller = CallContext.current();
  
          /*
           * Validation of cmd Monitor interval must be greater than response
           * timeout
           */
          Map<String, String> paramMap = cmd.getFullUrlParams();
  
          if (paramMap.containsKey(ApiConstants.HEALTHCHECK_RESPONSE_TIMEOUT) && paramMap.containsKey(ApiConstants.HEALTHCHECK_INTERVAL_TIME)) {
              if (cmd.getResponsTimeOut() > cmd.getHealthCheckInterval())
                  throw new InvalidParameterValueException("Failed to create HealthCheck policy : Monitor interval must be greater than response timeout");
          }
          /* Validation : check corresponding load balancer rule exist */
          LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId());
          if (loadBalancer == null) {
              throw new InvalidParameterValueException("Failed: LB rule id: " + cmd.getLbRuleId() + " not present ");
          }
  
          _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer);
  
          if (loadBalancer.getState() == FirewallRule.State.Revoke) {
              throw new InvalidParameterValueException(String.format("Failed: LB rule: %s is in deleting state: ", loadBalancer));
          }
  
          /*
           * Validate Whether LB Provider has the capabilities to support Health
           * Checks
           */
          if (!validateHealthCheck(cmd)) {
              throw new InvalidParameterValueException(
                      String.format("Failed to create HealthCheck policy: Validation Failed (HealthCheck Policy is not supported by LB Provider for the LB rule:%s)", loadBalancer));
          }
  
          /* Validation : check for the multiple hc policies to the rule id */
          List<LBHealthCheckPolicyVO> hcPolicies = _lb2healthcheckDao.listByLoadBalancerId(cmd.getLbRuleId(), false);
          if (hcPolicies.size() > 0) {
              throw new InvalidParameterValueException(String.format("Failed to create HealthCheck policy: Already policy attached  for the LB Rule:%s", loadBalancer));
          }
          /*
           * Specific validations using network element validator for specific
           * validations
           */
          LBHealthCheckPolicyVO hcpolicy =
              new LBHealthCheckPolicyVO(loadBalancer.getId(), cmd.getPingPath(), cmd.getDescription(), cmd.getResponsTimeOut(), cmd.getHealthCheckInterval(),
                  cmd.getHealthyThreshold(), cmd.getUnhealthyThreshold());
  
          List<LbHealthCheckPolicy> hcPolicyList = new ArrayList<LbHealthCheckPolicy>();
          hcPolicyList.add(new LbHealthCheckPolicy(hcpolicy.getpingpath(), hcpolicy.getDescription(), hcpolicy.getResponseTime(), hcpolicy.getHealthcheckInterval(),
              hcpolicy.getHealthcheckThresshold(), hcpolicy.getUnhealthThresshold()));
  
          // Finally Insert into DB
          LBHealthCheckPolicyVO policy =
              new LBHealthCheckPolicyVO(loadBalancer.getId(), cmd.getPingPath(), cmd.getDescription(), cmd.getResponsTimeOut(), cmd.getHealthCheckInterval(),
                  cmd.getHealthyThreshold(), cmd.getUnhealthyThreshold());
  
          Boolean forDisplay = cmd.getDisplay();
          if (forDisplay != null) {
              policy.setDisplay(forDisplay);
          }
  
          policy = _lb2healthcheckDao.persist(policy);
          return policy;
      }
  
      @Override
      public boolean validateLbRule(LoadBalancingRule lbRule) {
          Network network = _networkDao.findById(lbRule.getNetworkId());
          Purpose purpose = lbRule.getPurpose();
          if (purpose != Purpose.LoadBalancing) {
              logger.debug("Unable to validate network rules for purpose: " + purpose.toString());
              return false;
          }
          for (LoadBalancingServiceProvider ne : _lbProviders) {
              boolean validated = ne.validateLBRule(network, lbRule);
              if (!validated)
                  return false;
          }
          return true;
      }
  
      @Override
      @DB
      @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_CREATE, eventDescription = "Apply Stickinesspolicy to load balancer ", async = true)
      public boolean applyLBStickinessPolicy(CreateLBStickinessPolicyCmd cmd) {
          boolean success = true;
          FirewallRule.State backupState = null;
          long oldStickinessPolicyId = 0;
  
          LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId());
          if (loadBalancer == null) {
              throw new InvalidParameterValueException("Invalid Load balancer Id:" + cmd.getLbRuleId());
          }
  
          _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, loadBalancer);
  
          List<LBStickinessPolicyVO> stickinessPolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(cmd.getLbRuleId(), false);
          for (LBStickinessPolicyVO stickinessPolicy : stickinessPolicies) {
              if (stickinessPolicy.getId() == cmd.getEntityId()) {
                  backupState = loadBalancer.getState();
                  loadBalancer.setState(FirewallRule.State.Add);
                  _lbDao.persist(loadBalancer);
              } else {
                  oldStickinessPolicyId = stickinessPolicy.getId();
                  stickinessPolicy.setRevoke(true);
                  _lb2stickinesspoliciesDao.persist(stickinessPolicy);
              }
          }
          try {
              applyLoadBalancerConfig(cmd.getLbRuleId());
          } catch (ResourceUnavailableException e) {
              logger.warn("Unable to apply Stickiness policy to the lb rule: {} because resource is unavailable:", loadBalancer, e);
              if (isRollBackAllowedForProvider(loadBalancer)) {
                  loadBalancer.setState(backupState);
                  _lbDao.persist(loadBalancer);
                  deleteLBStickinessPolicy(cmd.getEntityId(), false);
                  logger.debug("LB Rollback rule: {} lb state rollback while creating sticky policy", loadBalancer);
              } else {
                  deleteLBStickinessPolicy(cmd.getEntityId(), false);
                  if (oldStickinessPolicyId != 0) {
                      LBStickinessPolicyVO stickinessPolicy = _lb2stickinesspoliciesDao.findById(oldStickinessPolicyId);
                      stickinessPolicy.setRevoke(false);
                      _lb2stickinesspoliciesDao.persist(stickinessPolicy);
                      try {
                          if (backupState.equals(FirewallRule.State.Active))
                              applyLoadBalancerConfig(cmd.getLbRuleId());
                      } catch (ResourceUnavailableException e1) {
                          logger.info("[ignored] applying load balancer config.", e1);
                      } finally {
                          loadBalancer.setState(backupState);
                          _lbDao.persist(loadBalancer);
                      }
                  }
              }
              success = false;
          }
  
          return success;
      }
  
      @Override
      @DB
      @ActionEvent(eventType = EventTypes.EVENT_LB_HEALTHCHECKPOLICY_CREATE, eventDescription = "Apply HealthCheckPolicy to load balancer ", async = true)
      public boolean applyLBHealthCheckPolicy(CreateLBHealthCheckPolicyCmd cmd) {
          boolean success = true;
  
          LoadBalancerVO loadBalancer = _lbDao.findById(cmd.getLbRuleId());
          if (loadBalancer == null) {
              throw new InvalidParameterValueException("Invalid Load balancer Id:" + cmd.getLbRuleId());
          }
          _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, loadBalancer);
          FirewallRule.State backupState = loadBalancer.getState();
          loadBalancer.setState(FirewallRule.State.Add);
          _lbDao.persist(loadBalancer);
          try {
              applyLoadBalancerConfig(cmd.getLbRuleId());
          } catch (ResourceUnavailableException e) {
              logger.warn("Unable to apply healthcheck policy to the lb rule: {} because resource is unavailable:", loadBalancer, e);
              if (isRollBackAllowedForProvider(loadBalancer)) {
                  loadBalancer.setState(backupState);
                  _lbDao.persist(loadBalancer);
                  logger.debug("LB Rollback rule: {} lb state rollback while creating healthcheck policy", loadBalancer);
              }
              deleteLBHealthCheckPolicy(cmd.getEntityId(), false);
              success = false;
          }
          return success;
      }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_DELETE, eventDescription = "revoking LB Stickiness policy ", async = true)
      public boolean deleteLBStickinessPolicy(long stickinessPolicyId, boolean apply) {
          boolean success = true;
  
          CallContext caller = CallContext.current();
          LBStickinessPolicyVO stickinessPolicy = _lb2stickinesspoliciesDao.findById(stickinessPolicyId);
  
          if (stickinessPolicy == null) {
              throw new InvalidParameterValueException("Invalid Stickiness policy id value: " + stickinessPolicyId);
          }
          LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(stickinessPolicy.getLoadBalancerId()));
          if (loadBalancer == null) {
              throw new InvalidParameterValueException(String.format("Invalid Load balancer: %d for Stickiness policy: %s",
                      stickinessPolicy.getLoadBalancerId(), stickinessPolicy));
          }
          long loadBalancerId = loadBalancer.getId();
          FirewallRule.State backupState = loadBalancer.getState();
          _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer);
  
          if (apply) {
              if (loadBalancer.getState() == FirewallRule.State.Active) {
                  loadBalancer.setState(FirewallRule.State.Add);
                  _lbDao.persist(loadBalancer);
              }
  
              boolean backupStickyState = stickinessPolicy.isRevoke();
              stickinessPolicy.setRevoke(true);
              _lb2stickinesspoliciesDao.persist(stickinessPolicy);
              logger.debug("Set load balancer rule for revoke: rule {}, stickinesspolicy {}", loadBalancer, stickinessPolicy);
  
              try {
                  if (!applyLoadBalancerConfig(loadBalancerId)) {
                      String error = String.format("Failed to remove load balancer rule %s for stickinesspolicy %s", loadBalancer, stickinessPolicy);
                      logger.warn(error);
                      throw new CloudRuntimeException(error);
                  }
              } catch (ResourceUnavailableException e) {
                  if (isRollBackAllowedForProvider(loadBalancer)) {
                      stickinessPolicy.setRevoke(backupStickyState);
                      _lb2stickinesspoliciesDao.persist(stickinessPolicy);
                      loadBalancer.setState(backupState);
                      _lbDao.persist(loadBalancer);
                      logger.debug("LB Rollback rule: {}  while deleting sticky policy: {}", loadBalancer, stickinessPolicy);
                  }
                  logger.warn("Unable to apply the load balancer config because resource is unavailable.", e);
                  success = false;
              }
          } else {
              _lb2stickinesspoliciesDao.expunge(stickinessPolicyId);
          }
          return success;
      }
  
      @DB
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_LB_HEALTHCHECKPOLICY_DELETE, eventDescription = "revoking LB HealthCheck policy ", async = true)
      public boolean deleteLBHealthCheckPolicy(long healthCheckPolicyId, boolean apply) {
          boolean success = true;
  
          CallContext caller = CallContext.current();
          LBHealthCheckPolicyVO healthCheckPolicy = _lb2healthcheckDao.findById(healthCheckPolicyId);
  
          if (healthCheckPolicy == null) {
              throw new InvalidParameterValueException("Invalid HealthCheck policy id value: " + healthCheckPolicyId);
          }
          LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(healthCheckPolicy.getLoadBalancerId()));
          if (loadBalancer == null) {
              throw new InvalidParameterValueException(String.format("Invalid Load balancer: %d for HealthCheck policy: %s", healthCheckPolicy.getLoadBalancerId(), healthCheckPolicy));
          }
          final long loadBalancerId = loadBalancer.getId();
          FirewallRule.State backupState = loadBalancer.getState();
          _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer);
  
          if (apply) {
              if (loadBalancer.getState() == FirewallRule.State.Active) {
                  loadBalancer.setState(FirewallRule.State.Add);
                  _lbDao.persist(loadBalancer);
              }
  
              boolean backupStickyState = healthCheckPolicy.isRevoke();
              healthCheckPolicy.setRevoke(true);
              _lb2healthcheckDao.persist(healthCheckPolicy);
              logger.debug("Set health check policy to revoke for loadbalancing rule : {}, healthCheckpolicy {}", loadBalancer, healthCheckPolicy);
  
              // removing the state of services set by the monitor.
              final List<LoadBalancerVMMapVO> maps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId);
              if (maps != null) {
                  Transaction.execute(new TransactionCallbackNoReturn() {
                      @Override
                      public void doInTransactionWithoutResult(TransactionStatus status) {
                          logger.debug("Resetting health state policy for services in loadbalancing rule: {}", loadBalancer);
                          for (LoadBalancerVMMapVO map : maps) {
                              map.setState(null);
                              _lb2VmMapDao.persist(map);
                          }
                      }
                  });
              }
  
              try {
                  if (!applyLoadBalancerConfig(loadBalancerId)) {
                      String error = String.format("Failed to remove load balancer rule %s for healthCheckpolicy %s", loadBalancer, healthCheckPolicy);
                      logger.warn(error);
                      throw new CloudRuntimeException(error);
                  }
              } catch (ResourceUnavailableException e) {
                  if (isRollBackAllowedForProvider(loadBalancer)) {
                      healthCheckPolicy.setRevoke(backupStickyState);
                      _lb2healthcheckDao.persist(healthCheckPolicy);
                      loadBalancer.setState(backupState);
                      _lbDao.persist(loadBalancer);
                      logger.debug("LB Rollback rule: {}  while deleting healthcheck policy: {}", loadBalancer, healthCheckPolicy);
                  }
                  logger.warn("Unable to apply the load balancer config because resource is unavailable.", e);
                  success = false;
              }
          } else {
              _lb2healthcheckDao.remove(healthCheckPolicy.getLoadBalancerId());
          }
          return success;
      }
  
      // This method will check the status of services which has monitors created
      // by CloudStack and update them in lbvmmap table
      @DB
      @Override
      public void updateLBHealthChecks(Scheme scheme) throws ResourceUnavailableException {
          List<LoadBalancerVO> rules = _lbDao.listAll();
          List<NetworkVO> networks = _networkDao.listAll();
          List<LoadBalancerTO> stateRules = null;
          boolean isHandled = false;
          for (NetworkVO ntwk : networks) {
              Network network = _networkDao.findById(ntwk.getId());
              String capability = getLBCapability(network.getId(), Capability.HealthCheckPolicy.getName());
  
              if (capability != null && capability.equalsIgnoreCase("true")) {
                  /*
                   * logger.debug(
                   * "HealthCheck Manager :: LB Provider in the Network has the Healthcheck policy capability :: "
                   * + provider.get(0).getName());
                   */
                  rules = _lbDao.listByNetworkIdAndScheme(network.getId(), scheme);
                  if (rules != null && rules.size() > 0) {
                      List<LoadBalancingRule> lbrules = new ArrayList<LoadBalancingRule>();
                      for (LoadBalancerVO lb : rules) {
                          List<LbDestination> dstList = getExistingDestinations(lb.getId());
                          List<LbHealthCheckPolicy> hcPolicyList = getHealthCheckPolicies(lb.getId());
                          // Now retrive the status of services from NS even there are no policies. because there is default monitor
                          Ip sourceIp = getSourceIp(lb);
                          LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, null, hcPolicyList, sourceIp, null, lb.getLbProtocol());
                          lbrules.add(loadBalancing);
                      }
                      if (lbrules.size() > 0) {
                          isHandled = false;
                          for (LoadBalancingServiceProvider lbElement : _lbProviders) {
                              stateRules = lbElement.updateHealthChecks(network, lbrules);
                              if (stateRules != null && stateRules.size() > 0) {
                                  for (LoadBalancerTO lbto : stateRules) {
                                      LoadBalancerVO ulb = _lbDao.findByUuid(lbto.getUuid());
                                      List<LoadBalancerVMMapVO> lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(ulb.getId());
                                      for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) {
                                          UserVm vm = _vmDao.findById(lbVmMap.getInstanceId());
                                          Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(ulb.getNetworkId(), vm.getId());
                                          String dstIp = lbVmMap.getInstanceIp() == null ? nic.getIPv4Address(): lbVmMap.getInstanceIp();
  
                                          for (int i = 0; i < lbto.getDestinations().length; i++) {
                                              LoadBalancerTO.DestinationTO des = lbto.getDestinations()[i];
                                              if (dstIp.equalsIgnoreCase(lbto.getDestinations()[i].getDestIp())) {
                                                  lbVmMap.setState(des.getMonitorState());
                                                  _lb2VmMapDao.persist(lbVmMap);
                                                  logger.debug("Updating the LB VM Map table with the service state");
                                              }
                                          }
                                      }
                                  }
                                  isHandled = true;
                              }
                              if (isHandled) {
                                  break;
                              }
                          }
                      }
                  }
              } else {
                  // logger.debug("HealthCheck Manager :: LB Provider in the Network DNOT the Healthcheck policy capability ");
              }
          }
      }
  
      private boolean isRollBackAllowedForProvider(LoadBalancerVO loadBalancer) {
          Network network = _networkDao.findById(loadBalancer.getNetworkId());
          List<Provider> provider = _networkMgr.getProvidersForServiceInNetwork(network, Service.Lb);
          if (provider == null || provider.size() == 0) {
              return false;
          }
          if (provider.get(0) == Provider.Netscaler || provider.get(0) == Provider.F5BigIp ||
              provider.get(0) == Provider.Netris ||
              provider.get(0) == Provider.VirtualRouter || provider.get(0) == Provider.VPCVirtualRouter) {
              return true;
          }
          return false;
      }
  
      @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, Map<Long, List<String>> vmIdIpMap, boolean isAutoScaleVM) {
          CallContext ctx = CallContext.current();
          Account caller = ctx.getCallingAccount();
  
          final LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
          if (loadBalancer == null) {
              throw new InvalidParameterValueException("Failed to assign to load balancer " + loadBalancerId + ", the load balancer was not found.");
          }
  
          if (!isAutoScaleVM && _autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) {
              throw new InvalidParameterValueException(String.format("Failed to assign to load balancer %s because it is being used by an Autoscale VM group.", loadBalancer));
          }
  
          if (instanceIds == null && vmIdIpMap.isEmpty()) {
              throw new InvalidParameterValueException("Both instanceids and vmidipmap  can't be null");
          }
  
          // instanceIds and vmIdipmap is passed
          if (instanceIds != null && !vmIdIpMap.isEmpty()) {
              for(long instanceId: instanceIds) {
                  if (!vmIdIpMap.containsKey(instanceId)) {
                      vmIdIpMap.put(instanceId, null);
                  }
              }
          }
  
          //only instanceids list passed
          if (instanceIds != null && vmIdIpMap.isEmpty()){
              vmIdIpMap = new HashMap<Long, List<String>>();
              for (long instanceId: instanceIds){
                  vmIdIpMap.put(instanceId, null);
              }
          }
  
          List<LoadBalancerVMMapVO> mappedInstances = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId, false);
          Set<Long> mappedInstanceIds = new HashSet<Long>();
          for (LoadBalancerVMMapVO mappedInstance : mappedInstances) {
              mappedInstanceIds.add(Long.valueOf(mappedInstance.getInstanceId()));
          }
  
          Map<Long, List<String>> existingVmIdIps = new HashMap<Long, List<String>>();
          // now get the ips of vm and add it to map
          for (LoadBalancerVMMapVO mappedInstance : mappedInstances) {
  
              List<String> ipsList = null;
              if (existingVmIdIps.containsKey(mappedInstance.getInstanceId())) {
                  ipsList = existingVmIdIps.get(mappedInstance.getInstanceId());
              } else {
                  ipsList = new ArrayList<String>();
              }
              ipsList.add(mappedInstance.getInstanceIp());
              existingVmIdIps.put(mappedInstance.getInstanceId(), ipsList);
          }
  
          final List<UserVm> vmsToAdd = new ArrayList<UserVm>();
  
          // check for conflict
          Set<Long> passedInstanceIds = vmIdIpMap.keySet();
          for (Long instanceId : passedInstanceIds) {
              UserVm vm = _vmDao.findById(instanceId);
              if (vm == null || vm.getState() == State.Destroyed || vm.getState() == State.Expunging) {
                  InvalidParameterValueException ex = new InvalidParameterValueException("Invalid instance id specified");
                  if (vm == null) {
                      ex.addProxyObject(instanceId.toString(), "instanceId");
                  } else {
                      ex.addProxyObject(vm.getUuid(), "instanceId");
                  }
                  throw ex;
              }
  
              _rulesMgr.checkRuleAndUserVm(loadBalancer, vm, caller);
  
              Account vmOwner = _accountDao.findById(vm.getAccountId());
              Network network = _networkDao.findById(loadBalancer.getNetworkId());
              _accountMgr.checkAccess(vmOwner, SecurityChecker.AccessType.UseEntry, false, network);
  
              // Let's check to make sure the vm has a nic in the same network as
              // the load balancing rule.
              List<? extends Nic> nics = _networkModel.getNics(vm.getId());
              Nic nicInSameNetwork = null;
              for (Nic nic : nics) {
                  if (nic.getNetworkId() == loadBalancer.getNetworkId()) {
                      nicInSameNetwork = nic;
                      break;
                  }
              }
  
              if (nicInSameNetwork == null) {
                  InvalidParameterValueException ex =
                          new InvalidParameterValueException("VM with id specified cannot be added because it doesn't belong in the same network.");
                  ex.addProxyObject(vm.getUuid(), "instanceId");
                  throw ex;
              }
  
              String priIp = nicInSameNetwork.getIPv4Address();
  
              if (existingVmIdIps.containsKey(instanceId)) {
                  // now check for ip address
                  List<String> mappedIps = existingVmIdIps.get(instanceId);
                  List<String> newIps = vmIdIpMap.get(instanceId);
  
                  if (newIps == null) {
                      newIps = new ArrayList<String>();
                      newIps.add(priIp);
                  }
  
                  for (String newIp: newIps) {
                      if (mappedIps.contains(newIp)) {
                          throw new InvalidParameterValueException("VM " + instanceId + " with " + newIp +" is already mapped to load balancer.");
                      }
                  }
              }
  
              List<String> vmIpsList = vmIdIpMap.get(instanceId);
              String vmLbIp = null;
  
              if (vmIpsList != null) {
  
                  //check if the ips belongs to nic secondary ip
                  for (String ip: vmIpsList) {
                      // skip the primary ip from vm secondary ip comparisions
                      if (ip.equals(priIp)) {
                          continue;
                      }
                      if(_nicSecondaryIpDao.findByIp4AddressAndNicId(ip,nicInSameNetwork.getId()) == null) {
                          throw new InvalidParameterValueException("VM ip "+ ip + " specified does not belong to " +
                                  "nic in network " + nicInSameNetwork.getNetworkId());
                      }
                  }
              } else {
                  vmIpsList = new ArrayList<String>();
                  vmIpsList.add(priIp);
              }
  
              // when vm id is passed in instance ids and in vmidipmap
              // assign for primary ip and ip passed in vmidipmap
              if (instanceIds != null ) {
                  if (instanceIds.contains(instanceId)) {
                      vmIpsList.add(priIp);
                  }
              }
  
              vmIdIpMap.put(instanceId, vmIpsList);
  
              if (logger.isDebugEnabled()) {
                  logger.debug("Adding " + vm + " to the load balancer pool");
              }
              vmsToAdd.add(vm);
          }
  
          final Set<Long> vmIds = vmIdIpMap.keySet();
          final Map<Long, List<String>> newMap = vmIdIpMap;
  
          Transaction.execute(new TransactionCallbackNoReturn() {
              @Override
              public void doInTransactionWithoutResult(TransactionStatus status) {
  
                  for (Long vmId : vmIds) {
                      final Set<String> lbVmIps = new HashSet<String>(newMap.get(vmId));
                      for (String vmIp: lbVmIps) {
                          LoadBalancerVMMapVO map = new LoadBalancerVMMapVO(loadBalancer.getId(), vmId, vmIp, false);
                          map = _lb2VmMapDao.persist(map);
                      }
                  }
              }
          });
  
          if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) {
              Network.Provider provider = getLoadBalancerServiceProvider(loadBalancer);
              if (Network.Provider.Netscaler.equals(provider)) {
                  // For netscaler autoscale loadbalancer, the rules need not be applied,
                  // meaning the call need not reach the resource layer.
                  // We can consider the job done.
                  return true;
              }
          }
          boolean success = false;
          FirewallRule.State backupState = loadBalancer.getState();
          try {
              loadBalancer.setState(FirewallRule.State.Add);
              _lbDao.persist(loadBalancer);
              applyLoadBalancerConfig(loadBalancerId);
              success = true;
          } catch (ResourceUnavailableException e) {
              logger.warn("Unable to apply the load balancer config because resource is unavailable.", e);
              success = false;
          } finally {
              if (!success) {
                  final List<Long> vmInstanceIds = new ArrayList<Long>();
                  Transaction.execute(new TransactionCallbackNoReturn() {
                      @Override
                      public void doInTransactionWithoutResult(TransactionStatus status) {
                          for (Long vmId : vmIds) {
                              vmInstanceIds.add(vmId);
                          }
                      }
                  });
                  if (!vmInstanceIds.isEmpty()) {
                      _lb2VmMapDao.remove(loadBalancer.getId(), vmInstanceIds, null);
                      logger.debug("LB Rollback rule: {} while attaching VM: {}", loadBalancer, vmInstanceIds);
                  }
                  loadBalancer.setState(backupState);
                  _lbDao.persist(loadBalancer);
                  CloudRuntimeException ex = new CloudRuntimeException("Failed to add specified loadbalancerruleid for vms "
                      + vmInstanceIds);
                  ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId");
                  // TBD: Also pack in the instanceIds in the exception using the
                  // right VO object or table name.
                  throw ex;
              }
  
          }
  
          return success;
      }
  
      @Override
      public boolean assignSSLCertToLoadBalancerRule(Long lbId, String certName, String publicCert, String privateKey) {
          logger.error("Calling the manager for LB");
          LoadBalancerVO loadBalancer = _lbDao.findById(lbId);
  
          return false;  //TODO
      }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_REMOVE_FROM_LOAD_BALANCER_RULE, eventDescription = "removing from load balancer", async = true)
      public boolean removeFromLoadBalancer(long loadBalancerId, List<Long> instanceIds, Map<Long, List<String>> vmIdIpsMap, boolean isAutoScaleVM) {
          return removeFromLoadBalancerInternal(loadBalancerId, instanceIds, true, vmIdIpsMap, isAutoScaleVM);
      }
  
      @Override
      public LbSslCert getLbSslCert(long lbRuleId) {
          LoadBalancerCertMapVO lbCertMap = _lbCertMapDao.findByLbRuleId(lbRuleId);
  
          if (lbCertMap == null)
              return null;
  
          SslCertVO certVO = _entityMgr.findById(SslCertVO.class, lbCertMap.getCertId());
          if (certVO == null) {
              logger.warn("Cert rule with cert ID " + lbCertMap.getCertId() + " but Cert is not found");
              return null;
          }
  
          return new LbSslCert(certVO.getCertificate(), certVO.getKey(), certVO.getPassword(), certVO.getChain(), certVO.getFingerPrint(), lbCertMap.isRevoke());
      }
  
      @Override
      @DB
      @ActionEvent(eventType = EventTypes.EVENT_LB_CERT_ASSIGN, eventDescription = "assigning certificate to load balancer", async = true)
      public boolean assignCertToLoadBalancer(long lbRuleId, Long certId, boolean forced) {
          CallContext caller = CallContext.current();
  
          LoadBalancerVO loadBalancer = _lbDao.findById(lbRuleId);
          if (loadBalancer == null) {
              throw new InvalidParameterValueException("Invalid load balancer id: " + lbRuleId);
          }
  
          SslCertVO certVO = _entityMgr.findById(SslCertVO.class, certId);
          if (certVO == null) {
              throw new InvalidParameterValueException("Invalid certificate id: " + certId);
          }
  
          _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer);
  
          // check if LB and Cert belong to the same account
          if (loadBalancer.getAccountId() != certVO.getAccountId()) {
              throw new InvalidParameterValueException("Access denied for account " + certVO.getAccountId());
          }
  
          String capability = getLBCapability(loadBalancer.getNetworkId(), Capability.SslTermination.getName());
          if (capability == null) {
              throw new InvalidParameterValueException("Ssl termination not supported by the loadbalancer");
          }
  
          validateCertMapRule(lbRuleId, forced);
  
          //check for correct port
          if (loadBalancer.getLbProtocol() == null || !(loadBalancer.getLbProtocol().equals(NetUtils.SSL_PROTO)))
              throw new InvalidParameterValueException("Bad LB protocol: Expected ssl got " + loadBalancer.getLbProtocol());
  
          boolean success = false;
          FirewallRule.State backupState = loadBalancer.getState();
  
          try {
  
              loadBalancer.setState(FirewallRule.State.Add);
              _lbDao.persist(loadBalancer);
              LoadBalancerCertMapVO certMap = new LoadBalancerCertMapVO(lbRuleId, certId, false);
              _lbCertMapDao.persist(certMap);
              applyLoadBalancerConfig(loadBalancer.getId());
              success = true;
          } catch (ResourceUnavailableException e) {
              if (isRollBackAllowedForProvider(loadBalancer)) {
  
                  loadBalancer.setState(backupState);
                  _lbDao.persist(loadBalancer);
                  LoadBalancerCertMapVO certMap = _lbCertMapDao.findByLbRuleId(lbRuleId);
                  _lbCertMapDao.remove(certMap.getId());
                  logger.debug("LB Rollback rule: {} while adding cert", loadBalancer);
              }
              logger.warn("Unable to apply the load balancer config because resource is unavailable.", e);
          }
          return success;
      }
  
      private void validateCertMapRule(long lbRuleId, boolean forced) {
          //check if the lb is already bound
          LoadBalancerCertMapVO certMapRule = _lbCertMapDao.findByLbRuleId(lbRuleId);
          if (certMapRule != null) {
              if (!forced) {
                  throw new InvalidParameterValueException("Another certificate is already bound to the LB");
              }
              logger.debug("Another certificate is already bound to the LB, removing it");
              removeCertFromLoadBalancer(lbRuleId);
          }
      }
  
      @Override
      @DB
      @ActionEvent(eventType = EventTypes.EVENT_LB_CERT_REMOVE, eventDescription = "removing certificate from load balancer", async = true)
      public boolean removeCertFromLoadBalancer(long lbRuleId) {
          CallContext caller = CallContext.current();
  
          LoadBalancerVO loadBalancer = _lbDao.findById(lbRuleId);
          LoadBalancerCertMapVO lbCertMap = _lbCertMapDao.findByLbRuleId(lbRuleId);
  
          if (loadBalancer == null) {
              throw new InvalidParameterValueException("Invalid load balancer value: " + lbRuleId);
          }
  
          if (lbCertMap == null) {
              throw new InvalidParameterValueException("No certificate is bound to lb with id: " + lbRuleId);
          }
  
          _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer);
  
          boolean success = false;
          FirewallRule.State backupState = loadBalancer.getState();
          try {
  
              loadBalancer.setState(FirewallRule.State.Add);
              _lbDao.persist(loadBalancer);
              lbCertMap.setRevoke(true);
              _lbCertMapDao.persist(lbCertMap);
  
              if (!applyLoadBalancerConfig(lbRuleId)) {
                  logger.warn("Failed to remove cert from load balancer rule {}", loadBalancer);
                  CloudRuntimeException ex = new CloudRuntimeException(String.format("Failed to remove certificate load balancer rule %s", loadBalancer));
                  ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId");
                  throw ex;
              }
              success = true;
          } catch (ResourceUnavailableException e) {
              if (isRollBackAllowedForProvider(loadBalancer)) {
                  lbCertMap.setRevoke(false);
                  _lbCertMapDao.persist(lbCertMap);
                  loadBalancer.setState(backupState);
                  _lbDao.persist(loadBalancer);
                  logger.debug(String.format("Rolled back certificate removal lb %s", loadBalancer));
              }
              logger.warn("Unable to apply the load balancer config because resource is unavailable.", e);
              if (!success) {
                  CloudRuntimeException ex = new CloudRuntimeException(String.format("Failed to remove certificate from load balancer rule %s", loadBalancer));
                  ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId");
                  throw ex;
              }
          }
          return success;
      }
  
      private boolean removeFromLoadBalancerInternal(long loadBalancerId, List<Long> instanceIds, boolean rollBack, Map<Long, List<String>> vmIdIpMap, boolean isAutoScaleVM) {
          CallContext caller = CallContext.current();
  
          LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(loadBalancerId));
          if (loadBalancer == null) {
              throw new InvalidParameterValueException("Invalid load balancer value: " + loadBalancerId);
          }
  
          _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer);
  
          if (instanceIds == null && vmIdIpMap.isEmpty()) {
              throw new InvalidParameterValueException("Both instanceids and vmidipmap  can't be null");
          }
  
          // instanceIds and vmIdipmap is passed
          if (instanceIds != null && !vmIdIpMap.isEmpty()) {
              for(long instanceId: instanceIds) {
                  if (!vmIdIpMap.containsKey(instanceId)) {
                      vmIdIpMap.put(instanceId, null);
                  }
              }
          }
  
          //only instanceids list passed
          if (instanceIds != null && vmIdIpMap.isEmpty()){
              vmIdIpMap = new HashMap<Long, List<String>>();
              for (long instanceId: instanceIds){
                  vmIdIpMap.put(instanceId, null);
              }
          }
  
  
  
          boolean success = false;
          FirewallRule.State backupState = loadBalancer.getState();
          Set<Long> vmIds = vmIdIpMap.keySet();
  
          if (!isAutoScaleVM) {
              for (long instanceId : vmIds) {
                  autoScaleManager.checkIfVmActionAllowed(instanceId);
              }
          }
  
          try {
              loadBalancer.setState(FirewallRule.State.Add);
              _lbDao.persist(loadBalancer);
  
              for (long instanceId : vmIds) {
                  List<String> lbVmIps = vmIdIpMap.get(instanceId);
  
                  if (lbVmIps == null || lbVmIps.isEmpty()) {
                      List<LoadBalancerVMMapVO> lbVms = _lb2VmMapDao.listByLoadBalancerIdAndVmId(loadBalancerId, instanceId);
                      if (lbVms == null) {
                          throw new InvalidParameterValueException("The instance id: "+ instanceId +" is not configured "
                                  + " for LB rule id " + loadBalancerId);
                      }
  
                      for (LoadBalancerVMMapVO lbvm: lbVms) {
                          lbvm.setRevoke(true);
                          _lb2VmMapDao.persist(lbvm);
                      }
                      logger.debug("Set load balancer rule for revoke: rule {}, vm {}", loadBalancer::toString, () -> _vmDao.findById(instanceId));
  
                  } else {
                      for (String vmIp: lbVmIps) {
                          LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmIdVmIp (loadBalancerId, instanceId, vmIp);
                          if (map == null) {
                              throw new InvalidParameterValueException(String.format("The instance id: %d is not configured for LB rule %s", instanceId, loadBalancer));
                          }
                          map.setRevoke(true);
                          _lb2VmMapDao.persist(map);
                          logger.debug("Set load balancer rule for revoke: rule {}, vmId {}, vmip {}", loadBalancer::toString, () -> _vmDao.findById(instanceId), vmIp::toString);
                      }
                  }
              }
  
              if (!applyLoadBalancerConfig(loadBalancerId)) {
                  logger.warn("Failed to remove load balancer rule {} for vms {}", loadBalancer, instanceIds);
                  CloudRuntimeException ex = new CloudRuntimeException("Failed to remove specified load balancer rule id for vms " + instanceIds);
                  ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId");
                  throw ex;
              }
  
              if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(loadBalancerId)) {
                  List<Long> vmIdsList = new ArrayList<>(vmIdIpMap.keySet());
                  _lb2VmMapDao.remove(loadBalancer.getId(), vmIdsList, null);
                  for (Long instanceId: vmIdsList){
                      autoScaleVmGroupVmMapDao.removeByVm(instanceId);
                  }
              }
  
              success = true;
          } catch (ResourceUnavailableException e) {
              if (rollBack && isRollBackAllowedForProvider(loadBalancer)) {
  
                  for (long instanceId : vmIds) {
                      List<String> lbVmIps = vmIdIpMap.get(instanceId);
  
                      if (lbVmIps == null || lbVmIps.isEmpty()) {
                          LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmId(loadBalancerId, instanceId);
                          map.setRevoke(false);
                          _lb2VmMapDao.persist(map);
                          logger.debug("LB Rollback rule: {},while removing vmId {}", loadBalancer, instanceId);
                      }else {
                          for (String vmIp: lbVmIps) {
                              LoadBalancerVMMapVO map = _lb2VmMapDao.findByLoadBalancerIdAndVmIdVmIp (loadBalancerId, instanceId, vmIp);
                              map.setRevoke(true);
                              _lb2VmMapDao.persist(map);
                              logger.debug("LB Rollback rule: {},while removing vmId {}, vmip {}", loadBalancer, instanceId, vmIp);
                          }
                      }
                  }
  
                  loadBalancer.setState(backupState);
                  _lbDao.persist(loadBalancer);
                  logger.debug("LB Rollback rule: {} while removing vm instances", loadBalancer);
              }
              logger.warn("Unable to apply the load balancer config because resource is unavailable.", e);
          }
          if (!success) {
              CloudRuntimeException ex = new CloudRuntimeException("Failed to remove specified load balancer rule id for vms " + vmIds);
              ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId");
              throw ex;
          }
          return success;
      }
  
      @Override
      public boolean removeVmFromLoadBalancers(long instanceId) {
          boolean success = true;
          List<LoadBalancerVMMapVO> maps = _lb2VmMapDao.listByInstanceId(instanceId);
          if (maps == null || maps.isEmpty()) {
              return true;
          }
  
          Map<Long, List<Long>> lbsToReconfigure = new HashMap<Long, List<Long>>();
  
          // first set all existing lb mappings with Revoke state
          for (LoadBalancerVMMapVO map : maps) {
              long lbId = map.getLoadBalancerId();
              List<Long> instances = lbsToReconfigure.get(lbId);
              if (instances == null) {
                  instances = new ArrayList<Long>();
              }
              instances.add(map.getInstanceId());
              lbsToReconfigure.put(lbId, instances);
  
              map.setRevoke(true);
              _lb2VmMapDao.persist(map);
              logger.debug("Set load balancer rule for revoke: rule {}, vm {}", () -> _lbDao.findById(map.getLoadBalancerId()), () -> _vmDao.findById(instanceId));
          }
  
          // Reapply all lbs that had the vm assigned
          if (lbsToReconfigure != null) {
              for (Map.Entry<Long, List<Long>> lb : lbsToReconfigure.entrySet()) {
                  if (!removeFromLoadBalancerInternal(lb.getKey(), lb.getValue(), false, new HashMap<>(), false)) {
                      success = false;
                  }
              }
          }
          return success;
      }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_DELETE, eventDescription = "deleting load balancer", async = true)
      public boolean deleteLoadBalancerRule(long loadBalancerId, boolean apply) {
          CallContext ctx = CallContext.current();
          Account caller = ctx.getCallingAccount();
  
          LoadBalancerVO rule = _lbDao.findById(loadBalancerId);
  
          if (rule == null) {
              throw new InvalidParameterValueException("Unable to find load balancer rule " + loadBalancerId);
          }
          _accountMgr.checkAccess(caller, null, true, rule);
  
          boolean result = deleteLoadBalancerRule(loadBalancerId, apply, caller, ctx.getCallingUserId(), true);
          if (!result) {
              throw new CloudRuntimeException(String.format("Unable to remove load balancer rule %s", rule));
          }
          return result;
      }
  
      @DB
      public boolean deleteLoadBalancerRule(final long loadBalancerId, boolean apply, Account caller, long callerUserId, boolean rollBack) {
          List<AutoScaleVmGroupVO> vmGroups = _autoScaleVmGroupDao.listByLoadBalancer(loadBalancerId);
          if (CollectionUtils.isNotEmpty(vmGroups)) {
              throw new CloudRuntimeException(String.format("Cannot delete load balancer rule %d because it is being used by %s Autoscale VM group.", loadBalancerId, vmGroups.size()));
          }
  
          final LoadBalancerVO lb = _lbDao.findById(loadBalancerId);
          FirewallRule.State backupState = lb.getState();
  
          // remove any ssl certs associated with this LB rule before trying to delete it.
          LoadBalancerCertMapVO lbCertMap = _lbCertMapDao.findByLbRuleId(loadBalancerId);
          if (lbCertMap != null) {
              boolean removeResult = removeCertFromLoadBalancer(loadBalancerId);
              if (!removeResult) {
                  throw new CloudRuntimeException(String.format("Unable to remove certificate from load balancer rule %s", lb));
              }
          }
  
          List<LoadBalancerVMMapVO> backupMaps = Transaction.execute(new TransactionCallback<List<LoadBalancerVMMapVO>>() {
              @Override
              public List<LoadBalancerVMMapVO> doInTransaction(TransactionStatus status) {
                  boolean generateUsageEvent = false;
  
                  if (lb.getState() == FirewallRule.State.Staged) {
                      if (logger.isDebugEnabled()) {
                          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> backupMaps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId);
                  List<LoadBalancerVMMapVO> maps = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId);
                  if (maps != null) {
                      for (LoadBalancerVMMapVO map : maps) {
                          map.setRevoke(true);
                          _lb2VmMapDao.persist(map);
                          logger.debug("Set load balancer rule for revoke: rule {}, vmId {}", lb::toString, () -> _vmDao.findById(map.getInstanceId()));
                      }
                  }
  
                  List<LBHealthCheckPolicyVO> hcPolicies = _lb2healthcheckDao.listByLoadBalancerIdAndDisplayFlag(loadBalancerId, null);
                  for (LBHealthCheckPolicyVO lbHealthCheck : hcPolicies) {
                      lbHealthCheck.setRevoke(true);
                      _lb2healthcheckDao.persist(lbHealthCheck);
                  }
  
                  if (generateUsageEvent) {
                      // Generate usage event right after all rules were marked for revoke
                      Network network = _networkModel.getNetwork(lb.getNetworkId());
                      UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_DELETE, lb.getAccountId(), network.getDataCenterId(), lb.getId(),
                              null, LoadBalancingRule.class.getName(), lb.getUuid());
                  }
  
                  return backupMaps;
              }
          });
  
          // gather external network usage stats for this lb rule
          NetworkVO network = _networkDao.findById(lb.getNetworkId());
          if (network != null) {
              if (_networkModel.networkIsConfiguredForExternalNetworking(network.getDataCenterId(), network.getId())) {
                  _externalDeviceUsageMgr.updateExternalLoadBalancerNetworkUsageStats(loadBalancerId);
              }
          }
  
          if (apply) {
              try {
                  if (!applyLoadBalancerConfig(loadBalancerId)) {
                      logger.warn("Unable to apply the load balancer config");
                      return false;
                  }
              } catch (ResourceUnavailableException e) {
                  if (rollBack && isRollBackAllowedForProvider(lb)) {
                      if (backupMaps != null) {
                          for (LoadBalancerVMMapVO map : backupMaps) {
                              _lb2VmMapDao.persist(map);
                              logger.debug("LB Rollback rule: {}, vmId {}", lb, map.getInstanceId());
                          }
                      }
                      lb.setState(backupState);
                      _lbDao.persist(lb);
                      logger.debug("LB Rollback rule: {} while deleting LB rule.", lb);
                  } else {
                      logger.warn("Unable to apply the load balancer config because resource is unavailable.", e);
                  }
                  return false;
              }
          }
  
          FirewallRuleVO relatedRule = _firewallDao.findByRelatedId(lb.getId());
          if (relatedRule != null) {
              logger.warn("Unable to remove firewall rule={} as it has related firewall rule={}; leaving it in Revoke state", lb, relatedRule);
              return false;
          } else {
              _firewallMgr.removeRule(lb);
          }
  
          // FIXME: breaking the dependency on ELB manager. This breaks
          // functionality of ELB using virtual router
          // Bug CS-15411 opened to document this
          // _elbMgr.handleDeleteLoadBalancerRule(lb, callerUserId, caller);
  
          logger.debug("Load balancer {} is removed successfully", lb);
  
          return true;
      }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_CREATE, eventDescription = "creating load balancer")
      public LoadBalancer createPublicLoadBalancerRule(String xId, String name, String description, int srcPortStart, int srcPortEnd, int defPortStart, int defPortEnd,
          Long ipAddrId, String protocol, String algorithm, long networkId, long lbOwnerId, boolean openFirewall, String lbProtocol, Boolean forDisplay) throws NetworkRuleConflictException,
          InsufficientAddressCapacityException {
              return createPublicLoadBalancerRule(xId, name, description, srcPortStart, srcPortEnd, defPortStart, defPortEnd, ipAddrId, protocol, algorithm, networkId, lbOwnerId, openFirewall, lbProtocol, forDisplay, null);
          }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_CREATE, eventDescription = "creating load balancer")
      public LoadBalancer createPublicLoadBalancerRule(String xId, String name, String description, int srcPortStart, int srcPortEnd, int defPortStart, int defPortEnd,
              Long ipAddrId, String protocol, String algorithm, long networkId, long lbOwnerId, boolean openFirewall, String lbProtocol, Boolean forDisplay, List<String> cidrList) throws NetworkRuleConflictException,
              InsufficientAddressCapacityException {
          Account lbOwner = _accountMgr.getAccount(lbOwnerId);
  
          if (srcPortStart != srcPortEnd) {
              throw new InvalidParameterValueException("Port ranges are not supported by the load balancer");
          }
  
          IPAddressVO ipVO = null;
          if (ipAddrId != null) {
              ipVO = _ipAddressDao.findById(ipAddrId);
          }
  
          Network network = _networkModel.getNetwork(networkId);
  
          // FIXME: breaking the dependency on ELB manager. This breaks
          // functionality of ELB using virtual router
          // Bug CS-15411 opened to document this
          // LoadBalancer result = _elbMgr.handleCreateLoadBalancerRule(lb,
          // lbOwner, lb.getNetworkId());
          LoadBalancer result = null;
          IpAddress systemIp = null;
          NetworkOffering off = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId());
  
          if (srcPortStart == DNS_PORT && ipVO.isSourceNat()) {
              List<NetworkOfferingServiceMapVO> offeringServices = _networkOfferingServiceDao.listByNetworkOfferingId(network.getNetworkOfferingId());
              for (NetworkOfferingServiceMapVO serviceMapVo: offeringServices) {
                  if (serviceMapVo.getService().equals(Service.Dns.getName())) {
                      throw new InvalidParameterValueException("Error adding load balancer rule, cannot add port 53 with network service offering having DNS service and Source NAT.");
                  }
              }
          }
  
          if (off.isElasticLb() && ipVO == null && network.getVpcId() == null) {
              systemIp = _ipAddrMgr.assignSystemIp(networkId, lbOwner, true, false);
              if (systemIp != null) {
                  ipVO = _ipAddressDao.findById(systemIp.getId());
              }
          }
  
          // Validate ip address
          if (ipVO == null) {
              throw new InvalidParameterValueException("Unable to create load balance rule; can't find/allocate source IP");
          } else if (ipVO.isOneToOneNat()) {
              throw new NetworkRuleConflictException("Can't do load balance on ip address: " + ipVO.getAddress());
          }
  
          String cidrString = generateCidrString(cidrList);
  
          boolean performedIpAssoc = false;
          try {
              if (ipVO.getAssociatedWithNetworkId() == null) {
                  boolean assignToVpcNtwk = network.getVpcId() != null && ipVO.getVpcId() != null && ipVO.getVpcId().longValue() == network.getVpcId();
                  if (assignToVpcNtwk) {
                      // set networkId just for verification purposes
                      _networkModel.checkIpForService(ipVO, Service.Lb, networkId);
  
                      logger.debug("The ip is not associated with the VPC network={} so assigning", network);
                      ipVO = _ipAddrMgr.associateIPToGuestNetwork(ipAddrId, networkId, false);
                      performedIpAssoc = true;
                  }
              } else {
                  _networkModel.checkIpForService(ipVO, Service.Lb, null);
              }
  
              if (ipVO.getAssociatedWithNetworkId() == null) {
                  throw new InvalidParameterValueException("Ip address " + ipVO + " is not assigned to the network " + network);
              }
  
              result = createPublicLoadBalancer(xId, name, description, srcPortStart, defPortStart, ipVO.getId(), protocol, algorithm, openFirewall, CallContext.current(),
                      lbProtocol, forDisplay, cidrString);
          } catch (Exception ex) {
              logger.warn("Failed to create load balancer due to ", ex);
              if (ex instanceof NetworkRuleConflictException) {
                  throw (NetworkRuleConflictException)ex;
              }
  
              if (ex instanceof InvalidParameterValueException) {
                  throw (InvalidParameterValueException)ex;
              }
  
          } finally {
              if (result == null && systemIp != null) {
                  logger.debug("Releasing system IP address " + systemIp + " as corresponding lb rule failed to create");
                  _ipAddrMgr.handleSystemIpRelease(systemIp);
              }
              // release ip address if ipassoc was perfored
              if (performedIpAssoc) {
                  ipVO = _ipAddressDao.findById(ipVO.getId());
                  _vpcMgr.unassignIPFromVpcNetwork(ipVO, network);
              }
          }
  
          if (result == null) {
              throw new CloudRuntimeException("Failed to create load balancer rule: " + name);
          }
  
          return result;
      }
     /**
      * Transforms the cidrList from a List of Strings to a String which contains all the CIDRs from cidrList separated by whitespaces. This is used to facilitate both the persistence
      * in the DB and also later when building the configuration String in the getRulesForPool method of the HAProxyConfigurator class.
     */
      protected String generateCidrString(List<String> cidrList) {
          if (cidrList == null) {
              logger.trace("The given CIDR list is null, therefore we will return null.");
              return null;
          }
          String cidrString;
          StringBuilder sb = new StringBuilder();
          for (String cidr: cidrList) {
              cidr = validateCidr(cidr);
              sb.append(cidr).append(' ');
          }
          cidrString = sb.toString();
          logger.trace(String.format("From the cidrList [%s] we generated the following CIDR String [%s].", cidrList, cidrString));
          return StringUtils.trim(cidrString);
      }
  
      private String validateCidr(String cidr) {
          cidr = StringUtils.trim(cidr);
          boolean validCidr = NetUtils.isValidIp4Cidr(cidr) || NetUtils.isValidIp6Cidr(cidr);
          boolean validIp = NetUtils.isValidIp4(cidr) || NetUtils.isValidIp6(cidr);
          if (!validCidr && !validIp) {
              throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("CIDR [%s] is invalid.", cidr));
          }
          return cidr;
      }
  
      @DB
      @Override
      public LoadBalancer createPublicLoadBalancer(final String xId, final String name, final String description, final int srcPort, final int destPort, final long sourceIpId,
                                                   final String protocol, final String algorithm, final boolean openFirewall, final CallContext caller, final String lbProtocol,
                                                   final Boolean forDisplay, String cidrList) throws NetworkRuleConflictException {
          if (!NetUtils.isValidPort(destPort)) {
              throw new InvalidParameterValueException("privatePort is an invalid value: " + destPort);
          }
  
          if ((algorithm == null) || !NetUtils.isValidAlgorithm(algorithm)) {
              throw new InvalidParameterValueException("Invalid algorithm: " + algorithm);
          }
  
          try {
              final IPAddressVO ipAddr = _ipAddressDao.acquireInLockTable(sourceIpId);
  
              // make sure ip address exists
              if (ipAddr == null || !ipAddr.readyToUse()) {
                  InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id specified");
                  if (ipAddr == null) {
                      ex.addProxyObject(String.valueOf(sourceIpId), "sourceIpId");
                  } else {
                      ex.addProxyObject(ipAddr.getUuid(), "sourceIpId");
                  }
                  throw ex;
              } else if (ipAddr.isOneToOneNat()) {
                  InvalidParameterValueException ex = new InvalidParameterValueException("Unable to create load balancer rule; specified sourceip id has static nat enabled");
                  ex.addProxyObject(ipAddr.getUuid(), "sourceIpId");
                  throw ex;
              }
  
              _accountMgr.checkAccess(caller.getCallingAccount(), null, true, ipAddr);
  
              final Long networkId = ipAddr.getAssociatedWithNetworkId();
              if (networkId == null) {
                  InvalidParameterValueException ex =
                          new InvalidParameterValueException("Unable to create load balancer rule ; specified sourceip id is not associated with any network");
                  ex.addProxyObject(ipAddr.getUuid(), "sourceIpId");
                  throw ex;
              }
  
              // verify that lb service is supported by the network
              isLbServiceSupportedInNetwork(networkId, Scheme.Public);
  
              _firewallMgr.validateFirewallRule(caller.getCallingAccount(), ipAddr, srcPort, srcPort, protocol, Purpose.LoadBalancing, FirewallRuleType.User, networkId, null);
  
              return Transaction.execute((TransactionCallbackWithException<LoadBalancerVO, NetworkRuleConflictException>) status -> {
                  LoadBalancerVO newRule = new LoadBalancerVO(xId, name, description, sourceIpId, srcPort, destPort, algorithm, networkId, ipAddr.getAllocatedToAccountId(),
                          ipAddr.getAllocatedInDomainId(), lbProtocol, cidrList);
  
                  if (forDisplay != null) {
                      newRule.setDisplay(forDisplay);
                  }
  
                  // verify rule is supported by Lb provider of the network
                  Ip sourceIp = getSourceIp(newRule);
                  LoadBalancingRule loadBalancing = new LoadBalancingRule(newRule, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), sourceIp, null, lbProtocol);
                  if (!validateLbRule(loadBalancing)) {
                      throw new InvalidParameterValueException("LB service provider cannot support this rule");
                  }
  
                  newRule = _lbDao.persist(newRule);
  
                  //create rule for all CIDRs
                  if (openFirewall) {
                      _firewallMgr.createRuleForAllCidrs(sourceIpId, caller.getCallingAccount(), srcPort, srcPort, protocol, null, null, newRule.getId(), networkId);
                  }
  
                  boolean success = true;
  
                  try {
                      _firewallMgr.detectRulesConflict(newRule);
                      if (!_firewallDao.setStateToAdd(newRule)) {
                          throw new CloudRuntimeException("Unable to update the state to add for " + newRule);
                      }
                      logger.debug("Load balancer {} for Ip address: {}, public port {}, private port {} is added successfully.", newRule, ipAddr, srcPort, destPort);
                      CallContext.current().setEventDetails("Load balancer Id: " + newRule.getId());
                      UsageEventUtils.publishUsageEvent(EventTypes.EVENT_LOAD_BALANCER_CREATE, ipAddr.getAllocatedToAccountId(), ipAddr.getDataCenterId(), newRule.getId(),
                              null, LoadBalancingRule.class.getName(), newRule.getUuid());
  
                      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) {
                          _firewallMgr.revokeRelatedFirewallRule(newRule.getId(), false);
                          removeLBRule(newRule);
                      }
                  }
              });
          } finally {
              _ipAddressDao.releaseFromLockTable(sourceIpId);
          }
      }
  
      @Override
      public boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException {
          LoadBalancerVO lb = _lbDao.findById(lbRuleId);
          List<LoadBalancerVO> lbs;
          if (isRollBackAllowedForProvider(lb)) {
              // this is for Netscalar type of devices. if their is failure the db
              // entries will be rollbacked.
              lbs = Arrays.asList(lb);
          } else {
              boolean onlyRulesInTransitionState = true;
              for (LoadBalancingServiceProvider lbElement : _lbProviders) {
                  Provider provider = lbElement.getProvider();
                  boolean isLbProvider = _networkModel.isProviderSupportServiceInNetwork(lb.getNetworkId(), Service.Lb, provider);
                  if (!isLbProvider) {
                      continue;
                  }
                  onlyRulesInTransitionState = lbElement.handlesOnlyRulesInTransitionState();
                  break;
              }
  
              // get all rules in transition state
              if (onlyRulesInTransitionState) {
                  lbs = _lbDao.listInTransitionStateByNetworkIdAndScheme(lb.getNetworkId(), lb.getScheme());
              } else {
                  lbs = _lbDao.listByNetworkIdAndScheme(lb.getNetworkId(), lb.getScheme());
              }
  
          }
          return applyLoadBalancerRules(lbs, true);
      }
  
      @Override
      public boolean revokeLoadBalancersForNetwork(Network network, Scheme scheme) throws ResourceUnavailableException {
          List<LoadBalancerVO> lbs = _lbDao.listByNetworkIdAndScheme(network.getId(), scheme);
          if (logger.isDebugEnabled()) {
              logger.debug("Revoking {} {} load balancing rules for network {}", lbs.size(), scheme, network);
          }
          if (lbs != null) {
              for (LoadBalancerVO lb : lbs) { // called during restart, not persisting state in db
                  lb.setState(FirewallRule.State.Revoke);
              }
              return applyLoadBalancerRules(lbs, false); // called during restart, not persisting state in db
          } else {
              logger.info("Network {} doesn't have load balancer rules, nothing to revoke", network);
              return true;
          }
      }
  
      @Override
      public boolean applyLoadBalancersForNetwork(Network network, Scheme scheme) throws ResourceUnavailableException {
          List<LoadBalancerVO> lbs = _lbDao.listByNetworkIdAndScheme(network.getId(), scheme);
          if (lbs != null) {
              logger.debug("Applying load balancer rules of scheme {} in network {}", scheme, network);
              return applyLoadBalancerRules(lbs, true);
          } else {
              logger.info("Network {} doesn't have load balancer rules of scheme {}, nothing to apply", network, scheme);
              return true;
          }
      }
  
      protected boolean applyLbRules(Network network, List<LoadBalancingRule> rules) throws ResourceUnavailableException {
          boolean handled = false;
          for (LoadBalancingServiceProvider lbElement : _lbProviders) {
              Provider provider = lbElement.getProvider();
              boolean isLbProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Lb, provider);
              if (!isLbProvider) {
                  continue;
              }
              handled = lbElement.applyLBRules(network, rules);
              if (handled)
                  break;
          }
          return handled;
      }
  
      protected LoadBalancingRule getLoadBalancerRuleToApply(LoadBalancerVO lb) {
  
          List<LbStickinessPolicy> policyList = getStickinessPolicies(lb.getId());
          Ip sourceIp = getSourceIp(lb);
          LbSslCert sslCert = getLbSslCert(lb.getId());
          LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, null, policyList, null, sourceIp, sslCert, lb.getLbProtocol());
  
          if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(lb.getId())) {
              // Get the associated VmGroup
              AutoScaleVmGroupVO vmGroup = _autoScaleVmGroupDao.listByAll(lb.getId(), null).get(0);
              LbAutoScaleVmGroup lbAutoScaleVmGroup = getLbAutoScaleVmGroup(vmGroup, vmGroup.getState(), lb);
              loadBalancing.setAutoScaleVmGroup(lbAutoScaleVmGroup);
          } else {
              List<LbDestination> dstList = getExistingDestinations(lb.getId());
              loadBalancing.setDestinations(dstList);
              List<LbHealthCheckPolicy> hcPolicyList = getHealthCheckPolicies(lb.getId());
              loadBalancing.setHealthCheckPolicies(hcPolicyList);
          }
  
          return loadBalancing;
      }
  
      @DB
      protected boolean applyLoadBalancerRules(List<LoadBalancerVO> lbs, boolean updateRulesInDB) throws ResourceUnavailableException {
          List<LoadBalancingRule> rules = new ArrayList<LoadBalancingRule>();
          for (LoadBalancerVO lb : lbs) {
              rules.add(getLoadBalancerRuleToApply(lb));
          }
  
          if (!applyLbRules(rules, false)) {
              logger.debug("LB rules are not completely applied");
              return false;
          }
  
          if (updateRulesInDB) {
              for (final LoadBalancerVO lb : lbs) {
                  boolean checkForReleaseElasticIp = Transaction.execute(new TransactionCallback<Boolean>() {
                      @Override
                      public Boolean doInTransaction(TransactionStatus status) {
                          boolean checkForReleaseElasticIp = false;
  
                          if (lb.getState() == FirewallRule.State.Revoke) {
                              removeLBRule(lb);
                              logger.debug("LB {} is successfully removed", lb);
                              checkForReleaseElasticIp = true;
                          } else if (lb.getState() == FirewallRule.State.Add) {
                              lb.setState(FirewallRule.State.Active);
                              logger.debug("LB rule {} state is set to Active", lb);
                              _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());
                              _lb2VmMapDao.remove(lb.getId(), lbVmMap.getInstanceId(), lbVmMap.getInstanceIp(), null);
                              logger.debug("Load balancer rule {} is removed for vm {} instance ip {}",
                                      lb, lbVmMap.getInstanceId(), lbVmMap.getInstanceIp());;
                          }
  
  
                          if (_lb2VmMapDao.listByLoadBalancerId(lb.getId()).isEmpty()) {
                              lb.setState(FirewallRule.State.Add);
                              _lbDao.persist(lb);
                              logger.debug("LB rule {} state is set to Add as there are no more active LB-VM mappings", lb);
                          }
  
                          // remove LB-Stickiness policy mapping that were state to revoke
                          List<LBStickinessPolicyVO> stickinesspolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lb.getId(), true);
                          if (!stickinesspolicies.isEmpty()) {
                              _lb2stickinesspoliciesDao.remove(lb.getId(), true);
                              logger.debug("Load balancer rule {} is removed stickiness policies", lb);
                          }
  
                          // remove LB-HealthCheck policy mapping that were state to
                          // revoke
                          List<LBHealthCheckPolicyVO> healthCheckpolicies = _lb2healthcheckDao.listByLoadBalancerId(lb.getId(), true);
                          if (!healthCheckpolicies.isEmpty()) {
                              _lb2healthcheckDao.remove(lb.getId(), true);
                              logger.debug("Load balancer rule {} is removed health check monitors policies", lb);
                          }
  
                          LoadBalancerCertMapVO lbCertMap = _lbCertMapDao.findByLbRuleId(lb.getId());
                          if (lbCertMap != null && lbCertMap.isRevoke()) {
                              _lbCertMapDao.remove(lbCertMap.getId());
                              logger.debug("Load balancer rule {} removed certificate mapping", lb);
                          }
  
                          return checkForReleaseElasticIp;
                      }
                  });
  
                  if (checkForReleaseElasticIp && lb.getSourceIpAddressId() != null) {
                      boolean success = true;
                      long count = _firewallDao.countRulesByIpId(lb.getSourceIpAddressId());
                      if (count == 0) {
                          try {
                              success = handleSystemLBIpRelease(lb);
                          } catch (Exception ex) {
                              logger.warn("Failed to release system ip as a part of lb rule " + lb + " deletion due to exception ", ex);
                              success = false;
                          } finally {
                              if (!success) {
                                  logger.warn("Failed to release system ip as a part of lb rule " + lb + " deletion");
                              }
                          }
                      }
                  }
                  // if the rule is the last one for the ip address assigned to
                  // VPC, unassign it from the network
                  if (lb.getSourceIpAddressId() != null) {
                      _vpcMgr.unassignIPFromVpcNetwork(lb.getSourceIpAddressId(), lb.getNetworkId());
                  }
              }
          }
  
          return true;
      }
  
      protected boolean handleSystemLBIpRelease(LoadBalancerVO lb) {
          IpAddress ip = _ipAddressDao.findById(lb.getSourceIpAddressId());
          boolean success = true;
          if (ip.getSystem()) {
              logger.debug("Releasing system ip address {} as a part of delete lb rule", ip);
              if (!_ipAddrMgr.disassociatePublicIpAddress(ip, CallContext.current().getCallingUserId(), CallContext.current().getCallingAccount())) {
                  logger.warn("Unable to release system ip address={} as a part of delete lb rule", ip);
                  success = false;
              } else {
                  logger.warn("Successfully released system ip address={} as a part of delete lb rule", ip);
              }
          }
          return success;
      }
  
      @Override
      public boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId) {
  
          //Included revoked rules to remove the rules of ips which are in revoke state
          List<FirewallRuleVO> rules = _firewallDao.listByIpAndPurpose(ipId, Purpose.LoadBalancing);
  
          if (deleteRulesFails(caller, callerUserId, rules)) return false;
          return true;
      }
  
      private boolean deleteRulesFails(Account caller, long callerUserId, List<FirewallRuleVO> rules) {
          if (rules != null) {
              logger.debug("Found {} lb rules to cleanup", rules.size());
              for (FirewallRule rule : rules) {
                  if (deleteRuleFails(caller, callerUserId, rule)) return true;
              }
          }
          return false;
      }
  
      private boolean deleteRuleFails(Account caller, long callerUserId, FirewallRule rule) {
          boolean result = deleteLoadBalancerRule(rule.getId(), true, caller, callerUserId, false);
          if (result == false) {
              logger.warn("Unable to remove load balancer rule {}", rule);
              return true;
          }
          return false;
      }
  
      @Override
      public boolean removeAllLoadBalanacersForNetwork(long networkId, Account caller, long callerUserId) {
          List<FirewallRuleVO> rules = _firewallDao.listByNetworkAndPurposeAndNotRevoked(networkId, Purpose.LoadBalancing);
          if (deleteRulesFails(caller, callerUserId, rules)) return false;
          return true;
      }
  
      @Override
      public List<LbStickinessPolicy> getStickinessPolicies(long lbId) {
          List<LbStickinessPolicy> stickinessPolicies = new ArrayList<LbStickinessPolicy>();
          List<LBStickinessPolicyVO> sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerId(lbId, false);
  
          for (LBStickinessPolicyVO sDbPolicy : sDbpolicies) {
              LbStickinessPolicy sPolicy = new LbStickinessPolicy(sDbPolicy.getMethodName(), sDbPolicy.getParams(), sDbPolicy.isRevoke());
              stickinessPolicies.add(sPolicy);
          }
          return stickinessPolicies;
      }
  
      @Override
      public List<LbHealthCheckPolicy> getHealthCheckPolicies(long lbId) {
          List<LbHealthCheckPolicy> healthCheckPolicies = new ArrayList<LbHealthCheckPolicy>();
          List<LBHealthCheckPolicyVO> hcDbpolicies = _lb2healthcheckDao.listByLoadBalancerIdAndDisplayFlag(lbId, null);
  
          for (LBHealthCheckPolicyVO policy : hcDbpolicies) {
              String pingpath = policy.getpingpath();
              LbHealthCheckPolicy hDbPolicy =
                  new LbHealthCheckPolicy(pingpath, policy.getDescription(), policy.getResponseTime(), policy.getHealthcheckInterval(), policy.getHealthcheckThresshold(),
                      policy.getUnhealthThresshold(), policy.isRevoke());
              healthCheckPolicies.add(hDbPolicy);
          }
          return healthCheckPolicies;
      }
  
      @Override
      public List<LbDestination> getExistingDestinations(long lbId) {
          List<LbDestination> dstList = new ArrayList<LbDestination>();
          List<LoadBalancerVMMapVO> lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lbId);
          LoadBalancerVO lb = _lbDao.findById(lbId);
  
          String dstIp = null;
          for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) {
              UserVm vm = _vmDao.findById(lbVmMap.getInstanceId());
              Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vm.getId());
              dstIp = lbVmMap.getInstanceIp() == null ? nic.getIPv4Address(): lbVmMap.getInstanceIp();
              LbDestination lbDst = new LbDestination(lb.getDefaultPortStart(), lb.getDefaultPortEnd(), dstIp, lbVmMap.isRevoke());
              dstList.add(lbDst);
          }
          return dstList;
      }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_UPDATE, eventDescription = "updating load balancer", async = true)
      public LoadBalancer updateLoadBalancerRule(UpdateLoadBalancerRuleCmd cmd) {
          Account caller = CallContext.current().getCallingAccount();
          Long lbRuleId = cmd.getId();
          String name = cmd.getLoadBalancerName();
          String description = cmd.getDescription();
          String algorithm = cmd.getAlgorithm();
          LoadBalancerVO lb = _lbDao.findById(lbRuleId);
          LoadBalancerVO lbBackup = _lbDao.findById(lbRuleId);
          String customId = cmd.getCustomId();
          Boolean forDisplay = cmd.getDisplay();
          String lbProtocol = cmd.getLbProtocol();
  
          if (lb == null) {
              throw new InvalidParameterValueException("Unable to find lb rule by id=" + lbRuleId);
          }
  
          // check permissions
          _accountMgr.checkAccess(caller, null, true, lb);
  
          if (name != null) {
              lb.setName(name);
          }
  
          if (description != null) {
              lb.setDescription(description);
          }
  
          if (algorithm != null) {
              lb.setAlgorithm(algorithm);
          }
  
          if (customId != null) {
              lb.setUuid(customId);
          }
  
          if (forDisplay != null) {
              lb.setDisplay(forDisplay);
          }
  
          if (StringUtils.isNotBlank(lbProtocol)) {
              lb.setLbProtocol(lbProtocol);
          }
  
          validateInputsForExternalNetworkProvider(lb, algorithm, lbProtocol);
  
          List<String> cidrList = cmd.getCidrList();
  
          if (cidrList != null) {
              String cidrListStr = StringUtils.join(cidrList, ",");
              if (!cidrListStr.isEmpty() && !NetUtils.isValidCidrList(cidrListStr)) {
                  throw new InvalidParameterValueException("Invalid CIDR list: " + cidrListStr);
              }
              lb.setCidrList(cidrListStr);
          }
  
          // Validate rule in LB provider
          LoadBalancingRule rule = getLoadBalancerRuleToApply(lb);
          if (!validateLbRule(rule)) {
              throw new InvalidParameterValueException(String.format("Modifications in lb rule %s are not supported.", lb));
          }
  
          LoadBalancerVO tmplbVo = _lbDao.findById(lbRuleId);
          boolean success = _lbDao.update(lbRuleId, lb);
  
          // Check if algorithm, lb protocol, or cidrlist has changed, and reapply the lb config if needed
          boolean algorithmChanged = !Objects.equals(algorithm, tmplbVo.getAlgorithm());
          boolean protocolChanged = !Objects.equals(lbProtocol, tmplbVo.getLbProtocol());
          boolean cidrListChanged = !Objects.equals(tmplbVo.getCidrList(), lb.getCidrList());
  
          if (algorithmChanged || protocolChanged || cidrListChanged) {
              try {
                  lb.setState(FirewallRule.State.Add);
                  _lbDao.persist(lb);
                  applyLoadBalancerConfig(lbRuleId);
                  if (!lb.getLbProtocol().equals(NetUtils.SSL_PROTO)) {
                      removeCertMapIfExists(lb);
                  }
              } catch (ResourceUnavailableException e) {
                  if (isRollBackAllowedForProvider(lb)) {
                      /*
                       * NOTE : We use lb object to update db instead of lbBackup
                       * object since db layer will fail to update if there is no
                       * change in the object.
                       */
                      if (lbBackup.getName() != null) {
                          lb.setName(lbBackup.getName());
                      }
                      if (lbBackup.getDescription() != null) {
                          lb.setDescription(lbBackup.getDescription());
                      }
                      if (lbBackup.getAlgorithm() != null) {
                          lb.setAlgorithm(lbBackup.getAlgorithm());
                      }
  
                      lb.setCidrList(lbBackup.getCidrList());
                      lb.setState(lbBackup.getState());
                      _lbDao.update(lb.getId(), lb);
                      _lbDao.persist(lb);
  
                      logger.debug("LB Rollback rule: {} while updating LB rule.", lb);
                  }
                  logger.warn("Unable to apply the load balancer config because resource is unavailable.", e);
                  success = false;
              }
          }
  
          if (!success) {
              throw new CloudRuntimeException(String.format("Failed to update load balancer rule: %s", lb));
          }
  
          return lb;
      }
  
      private void validateInputsForExternalNetworkProvider(LoadBalancerVO lb, String algorithm, String protocol) {
          Network network = _networkDao.findById(lb.getNetworkId());
          if (_networkOfferingServiceDao.canProviderSupportServiceInNetworkOffering(network.getNetworkOfferingId(), Service.Lb, Provider.Netris)) {
              if (Objects.nonNull(algorithm)) {
                  throw new InvalidParameterValueException(String.format("Algorithm: %s specified for Netris Provider is not supported.", algorithm));
              }
              if (Objects.nonNull(protocol) && "tcp-proxy".equalsIgnoreCase(protocol)) {
                  throw new InvalidParameterValueException("TCP Proxy protocol is not supported for Netris Provider.");
              }
          }
      }
  
      private void removeCertMapIfExists(LoadBalancerVO lb) {
          LoadBalancerCertMapVO loadBalancerCertMapVO = _lbCertMapDao.findByLbRuleId(lb.getId());
          if (loadBalancerCertMapVO != null) {
              logger.debug("Removing SSL cert for load balancer %s as the new protocol is not ssl but %s", lb, lb.getLbProtocol());
              _lbCertMapDao.remove(loadBalancerCertMapVO.getId());
          }
      }
  
      @Override
      public Pair<List<? extends UserVm>, List<String>> listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws PermissionDeniedException {
          Account caller = CallContext.current().getCallingAccount();
          Long loadBalancerId = cmd.getId();
          Boolean applied = cmd.isApplied();
  
          if (applied == null) {
              logger.info(String.format("The [%s] parameter was not passed. Using the default value [%s].", ApiConstants.APPLIED, Boolean.TRUE));
              applied = Boolean.TRUE;
          }
  
          LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
          if (loadBalancer == null) {
              String msg = String.format("Unable to find the load balancer with ID [%s].", cmd.getId());
              logger.error(msg);
              throw new CloudRuntimeException(msg);
          }
  
          String loadBalancerAsString = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(loadBalancer, "uuid", "name");
  
          _accountMgr.checkAccess(caller, null, true, loadBalancer);
  
          List<UserVmVO> loadBalancerInstances = new ArrayList<>();
          List<String> serviceStates = new ArrayList<>();
          List<LoadBalancerVMMapVO> vmLoadBalancerMappings = _lb2VmMapDao.listByLoadBalancerId(loadBalancerId);
  
          if (vmLoadBalancerMappings == null) {
              String msg = String.format("Unable to find map of VMs related to load balancer [%s].", loadBalancerAsString);
              logger.error(msg);
              throw new CloudRuntimeException(msg);
          }
  
          Map<Long, String> vmServiceState = new HashMap<>(vmLoadBalancerMappings.size());
          List<Long> appliedInstanceIdList = new ArrayList<>();
  
          for (LoadBalancerVMMapVO vmLoadBalancerMapping : vmLoadBalancerMappings) {
              appliedInstanceIdList.add(vmLoadBalancerMapping.getInstanceId());
              vmServiceState.put(vmLoadBalancerMapping.getInstanceId(), vmLoadBalancerMapping.getState());
          }
  
          List<UserVmVO> userVms = _vmDao.listByIds(appliedInstanceIdList);
  
          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;
              }
  
              String userVmAsString = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(userVm, "uuid", "name");
  
              boolean isApplied = appliedInstanceIdList.contains(userVm.getId());
              String isAppliedMsg = isApplied ? "is applied" : "is not applied";
              logger.debug(String.format("The user VM [%s] %s to a rule of the load balancer [%s].", userVmAsString, isAppliedMsg, loadBalancerAsString));
  
              if (isApplied != applied) {
                  logger.debug(String.format("Skipping adding service state from the user VM [%s] to the service state list. This happens because the VM %s to the load "
                          + "balancer rule and the [%s] parameter was passed as [%s].", userVmAsString, isAppliedMsg, ApiConstants.APPLIED, applied));
                  continue;
              }
  
              loadBalancerInstances.add(userVm);
              String serviceState = vmServiceState.get(userVm.getId());
              logger.debug(String.format("Adding the service state [%s] from the user VM [%s] to the service state list.", serviceState, userVmAsString));
              serviceStates.add(serviceState);
          }
  
          return new Pair<>(loadBalancerInstances, serviceStates);
      }
  
      @Override
      public List<String> listLbVmIpAddress (long id, long vmId) {
  
          List <LoadBalancerVMMapVO> listLbvmMapVo = _lb2VmMapDao.listByLoadBalancerIdAndVmId(id, vmId);
  
          List <String> vmIps = new ArrayList<String>();
          for (LoadBalancerVMMapVO lbVmVo : listLbvmMapVo) {
              vmIps.add(lbVmVo.getInstanceIp());
          }
          return vmIps;
      }
  
      @Override
      public List<LbStickinessMethod> getStickinessMethods(long networkid) {
          String capability = getLBCapability(networkid, Capability.SupportedStickinessMethods.getName());
          if (capability == null) {
              return null;
          }
          Gson gson = new Gson();
          java.lang.reflect.Type listType = new TypeToken<List<LbStickinessMethod>>() {
          }.getType();
          List<LbStickinessMethod> result = gson.fromJson(capability, listType);
          return result;
      }
  
      @Override
      public List<LBStickinessPolicyVO> searchForLBStickinessPolicies(ListLBStickinessPoliciesCmd cmd) throws PermissionDeniedException {
          Account caller = CallContext.current().getCallingAccount();
          Long loadBalancerId = cmd.getLbRuleId();
          Long stickinessId = cmd.getId();
  
          boolean forDisplay = cmd.getDisplay();
          LoadBalancerVO loadBalancer = null;
  
          if (loadBalancerId == null) {
              loadBalancer = findLbByStickinessId(stickinessId);
          } else {
              loadBalancer = _lbDao.findById(loadBalancerId);
          }
  
          if (loadBalancer == null) {
              return null;
          }
  
          _accountMgr.checkAccess(caller, null, true, loadBalancer);
  
          List<LBStickinessPolicyVO> sDbpolicies = _lb2stickinesspoliciesDao.listByLoadBalancerIdAndDisplayFlag(loadBalancer.getId(), forDisplay);
  
          return sDbpolicies;
      }
  
      @Override
      public List<LBHealthCheckPolicyVO> searchForLBHealthCheckPolicies(ListLBHealthCheckPoliciesCmd cmd) throws PermissionDeniedException {
          Account caller = CallContext.current().getCallingAccount();
          Long loadBalancerId = cmd.getLbRuleId();
          Long policyId = cmd.getId();
          boolean forDisplay = cmd.getDisplay();
          if(loadBalancerId == null) {
              loadBalancerId = findLBIdByHealtCheckPolicyId(policyId);
          }
          LoadBalancerVO loadBalancer = _lbDao.findById(loadBalancerId);
          if (loadBalancer == null) {
              return null;
          }
  
          _accountMgr.checkAccess(caller, null, true, loadBalancer);
          List<LBHealthCheckPolicyVO> hcDbpolicies = _lb2healthcheckDao.listByLoadBalancerIdAndDisplayFlag(loadBalancerId, forDisplay);
  
          return hcDbpolicies;
      }
  
      @Override
      public Pair<List<? extends LoadBalancer>, Integer> searchForLoadBalancers(ListLoadBalancerRulesCmd cmd) {
          Long ipId = cmd.getPublicIpId();
          Long zoneId = cmd.getZoneId();
          Long id = cmd.getId();
          String name = cmd.getLoadBalancerRuleName();
          String keyword = cmd.getKeyword();
          Long instanceId = cmd.getVirtualMachineId();
          Long networkId = cmd.getNetworkId();
          Map<String, String> tags = cmd.getTags();
          Boolean forDisplay = cmd.getDisplay();
  
          Account caller = CallContext.current().getCallingAccount();
          List<Long> permittedAccounts = new ArrayList<Long>();
  
          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 searchFilter = new Filter(LoadBalancerVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
          SearchBuilder<LoadBalancerVO> sb = _lbDao.createSearchBuilder();
          _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
  
          sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
          sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
          sb.and("sourceIpAddress", sb.entity().getSourceIpAddressId(), SearchCriteria.Op.EQ);
          sb.and("networkId", sb.entity().getNetworkId(), SearchCriteria.Op.EQ);
          sb.and("scheme", sb.entity().getScheme(), SearchCriteria.Op.EQ);
          sb.and("display", sb.entity().isDisplay(), SearchCriteria.Op.EQ);
  
          if (instanceId != null) {
              SearchBuilder<LoadBalancerVMMapVO> lbVMSearch = _lb2VmMapDao.createSearchBuilder();
              lbVMSearch.and("instanceId", lbVMSearch.entity().getInstanceId(), SearchCriteria.Op.EQ);
              sb.join("lbVMSearch", lbVMSearch, sb.entity().getId(), lbVMSearch.entity().getLoadBalancerId(), JoinBuilder.JoinType.INNER);
          }
  
          if (zoneId != null) {
              SearchBuilder<IPAddressVO> ipSearch = _ipAddressDao.createSearchBuilder();
              ipSearch.and("zoneId", ipSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
              sb.join("ipSearch", ipSearch, sb.entity().getSourceIpAddressId(), ipSearch.entity().getId(), JoinBuilder.JoinType.INNER);
          }
  
          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<LoadBalancerVO> sc = sb.create();
          _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
  
          if (keyword != null) {
              SearchCriteria<LoadBalancerVO> ssc = _lbDao.createSearchCriteria();
              ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
              ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%");
              sc.addAnd("name", SearchCriteria.Op.SC, ssc);
          }
  
          if (name != null) {
              sc.setParameters("name", name);
          }
  
          if (id != null) {
              sc.setParameters("id", id);
          }
  
          if (ipId != null) {
              sc.setParameters("sourceIpAddress", ipId);
          }
  
          if (instanceId != null) {
              sc.setJoinParameters("lbVMSearch", "instanceId", instanceId);
          }
  
          if (zoneId != null) {
              sc.setJoinParameters("ipSearch", "zoneId", zoneId);
          }
  
          if (networkId != null) {
              sc.setParameters("networkId", networkId);
          }
  
          if (tags != null && !tags.isEmpty()) {
              int count = 0;
              sc.setJoinParameters("tagSearch", "resourceType", ResourceObjectType.LoadBalancer.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 (forDisplay != null) {
              sc.setParameters("display", forDisplay);
          }
  
          //list only Public load balancers using this command
          sc.setParameters("scheme", Scheme.Public);
  
          Pair<List<LoadBalancerVO>, Integer> result = _lbDao.searchAndCount(sc, searchFilter);
          return new Pair<List<? extends LoadBalancer>, Integer>(result.first(), result.second());
      }
  
      @Override
      public LoadBalancerVO findById(long lbId) {
          return _lbDao.findById(lbId);
      }
  
      @Override
      public LoadBalancerVO findLbByStickinessId(long stickinessPolicyId) {
          LBStickinessPolicyVO stickinessPolicy = _lb2stickinesspoliciesDao.findById(stickinessPolicyId);
  
          if (stickinessPolicy == null) {
              return null;
          }
          return _lbDao.findById(stickinessPolicy.getLoadBalancerId());
      }
  
      @Override
      public void removeLBRule(LoadBalancer rule) {
          // remove the rule
          _lbDao.remove(rule.getId());
      }
  
      public boolean applyLbRules(List<LoadBalancingRule> rules, boolean continueOnError) throws ResourceUnavailableException {
          if (rules == null || rules.size() == 0) {
              logger.debug("There are no Load Balancing Rules to forward to the network elements");
              return true;
          }
  
          boolean success = true;
          Network network = _networkModel.getNetwork(rules.get(0).getNetworkId());
          List<PublicIp> publicIps = new ArrayList<PublicIp>();
  
          // get the list of public ip's owned by the network
          List<IPAddressVO> userIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null);
          if (userIps != null && !userIps.isEmpty()) {
              for (IPAddressVO userIp : userIps) {
                  PublicIp publicIp = PublicIp.createFromAddrAndVlan(userIp, _vlanDao.findById(userIp.getVlanId()));
                  publicIps.add(publicIp);
              }
          }
  
          // rules can not programmed unless IP is associated with network
          // service provider, so run IP assoication for
          // the network so as to ensure IP is associated before applying
          // rules (in add state)
          _ipAddrMgr.applyIpAssociations(network, false, continueOnError, publicIps);
  
          try {
              applyLbRules(network, rules);
          } catch (ResourceUnavailableException e) {
              if (!continueOnError) {
                  throw e;
              }
              logger.warn("Problems with applying load balancing rules but pushing on", e);
              success = false;
          }
  
          // if all the rules configured on public IP are revoked then
          // dis-associate IP with network service provider
          _ipAddrMgr.applyIpAssociations(network, true, continueOnError, publicIps);
  
          return success;
      }
  
      @Override
      public Map<Ip, UserVm> getLbInstances(long lbId) {
          Map<Ip, UserVm> dstList = new HashMap<Ip, UserVm>();
          List<LoadBalancerVMMapVO> lbVmMaps = _lb2VmMapDao.listByLoadBalancerId(lbId);
          LoadBalancerVO lb = _lbDao.findById(lbId);
  
          for (LoadBalancerVMMapVO lbVmMap : lbVmMaps) {
              UserVm vm = _vmDao.findById(lbVmMap.getInstanceId());
              Nic nic = _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(lb.getNetworkId(), vm.getId());
              Ip ip = new Ip(nic.getIPv4Address());
              dstList.put(ip, vm);
          }
          return dstList;
      }
  
      @Override
      public boolean isLbRuleMappedToVmGuestIp(String vmSecondaryIp) {
          List<LoadBalancerVMMapVO> lbVmMap = _lb2VmMapDao.listByInstanceIp(vmSecondaryIp);
          if (lbVmMap == null || lbVmMap.isEmpty()) {
              return  false;
          }
          return true;
      }
  
      @Override
      public void isLbServiceSupportedInNetwork(long networkId, Scheme scheme) {
          Network network = _networkDao.findById(networkId);
  
          //1) Check if the LB service is supported
          if (!_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Lb)) {
              InvalidParameterValueException ex = new InvalidParameterValueException("LB service is not supported in specified network id");
              ex.addProxyObject(network.getUuid(), "networkId");
              throw ex;
          }
  
          //2) Check if the Scheme is supported\
          NetworkOffering off = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId());
          if (scheme == Scheme.Public) {
              if (!off.isPublicLb()) {
                  throw new InvalidParameterValueException("Scheme " + scheme + " is not supported by the network offering " + off);
              }
          } else {
              if (!off.isInternalLb()) {
                  throw new InvalidParameterValueException("Scheme " + scheme + " is not supported by the network offering " + off);
              }
          }
  
          //3) Check if the provider supports the scheme
          LoadBalancingServiceProvider lbProvider = _networkMgr.getLoadBalancingProviderForNetwork(network, scheme);
          if (lbProvider == null) {
              throw new InvalidParameterValueException("Lb rule with scheme " + scheme.toString() + " is not supported by lb providers in network " + network);
          }
      }
  
      public List<LoadBalancingServiceProvider> getLbProviders() {
          return _lbProviders;
      }
  
      @Inject
      public void setLbProviders(List<LoadBalancingServiceProvider> lbProviders) {
          this._lbProviders = lbProviders;
      }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_LB_STICKINESSPOLICY_UPDATE, eventDescription = "updating lb stickiness policy", async = true)
      public StickinessPolicy updateLBStickinessPolicy(long id, String customId, Boolean forDisplay) {
          LBStickinessPolicyVO policy = _lb2stickinesspoliciesDao.findById(id);
          if (policy == null) {
              throw new InvalidParameterValueException("Fail to find stickiness policy with " + id);
          }
  
          LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(policy.getLoadBalancerId()));
          if (loadBalancer == null) {
              throw new InvalidParameterValueException(String.format("Invalid Load balancer: %d for Stickiness policy: %s", policy.getLoadBalancerId(), policy));
          }
  
          _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, loadBalancer);
  
          if (customId != null) {
              policy.setUuid(customId);
          }
  
          if (forDisplay != null) {
              policy.setDisplay(forDisplay);
          }
  
          _lb2stickinesspoliciesDao.update(id, policy);
          return _lb2stickinesspoliciesDao.findById(id);
      }
  
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_LB_HEALTHCHECKPOLICY_UPDATE, eventDescription = "updating lb healthcheck policy", async = true)
      public HealthCheckPolicy updateLBHealthCheckPolicy(long id, String customId, Boolean forDisplay) {
          LBHealthCheckPolicyVO policy = _lb2healthcheckDao.findById(id);
          if (policy == null) {
              throw new InvalidParameterValueException("Fail to find stickiness policy with " + id);
          }
  
          LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(policy.getLoadBalancerId()));
          if (loadBalancer == null) {
              throw new InvalidParameterValueException(String.format("Invalid Load balancer : %d for Stickiness policy: %s", policy.getLoadBalancerId(), policy));
          }
  
          _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, loadBalancer);
  
          if (customId != null) {
              policy.setUuid(customId);
          }
  
          if (forDisplay != null) {
              policy.setDisplay(forDisplay);
          }
  
          _lb2healthcheckDao.update(id, policy);
          return _lb2healthcheckDao.findById(id);
      }
  
      @Override
      public Long findLBIdByHealtCheckPolicyId(long lbHealthCheckPolicy) {
          LBHealthCheckPolicyVO policy= _lb2healthcheckDao.findById(lbHealthCheckPolicy);
          if(policy != null) {
              return policy.getLoadBalancerId();
          }
          return null;
      }
  }
  