You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by GitBox <gi...@apache.org> on 2021/06/29 19:18:16 UTC

[GitHub] [cloudstack] GutoVeronezi commented on a change in pull request #4141: [New feature] Load balancer customization (haproxy-based)

GutoVeronezi commented on a change in pull request #4141:
URL: https://github.com/apache/cloudstack/pull/4141#discussion_r660776060



##########
File path: api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java
##########
@@ -179,6 +182,22 @@ public boolean isInline() {
         return inline;
     }
 
+    public LoadBalancerConfigTO[] getLbConfigs() {
+        return this.lbConfigs;
+    }
+
+    public void setLbConfigs(List<? extends LoadBalancerConfig> lbConfigs) {
+        if (lbConfigs == null || lbConfigs.size() == 0) {

Review comment:
       We could use `org.apache.commons.lang3.ArrayUtils` here:
   
   ```java
   ...
   if (ArrayUtils.isEmpty(lbConfigs)) {
   ...
   ```

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java
##########
@@ -103,4 +110,32 @@ public Long getCertId() {
     public Long getLbRuleId() {
         return lbRuleId;
     }
+
+    public boolean isForced() {
+        return (forced != null) ? forced : false;

Review comment:
       We could use `org.apache.commons.lang3.BooleanUtils` here:
   
   ```java
   ...
   return BooleanUtils.toBoolean(forced);
   ...
   ```

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java
##########
@@ -103,4 +110,32 @@ public Long getCertId() {
     public Long getLbRuleId() {
         return lbRuleId;
     }
+
+    public boolean isForced() {
+        return (forced != null) ? forced : false;
+    }
+
+    @Override
+    public ApiCommandJobType getInstanceType() {
+        return ApiCommandJobType.LoadBalancerRule;
+    }
+
+    @Override
+    public Long getInstanceId() {
+        return lbRuleId;
+    }
+
+    @Override
+    public String getSyncObjType() {
+        return BaseAsyncCmd.networkSyncObject;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId());
+        if (lb == null) {
+            return null;
+        }
+        return lb.getNetworkId();

Review comment:
       We could implement a ternary here.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,216 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.LoadBalancer;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "createLoadBalancerConfig", description = "Creates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class CreateLoadBalancerConfigCmd extends BaseAsyncCreateCmd {
+    public static final Logger LOGGER = Logger.getLogger(CreateLoadBalancerConfigCmd.class.getName());
+
+    private static final String s_name = "createloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.NAME,
+               type = CommandType.STRING,
+               required = true,
+               description = "name of the load balancer config")
+    private String name;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    @Parameter(name = ApiConstants.FORCED,
+               type = CommandType.BOOLEAN,
+               required = false,
+               description = "Force add a load balancer config. Existing config will be removed")
+    private Boolean forced;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public boolean isForced() {
+        return (forced != null) ? forced : false;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+
+        LoadBalancerConfig config = null;
+        try {
+            config = _entityMgr.findById(LoadBalancerConfig.class, getEntityId());

Review comment:
       As `config` is not referenced outside the try block, we could declare it inside.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveCertFromLoadBalancerCmd.java
##########
@@ -90,4 +91,28 @@ public long getEntityOwnerId() {
     public Long getLbRuleId() {
         return this.lbRuleId;
     }
+
+    @Override
+    public ApiCommandJobType getInstanceType() {
+        return ApiCommandJobType.LoadBalancerRule;
+    }
+
+    @Override
+    public Long getInstanceId() {
+        return lbRuleId;
+    }
+
+    @Override
+    public String getSyncObjType() {
+        return BaseAsyncCmd.networkSyncObject;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId());
+        if (lb == null) {
+            return null;
+        }
+        return lb.getNetworkId();

Review comment:
       We could implement a ternary here.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,148 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "deleteLoadBalancerConfig", description = "Deletes a load balancer config.",
+        responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class DeleteLoadBalancerConfigCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(DeleteLoadBalancerConfigCmd.class.getName());
+    private static final String s_name = "deleteloadbalancerconfigresponse";
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+               type = CommandType.UUID,
+               entityType = LoadBalancerConfigResponse.class,
+               required = true,
+               description = "the ID of the load balancer config")
+    private Long id;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        boolean result = _lbConfigService.deleteLoadBalancerConfig(this);
+
+        if (result) {
+            SuccessResponse response = new SuccessResponse(getCommandName());
+            this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete load balancer config");
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (config.getVpcId() != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return config.getNetworkId();
+        } else if (config.getVpcId() != null) {
+            return config.getVpcId();
+        }
+        return null;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config != null) {
+            if (config.getNetworkId() != null) {
+                Network network = _entityMgr.findById(Network.class, config.getNetworkId());
+                if (network != null) {
+                    return network.getAccountId();
+                }
+            } else if (config.getVpcId() != null) {
+                Vpc vpc = _entityMgr.findById(Vpc.class, config.getVpcId());
+                if (vpc != null) {
+                    return vpc.getAccountId();
+                }
+            } else if (config.getLoadBalancerId() != null) {
+                FirewallRule rule = _entityMgr.findById(FirewallRule.class, config.getLoadBalancerId());
+                if (rule != null) {
+                    return rule.getAccountId();
+                }
+            }

Review comment:
       We could add this code to the helper that I mentioned before.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,156 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "updateLoadBalancerConfig", description = "Updates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class UpdateLoadBalancerConfigCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(UpdateLoadBalancerConfigCmd.class.getName());
+    private static final String s_name = "updateloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+               type = CommandType.UUID,
+               entityType = LoadBalancerConfigResponse.class,
+               required = true,
+               description = "the ID of the load balancer config to update")
+    private Long id;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        LoadBalancerConfig result = _lbConfigService.updateLoadBalancerConfig(this);
+        if (result != null) {
+            LoadBalancerConfigResponse response = _responseGenerator.createLoadBalancerConfigResponse(result);
+            response.setResponseName(getCommandName());
+            this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update load balancer config");
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (config.getVpcId() != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return config.getNetworkId();
+        } else if (config.getVpcId() != null) {
+            return config.getVpcId();
+        }
+        return null;

Review comment:
       We could add this code to the helper that I mentioned before.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,156 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "updateLoadBalancerConfig", description = "Updates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class UpdateLoadBalancerConfigCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(UpdateLoadBalancerConfigCmd.class.getName());
+    private static final String s_name = "updateloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+               type = CommandType.UUID,
+               entityType = LoadBalancerConfigResponse.class,
+               required = true,
+               description = "the ID of the load balancer config to update")
+    private Long id;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        LoadBalancerConfig result = _lbConfigService.updateLoadBalancerConfig(this);
+        if (result != null) {
+            LoadBalancerConfigResponse response = _responseGenerator.createLoadBalancerConfigResponse(result);
+            response.setResponseName(getCommandName());
+            this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update load balancer config");
+        }

Review comment:
       We could invert the logic here to reduce block complexity:
   
   ```java
   ...
   if (result == null){
     throw...
   }
   
   ...
   ``` 

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,156 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "updateLoadBalancerConfig", description = "Updates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class UpdateLoadBalancerConfigCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(UpdateLoadBalancerConfigCmd.class.getName());
+    private static final String s_name = "updateloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+               type = CommandType.UUID,
+               entityType = LoadBalancerConfigResponse.class,
+               required = true,
+               description = "the ID of the load balancer config to update")
+    private Long id;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        LoadBalancerConfig result = _lbConfigService.updateLoadBalancerConfig(this);
+        if (result != null) {
+            LoadBalancerConfigResponse response = _responseGenerator.createLoadBalancerConfigResponse(result);
+            response.setResponseName(getCommandName());
+            this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update load balancer config");
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (config.getVpcId() != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return config.getNetworkId();
+        } else if (config.getVpcId() != null) {
+            return config.getVpcId();
+        }
+        return null;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config != null) {
+            if (config.getNetworkId() != null) {
+                Network network = _entityMgr.findById(Network.class, config.getNetworkId());
+                if (network != null) {
+                    return network.getAccountId();
+                }
+            } else if (config.getVpcId() != null) {
+                Vpc vpc = _entityMgr.findById(Vpc.class, config.getVpcId());
+                if (vpc != null) {
+                    return vpc.getAccountId();
+                }
+            } else if (config.getLoadBalancerId() != null) {
+                FirewallRule rule = _entityMgr.findById(FirewallRule.class, config.getLoadBalancerId());
+                if (rule != null) {
+                    return rule.getAccountId();
+                }
+            }

Review comment:
       We could add this code to the helper that I mentioned before.

##########
File path: core/src/main/java/com/cloud/network/HAProxyConfigurator.java
##########
@@ -531,42 +654,111 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
                 }
             }
             if (httpbasedStickiness) {
-                result.addAll(dstWithCookieSubRule);
+                backendConfigs.addAll(dstWithCookieSubRule);
             } else {
-                result.addAll(dstSubRule);
+                backendConfigs.addAll(dstSubRule);
             }
-            result.add(stickinessSubRule);
+            backendConfigs.add(stickinessSubRule);
         } else {
-            result.addAll(dstSubRule);
+            backendConfigs.addAll(dstSubRule);
         }
         if (stickinessSubRule != null && !destsAvailable) {
             s_logger.warn("Haproxy stickiness policy for lb rule: " + lbTO.getSrcIp() + ":" + lbTO.getSrcPort() + ": Not Applied, cause:  backends are unavailable");
         }
-        if (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled || httpbasedStickiness) {
+        boolean http = false;
+        String cfgLbHttp = lbConfigsMap.get(LoadBalancerConfigKey.LbHttp.key());
+        if (publicPort == NetUtils.HTTP_PORT && cfgLbHttp == null) {
+            http = true;
+        } else if (cfgLbHttp != null && cfgLbHttp.equalsIgnoreCase("true")) {
+            http = true;
+        }
+
+        boolean keepAliveEnabled = lbCmd.keepAliveEnabled;
+        String cfgLbHttpKeepalive = lbConfigsMap.get(LoadBalancerConfigKey.LbHttpKeepalive.key());
+        if (cfgLbHttpKeepalive != null && cfgLbHttpKeepalive.equalsIgnoreCase("true")) {
+            keepAliveEnabled = true;
+        } else if (cfgLbHttpKeepalive != null && cfgLbHttpKeepalive.equalsIgnoreCase("false")) {
+            keepAliveEnabled = false;
+        }
+
+        if (http || httpbasedStickiness || sslOffloading) {
             sb = new StringBuilder();
             sb.append("\t").append("mode http");
+            frontendConfigs.add(sb.toString());
+            backendConfigsForHttp.add(sb.toString());
+            if (keepAliveEnabled) {
+                sb = new StringBuilder();
+                sb.append("\t").append("no option forceclose");
+                frontendConfigs.add(sb.toString());
+                backendConfigsForHttp.add(sb.toString());
+            } else {
+                sb = new StringBuilder();
+                sb.append("\t").append("option httpclose");
+                frontendConfigs.add(sb.toString());
+                backendConfigsForHttp.add(sb.toString());
+            }
+        }
+
+        if (isTransparent) {
+            sb = new StringBuilder();
+            sb.append("frontend ").append(poolName);
             result.add(sb.toString());
+            result.addAll(frontendConfigs);
             sb = new StringBuilder();
-            sb.append("\t").append("option httpclose");
+            sb.append("\tacl local_subnet src ").append(lbCmd.getNetworkCidr());
+            sb.append("\n\tuse_backend ").append(poolName).append("-backend-local if local_subnet");
+            sb.append("\n\tdefault_backend ").append(poolName).append("-backend");
+            sb.append("\n\n");
+            sb.append("backend ").append(poolName).append("-backend");
+            result.add(sb.toString());
+            result.addAll(backendConfigsForHttp);
+            result.addAll(backendConfigs);
+            sb = new StringBuilder();
+            sb.append("\t").append("source 0.0.0.0 usesrc clientip");
+            sb.append("\n\n");

Review comment:
       Instead of using `StringBuilder`, we could use `String.format`, it is more clean and readable.

##########
File path: api/src/main/java/org/apache/cloudstack/network/lb/LoadBalancerConfigKey.java
##########
@@ -0,0 +1,221 @@
+// 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 org.apache.cloudstack.network.lb;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.SSLConfiguration;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.cloud.utils.Pair;
+
+public enum LoadBalancerConfigKey {
+
+    LbStatsEnable(Category.Stats, "lb.stats.enable", "LB stats enable", Boolean.class, "true", "Enable statistics reporting with default settings, default is 'true'", Scope.Network, Scope.Vpc),
+
+    LbStatsUri(Category.Stats, "lb.stats.uri", "LB stats URI", String.class, "/admin?stats", "Enable statistics and define the URI prefix to access them, default is '/admin?stats'", Scope.Network, Scope.Vpc),
+
+    LbStatsAuth(Category.Stats, "lb.stats.auth", "LB stats auth", String.class, "admin1:AdMiN123", "Enable statistics with authentication and grant access to an account, default is 'admin1:AdMiN123'", Scope.Network, Scope.Vpc),
+
+    GlobalStatsSocket(Category.Stats, "global.stats.socket", "Stats socket enabled/disabled", Boolean.class, "false", "Binds a UNIX socket to /var/run/haproxy.socket, default is 'false'", Scope.Network, Scope.Vpc),
+
+    LbTimeoutConnect(Category.General, "lb.timeout.connect", "Maximum time (in ms) to wait for a connection to succeed", Long.class, "5000", "Set the maximum time to wait for a connection attempt to a server to succeed.", Scope.Network, Scope.Vpc, Scope.LoadBalancerRule),
+
+    LbTimeoutServer(Category.General, "lb.timeout.server", "Maximum inactivity time (in ms) on server side", Long.class, "50000", "Set the maximum inactivity time on the server side.", Scope.Network, Scope.Vpc, Scope.LoadBalancerRule),
+
+    LbTimeoutClient(Category.General, "lb.timeout.client", "Maximum inactivity time (in ms) on client side", Long.class, "50000", "Set the maximum inactivity time on the client side.", Scope.Network, Scope.Vpc, Scope.LoadBalancerRule),
+
+    LbHttp(Category.LoadBalancer, "lb.http", "LB http enabled/disabled", Boolean.class, "true for port 80; false for other ports", "If LB is http, default is 'true' for port 80 and 'false' for others'", Scope.LoadBalancerRule),
+
+    LbHttp2(Category.LoadBalancer, "lb.http2", "Enable/disable HTTP2 support", Boolean.class, "false", "Enable or disable HTTP2 support in HAproxy", Scope.LoadBalancerRule),
+
+    LbHttpKeepalive(Category.LoadBalancer, "lb.http.keepalive", "LB http keepalive enabled/disabled", Boolean.class, "<Inherited from network offering>", "Enable or disable HTTP keep-alive, default is inherited from network offering", Scope.LoadBalancerRule),
+
+    LbBackendHttps(Category.LoadBalancer, "lb.backend.https", "If backend server is https", Boolean.class, "false", "If backend server is https. If yes, use 'check ssl verify none' instead of 'check'", Scope.LoadBalancerRule),
+
+    LbTransparent(Category.LoadBalancer, "lb.transparent.mode", "LB transparent mode enabled/disabled", Boolean.class, "false", "Enable or disable transparent mode, default is 'false'", Scope.LoadBalancerRule),
+
+    GlobalMaxConn(Category.LoadBalancer, "global.maxconn", "LB max connection", Long.class, "4096", "Maximum per process number of concurrent connections, default is '4096'", Scope.Network, Scope.Vpc),
+
+    GlobalMaxPipes(Category.LoadBalancer, "global.maxpipes", "LB max pipes", Long.class, "<global.maxconn/4>", "Maximum number of per process pipes, default is 'maxconn/4'", Scope.Network, Scope.Vpc),
+
+    LbMaxConn(Category.LoadBalancer, "lb.maxconn", "LB max connection", Long.class, "<2000 in haproxy>", "Maximum per process number of concurrent connections per site/vm", Scope.LoadBalancerRule),
+
+    LbFullConn(Category.LoadBalancer, "lb.fullconn", "LB full connection", Long.class, "<maxconn/10 in haproxy>", "Specify at what backend load the servers will reach their maxconn, default is 'maxconn/10'", Scope.LoadBalancerRule),
+
+    LbServerMaxConn(Category.LoadBalancer, "lb.server.maxconn", "LB max connection per server", Long.class, "<0 means unlimited in haproxy>", "LB max connection per server, default is ''", Scope.LoadBalancerRule),
+
+    LbServerMinConn(Category.LoadBalancer, "lb.server.minconn", "LB minimum connection per server", Long.class, "", "LB minimum connection per server, default is ''", Scope.LoadBalancerRule),
+
+    LbServerMaxQueue(Category.LoadBalancer, "lb.server.maxqueue", "Max conn wait in queue per server", Long.class, "<0 means unlimited in haproxy>", "Maximum number of connections which will wait in queue for this server, default is ''", Scope.LoadBalancerRule),
+
+    LbSslConfiguration(Category.LoadBalancer, "lb.ssl.configuration", "SSL configuration, could be 'none', 'old' or 'intermediate'", String.class, "Inherited from global setting" , "if 'none', no SSL configurations will be added, if 'old', refer to https://ssl-config.mozilla.org/#server=haproxy&server-version=1.8.17&config=old&openssl-version=1.0.2l if 'intermediate', refer to https://ssl-config.mozilla.org/#server=haproxy&server-version=1.8.17&config=intermediate&openssl-version=1.0.2l default value is 'none'", Scope.LoadBalancerRule);
+
+    public static enum Category {
+        General, Advanced, Stats, LoadBalancer
+    }
+
+    private final Category _category;
+    private final Scope[] _scope;
+    private final String _key;
+    private final String _displayText;
+    private final String _description;
+    private final Class<?> _type;
+    private final String _defaultValue;
+
+    private LoadBalancerConfigKey(Category category, String key, String displayText, Class<?> type, String defaultValue, String description, Scope... scope) {
+        _category = category;
+        _scope = scope;
+        _key = key;
+        _displayText = displayText;
+        _type = type;
+        _defaultValue = defaultValue;
+        _description = description;
+    }
+
+    public Category category() {
+        return _category;
+    }
+
+    public Class<?> type() {
+        return _type;
+    }
+
+    public String key() {
+        return _key;
+    }
+
+    public String displayText() {
+        return _displayText;
+    }
+
+    public String defaultValue() {
+        return _defaultValue;
+    }
+
+    public String description() {
+        return _description;
+    }
+
+    public Scope[] scope() {
+        return _scope;
+    }
+
+    @Override
+    public String toString() {
+        return _key;
+    }
+
+    private static final HashMap<Scope, Map<String, LoadBalancerConfigKey>> Configs = new HashMap<Scope, Map<String, LoadBalancerConfigKey>>();
+    static {
+        Configs.put(Scope.Network, new LinkedHashMap<String, LoadBalancerConfigKey>());
+        Configs.put(Scope.Vpc, new LinkedHashMap<String, LoadBalancerConfigKey>());
+        Configs.put(Scope.LoadBalancerRule, new LinkedHashMap<String, LoadBalancerConfigKey>());
+        for (LoadBalancerConfigKey c : LoadBalancerConfigKey.values()) {
+            Scope[] scopes = c.scope();
+            for (Scope scope : scopes) {
+                Map<String, LoadBalancerConfigKey> currentConfigs = Configs.get(scope);
+                currentConfigs.put(c.key(), c);
+                Configs.put(scope, currentConfigs);
+            }
+        }
+    }
+
+    public static Map<String, LoadBalancerConfigKey> getConfigsByScope(Scope scope) {
+        return Configs.get(scope);
+    }
+
+    public static LoadBalancerConfigKey getConfigsByScopeAndName(Scope scope, String name) {
+        Map<String, LoadBalancerConfigKey> configs = Configs.get(scope);
+        if (configs.keySet().contains(name)) {
+            return configs.get(name);
+        }
+        return null;
+    }
+
+    public static Scope getScopeFromString(String scope) {
+        if (scope == null) {
+            return null;
+        }
+        for (Scope offScope : Scope.values()) {
+            if (offScope.name().equalsIgnoreCase(scope)) {
+                return offScope;
+            }
+        }
+        return null;
+    }
+
+    public static Pair<LoadBalancerConfigKey, String> validate(Scope scope, String key, String value) {
+        Map<String, LoadBalancerConfigKey> configs = Configs.get(scope);
+        if (configs == null) {
+            return new Pair<LoadBalancerConfigKey, String>(null, "Invalid scope " + scope);
+        }
+        LoadBalancerConfigKey config = null;
+        for (LoadBalancerConfigKey c : configs.values()) {
+            if (c.key().equals(key)) {
+                config = c;
+                break;
+            }
+        }
+        if (config == null) {
+            return new Pair<LoadBalancerConfigKey, String>(null, "Invalid key " + key);
+        }
+        if (value == null) {
+            return new Pair<LoadBalancerConfigKey, String>(null, "Invalid null value for parameter " + key);
+        }
+        Class<?> type = config.type();
+        String errMsg = null;
+        try {
+            if (type.equals(Integer.class)) {
+                errMsg = "Please enter a valid integer value for parameter " + key;
+                Integer.parseInt(value);
+            } else if (type.equals(Float.class)) {
+                errMsg = "Please enter a valid float value for parameter " + key;
+                Float.parseFloat(value);
+            } else if (type.equals(Long.class)) {
+                errMsg = "Please enter a valid long value for parameter " + key;
+                Long.parseLong(value);
+            }
+        } catch (final Exception e) {
+            // catching generic exception as some throws NullPointerException and some throws NumberFormatExcpeion

Review comment:
       As they are known, we could catch them, instead of catch them all.

##########
File path: server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
##########
@@ -1748,7 +1816,22 @@ private void updateWithLbRules(final DomainRouterJoinVO routerJoinVO, final Stri
                             .append(",protocol=").append(appLoadBalancerVO.getLbProtocol());
                 }
                 loadBalancingData.append(",stickiness=").append(getStickinessPolicies(firewallRuleVO.getId()));
-                loadBalancingData.append(",keepAliveEnabled=").append(offering.isKeepAliveEnabled()).append(",vmIps=");
+                if (isHttp != null) {
+                    loadBalancingData.append(",http=").append(isHttp);
+                } else if (firewallRuleVO.getSourcePortStart() == NetUtils.HTTP_PORT) {
+                    loadBalancingData.append(",http=").append(true);
+                }
+                if (isHttpKeepalive != null) {
+                    loadBalancingData.append(",keepAliveEnabled=").append(isHttpKeepalive);
+                } else {
+                    loadBalancingData.append(",keepAliveEnabled=").append(offering.isKeepAliveEnabled());
+                }
+                if (isTransparent != null) {
+                    loadBalancingData.append(",transparent=").append(isTransparent);
+                }

Review comment:
       This code seems repeated, but changing parameters; We could extract it to a method.

##########
File path: server/src/main/java/org/apache/cloudstack/network/lb/LoadBalancerConfigManagerImpl.java
##########
@@ -0,0 +1,396 @@
+// 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 org.apache.cloudstack.network.lb;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.Network.Provider;
+import com.cloud.network.Network.Service;
+import com.cloud.network.dao.LoadBalancerConfigDao;
+import com.cloud.network.dao.LoadBalancerConfigVO;
+import com.cloud.network.dao.LoadBalancerDao;
+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.lb.LoadBalancingRulesManager;
+import com.cloud.network.rules.LoadBalancerContainer.Scheme;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerConfigCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.DeleteLoadBalancerConfigCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerConfigsCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.ReplaceLoadBalancerConfigsCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerConfigCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+public class LoadBalancerConfigManagerImpl extends ManagerBase implements LoadBalancerConfigService, LoadBalancerConfigManager {
+    private static final Logger LOGGER = Logger.getLogger(LoadBalancerConfigManagerImpl.class);
+
+    @Inject
+    LoadBalancerConfigDao _lbConfigDao;
+    @Inject
+    NetworkDao _networkDao;
+    @Inject
+    VpcDao _vpcDao;
+    @Inject
+    LoadBalancerDao _lbDao;
+    @Inject
+    AccountManager _accountMgr;
+    @Inject
+    LoadBalancingRulesManager _lbMgr;
+    @Inject
+    NetworkServiceMapDao _ntwkSrvcDao;
+
+    @Override
+    public List<? extends LoadBalancerConfig> searchForLoadBalancerConfigs(ListLoadBalancerConfigsCmd cmd) {
+        Long id = cmd.getId();
+        String scopeStr = cmd.getScope();
+        Long networkId = cmd.getNetworkId();
+        Long vpcId = cmd.getVpcId();
+        Long loadBalancerId = cmd.getLoadBalancerId();
+        String name = cmd.getName();
+
+        if (id == null && scopeStr == null) {
+            throw new InvalidParameterValueException("At least one of id/scope is required");
+        }
+
+        //validate parameters
+        Scope scope = null;
+        if (scopeStr != null) {
+            scope = LoadBalancerConfigKey.getScopeFromString(scopeStr);
+            if (scope == null) {
+                throw new InvalidParameterValueException("Invalid scope " + scopeStr);
+            }
+            checkPermission(scope, networkId, vpcId, loadBalancerId, cmd.listAll());
+        }
+
+        if (id != null) {
+            LoadBalancerConfigVO config = _lbConfigDao.findById(id);
+            if (config == null) {
+                throw new InvalidParameterValueException("Cannot find load balancer config by id " + id);
+            }
+            checkPermission(config);
+        }
+
+        if (cmd.listAll()) {
+            if (id != null || name != null) {
+                throw new InvalidParameterValueException("id and name must be null if listall is true");
+            }
+        }
+
+        SearchCriteria<LoadBalancerConfigVO> sc = _lbConfigDao.createSearchCriteria();
+        if (id != null) {
+            sc.addAnd("id", SearchCriteria.Op.EQ, id);
+        }
+        if (scope != null) {
+            sc.addAnd("scope", SearchCriteria.Op.EQ, scope);
+        }
+        if (networkId != null) {
+            sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId);
+        }
+        if (vpcId != null) {
+            sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId);
+        }
+        if (loadBalancerId != null) {
+            sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
+        }
+        if (name != null) {
+            sc.addAnd("name", SearchCriteria.Op.EQ, name);
+        }
+        List<LoadBalancerConfigVO> configs = new ArrayList<LoadBalancerConfigVO>();
+        if (id != null || networkId != null || vpcId != null || loadBalancerId != null) {

Review comment:
       We could use `org.apache.commons.lang3.ObjectUtils` here:
   
   ```java
   ...
   if (ObjectUtils.anyNotNull(id, networkId...
   ...
   ```

##########
File path: server/src/main/java/org/apache/cloudstack/network/lb/LoadBalancerConfigManagerImpl.java
##########
@@ -0,0 +1,396 @@
+// 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 org.apache.cloudstack.network.lb;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.Network.Provider;
+import com.cloud.network.Network.Service;
+import com.cloud.network.dao.LoadBalancerConfigDao;
+import com.cloud.network.dao.LoadBalancerConfigVO;
+import com.cloud.network.dao.LoadBalancerDao;
+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.lb.LoadBalancingRulesManager;
+import com.cloud.network.rules.LoadBalancerContainer.Scheme;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerConfigCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.DeleteLoadBalancerConfigCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerConfigsCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.ReplaceLoadBalancerConfigsCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerConfigCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+public class LoadBalancerConfigManagerImpl extends ManagerBase implements LoadBalancerConfigService, LoadBalancerConfigManager {
+    private static final Logger LOGGER = Logger.getLogger(LoadBalancerConfigManagerImpl.class);
+
+    @Inject
+    LoadBalancerConfigDao _lbConfigDao;
+    @Inject
+    NetworkDao _networkDao;
+    @Inject
+    VpcDao _vpcDao;
+    @Inject
+    LoadBalancerDao _lbDao;
+    @Inject
+    AccountManager _accountMgr;
+    @Inject
+    LoadBalancingRulesManager _lbMgr;
+    @Inject
+    NetworkServiceMapDao _ntwkSrvcDao;
+
+    @Override
+    public List<? extends LoadBalancerConfig> searchForLoadBalancerConfigs(ListLoadBalancerConfigsCmd cmd) {
+        Long id = cmd.getId();
+        String scopeStr = cmd.getScope();
+        Long networkId = cmd.getNetworkId();
+        Long vpcId = cmd.getVpcId();
+        Long loadBalancerId = cmd.getLoadBalancerId();
+        String name = cmd.getName();
+
+        if (id == null && scopeStr == null) {
+            throw new InvalidParameterValueException("At least one of id/scope is required");
+        }
+
+        //validate parameters
+        Scope scope = null;
+        if (scopeStr != null) {
+            scope = LoadBalancerConfigKey.getScopeFromString(scopeStr);
+            if (scope == null) {
+                throw new InvalidParameterValueException("Invalid scope " + scopeStr);
+            }
+            checkPermission(scope, networkId, vpcId, loadBalancerId, cmd.listAll());
+        }
+
+        if (id != null) {
+            LoadBalancerConfigVO config = _lbConfigDao.findById(id);
+            if (config == null) {
+                throw new InvalidParameterValueException("Cannot find load balancer config by id " + id);
+            }
+            checkPermission(config);
+        }
+
+        if (cmd.listAll()) {
+            if (id != null || name != null) {
+                throw new InvalidParameterValueException("id and name must be null if listall is true");
+            }
+        }
+
+        SearchCriteria<LoadBalancerConfigVO> sc = _lbConfigDao.createSearchCriteria();
+        if (id != null) {
+            sc.addAnd("id", SearchCriteria.Op.EQ, id);
+        }
+        if (scope != null) {
+            sc.addAnd("scope", SearchCriteria.Op.EQ, scope);
+        }
+        if (networkId != null) {
+            sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId);
+        }
+        if (vpcId != null) {
+            sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId);
+        }
+        if (loadBalancerId != null) {
+            sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
+        }
+        if (name != null) {
+            sc.addAnd("name", SearchCriteria.Op.EQ, name);
+        }
+        List<LoadBalancerConfigVO> configs = new ArrayList<LoadBalancerConfigVO>();
+        if (id != null || networkId != null || vpcId != null || loadBalancerId != null) {
+            configs = _lbConfigDao.search(sc, null);
+        }
+        if (cmd.listAll()) {
+            LOGGER.debug("Adding config keys for scope " + scope);
+            Map<String, LoadBalancerConfigVO> configsMap = new LinkedHashMap<String, LoadBalancerConfigVO>();
+            for (LoadBalancerConfigVO config : configs) {
+                configsMap.put(config.getName(), config);
+            }
+            List<LoadBalancerConfigVO> result = new ArrayList<LoadBalancerConfigVO>();
+            Map<String, LoadBalancerConfigKey> configKeys = LoadBalancerConfigKey.getConfigsByScope(scope);
+            for (LoadBalancerConfigKey configKey : configKeys.values()) {
+                if (configsMap.get(configKey.key()) != null) {
+                    result.add(configsMap.get(configKey.key()));
+                } else {
+                    result.add(new LoadBalancerConfigVO(scope, null, null, null, configKey, null));
+                }
+            }
+            return result;
+        } else {
+            return configs;
+        }
+    }
+
+    @Override
+    public LoadBalancerConfig createLoadBalancerConfig(CreateLoadBalancerConfigCmd cmd) {
+        String scopeStr = cmd.getScope();
+        Long networkId = cmd.getNetworkId();
+        Long vpcId = cmd.getVpcId();
+        Long loadBalancerId = cmd.getLoadBalancerId();
+        String name = cmd.getName();
+        String value = cmd.getValue();
+
+        //validate parameters
+        Scope scope = LoadBalancerConfigKey.getScopeFromString(scopeStr);
+        if (scope == null) {
+            throw new InvalidParameterValueException("Invalid scope " + scopeStr);
+        }
+        LoadBalancerConfigKey configKey = validateParameters(scope, name, value);
+
+        checkPermission(scope, networkId, vpcId, loadBalancerId);
+
+        LoadBalancerConfigVO existingConfig = _lbConfigDao.findConfig(scope, networkId, vpcId, loadBalancerId, name);
+        if (existingConfig != null) {
+            if (cmd.isForced()) {
+                _lbConfigDao.remove(existingConfig.getId());
+            } else {
+                throw new InvalidParameterValueException("config " + name + " already exists, please add forced=true or update it instead");           }
+        }
+        LoadBalancerConfigVO config = _lbConfigDao.persist(new LoadBalancerConfigVO(scope, networkId, vpcId, loadBalancerId, configKey, value));
+
+        applyLbConfigsForNetwork(config.getNetworkId(), config.getVpcId(), config.getLoadBalancerId());
+
+        return config;
+    }
+
+    @Override
+    public boolean deleteLoadBalancerConfig(DeleteLoadBalancerConfigCmd cmd) {
+        Long id = cmd.getId();
+        LoadBalancerConfigVO config = _lbConfigDao.findById(id);
+        if (config == null) {
+            throw new InvalidParameterValueException("Cannot find load balancer config by id " + id);
+        }
+        checkPermission(config);
+
+        boolean result = _lbConfigDao.remove(id);
+
+        applyLbConfigsForNetwork(config.getNetworkId(), config.getVpcId(), config.getLoadBalancerId());
+
+        return result;
+    }
+
+    @Override
+    public LoadBalancerConfig updateLoadBalancerConfig(UpdateLoadBalancerConfigCmd cmd) {
+        Long id = cmd.getId();
+        String value = cmd.getValue();
+
+        LoadBalancerConfigVO config = _lbConfigDao.findById(id);
+        if (config == null) {
+            throw new InvalidParameterValueException("Cannot find load balancer config by id " + id);
+        }
+        //validate parameters
+        LoadBalancerConfigKey configKey = validateParameters(config.getScope(), config.getName(), value);
+
+        checkPermission(config);
+        config.setValue(value);
+
+        _lbConfigDao.update(config.getId(), config);
+
+        applyLbConfigsForNetwork(config.getNetworkId(), config.getVpcId(), config.getLoadBalancerId());
+
+        return config;
+    }
+
+    @Override
+    public List<? extends LoadBalancerConfig> replaceLoadBalancerConfigs(ReplaceLoadBalancerConfigsCmd cmd) {
+        String scopeStr = cmd.getScope();
+        Long networkId = cmd.getNetworkId();
+        Long vpcId = cmd.getVpcId();
+        Long loadBalancerId = cmd.getLoadBalancerId();
+        Map<String, String> configList = cmd.getConfigList();
+        if (configList == null) {
+            throw new InvalidParameterValueException("Invalid config list");
+        }
+
+        //validate parameters
+        Scope scope = LoadBalancerConfigKey.getScopeFromString(scopeStr);
+        if (scope == null) {
+            throw new InvalidParameterValueException("Invalid scope " + scopeStr);
+        }
+        List<LoadBalancerConfigVO> configs = new ArrayList<LoadBalancerConfigVO>();
+        for (String name : configList.keySet()) {
+            String value = configList.get(name);
+            LoadBalancerConfigKey configKey = validateParameters(scope, name, value);
+            configs.add(new LoadBalancerConfigVO(scope, networkId, vpcId, loadBalancerId, configKey, value));
+        }
+
+        checkPermission(scope, networkId, vpcId, loadBalancerId);
+
+        configs = _lbConfigDao.saveConfigs(configs);
+
+        applyLbConfigsForNetwork(networkId, vpcId, loadBalancerId);
+
+        return configs;
+    }
+
+    private LoadBalancerConfigKey validateParameters(Scope scope, String name, String value) {
+        Pair<LoadBalancerConfigKey, String> res = LoadBalancerConfigKey.validate(scope, name, value);
+        if (res.second() != null) {
+            throw new InvalidParameterValueException(res.second());
+        }
+        return res.first();
+    }
+
+    private void checkPermission(LoadBalancerConfigVO config) {
+        checkPermission(config.getScope(), config.getNetworkId(), config.getVpcId(), config.getLoadBalancerId());
+    }
+
+    private void checkPermission(Scope scope, Long networkId, Long vpcId, Long loadBalancerId) {
+        checkPermission(scope, networkId, vpcId, loadBalancerId, false);
+    }
+
+    private void checkPermission(Scope scope, Long networkId, Long vpcId, Long loadBalancerId, Boolean listAll) {
+        Account caller = CallContext.current().getCallingAccount();
+        if (scope == Scope.Network) {
+            if (networkId == null) {
+                if (listAll) {
+                    return;
+                }
+                throw new InvalidParameterValueException("networkId is required");
+            }
+            if (vpcId != null || loadBalancerId != null) {

Review comment:
       We could use `org.apache.commons.lang3.ObjectUtils` here:
   
   ```java
   ...
   ObjectUtils.anyNotNull(...
   ...
   ```

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,216 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.LoadBalancer;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "createLoadBalancerConfig", description = "Creates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class CreateLoadBalancerConfigCmd extends BaseAsyncCreateCmd {
+    public static final Logger LOGGER = Logger.getLogger(CreateLoadBalancerConfigCmd.class.getName());
+
+    private static final String s_name = "createloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.NAME,
+               type = CommandType.STRING,
+               required = true,
+               description = "name of the load balancer config")
+    private String name;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    @Parameter(name = ApiConstants.FORCED,
+               type = CommandType.BOOLEAN,
+               required = false,
+               description = "Force add a load balancer config. Existing config will be removed")
+    private Boolean forced;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public boolean isForced() {
+        return (forced != null) ? forced : false;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+
+        LoadBalancerConfig config = null;
+        try {
+            config = _entityMgr.findById(LoadBalancerConfig.class, getEntityId());
+            LoadBalancerConfigResponse lbConfigResponse = new LoadBalancerConfigResponse();
+            if (config != null) {
+                lbConfigResponse = _responseGenerator.createLoadBalancerConfigResponse(config);
+                setResponseObject(lbConfigResponse);
+            }
+            lbConfigResponse.setResponseName(getCommandName());
+        } catch (Exception ex) {
+            LOGGER.warn("Failed to create LB config due to exception ", ex);
+        }
+    }
+
+    @Override
+    public void create() {
+        try {
+            LoadBalancerConfig result = _lbConfigService.createLoadBalancerConfig(this);
+            this.setEntityId(result.getId());
+            this.setEntityUuid(result.getUuid());
+        } catch (InvalidParameterValueException e) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        if (networkId != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (vpcId != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        if (networkId != null) {
+            return getNetworkId();
+        } else if (vpcId != null) {

Review comment:
       This `else` statement does not make difference here, we could remove it.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ReplaceLoadBalancerConfigsCmd.java
##########
@@ -0,0 +1,173 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "replaceLoadBalancerConfigs", description = "Replaces load balancer configs of vpc/network/rule",
+        responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ReplaceLoadBalancerConfigsCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(ReplaceLoadBalancerConfigsCmd.class.getName());
+    private static final String s_name = "replaceloadbalancerconfigsresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.CONFIG,
+               type = CommandType.MAP,
+               description = "configs list, Example: config[0].global.maxconn=40960")
+    private Map<String, String> configList;
+
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public Map<String, String> getConfigList() {
+        if (configList == null || configList.isEmpty()) {
+            return null;
+        }
+
+        Collection<String> paramsCollection = configList.values();
+        return (Map<String, String>) (paramsCollection.toArray())[0];

Review comment:
       Is this code right?  `(paramsCollection.toArray())[0]`  returns a String and it's trying to convert the String to a `Map<String, String>`, which will cause a `ClassCastException`. Shouldn't it just returns `configList`?

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java
##########
@@ -255,7 +255,7 @@ public Boolean getOpenFirewall() {
     }
 
     public String getLbProtocol() {
-        return lbProtocol;
+        return lbProtocol != null ? lbProtocol.toLowerCase().trim() : null;

Review comment:
       We could use `org.apache.commons.lang3.StringUtils` here:
   
   ```java
   ...
   return StringUtils.trim(StringUtils.lowerCase(lbProtocol));
   ...
   ```

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ListLoadBalancerConfigsCmd.java
##########
@@ -0,0 +1,143 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseListCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+@APICommand(name = "listLoadBalancerConfigs", description = "Lists load balancer configs.",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ListLoadBalancerConfigsCmd extends BaseListCmd {
+    public static final Logger LOGGER = Logger.getLogger(ListLoadBalancerConfigsCmd.class.getName());
+
+    private static final String s_name = "listloadbalancerconfigsresponse";
+
+    // ///////////////////////////////////////////////////
+    // ////////////// API parameters /////////////////////
+    // ///////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+               type = CommandType.UUID,
+               entityType = LoadBalancerConfigResponse.class,
+               description = "the ID of the load balancer config")
+    private Long id;
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.NAME,
+               type = CommandType.STRING,
+               description = "name of the load balancer config")
+    private String name;
+
+    @Parameter(name = ApiConstants.LIST_ALL,
+               type = CommandType.BOOLEAN,
+               description = "If set to true, list all available configs for the scope. Default value is false")
+    private Boolean listAll;
+
+    // ///////////////////////////////////////////////////
+    // ///////////////// Accessors ///////////////////////
+    // ///////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean listAll() {
+        return listAll == null ? false : listAll;

Review comment:
       We could use `org.apache.commons.lang3.BooleanUtils` here:
   
   ```java
   ...
   return BooleanUtils.toBoolean(listAll);
   ...
   ```

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ReplaceLoadBalancerConfigsCmd.java
##########
@@ -0,0 +1,173 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "replaceLoadBalancerConfigs", description = "Replaces load balancer configs of vpc/network/rule",
+        responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ReplaceLoadBalancerConfigsCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(ReplaceLoadBalancerConfigsCmd.class.getName());
+    private static final String s_name = "replaceloadbalancerconfigsresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.CONFIG,
+               type = CommandType.MAP,
+               description = "configs list, Example: config[0].global.maxconn=40960")
+    private Map<String, String> configList;
+
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public Map<String, String> getConfigList() {
+        if (configList == null || configList.isEmpty()) {
+            return null;
+        }
+
+        Collection<String> paramsCollection = configList.values();
+        return (Map<String, String>) (paramsCollection.toArray())[0];
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        List<? extends LoadBalancerConfig> configs = _lbConfigService.replaceLoadBalancerConfigs(this);
+        SuccessResponse response = new SuccessResponse(getCommandName());
+        this.setResponseObject(response);
+    }
+
+    @Override
+    public String getSyncObjType() {
+        if (networkId != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (vpcId != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        if (networkId != null) {
+            return networkId;
+        } else if (vpcId != null) {
+            return vpcId;
+        }
+        return null;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        if (networkId != null) {
+            Network network = _entityMgr.findById(Network.class, networkId);
+            if (network != null) {
+                return network.getAccountId();
+            }
+        } else if (vpcId != null) {
+            Vpc vpc = _entityMgr.findById(Vpc.class, vpcId);
+            if (vpc != null) {
+                return vpc.getAccountId();
+            }

Review comment:
       We could add this code to the helper that I mentioned before.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,148 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "deleteLoadBalancerConfig", description = "Deletes a load balancer config.",
+        responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class DeleteLoadBalancerConfigCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(DeleteLoadBalancerConfigCmd.class.getName());
+    private static final String s_name = "deleteloadbalancerconfigresponse";
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+               type = CommandType.UUID,
+               entityType = LoadBalancerConfigResponse.class,
+               required = true,
+               description = "the ID of the load balancer config")
+    private Long id;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        boolean result = _lbConfigService.deleteLoadBalancerConfig(this);
+
+        if (result) {
+            SuccessResponse response = new SuccessResponse(getCommandName());
+            this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete load balancer config");
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (config.getVpcId() != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;

Review comment:
       This code is repeated along some classes, e.g. https://github.com/apache/cloudstack/blob/4125854da3d4ab815bcfeefaae18aee5dc69d1eb/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerConfigCmd.java#L167-L173
   
   We could unify them in a helper. As well as some others code that are repeated.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ReplaceLoadBalancerConfigsCmd.java
##########
@@ -0,0 +1,173 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "replaceLoadBalancerConfigs", description = "Replaces load balancer configs of vpc/network/rule",
+        responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ReplaceLoadBalancerConfigsCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(ReplaceLoadBalancerConfigsCmd.class.getName());
+    private static final String s_name = "replaceloadbalancerconfigsresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.CONFIG,
+               type = CommandType.MAP,
+               description = "configs list, Example: config[0].global.maxconn=40960")
+    private Map<String, String> configList;
+
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public Map<String, String> getConfigList() {
+        if (configList == null || configList.isEmpty()) {

Review comment:
       We could use `org.apache.commons.collections.MapUtils` here:
   
   ```java
   ...
   if (MapUtils.isEmpty(configList)) {
   ...
   ```

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,156 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "updateLoadBalancerConfig", description = "Updates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class UpdateLoadBalancerConfigCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(UpdateLoadBalancerConfigCmd.class.getName());
+    private static final String s_name = "updateloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+               type = CommandType.UUID,
+               entityType = LoadBalancerConfigResponse.class,
+               required = true,
+               description = "the ID of the load balancer config to update")
+    private Long id;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        LoadBalancerConfig result = _lbConfigService.updateLoadBalancerConfig(this);
+        if (result != null) {
+            LoadBalancerConfigResponse response = _responseGenerator.createLoadBalancerConfigResponse(result);
+            response.setResponseName(getCommandName());
+            this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update load balancer config");
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (config.getVpcId() != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;

Review comment:
       We could add this code to the helper that I mentioned before.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,216 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.LoadBalancer;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "createLoadBalancerConfig", description = "Creates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class CreateLoadBalancerConfigCmd extends BaseAsyncCreateCmd {
+    public static final Logger LOGGER = Logger.getLogger(CreateLoadBalancerConfigCmd.class.getName());
+
+    private static final String s_name = "createloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.NAME,
+               type = CommandType.STRING,
+               required = true,
+               description = "name of the load balancer config")
+    private String name;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    @Parameter(name = ApiConstants.FORCED,
+               type = CommandType.BOOLEAN,
+               required = false,
+               description = "Force add a load balancer config. Existing config will be removed")
+    private Boolean forced;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public boolean isForced() {
+        return (forced != null) ? forced : false;

Review comment:
       We could use `org.apache.commons.lang3.BooleanUtils` here:
   
   ```java
   ...
   return BooleanUtils.toBoolean(forced);
   ...
   ```

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ReplaceLoadBalancerConfigsCmd.java
##########
@@ -0,0 +1,173 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "replaceLoadBalancerConfigs", description = "Replaces load balancer configs of vpc/network/rule",
+        responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ReplaceLoadBalancerConfigsCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(ReplaceLoadBalancerConfigsCmd.class.getName());
+    private static final String s_name = "replaceloadbalancerconfigsresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.CONFIG,
+               type = CommandType.MAP,
+               description = "configs list, Example: config[0].global.maxconn=40960")
+    private Map<String, String> configList;
+
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public Map<String, String> getConfigList() {
+        if (configList == null || configList.isEmpty()) {
+            return null;
+        }
+
+        Collection<String> paramsCollection = configList.values();
+        return (Map<String, String>) (paramsCollection.toArray())[0];
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        List<? extends LoadBalancerConfig> configs = _lbConfigService.replaceLoadBalancerConfigs(this);
+        SuccessResponse response = new SuccessResponse(getCommandName());
+        this.setResponseObject(response);
+    }
+
+    @Override
+    public String getSyncObjType() {
+        if (networkId != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (vpcId != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;

Review comment:
       We could add this code to the helper that I mentioned before.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,216 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.LoadBalancer;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "createLoadBalancerConfig", description = "Creates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class CreateLoadBalancerConfigCmd extends BaseAsyncCreateCmd {
+    public static final Logger LOGGER = Logger.getLogger(CreateLoadBalancerConfigCmd.class.getName());
+
+    private static final String s_name = "createloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.NAME,
+               type = CommandType.STRING,
+               required = true,
+               description = "name of the load balancer config")
+    private String name;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    @Parameter(name = ApiConstants.FORCED,
+               type = CommandType.BOOLEAN,
+               required = false,
+               description = "Force add a load balancer config. Existing config will be removed")
+    private Boolean forced;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public boolean isForced() {
+        return (forced != null) ? forced : false;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+
+        LoadBalancerConfig config = null;
+        try {
+            config = _entityMgr.findById(LoadBalancerConfig.class, getEntityId());
+            LoadBalancerConfigResponse lbConfigResponse = new LoadBalancerConfigResponse();
+            if (config != null) {
+                lbConfigResponse = _responseGenerator.createLoadBalancerConfigResponse(config);
+                setResponseObject(lbConfigResponse);
+            }
+            lbConfigResponse.setResponseName(getCommandName());
+        } catch (Exception ex) {
+            LOGGER.warn("Failed to create LB config due to exception ", ex);
+        }
+    }
+
+    @Override
+    public void create() {
+        try {
+            LoadBalancerConfig result = _lbConfigService.createLoadBalancerConfig(this);
+            this.setEntityId(result.getId());
+            this.setEntityUuid(result.getUuid());
+        } catch (InvalidParameterValueException e) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        if (networkId != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (vpcId != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        if (networkId != null) {
+            return getNetworkId();
+        } else if (vpcId != null) {
+            return getVpcId();
+        }
+        return null;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        if (Scope.Network.name().equalsIgnoreCase(scope) && networkId != null) {
+            Network network = _entityMgr.findById(Network.class, networkId);
+            if (network != null) {
+                return network.getAccountId();
+            }
+        } else if (Scope.Vpc.name().equalsIgnoreCase(scope) && vpcId != null) {
+            Vpc vpc = _entityMgr.findById(Vpc.class, vpcId);
+            if (vpc != null) {
+                return vpc.getAccountId();
+            }
+        } else if (Scope.LoadBalancerRule.name().equalsIgnoreCase(scope) && loadBalancerId != null) {
+            LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, loadBalancerId);
+            if (lb != null) {
+                return lb.getAccountId();
+            }
+        }

Review comment:
       This code is repeated, but changing parameters; We could extract it to a method.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,216 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.LoadBalancer;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "createLoadBalancerConfig", description = "Creates a load balancer config",
+        responseObject = LoadBalancerConfigResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class CreateLoadBalancerConfigCmd extends BaseAsyncCreateCmd {
+    public static final Logger LOGGER = Logger.getLogger(CreateLoadBalancerConfigCmd.class.getName());
+
+    private static final String s_name = "createloadbalancerconfigresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.NAME,
+               type = CommandType.STRING,
+               required = true,
+               description = "name of the load balancer config")
+    private String name;
+
+    @Parameter(name = ApiConstants.VALUE,
+               type = CommandType.STRING,
+               required = true,
+               description = "value of the load balancer config")
+    private String value;
+
+    @Parameter(name = ApiConstants.FORCED,
+               type = CommandType.BOOLEAN,
+               required = false,
+               description = "Force add a load balancer config. Existing config will be removed")
+    private Boolean forced;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public boolean isForced() {
+        return (forced != null) ? forced : false;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+
+        LoadBalancerConfig config = null;
+        try {
+            config = _entityMgr.findById(LoadBalancerConfig.class, getEntityId());
+            LoadBalancerConfigResponse lbConfigResponse = new LoadBalancerConfigResponse();
+            if (config != null) {
+                lbConfigResponse = _responseGenerator.createLoadBalancerConfigResponse(config);
+                setResponseObject(lbConfigResponse);
+            }
+            lbConfigResponse.setResponseName(getCommandName());
+        } catch (Exception ex) {
+            LOGGER.warn("Failed to create LB config due to exception ", ex);
+        }
+    }
+
+    @Override
+    public void create() {
+        try {
+            LoadBalancerConfig result = _lbConfigService.createLoadBalancerConfig(this);
+            this.setEntityId(result.getId());
+            this.setEntityUuid(result.getUuid());
+        } catch (InvalidParameterValueException e) {
+            throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage());
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        if (networkId != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (vpcId != null) {

Review comment:
       This `else` statement does not make difference here, we could remove it.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/DeleteLoadBalancerConfigCmd.java
##########
@@ -0,0 +1,148 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.LoadBalancerConfigResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "deleteLoadBalancerConfig", description = "Deletes a load balancer config.",
+        responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class DeleteLoadBalancerConfigCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(DeleteLoadBalancerConfigCmd.class.getName());
+    private static final String s_name = "deleteloadbalancerconfigresponse";
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID,
+               type = CommandType.UUID,
+               entityType = LoadBalancerConfigResponse.class,
+               required = true,
+               description = "the ID of the load balancer config")
+    private Long id;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        boolean result = _lbConfigService.deleteLoadBalancerConfig(this);
+
+        if (result) {
+            SuccessResponse response = new SuccessResponse(getCommandName());
+            this.setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete load balancer config");
+        }
+    }
+
+    @Override
+    public String getSyncObjType() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (config.getVpcId() != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        LoadBalancerConfig config = _entityMgr.findById(LoadBalancerConfig.class, getId());
+        if (config == null) {
+            throw new InvalidParameterValueException("Unable to find load balancer config: " + id);
+        }
+        if (config.getNetworkId() != null) {
+            return config.getNetworkId();
+        } else if (config.getVpcId() != null) {
+            return config.getVpcId();
+        }
+        return null;

Review comment:
       We could add this code to the helper that I mentioned before.

##########
File path: api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/ReplaceLoadBalancerConfigsCmd.java
##########
@@ -0,0 +1,173 @@
+// 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 org.apache.cloudstack.api.command.user.loadbalancer;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.response.FirewallRuleResponse;
+import org.apache.cloudstack.api.response.NetworkResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.VpcResponse;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig;
+import org.apache.log4j.Logger;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.network.Network;
+import com.cloud.network.vpc.Vpc;
+
+@APICommand(name = "replaceLoadBalancerConfigs", description = "Replaces load balancer configs of vpc/network/rule",
+        responseObject = SuccessResponse.class,
+        requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
+        since = "4.15",
+        authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
+public class ReplaceLoadBalancerConfigsCmd extends BaseAsyncCmd {
+    public static final Logger LOGGER = Logger.getLogger(ReplaceLoadBalancerConfigsCmd.class.getName());
+    private static final String s_name = "replaceloadbalancerconfigsresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.SCOPE,
+               type = CommandType.STRING,
+               required = true,
+               description = "the scope of the config: network, vpc or rule")
+    private String scope;
+
+    @Parameter(name = ApiConstants.NETWORK_ID,
+               type = CommandType.UUID,
+               entityType = NetworkResponse.class,
+               description = "the ID of network to update")
+    private Long networkId;
+
+    @Parameter(name = ApiConstants.VPC_ID,
+               type = CommandType.UUID,
+               entityType = VpcResponse.class,
+               description = "the ID of vpc to update")
+    private Long vpcId;
+
+    @Parameter(name = ApiConstants.LOAD_BALANCER_ID,
+               type = CommandType.UUID,
+               entityType = FirewallRuleResponse.class,
+               description = "the ID of the load balancer rule to update")
+    private Long loadBalancerId;
+
+    @Parameter(name = ApiConstants.CONFIG,
+               type = CommandType.MAP,
+               description = "configs list, Example: config[0].global.maxconn=40960")
+    private Map<String, String> configList;
+
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getScope() {
+        return scope;
+    }
+
+    public Long getVpcId() {
+        return vpcId;
+    }
+
+    public Long getNetworkId() {
+        return networkId;
+    }
+
+    public Long getLoadBalancerId() {
+        return loadBalancerId;
+    }
+
+    public Map<String, String> getConfigList() {
+        if (configList == null || configList.isEmpty()) {
+            return null;
+        }
+
+        Collection<String> paramsCollection = configList.values();
+        return (Map<String, String>) (paramsCollection.toArray())[0];
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public void execute() {
+        List<? extends LoadBalancerConfig> configs = _lbConfigService.replaceLoadBalancerConfigs(this);
+        SuccessResponse response = new SuccessResponse(getCommandName());
+        this.setResponseObject(response);
+    }
+
+    @Override
+    public String getSyncObjType() {
+        if (networkId != null) {
+            return BaseAsyncCmd.networkSyncObject;
+        } else if (vpcId != null) {
+            return BaseAsyncCmd.vpcSyncObject;
+        }
+        return null;
+    }
+
+    @Override
+    public Long getSyncObjId() {
+        if (networkId != null) {
+            return networkId;
+        } else if (vpcId != null) {
+            return vpcId;
+        }
+        return null;

Review comment:
       We could add this code to the helper that I mentioned before.

##########
File path: api/src/main/java/org/apache/cloudstack/network/lb/LoadBalancerConfigKey.java
##########
@@ -0,0 +1,221 @@
+// 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 org.apache.cloudstack.network.lb;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.SSLConfiguration;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.cloud.utils.Pair;
+
+public enum LoadBalancerConfigKey {
+
+    LbStatsEnable(Category.Stats, "lb.stats.enable", "LB stats enable", Boolean.class, "true", "Enable statistics reporting with default settings, default is 'true'", Scope.Network, Scope.Vpc),
+
+    LbStatsUri(Category.Stats, "lb.stats.uri", "LB stats URI", String.class, "/admin?stats", "Enable statistics and define the URI prefix to access them, default is '/admin?stats'", Scope.Network, Scope.Vpc),
+
+    LbStatsAuth(Category.Stats, "lb.stats.auth", "LB stats auth", String.class, "admin1:AdMiN123", "Enable statistics with authentication and grant access to an account, default is 'admin1:AdMiN123'", Scope.Network, Scope.Vpc),
+
+    GlobalStatsSocket(Category.Stats, "global.stats.socket", "Stats socket enabled/disabled", Boolean.class, "false", "Binds a UNIX socket to /var/run/haproxy.socket, default is 'false'", Scope.Network, Scope.Vpc),
+
+    LbTimeoutConnect(Category.General, "lb.timeout.connect", "Maximum time (in ms) to wait for a connection to succeed", Long.class, "5000", "Set the maximum time to wait for a connection attempt to a server to succeed.", Scope.Network, Scope.Vpc, Scope.LoadBalancerRule),
+
+    LbTimeoutServer(Category.General, "lb.timeout.server", "Maximum inactivity time (in ms) on server side", Long.class, "50000", "Set the maximum inactivity time on the server side.", Scope.Network, Scope.Vpc, Scope.LoadBalancerRule),
+
+    LbTimeoutClient(Category.General, "lb.timeout.client", "Maximum inactivity time (in ms) on client side", Long.class, "50000", "Set the maximum inactivity time on the client side.", Scope.Network, Scope.Vpc, Scope.LoadBalancerRule),
+
+    LbHttp(Category.LoadBalancer, "lb.http", "LB http enabled/disabled", Boolean.class, "true for port 80; false for other ports", "If LB is http, default is 'true' for port 80 and 'false' for others'", Scope.LoadBalancerRule),
+
+    LbHttp2(Category.LoadBalancer, "lb.http2", "Enable/disable HTTP2 support", Boolean.class, "false", "Enable or disable HTTP2 support in HAproxy", Scope.LoadBalancerRule),
+
+    LbHttpKeepalive(Category.LoadBalancer, "lb.http.keepalive", "LB http keepalive enabled/disabled", Boolean.class, "<Inherited from network offering>", "Enable or disable HTTP keep-alive, default is inherited from network offering", Scope.LoadBalancerRule),
+
+    LbBackendHttps(Category.LoadBalancer, "lb.backend.https", "If backend server is https", Boolean.class, "false", "If backend server is https. If yes, use 'check ssl verify none' instead of 'check'", Scope.LoadBalancerRule),
+
+    LbTransparent(Category.LoadBalancer, "lb.transparent.mode", "LB transparent mode enabled/disabled", Boolean.class, "false", "Enable or disable transparent mode, default is 'false'", Scope.LoadBalancerRule),
+
+    GlobalMaxConn(Category.LoadBalancer, "global.maxconn", "LB max connection", Long.class, "4096", "Maximum per process number of concurrent connections, default is '4096'", Scope.Network, Scope.Vpc),
+
+    GlobalMaxPipes(Category.LoadBalancer, "global.maxpipes", "LB max pipes", Long.class, "<global.maxconn/4>", "Maximum number of per process pipes, default is 'maxconn/4'", Scope.Network, Scope.Vpc),
+
+    LbMaxConn(Category.LoadBalancer, "lb.maxconn", "LB max connection", Long.class, "<2000 in haproxy>", "Maximum per process number of concurrent connections per site/vm", Scope.LoadBalancerRule),
+
+    LbFullConn(Category.LoadBalancer, "lb.fullconn", "LB full connection", Long.class, "<maxconn/10 in haproxy>", "Specify at what backend load the servers will reach their maxconn, default is 'maxconn/10'", Scope.LoadBalancerRule),
+
+    LbServerMaxConn(Category.LoadBalancer, "lb.server.maxconn", "LB max connection per server", Long.class, "<0 means unlimited in haproxy>", "LB max connection per server, default is ''", Scope.LoadBalancerRule),
+
+    LbServerMinConn(Category.LoadBalancer, "lb.server.minconn", "LB minimum connection per server", Long.class, "", "LB minimum connection per server, default is ''", Scope.LoadBalancerRule),
+
+    LbServerMaxQueue(Category.LoadBalancer, "lb.server.maxqueue", "Max conn wait in queue per server", Long.class, "<0 means unlimited in haproxy>", "Maximum number of connections which will wait in queue for this server, default is ''", Scope.LoadBalancerRule),
+
+    LbSslConfiguration(Category.LoadBalancer, "lb.ssl.configuration", "SSL configuration, could be 'none', 'old' or 'intermediate'", String.class, "Inherited from global setting" , "if 'none', no SSL configurations will be added, if 'old', refer to https://ssl-config.mozilla.org/#server=haproxy&server-version=1.8.17&config=old&openssl-version=1.0.2l if 'intermediate', refer to https://ssl-config.mozilla.org/#server=haproxy&server-version=1.8.17&config=intermediate&openssl-version=1.0.2l default value is 'none'", Scope.LoadBalancerRule);
+
+    public static enum Category {
+        General, Advanced, Stats, LoadBalancer
+    }
+
+    private final Category _category;
+    private final Scope[] _scope;
+    private final String _key;
+    private final String _displayText;
+    private final String _description;
+    private final Class<?> _type;
+    private final String _defaultValue;
+
+    private LoadBalancerConfigKey(Category category, String key, String displayText, Class<?> type, String defaultValue, String description, Scope... scope) {
+        _category = category;
+        _scope = scope;
+        _key = key;
+        _displayText = displayText;
+        _type = type;
+        _defaultValue = defaultValue;
+        _description = description;
+    }
+
+    public Category category() {
+        return _category;
+    }
+
+    public Class<?> type() {
+        return _type;
+    }
+
+    public String key() {
+        return _key;
+    }
+
+    public String displayText() {
+        return _displayText;
+    }
+
+    public String defaultValue() {
+        return _defaultValue;
+    }
+
+    public String description() {
+        return _description;
+    }
+
+    public Scope[] scope() {
+        return _scope;
+    }
+
+    @Override
+    public String toString() {
+        return _key;
+    }
+
+    private static final HashMap<Scope, Map<String, LoadBalancerConfigKey>> Configs = new HashMap<Scope, Map<String, LoadBalancerConfigKey>>();
+    static {
+        Configs.put(Scope.Network, new LinkedHashMap<String, LoadBalancerConfigKey>());
+        Configs.put(Scope.Vpc, new LinkedHashMap<String, LoadBalancerConfigKey>());
+        Configs.put(Scope.LoadBalancerRule, new LinkedHashMap<String, LoadBalancerConfigKey>());
+        for (LoadBalancerConfigKey c : LoadBalancerConfigKey.values()) {
+            Scope[] scopes = c.scope();
+            for (Scope scope : scopes) {
+                Map<String, LoadBalancerConfigKey> currentConfigs = Configs.get(scope);
+                currentConfigs.put(c.key(), c);
+                Configs.put(scope, currentConfigs);
+            }
+        }
+    }
+
+    public static Map<String, LoadBalancerConfigKey> getConfigsByScope(Scope scope) {
+        return Configs.get(scope);
+    }
+
+    public static LoadBalancerConfigKey getConfigsByScopeAndName(Scope scope, String name) {
+        Map<String, LoadBalancerConfigKey> configs = Configs.get(scope);
+        if (configs.keySet().contains(name)) {
+            return configs.get(name);
+        }
+        return null;

Review comment:
       The `get` method already returns `null` if the map does not contains the key, so we can remove this validation.

##########
File path: core/src/main/java/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java
##########
@@ -72,4 +81,36 @@ public NicTO getNic() {
     public Long getVpcId() {
         return vpcId;
     }
+
+    public LoadBalancerConfigTO[] getNetworkLbConfigs() {
+        return this.networkLbConfigs;
+    }
+
+    public void setNetworkLbConfigs(List<? extends LoadBalancerConfig> networkLbConfigs) {
+        if (networkLbConfigs == null || networkLbConfigs.size() == 0) {

Review comment:
       We could use `org.apache.commons.lang3.ArrayUtils` here:
   
   ```java
   ...
   if (ArrayUtils.isEmpty(networkLbConfigs)) {
   ...
   ```

##########
File path: core/src/main/java/com/cloud/network/HAProxyConfigurator.java
##########
@@ -505,8 +601,35 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
             .append(" ")
             .append(dest.getDestIp())
             .append(":")
-            .append(dest.getDestPort())
-            .append(" check");
+            .append(dest.getDestPort());
+            if ("true".equalsIgnoreCase(lbConfigsMap.get(LoadBalancerConfigKey.LbBackendHttps.key()))) {
+                sb.append(" check ssl verify none");
+            } else {
+                sb.append(" check");
+            }
+
+            if (sslOffloading) {
+                sb.append(getCustomizedSslConfigs(lbConfigsMap, lbCmd));
+            }
+
+            if (lbConfigsMap.get(LoadBalancerConfigKey.LbServerMaxConn.key()) != null) {
+                long maxConnEach = Long.parseLong(lbConfigsMap.get(LoadBalancerConfigKey.LbServerMaxConn.key()));
+                if (maxConnEach > 0) {
+                    sb.append(" maxconn ").append(maxConnEach);
+                }
+            }
+            if (lbConfigsMap.get(LoadBalancerConfigKey.LbServerMinConn.key()) != null) {
+                long minConnEach = Long.parseLong(lbConfigsMap.get(LoadBalancerConfigKey.LbServerMinConn.key()));
+                if (minConnEach > 0) {
+                    sb.append(" minconn ").append(minConnEach);
+                }
+            }
+            if (lbConfigsMap.get(LoadBalancerConfigKey.LbServerMaxQueue.key()) != null) {
+                long maxQueueEach = Long.parseLong(lbConfigsMap.get(LoadBalancerConfigKey.LbServerMaxQueue.key()));
+                if (maxQueueEach > 0) {
+                    sb.append(" maxqueue ").append(maxQueueEach);
+                }
+            }

Review comment:
       This code seems repeated, but changing parameters; We could extract it to a method.

##########
File path: core/src/main/java/com/cloud/network/HAProxyConfigurator.java
##########
@@ -531,42 +654,111 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
                 }
             }
             if (httpbasedStickiness) {
-                result.addAll(dstWithCookieSubRule);
+                backendConfigs.addAll(dstWithCookieSubRule);
             } else {
-                result.addAll(dstSubRule);
+                backendConfigs.addAll(dstSubRule);
             }
-            result.add(stickinessSubRule);
+            backendConfigs.add(stickinessSubRule);
         } else {
-            result.addAll(dstSubRule);
+            backendConfigs.addAll(dstSubRule);
         }
         if (stickinessSubRule != null && !destsAvailable) {
             s_logger.warn("Haproxy stickiness policy for lb rule: " + lbTO.getSrcIp() + ":" + lbTO.getSrcPort() + ": Not Applied, cause:  backends are unavailable");
         }
-        if (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled || httpbasedStickiness) {
+        boolean http = false;
+        String cfgLbHttp = lbConfigsMap.get(LoadBalancerConfigKey.LbHttp.key());
+        if (publicPort == NetUtils.HTTP_PORT && cfgLbHttp == null) {
+            http = true;
+        } else if (cfgLbHttp != null && cfgLbHttp.equalsIgnoreCase("true")) {
+            http = true;
+        }
+
+        boolean keepAliveEnabled = lbCmd.keepAliveEnabled;
+        String cfgLbHttpKeepalive = lbConfigsMap.get(LoadBalancerConfigKey.LbHttpKeepalive.key());
+        if (cfgLbHttpKeepalive != null && cfgLbHttpKeepalive.equalsIgnoreCase("true")) {
+            keepAliveEnabled = true;
+        } else if (cfgLbHttpKeepalive != null && cfgLbHttpKeepalive.equalsIgnoreCase("false")) {
+            keepAliveEnabled = false;
+        }
+
+        if (http || httpbasedStickiness || sslOffloading) {
             sb = new StringBuilder();
             sb.append("\t").append("mode http");
+            frontendConfigs.add(sb.toString());
+            backendConfigsForHttp.add(sb.toString());
+            if (keepAliveEnabled) {
+                sb = new StringBuilder();
+                sb.append("\t").append("no option forceclose");
+                frontendConfigs.add(sb.toString());
+                backendConfigsForHttp.add(sb.toString());
+            } else {
+                sb = new StringBuilder();
+                sb.append("\t").append("option httpclose");
+                frontendConfigs.add(sb.toString());
+                backendConfigsForHttp.add(sb.toString());
+            }

Review comment:
       This code seems repeated, but changing parameters; We could extract it to a method.

##########
File path: core/src/main/java/com/cloud/network/HAProxyConfigurator.java
##########
@@ -531,42 +654,111 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
                 }
             }
             if (httpbasedStickiness) {
-                result.addAll(dstWithCookieSubRule);
+                backendConfigs.addAll(dstWithCookieSubRule);
             } else {
-                result.addAll(dstSubRule);
+                backendConfigs.addAll(dstSubRule);
             }
-            result.add(stickinessSubRule);
+            backendConfigs.add(stickinessSubRule);
         } else {
-            result.addAll(dstSubRule);
+            backendConfigs.addAll(dstSubRule);
         }
         if (stickinessSubRule != null && !destsAvailable) {
             s_logger.warn("Haproxy stickiness policy for lb rule: " + lbTO.getSrcIp() + ":" + lbTO.getSrcPort() + ": Not Applied, cause:  backends are unavailable");
         }
-        if (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled || httpbasedStickiness) {
+        boolean http = false;
+        String cfgLbHttp = lbConfigsMap.get(LoadBalancerConfigKey.LbHttp.key());
+        if (publicPort == NetUtils.HTTP_PORT && cfgLbHttp == null) {
+            http = true;
+        } else if (cfgLbHttp != null && cfgLbHttp.equalsIgnoreCase("true")) {
+            http = true;
+        }
+
+        boolean keepAliveEnabled = lbCmd.keepAliveEnabled;
+        String cfgLbHttpKeepalive = lbConfigsMap.get(LoadBalancerConfigKey.LbHttpKeepalive.key());
+        if (cfgLbHttpKeepalive != null && cfgLbHttpKeepalive.equalsIgnoreCase("true")) {
+            keepAliveEnabled = true;
+        } else if (cfgLbHttpKeepalive != null && cfgLbHttpKeepalive.equalsIgnoreCase("false")) {
+            keepAliveEnabled = false;
+        }
+
+        if (http || httpbasedStickiness || sslOffloading) {
             sb = new StringBuilder();
             sb.append("\t").append("mode http");
+            frontendConfigs.add(sb.toString());
+            backendConfigsForHttp.add(sb.toString());
+            if (keepAliveEnabled) {
+                sb = new StringBuilder();
+                sb.append("\t").append("no option forceclose");
+                frontendConfigs.add(sb.toString());
+                backendConfigsForHttp.add(sb.toString());
+            } else {
+                sb = new StringBuilder();
+                sb.append("\t").append("option httpclose");
+                frontendConfigs.add(sb.toString());
+                backendConfigsForHttp.add(sb.toString());
+            }
+        }
+
+        if (isTransparent) {
+            sb = new StringBuilder();
+            sb.append("frontend ").append(poolName);
             result.add(sb.toString());
+            result.addAll(frontendConfigs);
             sb = new StringBuilder();
-            sb.append("\t").append("option httpclose");
+            sb.append("\tacl local_subnet src ").append(lbCmd.getNetworkCidr());
+            sb.append("\n\tuse_backend ").append(poolName).append("-backend-local if local_subnet");
+            sb.append("\n\tdefault_backend ").append(poolName).append("-backend");
+            sb.append("\n\n");
+            sb.append("backend ").append(poolName).append("-backend");
+            result.add(sb.toString());
+            result.addAll(backendConfigsForHttp);
+            result.addAll(backendConfigs);
+            sb = new StringBuilder();
+            sb.append("\t").append("source 0.0.0.0 usesrc clientip");
+            sb.append("\n\n");
+            sb.append("backend ").append(poolName).append("-backend-local");
             result.add(sb.toString());
+            result.addAll(backendConfigsForHttp);
+            result.addAll(backendConfigs);
+        } else {
+            // add line like this: "listen  65_37_141_30-80\n\tbind 65.37.141.30:80"
+            sb = new StringBuilder();
+            sb.append("listen ").append(poolName);

Review comment:
       Instead of using `StringBuilder`, we could use `String.format`, it is more clean and readable.

##########
File path: core/src/main/java/com/cloud/network/HAProxyConfigurator.java
##########
@@ -467,24 +484,103 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
         return sb.toString();
     }
 
-    private List<String> getRulesForPool(final LoadBalancerTO lbTO, final boolean keepAliveEnabled) {
+    private String getCustomizedSslConfigs(HashMap<String, String> lbConfigsMap, final LoadBalancerConfigCommand lbCmd){
+        String lbSslConfiguration = lbConfigsMap.get(LoadBalancerConfigKey.LbSslConfiguration.key());
+        if (lbSslConfiguration == null) {
+            lbSslConfiguration = lbCmd.lbSslConfiguration;
+        }
+        if (SSLConfiguration.OLD.toString().equalsIgnoreCase(lbSslConfiguration)) {
+            return sslConfigurationOld;
+        } else if (SSLConfiguration.INTERMEDIATE.toString().equalsIgnoreCase(lbSslConfiguration)) {
+            return sslConfigurationIntermediate;
+        }
+        return "";
+    }
+
+    private List<String> getRulesForPool(final LoadBalancerTO lbTO, final LoadBalancerConfigCommand lbCmd, HashMap<String, String> networkLbConfigsMap) {
         StringBuilder sb = new StringBuilder();
         final String poolName = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString();
         final String publicIP = lbTO.getSrcIp();
         final int publicPort = lbTO.getSrcPort();
         final String algorithm = lbTO.getAlgorithm();
 
+        final LoadBalancerConfigTO[] lbConfigs = lbTO.getLbConfigs();
+        final HashMap<String, String> lbConfigsMap = new HashMap<String, String>();
+        if (lbConfigs != null) {
+            for (LoadBalancerConfigTO lbConfig: lbConfigs) {
+                lbConfigsMap.put(lbConfig.getName(), lbConfig.getValue());
+            }
+        }
+
+        boolean isTransparent = false;
+        if ("true".equalsIgnoreCase(lbConfigsMap.get(LoadBalancerConfigKey.LbTransparent.key()))) {
+            isTransparent = true;
+        }
+
+        boolean sslOffloading = false;
+        if (lbTO.getSslCert() != null && ! lbTO.getSslCert().isRevoked()
+                && lbTO.getLbProtocol() != null && lbTO.getLbProtocol().equals(NetUtils.SSL_PROTO)) {
+            sslOffloading = true;
+        }
+
+        final List<String> frontendConfigs = new ArrayList<String>();
+        final List<String> backendConfigs = new ArrayList<String>();
+        final List<String> backendConfigsForHttp = new ArrayList<String>();
         final List<String> result = new ArrayList<String>();
-        // add line like this: "listen  65_37_141_30-80\n\tbind 65.37.141.30:80"
-        sb = new StringBuilder();
-        sb.append("listen ").append(poolName);
-        result.add(sb.toString());
+
         sb = new StringBuilder();
         sb.append("\tbind ").append(publicIP).append(":").append(publicPort);
-        result.add(sb.toString());
+        if (sslOffloading) {
+            sb.append(" ssl crt ").append(SSL_CERTS_DIR).append(poolName).append(".pem");
+            // check for http2 support
+            if ("true".equalsIgnoreCase(lbConfigsMap.get(LoadBalancerConfigKey.LbHttp2.key()))) {
+                sb.append(" alpn h2,http/1.1");
+            }
+
+            sb.append(getCustomizedSslConfigs(lbConfigsMap, lbCmd));
+
+            sb.append("\n\thttp-request add-header X-Forwarded-Proto https");
+        }
+        frontendConfigs.add(sb.toString());
         sb = new StringBuilder();
         sb.append("\t").append("balance ").append(algorithm);
-        result.add(sb.toString());
+        backendConfigs.add(sb.toString());
+
+        String timeoutConnect = lbConfigsMap.get(LoadBalancerConfigKey.LbTimeoutConnect.key());
+        if (timeoutConnect != null) {
+            sb = new StringBuilder();
+            sb.append("\t").append("timeout connect    " + timeoutConnect);
+            backendConfigs.add(sb.toString());
+        }
+        String timeoutClient = lbConfigsMap.get(LoadBalancerConfigKey.LbTimeoutClient.key());
+        if (timeoutClient != null) {
+            sb = new StringBuilder();
+            sb.append("\t").append("timeout client     " + timeoutClient);
+            frontendConfigs.add(sb.toString());
+        }
+        String timeoutServer = lbConfigsMap.get(LoadBalancerConfigKey.LbTimeoutServer.key());
+        if (timeoutServer != null) {
+            sb = new StringBuilder();
+            sb.append("\t").append("timeout server     " + timeoutServer);
+            backendConfigs.add(sb.toString());
+        }
+
+        if (lbConfigsMap.get(LoadBalancerConfigKey.LbMaxConn.key()) != null) {
+            long maxConnValue = Long.parseLong(lbConfigsMap.get(LoadBalancerConfigKey.LbMaxConn.key()));
+            if (maxConnValue > 0) {
+                sb = new StringBuilder();
+                sb.append("\tmaxconn ").append(maxConnValue);
+                frontendConfigs.add(sb.toString());
+            }
+        }
+        if (lbConfigsMap.get(LoadBalancerConfigKey.LbFullConn.key()) != null) {
+            long fullConnValue = Long.parseLong(lbConfigsMap.get(LoadBalancerConfigKey.LbFullConn.key()));
+            if (fullConnValue > 0) {
+                sb = new StringBuilder();
+                sb.append("\tfullconn ").append(fullConnValue);
+                backendConfigs.add(sb.toString());
+            }
+        }

Review comment:
       This code seems repeated, but changing parameters; We could extract it to a method.

##########
File path: engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerConfigDaoImpl.java
##########
@@ -0,0 +1,163 @@
+// 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.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.SearchCriteria.Op;
+import com.cloud.utils.db.TransactionLegacy;
+
+public class LoadBalancerConfigDaoImpl extends GenericDaoBase<LoadBalancerConfigVO, Long> implements LoadBalancerConfigDao {
+
+    final SearchBuilder<LoadBalancerConfigVO> AllFieldsSearch;
+
+    public LoadBalancerConfigDaoImpl() {
+        AllFieldsSearch = createSearchBuilder();
+        AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ);
+        AllFieldsSearch.and("ids", AllFieldsSearch.entity().getId(), Op.IN);
+        AllFieldsSearch.and("uuid", AllFieldsSearch.entity().getUuid(), Op.EQ);
+        AllFieldsSearch.and("scope", AllFieldsSearch.entity().getScope(), Op.EQ);
+        AllFieldsSearch.and("networkId", AllFieldsSearch.entity().getNetworkId(), Op.EQ);
+        AllFieldsSearch.and("vpcId", AllFieldsSearch.entity().getVpcId(), Op.EQ);
+        AllFieldsSearch.and("loadBalancerId", AllFieldsSearch.entity().getLoadBalancerId(), Op.EQ);
+        AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), Op.EQ);
+        AllFieldsSearch.and("value", AllFieldsSearch.entity().getValue(), Op.EQ);
+        AllFieldsSearch.done();
+    }
+
+    @Override
+    public List<LoadBalancerConfigVO> listByNetworkId(Long networkId) {
+        SearchCriteria<LoadBalancerConfigVO> sc = AllFieldsSearch.create();
+        sc.setParameters("scope", Scope.Network);
+        sc.setParameters("networkId", networkId);
+        return listBy(sc);
+    }
+
+    @Override
+    public List<LoadBalancerConfigVO> listByVpcId(Long vpcId) {
+        SearchCriteria<LoadBalancerConfigVO> sc = AllFieldsSearch.create();
+        sc.setParameters("scope", Scope.Vpc);
+        sc.setParameters("vpcId", vpcId);
+        return listBy(sc);
+    }
+
+    @Override
+    public List<LoadBalancerConfigVO> listByLoadBalancerId(Long loadBalancerId) {
+        SearchCriteria<LoadBalancerConfigVO> sc = AllFieldsSearch.create();
+        sc.setParameters("scope", Scope.LoadBalancerRule);
+        sc.setParameters("loadBalancerId", loadBalancerId);
+        return listBy(sc);
+    }
+
+    @Override
+    public void removeByNetworkId(Long networkId) {
+        SearchCriteria<LoadBalancerConfigVO> sc = AllFieldsSearch.create();
+        sc.setParameters("scope", Scope.Network);
+        sc.setParameters("networkId", networkId);
+        remove(sc);
+    }
+
+    @Override
+    public void removeByVpcId(Long vpcId) {
+        SearchCriteria<LoadBalancerConfigVO> sc = AllFieldsSearch.create();
+        sc.setParameters("scope", Scope.Vpc);
+        sc.setParameters("vpcId", vpcId);
+        remove(sc);
+    }
+
+    @Override
+    public void removeByLoadBalancerId(Long loadBalancerId) {
+        SearchCriteria<LoadBalancerConfigVO> sc = AllFieldsSearch.create();
+        sc.setParameters("scope", Scope.LoadBalancerRule);
+        sc.setParameters("loadBalancerId", loadBalancerId);
+        remove(sc);
+    }
+
+    @Override
+    public LoadBalancerConfigVO findConfig(Scope scope, Long networkId, Long vpcId, Long loadBalancerId, String name) {
+        SearchCriteria<LoadBalancerConfigVO> sc = AllFieldsSearch.create();
+        if (scope != null) {
+            sc.setParameters("scope", scope);
+        }
+        if (networkId != null) {
+            sc.setParameters("networkId", networkId);
+        }
+        if (vpcId != null) {
+            sc.setParameters("vpcId", vpcId);
+        }
+        if (loadBalancerId != null) {
+            sc.setParameters("loadBalancerId", loadBalancerId);
+        }
+        if (name != null) {
+            sc.setParameters("name", name);
+        }

Review comment:
       This code seems repeated, but changing parameters; We could extract it to a method or create a `setParametersIfNotNull` in `SearchCriteria`.

##########
File path: server/src/main/java/com/cloud/api/ApiResponseHelper.java
##########
@@ -1040,6 +1043,78 @@ public LoadBalancerResponse createLoadBalancerResponse(LoadBalancer loadBalancer
         return lbResponse;
     }
 
+    @Override
+    public LoadBalancerConfigResponse createLoadBalancerConfigResponse(LoadBalancerConfig config) {
+        Network network = null;
+        Vpc vpc = null;
+        LoadBalancer lb = null;
+        if (config.getNetworkId() != null) {
+            network = ApiDBUtils.findNetworkById(config.getNetworkId());
+        }
+        if (config.getVpcId() != null) {
+            vpc = ApiDBUtils.findVpcById(config.getVpcId());
+        }
+        if (config.getLoadBalancerId() != null) {
+            lb = ApiDBUtils.findLoadBalancerById(config.getLoadBalancerId());
+        }
+        return setLoadBalancerConfigResponse(network, vpc, lb, config);
+    }
+
+    @Override
+    public List<LoadBalancerConfigResponse> createLoadBalancerConfigResponse(List<? extends LoadBalancerConfig> configs) {
+        List<LoadBalancerConfigResponse> lbConfigResponses = new ArrayList<LoadBalancerConfigResponse>();
+        if (configs == null || configs.size() == 0) {
+            return lbConfigResponses;
+        }
+        LoadBalancerConfig config = configs.get(0);
+        Network network = null;
+        Vpc vpc = null;
+        LoadBalancer lb = null;
+        if (config.getNetworkId() != null) {
+            network = ApiDBUtils.findNetworkById(config.getNetworkId());
+        }
+        if (config.getVpcId() != null) {
+            vpc = ApiDBUtils.findVpcById(config.getVpcId());
+        }
+        if (config.getLoadBalancerId() != null) {
+            lb = ApiDBUtils.findLoadBalancerById(config.getLoadBalancerId());
+        }

Review comment:
       We could add this code to the helper that I mentioned before, as it is repeated in others methods.

##########
File path: server/src/main/java/com/cloud/api/ApiResponseHelper.java
##########
@@ -1040,6 +1043,78 @@ public LoadBalancerResponse createLoadBalancerResponse(LoadBalancer loadBalancer
         return lbResponse;
     }
 
+    @Override
+    public LoadBalancerConfigResponse createLoadBalancerConfigResponse(LoadBalancerConfig config) {
+        Network network = null;
+        Vpc vpc = null;
+        LoadBalancer lb = null;
+        if (config.getNetworkId() != null) {
+            network = ApiDBUtils.findNetworkById(config.getNetworkId());
+        }
+        if (config.getVpcId() != null) {
+            vpc = ApiDBUtils.findVpcById(config.getVpcId());
+        }
+        if (config.getLoadBalancerId() != null) {
+            lb = ApiDBUtils.findLoadBalancerById(config.getLoadBalancerId());
+        }
+        return setLoadBalancerConfigResponse(network, vpc, lb, config);
+    }
+
+    @Override
+    public List<LoadBalancerConfigResponse> createLoadBalancerConfigResponse(List<? extends LoadBalancerConfig> configs) {
+        List<LoadBalancerConfigResponse> lbConfigResponses = new ArrayList<LoadBalancerConfigResponse>();
+        if (configs == null || configs.size() == 0) {

Review comment:
       We could use `org.apache.commons.collections.CollectionUtils` here:
   
   ```java
   ...
   if (CollectionUtils.isEmpty(configs)) {
   ...
   ```

##########
File path: server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
##########
@@ -1757,6 +1840,52 @@ private void updateWithLbRules(final DomainRouterJoinVO routerJoinVO, final Stri
         }
     }
 
+    private void updateLbValues(final HashMap<String, String> lbConfigsMap, StringBuilder loadBalancingData) {
+        String lbMaxConn = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbMaxConn.key(), null);
+        String lbFullConn = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbFullConn.key(), null);
+        String lbTimeoutConnect = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbTimeoutConnect.key(), null);
+        String lbTimeoutServer = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbTimeoutServer.key(), null);
+        String lbTimeoutClient = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbTimeoutClient.key(), null);
+        String lbBackendHttps = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbBackendHttps.key(), null);
+        String lbHttp2 = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbHttp2.key(), null);
+
+        // Process lb.server values
+        String serverMaxconn = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbServerMaxConn.key(), null);
+        String serverMinconn = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbServerMinConn.key(), null);
+        String serverMaxqueue = lbConfigsMap.getOrDefault(LoadBalancerConfigKey.LbServerMaxQueue.key(), null);
+
+        if (lbMaxConn != null) {
+            loadBalancingData.append(",lb.maxconn=").append(lbMaxConn);
+        }
+        if (lbFullConn != null) {
+            loadBalancingData.append(",lb.fullconn=").append(lbFullConn);
+        }
+        if (lbTimeoutConnect != null) {
+            loadBalancingData.append(",lb.timeout.connect=").append(lbTimeoutConnect);
+        }
+        if (lbTimeoutServer != null) {
+            loadBalancingData.append(",lb.timeout.server=").append(lbTimeoutServer);
+        }
+        if (lbTimeoutClient != null) {
+            loadBalancingData.append(",lb.timeout.client=").append(lbTimeoutClient);
+        }
+        if (lbBackendHttps != null) {
+            loadBalancingData.append(",lb.backend.https=").append(lbBackendHttps);
+        }
+        if (lbHttp2 != null) {
+            loadBalancingData.append(",http2=").append(lbHttp2);
+        }
+        if (serverMaxconn != null) {
+            loadBalancingData.append(",server.maxconn=").append(serverMaxconn);
+        }
+        if (serverMinconn != null) {
+            loadBalancingData.append(",server.minconn=").append(serverMinconn);
+        }
+        if (serverMaxqueue != null) {
+            loadBalancingData.append(",server.maxqueue=").append(serverMaxqueue);
+        }

Review comment:
       This code seems repeated, but changing parameters; We could extract it to a method.

##########
File path: server/src/main/java/org/apache/cloudstack/network/lb/LoadBalancerConfigManagerImpl.java
##########
@@ -0,0 +1,396 @@
+// 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 org.apache.cloudstack.network.lb;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.Network.Provider;
+import com.cloud.network.Network.Service;
+import com.cloud.network.dao.LoadBalancerConfigDao;
+import com.cloud.network.dao.LoadBalancerConfigVO;
+import com.cloud.network.dao.LoadBalancerDao;
+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.lb.LoadBalancingRulesManager;
+import com.cloud.network.rules.LoadBalancerContainer.Scheme;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerConfigCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.DeleteLoadBalancerConfigCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerConfigsCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.ReplaceLoadBalancerConfigsCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerConfigCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+public class LoadBalancerConfigManagerImpl extends ManagerBase implements LoadBalancerConfigService, LoadBalancerConfigManager {
+    private static final Logger LOGGER = Logger.getLogger(LoadBalancerConfigManagerImpl.class);
+
+    @Inject
+    LoadBalancerConfigDao _lbConfigDao;
+    @Inject
+    NetworkDao _networkDao;
+    @Inject
+    VpcDao _vpcDao;
+    @Inject
+    LoadBalancerDao _lbDao;
+    @Inject
+    AccountManager _accountMgr;
+    @Inject
+    LoadBalancingRulesManager _lbMgr;
+    @Inject
+    NetworkServiceMapDao _ntwkSrvcDao;
+
+    @Override
+    public List<? extends LoadBalancerConfig> searchForLoadBalancerConfigs(ListLoadBalancerConfigsCmd cmd) {
+        Long id = cmd.getId();
+        String scopeStr = cmd.getScope();
+        Long networkId = cmd.getNetworkId();
+        Long vpcId = cmd.getVpcId();
+        Long loadBalancerId = cmd.getLoadBalancerId();
+        String name = cmd.getName();
+
+        if (id == null && scopeStr == null) {
+            throw new InvalidParameterValueException("At least one of id/scope is required");
+        }
+
+        //validate parameters
+        Scope scope = null;
+        if (scopeStr != null) {
+            scope = LoadBalancerConfigKey.getScopeFromString(scopeStr);
+            if (scope == null) {
+                throw new InvalidParameterValueException("Invalid scope " + scopeStr);
+            }
+            checkPermission(scope, networkId, vpcId, loadBalancerId, cmd.listAll());
+        }
+
+        if (id != null) {
+            LoadBalancerConfigVO config = _lbConfigDao.findById(id);
+            if (config == null) {
+                throw new InvalidParameterValueException("Cannot find load balancer config by id " + id);
+            }
+            checkPermission(config);
+        }
+
+        if (cmd.listAll()) {
+            if (id != null || name != null) {
+                throw new InvalidParameterValueException("id and name must be null if listall is true");
+            }
+        }
+
+        SearchCriteria<LoadBalancerConfigVO> sc = _lbConfigDao.createSearchCriteria();
+        if (id != null) {
+            sc.addAnd("id", SearchCriteria.Op.EQ, id);
+        }
+        if (scope != null) {
+            sc.addAnd("scope", SearchCriteria.Op.EQ, scope);
+        }
+        if (networkId != null) {
+            sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId);
+        }
+        if (vpcId != null) {
+            sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId);
+        }
+        if (loadBalancerId != null) {
+            sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
+        }
+        if (name != null) {
+            sc.addAnd("name", SearchCriteria.Op.EQ, name);
+        }
+        List<LoadBalancerConfigVO> configs = new ArrayList<LoadBalancerConfigVO>();
+        if (id != null || networkId != null || vpcId != null || loadBalancerId != null) {
+            configs = _lbConfigDao.search(sc, null);
+        }
+        if (cmd.listAll()) {
+            LOGGER.debug("Adding config keys for scope " + scope);
+            Map<String, LoadBalancerConfigVO> configsMap = new LinkedHashMap<String, LoadBalancerConfigVO>();
+            for (LoadBalancerConfigVO config : configs) {
+                configsMap.put(config.getName(), config);
+            }
+            List<LoadBalancerConfigVO> result = new ArrayList<LoadBalancerConfigVO>();
+            Map<String, LoadBalancerConfigKey> configKeys = LoadBalancerConfigKey.getConfigsByScope(scope);
+            for (LoadBalancerConfigKey configKey : configKeys.values()) {
+                if (configsMap.get(configKey.key()) != null) {
+                    result.add(configsMap.get(configKey.key()));
+                } else {
+                    result.add(new LoadBalancerConfigVO(scope, null, null, null, configKey, null));
+                }
+            }
+            return result;
+        } else {
+            return configs;
+        }
+    }
+
+    @Override
+    public LoadBalancerConfig createLoadBalancerConfig(CreateLoadBalancerConfigCmd cmd) {
+        String scopeStr = cmd.getScope();
+        Long networkId = cmd.getNetworkId();
+        Long vpcId = cmd.getVpcId();
+        Long loadBalancerId = cmd.getLoadBalancerId();
+        String name = cmd.getName();
+        String value = cmd.getValue();
+
+        //validate parameters
+        Scope scope = LoadBalancerConfigKey.getScopeFromString(scopeStr);
+        if (scope == null) {
+            throw new InvalidParameterValueException("Invalid scope " + scopeStr);
+        }
+        LoadBalancerConfigKey configKey = validateParameters(scope, name, value);
+
+        checkPermission(scope, networkId, vpcId, loadBalancerId);
+
+        LoadBalancerConfigVO existingConfig = _lbConfigDao.findConfig(scope, networkId, vpcId, loadBalancerId, name);
+        if (existingConfig != null) {
+            if (cmd.isForced()) {
+                _lbConfigDao.remove(existingConfig.getId());
+            } else {
+                throw new InvalidParameterValueException("config " + name + " already exists, please add forced=true or update it instead");           }
+        }
+        LoadBalancerConfigVO config = _lbConfigDao.persist(new LoadBalancerConfigVO(scope, networkId, vpcId, loadBalancerId, configKey, value));
+
+        applyLbConfigsForNetwork(config.getNetworkId(), config.getVpcId(), config.getLoadBalancerId());
+
+        return config;
+    }
+
+    @Override
+    public boolean deleteLoadBalancerConfig(DeleteLoadBalancerConfigCmd cmd) {
+        Long id = cmd.getId();
+        LoadBalancerConfigVO config = _lbConfigDao.findById(id);
+        if (config == null) {
+            throw new InvalidParameterValueException("Cannot find load balancer config by id " + id);
+        }
+        checkPermission(config);
+
+        boolean result = _lbConfigDao.remove(id);
+
+        applyLbConfigsForNetwork(config.getNetworkId(), config.getVpcId(), config.getLoadBalancerId());
+
+        return result;
+    }
+
+    @Override
+    public LoadBalancerConfig updateLoadBalancerConfig(UpdateLoadBalancerConfigCmd cmd) {
+        Long id = cmd.getId();
+        String value = cmd.getValue();
+
+        LoadBalancerConfigVO config = _lbConfigDao.findById(id);
+        if (config == null) {
+            throw new InvalidParameterValueException("Cannot find load balancer config by id " + id);
+        }
+        //validate parameters
+        LoadBalancerConfigKey configKey = validateParameters(config.getScope(), config.getName(), value);
+
+        checkPermission(config);
+        config.setValue(value);
+
+        _lbConfigDao.update(config.getId(), config);
+
+        applyLbConfigsForNetwork(config.getNetworkId(), config.getVpcId(), config.getLoadBalancerId());
+
+        return config;
+    }
+
+    @Override
+    public List<? extends LoadBalancerConfig> replaceLoadBalancerConfigs(ReplaceLoadBalancerConfigsCmd cmd) {
+        String scopeStr = cmd.getScope();
+        Long networkId = cmd.getNetworkId();
+        Long vpcId = cmd.getVpcId();
+        Long loadBalancerId = cmd.getLoadBalancerId();
+        Map<String, String> configList = cmd.getConfigList();
+        if (configList == null) {
+            throw new InvalidParameterValueException("Invalid config list");
+        }
+
+        //validate parameters
+        Scope scope = LoadBalancerConfigKey.getScopeFromString(scopeStr);
+        if (scope == null) {
+            throw new InvalidParameterValueException("Invalid scope " + scopeStr);
+        }
+        List<LoadBalancerConfigVO> configs = new ArrayList<LoadBalancerConfigVO>();
+        for (String name : configList.keySet()) {
+            String value = configList.get(name);
+            LoadBalancerConfigKey configKey = validateParameters(scope, name, value);
+            configs.add(new LoadBalancerConfigVO(scope, networkId, vpcId, loadBalancerId, configKey, value));
+        }
+
+        checkPermission(scope, networkId, vpcId, loadBalancerId);
+
+        configs = _lbConfigDao.saveConfigs(configs);
+
+        applyLbConfigsForNetwork(networkId, vpcId, loadBalancerId);
+
+        return configs;
+    }
+
+    private LoadBalancerConfigKey validateParameters(Scope scope, String name, String value) {
+        Pair<LoadBalancerConfigKey, String> res = LoadBalancerConfigKey.validate(scope, name, value);
+        if (res.second() != null) {
+            throw new InvalidParameterValueException(res.second());
+        }
+        return res.first();
+    }
+
+    private void checkPermission(LoadBalancerConfigVO config) {
+        checkPermission(config.getScope(), config.getNetworkId(), config.getVpcId(), config.getLoadBalancerId());
+    }
+
+    private void checkPermission(Scope scope, Long networkId, Long vpcId, Long loadBalancerId) {
+        checkPermission(scope, networkId, vpcId, loadBalancerId, false);
+    }
+
+    private void checkPermission(Scope scope, Long networkId, Long vpcId, Long loadBalancerId, Boolean listAll) {
+        Account caller = CallContext.current().getCallingAccount();
+        if (scope == Scope.Network) {
+            if (networkId == null) {
+                if (listAll) {
+                    return;
+                }
+                throw new InvalidParameterValueException("networkId is required");
+            }
+            if (vpcId != null || loadBalancerId != null) {
+                throw new InvalidParameterValueException("vpcId and loadBalancerId should be null if scope is Network");
+            }
+            if (networkId == null) {
+                throw new InvalidParameterValueException("networkId is required");
+            }
+            NetworkVO network = _networkDao.findById(networkId);
+            if (network == null) {
+                throw new InvalidParameterValueException("Cannot find network by id " + networkId);
+            }
+            // Perform permission check
+            _accountMgr.checkAccess(caller, null, true, network);
+            if (network.getVpcId() != null) {
+                throw new InvalidParameterValueException("network " + network.getName() + " is a VPC tier, please add LB configs to VPC instead");
+            }
+        } else if (scope == Scope.Vpc) {
+            if (vpcId == null) {
+                if (listAll) {
+                    return;
+                }
+                throw new InvalidParameterValueException("vpcId is required");
+            }
+            if (networkId != null || loadBalancerId != null) {
+                throw new InvalidParameterValueException("networkId and loadBalancerId should be null if scope is Vpc");
+            }
+            VpcVO vpc = _vpcDao.findById(vpcId);
+            if (vpc == null) {
+                throw new InvalidParameterValueException("Cannot find vpc by id " + vpcId);
+            }
+            // Perform permission check
+            _accountMgr.checkAccess(caller, null, true, vpc);
+        } else if (scope == Scope.LoadBalancerRule) {
+            if (loadBalancerId == null) {
+                if (listAll) {
+                    return;
+                }
+                throw new InvalidParameterValueException("loadBalancerId is required");
+            }
+            if (networkId != null || vpcId != null) {
+                throw new InvalidParameterValueException("networkId and vpcId should be null if scope is LoadBalancerRule");
+            }
+            LoadBalancerVO rule = _lbDao.findById(loadBalancerId);
+            if (rule == null) {
+                throw new InvalidParameterValueException("Cannot find load balancer rule by id " + loadBalancerId);
+            }
+            if (networkId != null) {
+                // Perform permission check
+                checkPermission(Scope.Network, rule.getNetworkId(), null, null);
+            }
+        }

Review comment:
       This code seems repeated, but changing parameters; We could extract it to a method.

##########
File path: server/src/main/java/org/apache/cloudstack/network/lb/LoadBalancerConfigManagerImpl.java
##########
@@ -0,0 +1,396 @@
+// 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 org.apache.cloudstack.network.lb;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.Network.Provider;
+import com.cloud.network.Network.Service;
+import com.cloud.network.dao.LoadBalancerConfigDao;
+import com.cloud.network.dao.LoadBalancerConfigVO;
+import com.cloud.network.dao.LoadBalancerDao;
+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.lb.LoadBalancingRulesManager;
+import com.cloud.network.rules.LoadBalancerContainer.Scheme;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.utils.Pair;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+import org.apache.cloudstack.api.command.user.loadbalancer.CreateLoadBalancerConfigCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.DeleteLoadBalancerConfigCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerConfigsCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.ReplaceLoadBalancerConfigsCmd;
+import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerConfigCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.network.lb.LoadBalancerConfig.Scope;
+import org.apache.log4j.Logger;
+
+public class LoadBalancerConfigManagerImpl extends ManagerBase implements LoadBalancerConfigService, LoadBalancerConfigManager {
+    private static final Logger LOGGER = Logger.getLogger(LoadBalancerConfigManagerImpl.class);
+
+    @Inject
+    LoadBalancerConfigDao _lbConfigDao;
+    @Inject
+    NetworkDao _networkDao;
+    @Inject
+    VpcDao _vpcDao;
+    @Inject
+    LoadBalancerDao _lbDao;
+    @Inject
+    AccountManager _accountMgr;
+    @Inject
+    LoadBalancingRulesManager _lbMgr;
+    @Inject
+    NetworkServiceMapDao _ntwkSrvcDao;
+
+    @Override
+    public List<? extends LoadBalancerConfig> searchForLoadBalancerConfigs(ListLoadBalancerConfigsCmd cmd) {
+        Long id = cmd.getId();
+        String scopeStr = cmd.getScope();
+        Long networkId = cmd.getNetworkId();
+        Long vpcId = cmd.getVpcId();
+        Long loadBalancerId = cmd.getLoadBalancerId();
+        String name = cmd.getName();
+
+        if (id == null && scopeStr == null) {
+            throw new InvalidParameterValueException("At least one of id/scope is required");
+        }
+
+        //validate parameters
+        Scope scope = null;
+        if (scopeStr != null) {
+            scope = LoadBalancerConfigKey.getScopeFromString(scopeStr);
+            if (scope == null) {
+                throw new InvalidParameterValueException("Invalid scope " + scopeStr);
+            }
+            checkPermission(scope, networkId, vpcId, loadBalancerId, cmd.listAll());
+        }
+
+        if (id != null) {
+            LoadBalancerConfigVO config = _lbConfigDao.findById(id);
+            if (config == null) {
+                throw new InvalidParameterValueException("Cannot find load balancer config by id " + id);
+            }
+            checkPermission(config);
+        }
+
+        if (cmd.listAll()) {
+            if (id != null || name != null) {
+                throw new InvalidParameterValueException("id and name must be null if listall is true");
+            }
+        }
+
+        SearchCriteria<LoadBalancerConfigVO> sc = _lbConfigDao.createSearchCriteria();
+        if (id != null) {
+            sc.addAnd("id", SearchCriteria.Op.EQ, id);
+        }
+        if (scope != null) {
+            sc.addAnd("scope", SearchCriteria.Op.EQ, scope);
+        }
+        if (networkId != null) {
+            sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId);
+        }
+        if (vpcId != null) {
+            sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId);
+        }
+        if (loadBalancerId != null) {
+            sc.addAnd("loadBalancerId", SearchCriteria.Op.EQ, loadBalancerId);
+        }
+        if (name != null) {
+            sc.addAnd("name", SearchCriteria.Op.EQ, name);
+        }

Review comment:
       This code seems repeated, but changing parameters; We could extract it to a method or create an `addAndNotNull` to `SearchCriteria`.

##########
File path: core/src/main/java/com/cloud/network/HAProxyConfigurator.java
##########
@@ -467,24 +484,103 @@ private String getLbSubRuleForStickiness(final LoadBalancerTO lbTO) {
         return sb.toString();
     }
 
-    private List<String> getRulesForPool(final LoadBalancerTO lbTO, final boolean keepAliveEnabled) {
+    private String getCustomizedSslConfigs(HashMap<String, String> lbConfigsMap, final LoadBalancerConfigCommand lbCmd){
+        String lbSslConfiguration = lbConfigsMap.get(LoadBalancerConfigKey.LbSslConfiguration.key());
+        if (lbSslConfiguration == null) {
+            lbSslConfiguration = lbCmd.lbSslConfiguration;
+        }
+        if (SSLConfiguration.OLD.toString().equalsIgnoreCase(lbSslConfiguration)) {
+            return sslConfigurationOld;
+        } else if (SSLConfiguration.INTERMEDIATE.toString().equalsIgnoreCase(lbSslConfiguration)) {
+            return sslConfigurationIntermediate;
+        }
+        return "";
+    }
+
+    private List<String> getRulesForPool(final LoadBalancerTO lbTO, final LoadBalancerConfigCommand lbCmd, HashMap<String, String> networkLbConfigsMap) {
         StringBuilder sb = new StringBuilder();
         final String poolName = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString();
         final String publicIP = lbTO.getSrcIp();
         final int publicPort = lbTO.getSrcPort();
         final String algorithm = lbTO.getAlgorithm();
 
+        final LoadBalancerConfigTO[] lbConfigs = lbTO.getLbConfigs();
+        final HashMap<String, String> lbConfigsMap = new HashMap<String, String>();
+        if (lbConfigs != null) {
+            for (LoadBalancerConfigTO lbConfig: lbConfigs) {
+                lbConfigsMap.put(lbConfig.getName(), lbConfig.getValue());
+            }
+        }
+
+        boolean isTransparent = false;
+        if ("true".equalsIgnoreCase(lbConfigsMap.get(LoadBalancerConfigKey.LbTransparent.key()))) {
+            isTransparent = true;
+        }
+
+        boolean sslOffloading = false;
+        if (lbTO.getSslCert() != null && ! lbTO.getSslCert().isRevoked()
+                && lbTO.getLbProtocol() != null && lbTO.getLbProtocol().equals(NetUtils.SSL_PROTO)) {
+            sslOffloading = true;
+        }
+
+        final List<String> frontendConfigs = new ArrayList<String>();
+        final List<String> backendConfigs = new ArrayList<String>();
+        final List<String> backendConfigsForHttp = new ArrayList<String>();
         final List<String> result = new ArrayList<String>();
-        // add line like this: "listen  65_37_141_30-80\n\tbind 65.37.141.30:80"
-        sb = new StringBuilder();
-        sb.append("listen ").append(poolName);
-        result.add(sb.toString());
+
         sb = new StringBuilder();
         sb.append("\tbind ").append(publicIP).append(":").append(publicPort);
-        result.add(sb.toString());
+        if (sslOffloading) {
+            sb.append(" ssl crt ").append(SSL_CERTS_DIR).append(poolName).append(".pem");
+            // check for http2 support
+            if ("true".equalsIgnoreCase(lbConfigsMap.get(LoadBalancerConfigKey.LbHttp2.key()))) {
+                sb.append(" alpn h2,http/1.1");
+            }
+
+            sb.append(getCustomizedSslConfigs(lbConfigsMap, lbCmd));
+
+            sb.append("\n\thttp-request add-header X-Forwarded-Proto https");
+        }
+        frontendConfigs.add(sb.toString());
         sb = new StringBuilder();
         sb.append("\t").append("balance ").append(algorithm);
-        result.add(sb.toString());
+        backendConfigs.add(sb.toString());
+
+        String timeoutConnect = lbConfigsMap.get(LoadBalancerConfigKey.LbTimeoutConnect.key());
+        if (timeoutConnect != null) {
+            sb = new StringBuilder();
+            sb.append("\t").append("timeout connect    " + timeoutConnect);
+            backendConfigs.add(sb.toString());
+        }
+        String timeoutClient = lbConfigsMap.get(LoadBalancerConfigKey.LbTimeoutClient.key());
+        if (timeoutClient != null) {
+            sb = new StringBuilder();
+            sb.append("\t").append("timeout client     " + timeoutClient);
+            frontendConfigs.add(sb.toString());
+        }
+        String timeoutServer = lbConfigsMap.get(LoadBalancerConfigKey.LbTimeoutServer.key());
+        if (timeoutServer != null) {
+            sb = new StringBuilder();
+            sb.append("\t").append("timeout server     " + timeoutServer);
+            backendConfigs.add(sb.toString());
+        }
+
+        if (lbConfigsMap.get(LoadBalancerConfigKey.LbMaxConn.key()) != null) {
+            long maxConnValue = Long.parseLong(lbConfigsMap.get(LoadBalancerConfigKey.LbMaxConn.key()));
+            if (maxConnValue > 0) {
+                sb = new StringBuilder();
+                sb.append("\tmaxconn ").append(maxConnValue);
+                frontendConfigs.add(sb.toString());
+            }
+        }
+        if (lbConfigsMap.get(LoadBalancerConfigKey.LbFullConn.key()) != null) {
+            long fullConnValue = Long.parseLong(lbConfigsMap.get(LoadBalancerConfigKey.LbFullConn.key()));
+            if (fullConnValue > 0) {
+                sb = new StringBuilder();
+                sb.append("\tfullconn ").append(fullConnValue);
+                backendConfigs.add(sb.toString());
+            }
+        }

Review comment:
       Also, instead of using `StringBuilder`, we could use `String.format`, it is more clean and readable.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@cloudstack.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org