NSX: Add support to create and delete Load balancer rules
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxLoadBalancerRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxLoadBalancerRuleCommand.java
new file mode 100644
index 0000000..861fa34
--- /dev/null
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxLoadBalancerRuleCommand.java
@@ -0,0 +1,63 @@
+// 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.agent.api;
+
+import org.apache.cloudstack.resource.NsxLoadBalancerMember;
+
+import java.util.List;
+
+public class CreateNsxLoadBalancerRuleCommand extends NsxNetworkCommand {
+
+    private final String publicPort;
+    private final String algorithm;
+    private final String protocol;
+    List<NsxLoadBalancerMember> memberList;
+
+    private final long lbId;
+    public CreateNsxLoadBalancerRuleCommand(long domainId, long accountId, long zoneId, Long networkResourceId,
+                                            String networkResourceName, boolean isResourceVpc,
+                                            List<NsxLoadBalancerMember> memberList, long lbId, String publicPort,
+                                            String algorithm, String protocol) {
+        super(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc);
+        this.lbId = lbId;
+        this.memberList = memberList;
+        this.publicPort = publicPort;
+        this.algorithm = algorithm;
+        this.protocol = protocol;
+    }
+
+
+    public long getLbId() {
+        return lbId;
+    }
+
+    public String getPublicPort() {
+        return publicPort;
+    }
+
+    public List<NsxLoadBalancerMember> getMemberList() {
+        return memberList;
+    }
+
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+}
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxLoadBalancerRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxLoadBalancerRuleCommand.java
new file mode 100644
index 0000000..21296af
--- /dev/null
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxLoadBalancerRuleCommand.java
@@ -0,0 +1,40 @@
+// 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.agent.api;
+
+import org.apache.cloudstack.resource.NsxLoadBalancerMember;
+
+import java.util.List;
+
+public class DeleteNsxLoadBalancerRuleCommand extends NsxNetworkCommand {
+    private long lbId;
+    List<NsxLoadBalancerMember> memberList;
+
+    public DeleteNsxLoadBalancerRuleCommand(long domainId, long accountId, long zoneId, Long networkResourceId,
+                                            String networkResourceName, boolean isResourceVpc,
+                                            List<NsxLoadBalancerMember> memberList, long lbId, long vmId) {
+        super(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc, vmId);
+        this.lbId = lbId;
+        this.memberList = memberList;
+    }
+
+    public long getLbId() {
+        return lbId;
+    }
+
+    public List<NsxLoadBalancerMember> getMemberList() { return memberList; }
+}
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxNetworkCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxNetworkCommand.java
index e5f49af..4cad50d 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxNetworkCommand.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxNetworkCommand.java
@@ -38,11 +38,16 @@
     }
 
     public NsxNetworkCommand(long domainId, long accountId, long zoneId, Long networkResourceId, String networkResourceName,
-                            boolean isResourceVpc, Long vmId) {
+                             boolean isResourceVpc) {
         super(domainId, accountId, zoneId);
         this.networkResourceId = networkResourceId;
         this.networkResourceName = networkResourceName;
         this.isResourceVpc = isResourceVpc;
+    }
+
+    public NsxNetworkCommand(long domainId, long accountId, long zoneId, Long networkResourceId, String networkResourceName,
+                            boolean isResourceVpc, Long vmId) {
+        this(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc);
         this.vmId = vmId;
     }
 
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxLoadBalancerMember.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxLoadBalancerMember.java
new file mode 100644
index 0000000..ec51822
--- /dev/null
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxLoadBalancerMember.java
@@ -0,0 +1,25 @@
+package org.apache.cloudstack.resource;
+
+public class NsxLoadBalancerMember {
+    private long vmId;
+    private String vmIp;
+    private int port;
+
+    public NsxLoadBalancerMember(long vmId, String vmIp, int port) {
+        this.vmId = vmId;
+        this.vmIp = vmIp;
+        this.port = port;
+    }
+
+    public long getVmId() {
+        return vmId;
+    }
+
+    public String getVmIp() {
+        return vmIp;
+    }
+
+    public int getPort() {
+        return port;
+    }
+}
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
index 5b3daa1..f07b81f 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java
@@ -16,6 +16,8 @@
 // under the License.
 package org.apache.cloudstack.resource;
 
+import java.util.List;
+
 public class NsxNetworkRule {
     private long domainId;
     private long accountId;
@@ -30,6 +32,8 @@
     private String publicPort;
     private String privatePort;
     private String protocol;
+    private String algorithm;
+    private List<NsxLoadBalancerMember> memberList;
 
     public long getDomainId() {
         return domainId;
@@ -135,6 +139,22 @@
         this.protocol = protocol;
     }
 
+    public void setAlgorithm(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    public List<NsxLoadBalancerMember> getMemberList() {
+        return memberList;
+    }
+
+    public void setMemberList(List<NsxLoadBalancerMember> memberList) {
+        this.memberList = memberList;
+    }
+
     public static final class Builder {
         private long domainId;
         private long accountId;
@@ -150,6 +170,8 @@
         private String publicPort;
         private String privatePort;
         private String protocol;
+        private String algorithm;
+        private List<NsxLoadBalancerMember> memberList;
 
         public Builder() {
         }
@@ -220,6 +242,16 @@
             return this;
         }
 
+        public Builder setAlgorithm(String algorithm) {
+            this.algorithm = algorithm;
+            return this;
+        }
+
+        public Builder setMemberList(List<NsxLoadBalancerMember> memberList) {
+            this.memberList = memberList;
+            return this;
+        }
+
         public NsxNetworkRule build() {
             NsxNetworkRule rule = new NsxNetworkRule();
             rule.setDomainId(this.domainId);
@@ -235,6 +267,8 @@
             rule.setPrivatePort(this.privatePort);
             rule.setProtocol(this.protocol);
             rule.setRuleId(this.ruleId);
+            rule.setAlgorithm(this.algorithm);
+            rule.setMemberList(this.memberList);
             return rule;
         }
     }
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java
index 2a375bc..d3cec1f 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java
@@ -36,10 +36,12 @@
 import org.apache.cloudstack.NsxAnswer;
 import org.apache.cloudstack.StartupNsxCommand;
 import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand;
+import org.apache.cloudstack.agent.api.CreateNsxLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateNsxPortForwardRuleCommand;
 import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand;
 import org.apache.cloudstack.agent.api.CreateNsxStaticNatCommand;
 import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand;
+import org.apache.cloudstack.agent.api.DeleteNsxLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand;
@@ -114,6 +116,10 @@
             return executeRequest((DeleteNsxNatRuleCommand) cmd);
         } else if (cmd instanceof CreateNsxPortForwardRuleCommand) {
           return executeRequest((CreateNsxPortForwardRuleCommand) cmd);
+        } else if (cmd instanceof CreateNsxLoadBalancerRuleCommand) {
+            return executeRequest((CreateNsxLoadBalancerRuleCommand) cmd);
+        } else if (cmd instanceof DeleteNsxLoadBalancerRuleCommand) {
+            return executeRequest((DeleteNsxLoadBalancerRuleCommand) cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
@@ -401,6 +407,33 @@
         return new NsxAnswer(cmd, true, null);
     }
 
+    private NsxAnswer executeRequest(CreateNsxLoadBalancerRuleCommand cmd) {
+        String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(),
+                cmd.getNetworkResourceId(), cmd.isResourceVpc());
+        String ruleName = NsxControllerUtils.getLoadBalancerRuleName(tier1GatewayName, cmd.getLbId());
+        try {
+            nsxApiClient.createAndAddNsxLbVirtualServer(tier1GatewayName, cmd.getLbId(), cmd.getPublicIp(), cmd.getPublicPort(),
+                    cmd.getMemberList(), cmd.getAlgorithm(), cmd.getProtocol());
+        } catch (Exception e) {
+            LOGGER.error(String.format("Failed to add NSX load balancer rule %s for network: %s", ruleName, cmd.getNetworkResourceName()));
+            return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage()));
+        }
+        return new NsxAnswer(cmd, true, null);
+    }
+
+    private NsxAnswer executeRequest(DeleteNsxLoadBalancerRuleCommand cmd) {
+        String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(),
+                cmd.getZoneId(), cmd.getNetworkResourceId(), cmd.isResourceVpc());
+        String ruleName = NsxControllerUtils.getLoadBalancerRuleName(tier1GatewayName, cmd.getLbId());
+        try {
+            nsxApiClient.deleteNsxLbResources(tier1GatewayName, cmd.getLbId(), cmd.getVmId());
+        } catch (Exception e) {
+            LOGGER.error(String.format("Failed to add NSX load balancer rule %s for network: %s", ruleName, cmd.getNetworkResourceName()));
+            return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage()));
+        }
+        return new NsxAnswer(cmd, true, null);
+    }
+
     @Override
     public boolean start() {
         return true;
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java
index 534030a..736eeec 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java
@@ -22,6 +22,10 @@
 import com.vmware.nsx.model.TransportZone;
 import com.vmware.nsx.model.TransportZoneListResult;
 import com.vmware.nsx_policy.infra.DhcpRelayConfigs;
+import com.vmware.nsx_policy.infra.LbAppProfiles;
+import com.vmware.nsx_policy.infra.LbPools;
+import com.vmware.nsx_policy.infra.LbServices;
+import com.vmware.nsx_policy.infra.LbVirtualServers;
 import com.vmware.nsx_policy.infra.Segments;
 import com.vmware.nsx_policy.infra.Services;
 import com.vmware.nsx_policy.infra.Sites;
@@ -33,6 +37,11 @@
 import com.vmware.nsx_policy.model.DhcpRelayConfig;
 import com.vmware.nsx_policy.model.EnforcementPointListResult;
 import com.vmware.nsx_policy.model.L4PortSetServiceEntry;
+import com.vmware.nsx_policy.model.LBAppProfileListResult;
+import com.vmware.nsx_policy.model.LBPool;
+import com.vmware.nsx_policy.model.LBPoolMember;
+import com.vmware.nsx_policy.model.LBService;
+import com.vmware.nsx_policy.model.LBVirtualServer;
 import com.vmware.nsx_policy.model.LocaleServicesListResult;
 import com.vmware.nsx_policy.model.PolicyNatRule;
 import com.vmware.nsx_policy.model.Segment;
@@ -41,6 +50,7 @@
 import com.vmware.nsx_policy.model.SiteListResult;
 import com.vmware.nsx_policy.model.Tier1;
 import com.vmware.vapi.bindings.Service;
+import com.vmware.vapi.bindings.Structure;
 import com.vmware.vapi.bindings.StubConfiguration;
 import com.vmware.vapi.cis.authn.SecurityContextFactory;
 import com.vmware.vapi.client.ApiClient;
@@ -51,15 +61,26 @@
 import com.vmware.vapi.internal.protocol.client.rest.authn.BasicAuthenticationAppender;
 import com.vmware.vapi.protocol.HttpConfiguration;
 import com.vmware.vapi.std.errors.Error;
+import org.apache.cloudstack.resource.NsxLoadBalancerMember;
 import org.apache.cloudstack.utils.NsxControllerUtils;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.log4j.Logger;
 
 import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import static org.apache.cloudstack.utils.NsxControllerUtils.getServerPoolMemberName;
+import static org.apache.cloudstack.utils.NsxControllerUtils.getServerPoolName;
+import static org.apache.cloudstack.utils.NsxControllerUtils.getServiceName;
+import static org.apache.cloudstack.utils.NsxControllerUtils.getVirtualServerName;
+import static org.apache.cloudstack.utils.NsxControllerUtils.getServiceEntryName;
+import static org.apache.cloudstack.utils.NsxControllerUtils.getLoadBalancerName;
+import static org.apache.cloudstack.utils.NsxControllerUtils.getLoadBalancerAlgorithm;
+
 public class NsxApiClient {
 
     private final Function<Class<? extends Service>, Service> nsxService;
@@ -96,6 +117,19 @@
         BYPASS
     }
 
+    public enum LBAlgorithm {
+        ROUND_ROBIN,
+        LEAST_CONNECTION,
+        IP_HASH
+    }
+
+    private enum LBSize {
+        SMALL,
+        MEDIUM,
+        LARGE,
+        XLARGE
+    }
+
     public enum  RouteAdvertisementType { TIER1_STATIC_ROUTES, TIER1_CONNECTED, TIER1_NAT,
         TIER1_LB_VIP, TIER1_LB_SNAT, TIER1_DNS_FORWARDER_IP, TIER1_IPSEC_LOCAL_ENDPOINT
     }
@@ -380,6 +414,162 @@
         }
     }
 
+    public void createNsxLbServerPool(List<NsxLoadBalancerMember> memberList, String tier1GatewayName, String lbServerPoolName, String algorithm) {
+        for (NsxLoadBalancerMember member : memberList) {
+            try {
+                String serverPoolMemberName = getServerPoolMemberName(tier1GatewayName, member.getVmId());
+                LbPools lbPools = (LbPools) nsxService.apply(LbPools.class);
+                LBPoolMember lbPoolMember = new LBPoolMember.Builder()
+                        .setDisplayName(serverPoolMemberName)
+                        .setIpAddress(member.getVmIp())
+                        .setPort(String.valueOf(member.getPort()))
+                        .build();
+                LBPool lbPool = new LBPool.Builder()
+                        .setId(lbServerPoolName)
+                        .setDisplayName(lbServerPoolName)
+                        .setAlgorithm(getLoadBalancerAlgorithm(algorithm))
+                        .setMembers(List.of(lbPoolMember))
+                        .build();
+                lbPools.patch(lbServerPoolName, lbPool);
+            } catch (Error error) {
+                ApiError ae = error.getData()._convertTo(ApiError.class);
+                String msg = String.format("Failed to create NSX LB server pool, due to: %s", ae.getErrorMessage());
+                LOGGER.error(msg);
+                throw new CloudRuntimeException(msg);
+            }
+        }
+    }
+
+    public void createNsxLoadBalancer(String tier1GatewayName, long lbId) {
+        try {
+            String lbName = getLoadBalancerName(tier1GatewayName);
+            LbServices lbServices = (LbServices) nsxService.apply(LbServices.class);
+            LBService lbService = getLbService(lbName);
+            if (Objects.nonNull(lbService)) {
+                return;
+            }
+            lbService = new LBService.Builder()
+                    .setId(lbName)
+                    .setDisplayName(lbName)
+                    .setEnabled(true)
+                    .setSize(LBSize.SMALL.name())
+                    .setConnectivityPath(TIER_1_GATEWAY_PATH_PREFIX + tier1GatewayName)
+                    .build();
+            lbServices.patch(lbName, lbService);
+        } catch (Error error) {
+            ApiError ae = error.getData()._convertTo(ApiError.class);
+            String msg = String.format("Failed to create NSX load balancer, due to: %s", ae.getErrorMessage());
+            LOGGER.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+    }
+
+    public void createAndAddNsxLbVirtualServer(String tier1GatewayName, long lbId, String publicIp, String publicPort,
+                                               List<NsxLoadBalancerMember> memberList, String algorithm, String protocol) {
+        try {
+            String lbServerPoolName = getServerPoolName(tier1GatewayName, lbId);
+            createNsxLbServerPool(memberList, tier1GatewayName, lbServerPoolName, algorithm);
+            createNsxLoadBalancer(tier1GatewayName, lbId);
+
+            String lbVirtualServerName = getVirtualServerName(tier1GatewayName, lbId);
+            String lbServiceName = getLoadBalancerName(tier1GatewayName);
+            LbVirtualServers lbVirtualServers = (LbVirtualServers) nsxService.apply(LbVirtualServers.class);
+            LBVirtualServer lbVirtualServer = new LBVirtualServer.Builder()
+                    .setId(lbVirtualServerName)
+                    .setDisplayName(lbVirtualServerName)
+                    .setApplicationProfilePath(getLbProfileForProtocol(protocol))
+                    .setIpAddress(publicIp)
+                    .setLbServicePath(getLbPath(lbServiceName))
+                    .setPoolPath(getLbPoolPath(lbServerPoolName))
+                    .setPorts(List.of(publicPort))
+                    .build();
+            lbVirtualServers.patch(lbVirtualServerName, lbVirtualServer);
+        } catch (Error error) {
+            ApiError ae = error.getData()._convertTo(ApiError.class);
+            String msg = String.format("Failed to create and add NSX virtual server to the Load Balancer, due to: %s", ae.getErrorMessage());
+            LOGGER.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+    }
+
+    public void deleteNsxLbResources(String tier1GatewayName, long lbId, long vmId) {
+        try {
+            // Delete associated Virtual servers
+            LbVirtualServers lbVirtualServers = (LbVirtualServers) nsxService.apply(LbVirtualServers.class);
+            String lbVirtualServerName = getVirtualServerName(tier1GatewayName, lbId);
+            lbVirtualServers.delete(lbVirtualServerName, true);
+
+            // Delete LB pool
+            LbPools lbPools = (LbPools) nsxService.apply(LbPools.class);
+            String lbServerPoolName = getServerPoolName(tier1GatewayName, vmId);
+            lbPools.delete(lbServerPoolName, true);
+
+            // Delete load balancer
+            String lbName = getLoadBalancerName(tier1GatewayName);
+            LbServices lbServices = (LbServices) nsxService.apply(LbServices.class);
+            lbServices.delete(lbName, true);
+
+        } catch (Error error) {
+            ApiError ae = error.getData()._convertTo(ApiError.class);
+            String msg = String.format("Failed to delete NSX Load Balancer resources, due to: %s", ae.getErrorMessage());
+            LOGGER.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+    }
+
+    private String getLbPoolPath(String lbPoolName) {
+        try {
+            LbPools lbPools = (LbPools) nsxService.apply(LbPools.class);
+            LBPool lbPool = lbPools.get(lbPoolName);
+            return Objects.nonNull(lbPool) ? lbPool.getPath() : null;
+        } catch (Error error) {
+            ApiError ae = error.getData()._convertTo(ApiError.class);
+            String msg = String.format("Failed to get NSX LB server pool, due to: %s", ae.getErrorMessage());
+            LOGGER.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+    }
+    private LBService getLbService(String lbName) {
+        try {
+            LbServices lbServices = (LbServices) nsxService.apply(LbServices.class);
+            LBService lbService = lbServices.get(lbName);
+            if (Objects.nonNull(lbService)) {
+                return lbService;
+            }
+        } catch (Exception e) {
+            return null;
+        }
+        return null;
+    }
+
+    private String getLbPath(String lbServiceName) {
+        try {
+            LbServices lbServices = (LbServices) nsxService.apply(LbServices.class);
+            LBService lbService = lbServices.get(lbServiceName);
+            return Objects.nonNull(lbService) ? lbService.getPath() : null;
+        } catch (Error error) {
+            ApiError ae = error.getData()._convertTo(ApiError.class);
+            String msg = String.format("Failed to get NSX LB server pool, due to: %s", ae.getErrorMessage());
+            LOGGER.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+    }
+
+    private String getLbProfileForProtocol(String protocol) {
+        try {
+            LbAppProfiles lbAppProfiles = (LbAppProfiles) nsxService.apply(LbAppProfiles.class);
+            LBAppProfileListResult lbAppProfileListResults = lbAppProfiles.list(null, null,
+                    null, null, null, null);
+            Optional<Structure> appProfile = lbAppProfileListResults.getResults().stream().filter(profile -> profile._getDataValue().getField("path").toString().contains(protocol.toLowerCase(Locale.ROOT))).findFirst();
+            return appProfile.map(structure -> structure._getDataValue().getField("path").toString()).orElse(null);
+        } catch (Error error) {
+            ApiError ae = error.getData()._convertTo(ApiError.class);
+            String msg = String.format("Failed to list NSX LB App profiles, due to: %s", ae.getErrorMessage());
+            LOGGER.error(msg);
+            throw new CloudRuntimeException(msg);
+        }
+    }
+
     public String getNsxInfraServices(String ruleName, String port, String protocol) {
         try {
             Services service = (Services) nsxService.apply(Services.class);
@@ -456,12 +646,4 @@
         }
         return null;
     }
-
-    private String getServiceName(String ruleName, String port, String protocol) {
-        return ruleName + "-SVC-" + port + "-" +protocol;
-    }
-
-    private String getServiceEntryName(String ruleName, String port, String protocol) {
-        return ruleName + "-SE-" + port + "-" + protocol;
-    }
 }
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
index c8ac091..2eb99fe 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java
@@ -24,6 +24,7 @@
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
 import com.cloud.agent.api.StartupCommand;
+import com.cloud.agent.api.to.LoadBalancerTO;
 import com.cloud.api.ApiDBUtils;
 import com.cloud.dc.DataCenterVO;
 import com.cloud.dc.dao.DataCenterDao;
@@ -45,6 +46,8 @@
 import com.cloud.network.PublicIpAddress;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.IPAddressVO;
+import com.cloud.network.dao.LoadBalancerVMMapDao;
+import com.cloud.network.dao.LoadBalancerVMMapVO;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.dao.PhysicalNetworkDao;
@@ -52,9 +55,11 @@
 import com.cloud.network.element.DhcpServiceProvider;
 import com.cloud.network.element.DnsServiceProvider;
 import com.cloud.network.element.IpDeployer;
+import com.cloud.network.element.LoadBalancingServiceProvider;
 import com.cloud.network.element.PortForwardingServiceProvider;
 import com.cloud.network.element.StaticNatServiceProvider;
 import com.cloud.network.element.VpcProvider;
+import com.cloud.network.lb.LoadBalancingRule;
 import com.cloud.network.rules.FirewallRule;
 import com.cloud.network.rules.PortForwardingRule;
 import com.cloud.network.rules.StaticNat;
@@ -83,12 +88,14 @@
 import com.cloud.vm.dao.VMInstanceDao;
 import net.sf.ehcache.config.InvalidConfigurationException;
 import org.apache.cloudstack.StartupNsxCommand;
+import org.apache.cloudstack.resource.NsxLoadBalancerMember;
 import org.apache.cloudstack.resource.NsxNetworkRule;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -99,7 +106,8 @@
 
 @Component
 public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider,
-        StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider, ResourceStateAdapter, Listener {
+        StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider,
+        LoadBalancingServiceProvider, ResourceStateAdapter, Listener {
 
     @Inject
     AccountManager accountMgr;
@@ -125,6 +133,8 @@
     VMInstanceDao vmInstanceDao;
     @Inject
     VpcDao vpcDao;
+    @Inject
+    LoadBalancerVMMapDao lbVmMapDao;
 
     private static final Logger LOGGER = Logger.getLogger(NsxElement.class);
 
@@ -505,7 +515,6 @@
 
             String privatePort = getPrivatePortRange(rule);
 
-            // TODO: add builder to reduce signature params ; should we pass port range?
             NsxNetworkRule networkRule = new NsxNetworkRule.Builder()
                     .setDomainId(domainId)
                     .setAccountId(accountId)
@@ -522,9 +531,13 @@
                     .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT))
                     .build();
             if (rule.getState() == FirewallRule.State.Add) {
-                return nsxService.createPortForwardRule(networkRule);
+                if (!nsxService.createPortForwardRule(networkRule)) {
+                    return false;
+                }
             } else if (rule.getState() == FirewallRule.State.Revoke) {
-                return nsxService.deletePortForwardRule(networkRule);
+                if (!nsxService.deletePortForwardRule(networkRule)) {
+                    return false;
+                }
             }
         }
         return true;
@@ -558,4 +571,76 @@
                 String.valueOf(rule.getSourcePortStart()) :
                 String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd()));
     }
+
+    @Override
+    public boolean applyLBRules(Network network, List<LoadBalancingRule> rules) throws ResourceUnavailableException {
+        for (LoadBalancingRule loadBalancingRule : rules) {
+            if (loadBalancingRule.getState() == FirewallRule.State.Active) {
+                continue;
+            }
+            IPAddressVO publicIp = ipAddressDao.findByIpAndDcId(network.getDataCenterId(),
+                    loadBalancingRule.getSourceIp().addr());
+
+            Pair<VpcVO, NetworkVO> vpcOrNetwork = getVpcOrNetwork(network.getVpcId(), network.getId());
+            VpcVO vpc = vpcOrNetwork.first();
+            NetworkVO networkVO = vpcOrNetwork.second();
+            Long networkResourceId = Objects.nonNull(vpc) ? vpc.getId() : networkVO.getId();
+            String networkResourceName = Objects.nonNull(vpc) ? vpc.getName() : networkVO.getName();
+            boolean isVpcResource = Objects.nonNull(vpc);
+            long domainId = Objects.nonNull(vpc) ? vpc.getDomainId() : networkVO.getDomainId();
+            long accountId = Objects.nonNull(vpc) ? vpc.getAccountId() : networkVO.getAccountId();
+            long zoneId = Objects.nonNull(vpc) ? vpc.getZoneId() : networkVO.getDataCenterId();
+            List<NsxLoadBalancerMember> lbMembers = getLoadBalancerMembers(loadBalancingRule);
+            NsxNetworkRule networkRule = new NsxNetworkRule.Builder()
+                    .setDomainId(domainId)
+                    .setAccountId(accountId)
+                    .setZoneId(zoneId)
+                    .setNetworkResourceId(networkResourceId)
+                    .setNetworkResourceName(networkResourceName)
+                    .setVpcResource(isVpcResource)
+                    .setMemberList(lbMembers)
+                    .setPublicIp(publicIp.getAddress().addr())
+                    .setPublicPort(String.valueOf(loadBalancingRule.getSourcePortStart()))
+                    .setRuleId(loadBalancingRule.getId())
+                    .setProtocol(loadBalancingRule.getProtocol().toUpperCase(Locale.ROOT))
+                    .setAlgorithm(loadBalancingRule.getAlgorithm())
+                    .build();
+            if (loadBalancingRule.getState() == FirewallRule.State.Add) {
+                if (!nsxService.createLbRule(networkRule)) {
+                    return false;
+                }
+            } else if (loadBalancingRule.getState() == FirewallRule.State.Revoke) {
+                if (!nsxService.deleteLbRule(networkRule)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean validateLBRule(Network network, LoadBalancingRule rule) {
+        return true;
+    }
+
+    @Override
+    public List<LoadBalancerTO> updateHealthChecks(Network network, List<LoadBalancingRule> lbrules) {
+        return new ArrayList<>();
+    }
+
+    @Override
+    public boolean handlesOnlyRulesInTransitionState() {
+        return false;
+    }
+
+    private List<NsxLoadBalancerMember> getLoadBalancerMembers(LoadBalancingRule lbRule) {
+        List<LoadBalancerVMMapVO> lbVms = lbVmMapDao.listByLoadBalancerId(lbRule.getId(), false);
+        List<NsxLoadBalancerMember> lbMembers = new ArrayList<>();
+
+        for (LoadBalancerVMMapVO lbVm : lbVms) {
+            NsxLoadBalancerMember member = new NsxLoadBalancerMember(lbVm.getInstanceId(), lbVm.getInstanceIp(), lbRule.getDefaultPortStart());
+            lbMembers.add(member);
+        }
+        return lbMembers;
+    }
 }
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java
index c2aef14..5e4d446 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java
@@ -23,9 +23,11 @@
 import com.cloud.network.vpc.dao.VpcDao;
 import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.cloudstack.NsxAnswer;
+import org.apache.cloudstack.agent.api.CreateNsxLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateNsxPortForwardRuleCommand;
 import org.apache.cloudstack.agent.api.CreateNsxStaticNatCommand;
 import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand;
+import org.apache.cloudstack.agent.api.DeleteNsxLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand;
@@ -124,4 +126,23 @@
         NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteCmd, netRule.getZoneId());
         return result.getResult();
     }
+
+    public boolean createLbRule(NsxNetworkRule netRule) {
+        CreateNsxLoadBalancerRuleCommand command = new CreateNsxLoadBalancerRuleCommand(netRule.getDomainId(),
+                netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(),
+                netRule.getNetworkResourceName(), netRule.isVpcResource(),  netRule.getMemberList(), netRule.getRuleId(),
+                netRule.getPublicPort(), netRule.getAlgorithm(), netRule.getProtocol());
+        command.setPublicIp(netRule.getPublicIp());
+        NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, netRule.getZoneId());
+        return result.getResult();
+    }
+
+    public boolean deleteLbRule(NsxNetworkRule netRule) {
+        DeleteNsxLoadBalancerRuleCommand command = new DeleteNsxLoadBalancerRuleCommand(netRule.getDomainId(),
+                netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(),
+                netRule.getNetworkResourceName(), netRule.isVpcResource(),  netRule.getMemberList(), netRule.getRuleId(),
+                netRule.getVmId());
+        NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, netRule.getZoneId());
+        return result.getResult();
+    }
 }
diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java
index 8c54547..d0416c8 100644
--- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java
+++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java
@@ -23,6 +23,7 @@
 import com.cloud.network.element.NsxProviderVO;
 import org.apache.cloudstack.NsxAnswer;
 import org.apache.cloudstack.agent.api.NsxCommand;
+import org.apache.cloudstack.service.NsxApiClient;
 import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
@@ -90,4 +91,43 @@
         String suffix = "-PF";
         return getTier1GatewayName(domainId, accountId, zoneId, networkResourceId, isVpcResource) + suffix + ruleId;
     }
+
+    public static String getServiceName(String ruleName, String port, String protocol) {
+        return ruleName + "-SVC-" + port + "-" +protocol;
+    }
+
+    public static String getServiceEntryName(String ruleName, String port, String protocol) {
+        return ruleName + "-SE-" + port + "-" + protocol;
+    }
+
+    public static String getLoadBalancerName(String tier1GatewayName) {
+        return tier1GatewayName + "-LB";
+    }
+
+    public static String getLoadBalancerRuleName(String tier1GatewayName, long lbId) {
+        return tier1GatewayName + "-LB" + lbId;
+    }
+
+    public static String getServerPoolName(String tier1GatewayName, long lbId) {
+        return  getLoadBalancerRuleName(tier1GatewayName, lbId) + "-SP";
+    }
+
+    public static String  getVirtualServerName(String tier1GatewayName, long lbId) {
+        return getLoadBalancerRuleName(tier1GatewayName, lbId) + "-VS";
+    }
+
+    public static String getServerPoolMemberName(String tier1GatewayName, long vmId) {
+        return tier1GatewayName + "-VM" + vmId;
+    }
+
+    public static String getLoadBalancerAlgorithm(String algorithm) {
+        switch (algorithm) {
+            case "leastconn":
+                return NsxApiClient.LBAlgorithm.LEAST_CONNECTION.name();
+            case "source":
+                return NsxApiClient.LBAlgorithm.IP_HASH.name();
+            default:
+                return NsxApiClient.LBAlgorithm.ROUND_ROBIN.name();
+        }
+    }
 }
diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
index 031f622..69574b2 100644
--- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
@@ -1225,6 +1225,7 @@
                 new NetworkOfferingVO(name, displayText, TrafficType.Guest, false, false, null,
                         null, true, Availability.Optional, null, GuestType.Isolated, false,
                         false, false, false, false, forVpc);
+        defaultNatNSXNetworkOffering.setPublicLb(true);
         defaultNatNSXNetworkOffering.setForNsx(true);
         defaultNatNSXNetworkOffering.setNsxMode(nsxMode.name());
         defaultNatNSXNetworkOffering.setState(NetworkOffering.State.Enabled);