This is an automated email from the ASF dual-hosted git repository.

pearl11594 pushed a commit to branch netris-integration-upstream
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit e02ffed25e10a1b15e825532e429b16ef6e6be0a
Author: Pearl Dsilva <pearl1...@gmail.com>
AuthorDate: Wed Feb 12 11:29:52 2025 -0500

    Phase5 - Support for LB - create, delete and Update operations (#49)
    
    * Add support for Netris ACLs
    
    * acl support
    
    * Make acl api call to netris to create the rule
    
    * refactor add acl rule to populate the right fields
    
    * support icmp type acl rule
    
    * acl rule creation - move netrisnetworkRule
    
    * Update ACL naming on Netris
    
    * Add support for Deletion of netris acls
    
    * Add support to delete and re-order ACL rules
    
    * support creation of default acl rules and replacing acl rules
    
    * fix NSXNetworkRule
    
    * Fix naming convention for NAT subnets to follow other resources
    
    * Use vpc ID for nat subnets
    
    * Phase5 - Support for LB - create, delete and Update operations
    
    * Use new nat subnet name for deletion of static nat rule
    
    * add support to add netris lb rule
    
    * support deletion of LB rule on Netris
    
    * add checks when editing unsupported fields of LB rule for Netris and hide 
columns on the UI
    
    * fix test failure
    
    * fix imports
    
    * add license
    
    * address comments
---
 .../com/cloud/network/netris/NetrisLbBackend.java  |  41 ++++
 .../cloud/network/netris/NetrisNetworkRule.java    |  14 ++
 .../com/cloud/network/netris/NetrisService.java    |   3 +
 ...reateOrUpdateNetrisLoadBalancerRuleCommand.java |  72 ++++++
 .../api/DeleteNetrisLoadBalancerRuleCommand.java   |  34 +++
 .../apache/cloudstack/resource/NetrisResource.java |  31 +++
 .../resource/NetrisResourceObjectUtils.java        |   7 +-
 .../apache/cloudstack/service/NetrisApiClient.java |   4 +
 .../cloudstack/service/NetrisApiClientImpl.java    | 269 +++++++++++++++++++--
 .../apache/cloudstack/service/NetrisElement.java   |  92 ++++++-
 .../cloudstack/service/NetrisServiceImpl.java      |  23 ++
 .../network/lb/LoadBalancingRulesManagerImpl.java  |  14 ++
 .../cloud/network/lb/UpdateLoadBalancerTest.java   |   7 +-
 .../cloudstack/service/NetrisServiceMockTest.java  |  10 +
 ui/src/views/network/LoadBalancing.vue             |  33 ++-
 15 files changed, 617 insertions(+), 37 deletions(-)

diff --git a/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java 
b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java
new file mode 100644
index 00000000000..e2dfd6b59c1
--- /dev/null
+++ b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java
@@ -0,0 +1,41 @@
+// 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.netris;
+
+public class NetrisLbBackend {
+    private long vmId;
+    private String vmIp;
+    private int port;
+
+    public NetrisLbBackend(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;
+    }
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java 
b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
index 67612141f65..dcd7bd22acb 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java
@@ -19,6 +19,8 @@ package com.cloud.network.netris;
 import com.cloud.network.SDNProviderNetworkRule;
 
 
+import java.util.List;
+
 public class NetrisNetworkRule {
     public enum NetrisRuleAction {
         PERMIT, DENY
@@ -26,11 +28,13 @@ public class NetrisNetworkRule {
 
     private SDNProviderNetworkRule baseRule;
     private NetrisRuleAction aclAction;
+    private List<NetrisLbBackend> lbBackends;
     private String reason;
 
     public NetrisNetworkRule(Builder builder) {
         this.baseRule = builder.baseRule;
         this.aclAction = builder.aclAction;
+        this.lbBackends = builder.lbBackends;
         this.reason = builder.reason;
     }
 
@@ -38,6 +42,10 @@ public class NetrisNetworkRule {
         return aclAction;
     }
 
+    public List<NetrisLbBackend> getLbBackends() {
+        return lbBackends;
+    }
+
     public String getReason() {
         return reason;
     }
@@ -50,6 +58,7 @@ public class NetrisNetworkRule {
     public static class Builder {
         private SDNProviderNetworkRule baseRule;
         private NetrisRuleAction aclAction;
+        private List<NetrisLbBackend> lbBackends;
         private String reason;
 
         public Builder baseRule(SDNProviderNetworkRule baseRule) {
@@ -62,6 +71,11 @@ public class NetrisNetworkRule {
             return this;
         }
 
+        public Builder lbBackends(List<NetrisLbBackend> lbBackends) {
+            this.lbBackends = lbBackends;
+            return this;
+        }
+
         public Builder reason(String reason) {
             this.reason = reason;
             return this;
diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java 
b/api/src/main/java/com/cloud/network/netris/NetrisService.java
index 76a239422e0..d386d4f8d58 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -54,4 +54,7 @@ public interface NetrisService {
     boolean deleteStaticRoute(long zoneId, long accountId, long domainId, 
String networkResourceName, Long networkResourceId, boolean isForVpc, String 
prefix, String nextHop, Long routeId);
 
     boolean releaseNatIp(long zoneId, String publicIp);
+
+    boolean createLbRule(NetrisNetworkRule rule);
+    boolean deleteLbRule(NetrisNetworkRule rule);
 }
diff --git 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisLoadBalancerRuleCommand.java
 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisLoadBalancerRuleCommand.java
new file mode 100644
index 00000000000..34aeb4ca5e0
--- /dev/null
+++ 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNetrisLoadBalancerRuleCommand.java
@@ -0,0 +1,72 @@
+// 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 com.cloud.network.netris.NetrisLbBackend;
+
+import java.util.List;
+
+public class CreateOrUpdateNetrisLoadBalancerRuleCommand extends NetrisCommand 
{
+    private final String publicPort;
+    private final String privatePort;
+    private final String algorithm;
+    private final String protocol;
+    List<NetrisLbBackend> lbBackends;
+    private String publicIp;
+    private final Long lbId;
+
+    public CreateOrUpdateNetrisLoadBalancerRuleCommand(long zoneId, Long 
accountId, Long domainId, String name, Long id, boolean isVpc,
+                                                       List<NetrisLbBackend> 
lbBackends, long lbId, String publicIp, String publicPort,
+                                                       String privatePort, 
String algorithm, String protocol) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.lbId = lbId;
+        this.publicIp = publicIp;
+        this.publicPort = publicPort;
+        this.privatePort = privatePort;
+        this.algorithm = algorithm;
+        this.protocol = protocol;
+        this.lbBackends = lbBackends;
+    }
+
+    public String getPublicPort() {
+        return publicPort;
+    }
+
+    public String getPrivatePort() {
+        return privatePort;
+    }
+
+    public String getAlgorithm() {
+        return algorithm;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public List<NetrisLbBackend> getLbBackends() {
+        return lbBackends;
+    }
+
+    public Long getLbId() {
+        return lbId;
+    }
+
+    public String getPublicIp() {
+        return publicIp;
+    }
+}
diff --git 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisLoadBalancerRuleCommand.java
 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisLoadBalancerRuleCommand.java
new file mode 100644
index 00000000000..c114466d729
--- /dev/null
+++ 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/DeleteNetrisLoadBalancerRuleCommand.java
@@ -0,0 +1,34 @@
+// 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;
+
+public class DeleteNetrisLoadBalancerRuleCommand extends NetrisCommand {
+    private Long lbId;
+
+    public DeleteNetrisLoadBalancerRuleCommand(long zoneId, Long accountId, 
Long domainId, String name, Long id, boolean isVpc, Long lbId) {
+        super(zoneId, accountId, domainId, name, id, isVpc);
+        this.lbId = lbId;
+    }
+
+    public Long getLbId() {
+        return lbId;
+    }
+
+    public void setLbId(Long lbId) {
+        this.lbId = lbId;
+    }
+}
diff --git 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
index 989987b9a41..5d4777d610c 100644
--- 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
+++ 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResource.java
@@ -32,8 +32,10 @@ import 
org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import 
org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisACLCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
@@ -118,6 +120,10 @@ public class NetrisResource implements ServerResource {
             return executeRequest((AddOrUpdateNetrisStaticRouteCommand) cmd);
         } else if (cmd instanceof ReleaseNatIpCommand) {
           return executeRequest((ReleaseNatIpCommand) cmd);
+        } else if (cmd instanceof CreateOrUpdateNetrisLoadBalancerRuleCommand) 
{
+            return 
executeRequest((CreateOrUpdateNetrisLoadBalancerRuleCommand) cmd);
+        } else if (cmd instanceof DeleteNetrisLoadBalancerRuleCommand) {
+          return executeRequest((DeleteNetrisLoadBalancerRuleCommand) cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
@@ -352,6 +358,31 @@ public class NetrisResource implements ServerResource {
         return new NetrisAnswer(cmd, true, "OK");
     }
 
+    private Answer executeRequest(CreateOrUpdateNetrisLoadBalancerRuleCommand 
cmd) {
+        boolean result = netrisApiClient.createLbRule(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Failed to 
create Netris LB rule for %s: %s, " +
+                    "for private port: %s and public port: %s", 
getNetworkType(cmd.isVpc()), cmd.getName(), cmd.getPrivatePort(), 
cmd.getPublicPort(), cmd.getPublicPort()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
+    private Answer executeRequest(DeleteNetrisLoadBalancerRuleCommand cmd) {
+        boolean result = netrisApiClient.deleteLbRule(cmd);
+        if (!result) {
+            return new NetrisAnswer(cmd, false, String.format("Failed to 
delete Netris LB rule for %s: %s, " +
+                    "for private port: %s and public port: %s", 
getNetworkType(cmd.isVpc()), cmd.getName()));
+        }
+        return new NetrisAnswer(cmd, true, "OK");
+    }
+
+    private String getNetworkType(Boolean isVpc) {
+        if (isVpc) {
+            return "VPC";
+        }
+        return "Network";
+    }
+
     @Override
     public boolean start() {
         return true;
diff --git 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
index 9caff9793ff..296f74b1c29 100644
--- 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
+++ 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/resource/NetrisResourceObjectUtils.java
@@ -24,7 +24,7 @@ import java.util.Objects;
 public class NetrisResourceObjectUtils {
 
     public enum NetrisObjectType {
-        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT, 
STATICROUTE, ACL
+        VPC, IPAM_ALLOCATION, IPAM_SUBNET, VNET, SNAT, STATICNAT, DNAT, 
STATICROUTE, ACL, LB
     }
 
     public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, 
NetrisObjectType netrisObjectType, String... suffixes) {
@@ -59,7 +59,7 @@ public class NetrisResourceObjectUtils {
                 break;
             case IPAM_SUBNET:
                 if (!isZoneLevel) {
-                    if (Objects.nonNull(objectId) && 
Objects.nonNull(objectName)) {
+                    if (Objects.nonNull(objectId) && 
Objects.nonNull(objectName) && !isVpc) {
                         stringBuilder.append(String.format("-N%s", objectId));
                     } else {
                         stringBuilder.append(String.format("-V%s-%s", 
suffixes[0], suffixes[1]));
@@ -86,6 +86,9 @@ public class NetrisResourceObjectUtils {
             case VNET:
             case ACL:
                break;
+            case LB:
+                stringBuilder.append(String.format("%s%s", prefix, objectId));
+                break;
             default:
                 stringBuilder.append(String.format("-%s", objectName));
                 break;
diff --git 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
index f5efb4b55ea..91351bf21f7 100644
--- 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
+++ 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClient.java
@@ -24,8 +24,10 @@ import 
org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
+import 
org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisACLCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
@@ -84,4 +86,6 @@ public interface NetrisApiClient {
     boolean addOrUpdateStaticRoute(AddOrUpdateNetrisStaticRouteCommand cmd);
     boolean deleteStaticRoute(DeleteNetrisStaticRouteCommand cmd);
     boolean releaseNatIp(ReleaseNatIpCommand cmd);
+    boolean createLbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd);
+    boolean deleteLbRule(DeleteNetrisLoadBalancerRuleCommand cmd);
 }
diff --git 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
index e61482a3564..e49c7c5882a 100644
--- 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
+++ 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisApiClientImpl.java
@@ -16,8 +16,10 @@
 // under the License.
 package org.apache.cloudstack.service;
 
+import com.cloud.network.netris.NetrisLbBackend;
 import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
 import inet.ipaddr.IPAddress;
 import inet.ipaddr.IPAddressString;
 import io.netris.ApiClient;
@@ -29,6 +31,7 @@ import io.netris.api.v1.RoutesApi;
 import io.netris.api.v1.SitesApi;
 import io.netris.api.v1.TenantsApi;
 import io.netris.api.v2.IpamApi;
+import io.netris.api.v2.L4LoadBalancerApi;
 import io.netris.api.v2.NatApi;
 import io.netris.api.v2.VNetApi;
 import io.netris.api.v2.VpcApi;
@@ -52,12 +55,20 @@ import io.netris.model.IpTreeAllocation;
 import io.netris.model.IpTreeAllocationTenant;
 import io.netris.model.IpTreeSubnet;
 import io.netris.model.IpTreeSubnetSites;
+import io.netris.model.L4LBSite;
+import io.netris.model.L4LbTenant;
+import io.netris.model.L4LbVpc;
+import io.netris.model.L4LoadBalancerBackendItem;
+import io.netris.model.L4LoadBalancerItem;
+import io.netris.model.L4lbAddItem;
+import io.netris.model.L4lbresBody;
 import io.netris.model.NatBodySiteSite;
 import io.netris.model.NatBodyVpcVpc;
 import io.netris.model.NatGetBody;
 import io.netris.model.NatPostBody;
 import io.netris.model.NatPutBody;
 import io.netris.model.NatResponseGetOk;
+import io.netris.model.ResAddEditBody;
 import io.netris.model.RoutesBody;
 import io.netris.model.RoutesBodyId;
 import io.netris.model.RoutesBodyVpcVpc;
@@ -90,10 +101,12 @@ import io.netris.model.response.TenantResponse;
 import io.netris.model.response.TenantsResponse;
 import org.apache.cloudstack.agent.api.CreateNetrisACLCommand;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
+import 
org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisACLCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
@@ -598,6 +611,169 @@ public class NetrisApiClientImpl implements 
NetrisApiClient {
         return true;
     }
 
+    @Override
+    public boolean createLbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand 
cmd) {
+        boolean isVpc = cmd.isVpc();
+        Long networkResourceId = cmd.getId();
+        String networkResourceName = cmd.getName();
+        Long domainId = cmd.getDomainId();
+        Long accountId = cmd.getAccountId();
+        Long zoneId = cmd.getZoneId();
+        Long lbId = cmd.getLbId();
+        String publicIp = cmd.getPublicIp();
+        List<NetrisLbBackend> lbBackends = cmd.getLbBackends();
+
+        try {
+            String resourcePrefix = isVpc ? "V" : "N";
+            String netrisResourceName = String.format("D%s-A%s-Z%s-%s%s-%s", 
domainId, accountId, zoneId, resourcePrefix, networkResourceId, 
networkResourceName);
+            VPCListing vpcResource = getNetrisVpcResource(netrisResourceName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", netrisResourceName, tenantId);
+                return false;
+            }
+            createLBSubnet(cmd, publicIp + "/32", vpcResource.getId());
+
+            String suffix = String.format("LB%s", lbId);
+            String lbName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.LB, suffix);
+            Pair<Boolean, List<BigDecimal>> resultAndMatchingLbId = 
getMatchingLbRule(lbName, vpcResource.getName());
+            Boolean result = resultAndMatchingLbId.first();
+            List<BigDecimal> matchingLbId = resultAndMatchingLbId.second();
+            if (Boolean.FALSE.equals(result)) {
+                logger.warn("Could not find the Netris LB rule with name {}", 
lbName);
+            }
+            if (!matchingLbId.isEmpty()) {
+                logger.warn("LB rule by name: {} already exists", lbName);
+                return true;
+            }
+
+            L4lbAddItem l4lbAddItem = getL4LbRule(cmd, vpcResource, lbName, 
publicIp, lbBackends);
+            L4LoadBalancerApi loadBalancerApi = 
apiClient.getApiStubForMethod(L4LoadBalancerApi.class);
+            ResAddEditBody response = 
loadBalancerApi.apiV2L4lbPost(l4lbAddItem);
+            if (Objects.isNull(response) || 
Boolean.FALSE.equals(response.isIsSuccess())) {
+                throw new CloudRuntimeException("Failed to create Netris LB 
rule");
+            }
+        } catch (ApiException e) {
+            logAndThrowException("Failed to create Netris load balancer rule", 
e);
+        }
+        return true;
+    }
+
+    private L4lbAddItem 
getL4LbRule(CreateOrUpdateNetrisLoadBalancerRuleCommand cmd, VPCListing 
vpcResource, String lbName,
+                             String publicIp, List<NetrisLbBackend> 
lbBackends) {
+        L4lbAddItem l4lbAddItem = new L4lbAddItem();
+        try {
+            l4lbAddItem.setName(lbName);
+
+            String protocol = cmd.getProtocol().toUpperCase(Locale.ROOT);
+            if (!Arrays.asList("TCP", "UDP").contains(protocol)) {
+                throw new CloudRuntimeException("Invalid protocol " + 
protocol);
+            }
+            
l4lbAddItem.setProtocol(cmd.getProtocol().toUpperCase(Locale.ROOT));
+            L4LBSite site = new L4LBSite();
+            site.setId(siteId);
+            site.setName(siteName);
+            l4lbAddItem.setSite(site);
+            l4lbAddItem.setSiteID(new BigDecimal(siteId));
+
+            L4LbTenant tenant = new L4LbTenant();
+            tenant.setId(tenantId);
+            tenant.setName(tenantName);
+            l4lbAddItem.setTenant(tenant);
+
+            L4LbVpc vpc = new L4LbVpc();
+            vpc.setId(vpcResource.getId());
+            l4lbAddItem.setVpc(vpc);
+
+            l4lbAddItem.setAutomatic(false);
+            l4lbAddItem.setIpFamily(NetUtils.isIpv4(publicIp) ? 
L4lbAddItem.IpFamilyEnum.IPv4 : L4lbAddItem.IpFamilyEnum.IPv6);
+            l4lbAddItem.setIp(publicIp);
+            l4lbAddItem.setStatus("enable");
+
+            List<L4LoadBalancerBackendItem> backends = new ArrayList<>();
+            for (NetrisLbBackend backend : lbBackends) {
+                L4LoadBalancerBackendItem backendItem = new 
L4LoadBalancerBackendItem();
+                backendItem.setIp(backend.getVmIp());
+                backendItem.setPort(backend.getPort());
+                backends.add(backendItem);
+            }
+            l4lbAddItem.setBackend(backends);
+            l4lbAddItem.setPort(Integer.valueOf(cmd.getPublicPort()));
+            l4lbAddItem.setHealthCheck(L4lbAddItem.HealthCheckEnum.NONE);
+        } catch (Exception e) {
+            throw new CloudRuntimeException("Failed to create Netris load 
balancer rule", e);
+        }
+        return  l4lbAddItem;
+    }
+
+    @Override
+    public boolean deleteLbRule(DeleteNetrisLoadBalancerRuleCommand cmd) {
+        boolean isVpc = cmd.isVpc();
+        String vpcName = null;
+        String networkName = null;
+        Long vpcId = null;
+        Long networkId = null;
+        if (isVpc) {
+            vpcName = cmd.getName();
+            vpcId = cmd.getId();
+        } else {
+            networkName = cmd.getName();
+            networkId = cmd.getId();
+        }
+        Long lbId = cmd.getLbId();
+        try {
+            String suffix = getNetrisVpcNameSuffix(vpcId, vpcName, networkId, 
networkName, isVpc);
+            String netrisVpcName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.VPC, suffix);
+            suffix = String.format("LB%s", lbId);
+            String lbName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, 
NetrisResourceObjectUtils.NetrisObjectType.LB, suffix);
+            Pair<Boolean, List<BigDecimal>> resultAndMatchingLbId = 
getMatchingLbRule(lbName, netrisVpcName);
+            Boolean result = resultAndMatchingLbId.first();
+            List<BigDecimal> matchingLbId = resultAndMatchingLbId.second();
+            if (!result) {
+                logger.error("Could not find the Netris LB rule with name {}", 
lbName);
+                return false;
+            }
+            if (matchingLbId.isEmpty()) {
+                logger.warn("There doesn't seem to be any LB rule on Netris 
matching {}", lbName);
+                return true;
+            }
+
+            L4LoadBalancerApi lbApi = 
apiClient.getApiStubForMethod(L4LoadBalancerApi.class);
+            lbApi.apiV2L4lbIdDelete(matchingLbId.get(0).intValue());
+        } catch (ApiException e) {
+            logAndThrowException("Failed to delete Netris load balancer rule", 
e);
+        }
+        return true;
+    }
+
+    private Pair<Boolean, List<BigDecimal>> getMatchingLbRule(String lbName, 
String vpcName) {
+        try {
+            VPCListing vpcResource = getVpcByNameAndTenant(vpcName);
+            if (vpcResource == null) {
+                logger.error("Could not find the Netris VPC resource with name 
{} and tenant ID {}", vpcName, tenantId);
+                return new Pair<>(false, Collections.emptyList());
+            }
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(vpcResource.getId());
+            FilterBySites siteFilter = new FilterBySites();
+            siteFilter.add(siteId);
+            L4LoadBalancerApi lbApi = 
apiClient.getApiStubForMethod(L4LoadBalancerApi.class);
+            L4lbresBody lbGetResponse = lbApi.apiV2L4lbGet(siteFilter, 
vpcFilter);
+            if (lbGetResponse == null || !lbGetResponse.isIsSuccess()) {
+                logger.warn("No LB rules were found to be present for the 
specific Netris VPC resource {}." +
+                        " Netris LB rules may have been deleted out of band.", 
vpcName);
+                return new Pair<>(true, Collections.emptyList());
+            }
+            List<L4LoadBalancerItem> lbList = lbGetResponse.getData();
+            return new Pair<>(true, lbList.stream()
+                    .filter(lb -> lbName.equals(lb.getName()))
+                    .map(acl -> BigDecimal.valueOf(acl.getId()))
+                    .collect(Collectors.toList()));
+        } catch (ApiException e) {
+            logAndThrowException("Failed to retrieve Netris LB rules", e);
+        }
+        return new Pair<>(true, Collections.emptyList());
+    }
+
     private Pair<Boolean, RoutesGetBody> staticRouteExists(Integer 
netrisVpcId, String prefix, String nextHop, String description) {
         try {
             FilterByVpc vpcFilter = new FilterByVpc();
@@ -904,7 +1080,7 @@ public class NetrisApiClientImpl implements 
NetrisApiClient {
         }
 
         if (StringUtils.isNotBlank(targetIpSubnet) && 
existsDestinationSubnet(targetIpSubnet)) {
-            logger.debug(String.format("Creating subnet with NAT purpose for 
%s", targetIpSubnet));
+            logger.debug("Creating subnet with NAT purpose for {}}", 
targetIpSubnet);
             createNatSubnet(cmd, targetIpSubnet, vpcResource.getId());
         }
 
@@ -1022,6 +1198,27 @@ public class NetrisApiClientImpl implements 
NetrisApiClient {
         }
     }
 
+    private void createLBSubnet(NetrisCommand cmd, String lbIp, Integer 
netrisVpcId) {
+        try {
+            FilterByVpc vpcFilter = new FilterByVpc();
+            vpcFilter.add(netrisVpcId);
+            String netrisSubnetName = 
NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd,
+                    NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET,
+                    String.valueOf(cmd.getId()), lbIp);
+            List<IpTreeSubnet> matchedSubnets = getSubnet(vpcFilter, 
netrisSubnetName);
+            VPCListing systemVpc = getSystemVpc();
+            if (matchedSubnets.isEmpty()) {
+                createIpamSubnetInternal(netrisSubnetName, lbIp, 
SubnetBody.PurposeEnum.LOAD_BALANCER, systemVpc, null);
+            } else if (IpTreeSubnet.PurposeEnum.LOAD_BALANCER != 
matchedSubnets.get(0).getPurpose()){
+                logger.debug("Updating existing NAT subnet {} to have load 
balancer purpose", netrisSubnetName);
+                
updateIpamSubnetInternal(matchedSubnets.get(0).getId().intValue(), 
netrisSubnetName, lbIp, SubnetBody.PurposeEnum.LOAD_BALANCER, systemVpc, null);
+            }
+            logger.debug("LB subnet: {} already exists", netrisSubnetName);
+        } catch (ApiException e) {
+            throw new CloudRuntimeException(String.format("Failed to create 
subnet for %s with LB purpose", lbIp));
+        }
+    }
+
     private NatPostBody.ProtocolEnum getProtocolFromString(String protocol) {
         return NatPostBody.ProtocolEnum.fromValue(protocol);
     }
@@ -1204,33 +1401,38 @@ public class NetrisApiClientImpl implements 
NetrisApiClient {
         }
     }
 
+    private SubnetBody getIpamSubnetBody(VPCListing vpc, 
SubnetBody.PurposeEnum purpose, String subnetName, String subnetPrefix, Boolean 
isGlobalRouting) {
+        SubnetBody subnetBody = new SubnetBody();
+        subnetBody.setName(subnetName);
+
+        AllocationBodyVpc vpcAllocationBody = new AllocationBodyVpc();
+        vpcAllocationBody.setName(vpc.getName());
+        vpcAllocationBody.setId(vpc.getId());
+        subnetBody.setVpc(vpcAllocationBody);
+
+        IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
+        allocationTenant.setId(new BigDecimal(tenantId));
+        allocationTenant.setName(tenantName);
+        subnetBody.setTenant(allocationTenant);
+
+        IpTreeSubnetSites subnetSites = new IpTreeSubnetSites();
+        subnetSites.setId(new BigDecimal(siteId));
+        subnetSites.setName(siteName);
+        subnetBody.setSites(List.of(subnetSites));
+
+        subnetBody.setPurpose(purpose);
+        subnetBody.setPrefix(subnetPrefix);
+        if (isGlobalRouting != null) {
+            subnetBody.setGlobalRouting(isGlobalRouting);
+        }
+        return subnetBody;
+    }
+
     private InlineResponse2004Data createIpamSubnetInternal(String subnetName, 
String subnetPrefix, SubnetBody.PurposeEnum purpose, VPCListing vpc, Boolean 
isGlobalRouting) {
         logger.debug("Creating Netris IPAM Subnet {} for VPC {}", 
subnetPrefix, vpc.getName());
         try {
 
-            SubnetBody subnetBody = new SubnetBody();
-            subnetBody.setName(subnetName);
-
-            AllocationBodyVpc vpcAllocationBody = new AllocationBodyVpc();
-            vpcAllocationBody.setName(vpc.getName());
-            vpcAllocationBody.setId(vpc.getId());
-            subnetBody.setVpc(vpcAllocationBody);
-
-            IpTreeAllocationTenant allocationTenant = new 
IpTreeAllocationTenant();
-            allocationTenant.setId(new BigDecimal(tenantId));
-            allocationTenant.setName(tenantName);
-            subnetBody.setTenant(allocationTenant);
-
-            IpTreeSubnetSites subnetSites = new IpTreeSubnetSites();
-            subnetSites.setId(new BigDecimal(siteId));
-            subnetSites.setName(siteName);
-            subnetBody.setSites(List.of(subnetSites));
-
-            subnetBody.setPurpose(purpose);
-            subnetBody.setPrefix(subnetPrefix);
-            if (isGlobalRouting != null) {
-                subnetBody.setGlobalRouting(isGlobalRouting);
-            }
+            SubnetBody subnetBody = getIpamSubnetBody(vpc, purpose, 
subnetName, subnetPrefix, isGlobalRouting);
             IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
             InlineResponse2004 subnetResponse = 
ipamApi.apiV2IpamSubnetPost(subnetBody);
             if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
@@ -1245,6 +1447,25 @@ public class NetrisApiClientImpl implements 
NetrisApiClient {
         }
     }
 
+    private InlineResponse2004Data updateIpamSubnetInternal(Integer 
netrisSubnetId, String subnetName, String subnetPrefix, SubnetBody.PurposeEnum 
purpose, VPCListing vpc, Boolean isGlobalRouting) {
+        logger.debug("Updating Netris IPAM Subnet {} for VPC {}", 
subnetPrefix, vpc.getName());
+        try {
+
+            SubnetBody subnetBody = getIpamSubnetBody(vpc, purpose, 
subnetName, subnetPrefix, isGlobalRouting);
+            IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+            InlineResponse2004 subnetResponse = 
ipamApi.apiV2IpamSubnetIdPut(subnetBody, netrisSubnetId);
+            if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
+                String reason = subnetResponse == null ? "Empty response" : 
"Operation failed on Netris";
+                logger.debug("The Netris IPAM Subnet {} update failed: {}", 
subnetName, reason);
+                throw new CloudRuntimeException(reason);
+            }
+            return subnetResponse.getData();
+        } catch (ApiException e) {
+            logAndThrowException(String.format("Error Updating Netris IPAM 
Subnet %s for VPC %s", subnetPrefix, vpc.getName()), e);
+            return null;
+        }
+    }
+
     VnetResAddBody createVnetInternal(VPCListing associatedVpc, String 
netrisVnetName, String netrisGateway, String netrisV6Gateway, Integer vxlanId, 
String netrisTag) {
         logger.debug("Creating Netris VPC vNet {} for CIDR {}", 
netrisVnetName, netrisGateway);
         try {
diff --git 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
index 0f25d216719..12e7239a8ee 100644
--- 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
+++ 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisElement.java
@@ -23,6 +23,7 @@ import com.cloud.agent.api.AgentControlCommand;
 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,16 +46,21 @@ import com.cloud.network.SDNProviderNetworkRule;
 import com.cloud.network.SDNProviderOpObject;
 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.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.NetworkACLServiceProvider;
 import com.cloud.network.element.PortForwardingServiceProvider;
 import com.cloud.network.element.StaticNatServiceProvider;
 import com.cloud.network.element.VirtualRouterElement;
 import com.cloud.network.element.VpcProvider;
+import com.cloud.network.lb.LoadBalancingRule;
+import com.cloud.network.netris.NetrisLbBackend;
 import com.cloud.network.netris.NetrisService;
 import com.cloud.network.rules.FirewallRule;
 import com.cloud.network.rules.LoadBalancerContainer;
@@ -107,7 +113,8 @@ import java.util.Set;
 
 @Component
 public class NetrisElement extends AdapterBase implements DhcpServiceProvider, 
DnsServiceProvider, VpcProvider,
-        StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider, 
NetworkACLServiceProvider, ResourceStateAdapter, Listener {
+        StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider, 
NetworkACLServiceProvider,
+        LoadBalancingServiceProvider, ResourceStateAdapter, Listener {
 
     @Inject
     NetworkModel networkModel;
@@ -133,6 +140,8 @@ public class NetrisElement extends AdapterBase implements 
DhcpServiceProvider, D
     private IPAddressDao ipAddressDao;
     @Inject
     private VMInstanceDao vmInstanceDao;
+    @Inject
+    LoadBalancerVMMapDao lbVmMapDao;
 
     protected Logger logger = LogManager.getLogger(getClass());
 
@@ -693,4 +702,85 @@ public class NetrisElement extends AdapterBase implements 
DhcpServiceProvider, D
                 return 0;
         }
     }
+
+    private SDNProviderOpObject getNetrisObject(Network network) {
+        Pair<VpcVO, NetworkVO> vpcOrNetwork = 
getVpcOrNetwork(network.getVpcId(), network.getId());
+        VpcVO vpc = vpcOrNetwork.first();
+        NetworkVO networkVO = vpcOrNetwork.second();
+        long domainId = getResourceId("domain", vpc, networkVO);
+        long accountId = getResourceId("account", vpc, networkVO);
+        long zoneId = getResourceId("zone", vpc, networkVO);
+
+        return new SDNProviderOpObject.Builder()
+                .vpcVO(vpc)
+                .networkVO(networkVO)
+                .domainId(domainId)
+                .accountId(accountId)
+                .zoneId(zoneId)
+                .build();
+    }
+
+    @Override
+    public boolean applyLBRules(Network network, List<LoadBalancingRule> 
rules) throws ResourceUnavailableException {
+        boolean result = true;
+        for (LoadBalancingRule loadBalancingRule : rules) {
+            IPAddressVO publicIp = 
ipAddressDao.findByIpAndDcId(network.getDataCenterId(),
+                    loadBalancingRule.getSourceIp().addr());
+
+            List<NetrisLbBackend> lbBackends = 
getLoadBalancerBackends(loadBalancingRule);
+            SDNProviderOpObject netrisObject = getNetrisObject(network);
+            SDNProviderNetworkRule baseNetworkRule = new 
SDNProviderNetworkRule.Builder()
+                    .setDomainId(netrisObject.getDomainId())
+                    .setAccountId(netrisObject.getAccountId())
+                    .setZoneId(netrisObject.getZoneId())
+                    .setNetworkResourceId(netrisObject.getNetworkResourceId())
+                    
.setNetworkResourceName(netrisObject.getNetworkResourceName())
+                    .setVpcResource(netrisObject.isVpcResource())
+                    .setPublicIp(LoadBalancerContainer.Scheme.Public == 
loadBalancingRule.getScheme() ?
+                            publicIp.getAddress().addr() : 
loadBalancingRule.getSourceIp().addr())
+                    
.setPrivatePort(String.valueOf(loadBalancingRule.getDefaultPortStart()))
+                    
.setPublicPort(String.valueOf(loadBalancingRule.getSourcePortStart()))
+                    .setRuleId(loadBalancingRule.getId())
+                    
.setProtocol(loadBalancingRule.getProtocol().toUpperCase(Locale.ROOT))
+                    .setAlgorithm(loadBalancingRule.getAlgorithm())
+                    .build();
+
+            NetrisNetworkRule networkRule = new NetrisNetworkRule.Builder()
+                    .baseRule(baseNetworkRule)
+                    .lbBackends(lbBackends)
+                    .build();
+            if (Arrays.asList(FirewallRule.State.Add, 
FirewallRule.State.Active).contains(loadBalancingRule.getState())) {
+                result &= netrisService.createLbRule(networkRule);
+            } else if (loadBalancingRule.getState() == 
FirewallRule.State.Revoke) {
+                result &= netrisService.deleteLbRule(networkRule);
+            }
+        }
+        return result;
+    }
+
+    private List<NetrisLbBackend> getLoadBalancerBackends(LoadBalancingRule 
lbRule) {
+        List<LoadBalancerVMMapVO> lbVms = 
lbVmMapDao.listByLoadBalancerId(lbRule.getId(), false);
+        List<NetrisLbBackend> lbMembers = new ArrayList<>();
+
+        for (LoadBalancerVMMapVO lbVm : lbVms) {
+            NetrisLbBackend member = new NetrisLbBackend(lbVm.getInstanceId(), 
lbVm.getInstanceIp(), lbRule.getDefaultPortStart());
+            lbMembers.add(member);
+        }
+        return lbMembers;
+    }
+
+    @Override
+    public boolean validateLBRule(Network network, LoadBalancingRule rule) {
+        return true;
+    }
+
+    @Override
+    public List<LoadBalancerTO> updateHealthChecks(Network network, 
List<LoadBalancingRule> lbrules) {
+        return List.of();
+    }
+
+    @Override
+    public boolean handlesOnlyRulesInTransitionState() {
+        return false;
+    }
 }
diff --git 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
index 337ea849dcb..62064c1ac07 100644
--- 
a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
+++ 
b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisServiceImpl.java
@@ -44,10 +44,12 @@ import com.cloud.utils.net.NetUtils;
 import inet.ipaddr.IPAddress;
 import inet.ipaddr.IPAddressString;
 import org.apache.cloudstack.agent.api.AddOrUpdateNetrisStaticRouteCommand;
+import 
org.apache.cloudstack.agent.api.CreateOrUpdateNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
 import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
 import org.apache.cloudstack.agent.api.CreateOrUpdateNetrisNatCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisACLCommand;
+import org.apache.cloudstack.agent.api.DeleteNetrisLoadBalancerRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisNatRuleCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisStaticRouteCommand;
 import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
@@ -455,6 +457,27 @@ public class NetrisServiceImpl implements NetrisService, 
Configurable {
         return answer.getResult();
     }
 
+    @Override
+    public boolean createLbRule(NetrisNetworkRule rule) {
+        SDNProviderNetworkRule baseRule = rule.getBaseRule();
+        CreateOrUpdateNetrisLoadBalancerRuleCommand cmd = new 
CreateOrUpdateNetrisLoadBalancerRuleCommand(baseRule.getZoneId(), 
baseRule.getAccountId(),
+                baseRule.getDomainId(), baseRule.getNetworkResourceName(), 
baseRule.getNetworkResourceId(), baseRule.isVpcResource(),
+                rule.getLbBackends(), baseRule.getRuleId(), 
baseRule.getPublicIp(), baseRule.getPublicPort(),
+                baseRule.getPrivatePort(), baseRule.getAlgorithm(), 
baseRule.getProtocol());
+        NetrisAnswer answer = sendNetrisCommand(cmd, baseRule.getZoneId());
+        return answer.getResult();
+    }
+
+    @Override
+    public boolean deleteLbRule(NetrisNetworkRule rule) {
+        SDNProviderNetworkRule baseRule = rule.getBaseRule();
+        DeleteNetrisLoadBalancerRuleCommand cmd = new 
DeleteNetrisLoadBalancerRuleCommand(baseRule.getZoneId(), 
baseRule.getAccountId(),
+                baseRule.getDomainId(), baseRule.getNetworkResourceName(), 
baseRule.getNetworkResourceId(), baseRule.isVpcResource(),
+                baseRule.getRuleId());
+        NetrisAnswer answer = sendNetrisCommand(cmd, baseRule.getZoneId());
+        return answer.getResult();
+    }
+
     private String getResourceSuffix(Long vpcId, Long networkId, boolean 
isForVpc) {
         String suffix;
         if (isForVpc) {
diff --git 
a/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java 
b/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
index 015cbe49049..1615eec4c5c 100644
--- 
a/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
+++ 
b/server/src/main/java/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java
@@ -24,6 +24,7 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -2245,6 +2246,7 @@ public class LoadBalancingRulesManagerImpl<Type> extends 
ManagerBase implements
             lb.setLbProtocol(lbProtocol);
         }
 
+        validateInputsForExternalNetworkProvider(lb, algorithm, lbProtocol);
         // Validate rule in LB provider
         LoadBalancingRule rule = getLoadBalancerRuleToApply(lb);
         if (!validateLbRule(rule)) {
@@ -2294,6 +2296,18 @@ public class LoadBalancingRulesManagerImpl<Type> extends 
ManagerBase implements
         return lb;
     }
 
+    private void validateInputsForExternalNetworkProvider(LoadBalancerVO lb, 
String algorithm, String protocol) {
+        Network network = _networkDao.findById(lb.getNetworkId());
+        if 
(_networkOfferingServiceDao.canProviderSupportServiceInNetworkOffering(network.getNetworkOfferingId(),
 Service.Lb, Provider.Netris)) {
+            if (Objects.nonNull(algorithm)) {
+                throw new 
InvalidParameterValueException(String.format("Algorithm: %s specified for 
Netris Provider is not supported.", algorithm));
+            }
+            if (Objects.nonNull(protocol) && 
"tcp-proxy".equalsIgnoreCase(protocol)) {
+                throw new InvalidParameterValueException("TCP Proxy protocol 
is not supported for Netris Provider.");
+            }
+        }
+    }
+
     @Override
     public Pair<List<? extends UserVm>, List<String>> 
listLoadBalancerInstances(ListLoadBalancerRuleInstancesCmd cmd) throws 
PermissionDeniedException {
         Account caller = CallContext.current().getCallingAccount();
diff --git 
a/server/src/test/java/com/cloud/network/lb/UpdateLoadBalancerTest.java 
b/server/src/test/java/com/cloud/network/lb/UpdateLoadBalancerTest.java
index c3e8923b943..729d2ea8ff5 100644
--- a/server/src/test/java/com/cloud/network/lb/UpdateLoadBalancerTest.java
+++ b/server/src/test/java/com/cloud/network/lb/UpdateLoadBalancerTest.java
@@ -33,6 +33,7 @@ import com.cloud.network.dao.LoadBalancerVO;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkVO;
 import com.cloud.network.element.LoadBalancingServiceProvider;
+import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
 import com.cloud.user.Account;
 import com.cloud.user.AccountVO;
 import com.cloud.user.MockAccountManagerImpl;
@@ -52,6 +53,7 @@ import java.util.UUID;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
@@ -63,6 +65,7 @@ public class UpdateLoadBalancerTest {
     private LoadBalancerDao lbDao = Mockito.mock(LoadBalancerDao.class);
     private NetworkDao netDao = Mockito.mock(NetworkDao.class);
     private NetworkModel netModel = Mockito.mock(NetworkModel.class);
+    private NetworkOfferingServiceMapDao ntwkOffServiceMapDao = 
Mockito.mock(NetworkOfferingServiceMapDao.class);
     private LoadBalancingServiceProvider lbServiceProvider= 
Mockito.mock(LoadBalancingServiceProvider.class);
 
     private static long domainId = 5L;
@@ -73,6 +76,7 @@ public class UpdateLoadBalancerTest {
         _lbMgr._accountMgr = new MockAccountManagerImpl();
         _lbMgr._autoScaleVmGroupDao = Mockito.mock(AutoScaleVmGroupDao.class);
         _lbMgr._networkDao = netDao;
+        _lbMgr._networkOfferingServiceDao = ntwkOffServiceMapDao;
         _lbMgr._networkModel = netModel;
         _lbMgr._lb2healthcheckDao = Mockito.mock(LBHealthCheckPolicyDao.class);
         _lbMgr._lb2stickinesspoliciesDao = 
Mockito.mock(LBStickinessPolicyDao.class);
@@ -99,7 +103,8 @@ public class UpdateLoadBalancerTest {
         
when(netDao.findById(anyLong())).thenReturn(Mockito.mock(NetworkVO.class));
         when(lbServiceProvider.validateLBRule(any(Network.class), 
any(LoadBalancingRule.class))).thenReturn(true);
         when(lbDao.update(isNull(), eq(lb))).thenReturn(true);
-
+        
when(netDao.findById(nullable(Long.class))).thenReturn(Mockito.mock(NetworkVO.class));
+        
when(ntwkOffServiceMapDao.canProviderSupportServiceInNetworkOffering(nullable(Long.class),
 any(Network.Service.class), any(Network.Provider.class))).thenReturn(false);
         _lbMgr.updateLoadBalancerRule(updateLbRuleCmd);
 
         InOrder inOrder = Mockito.inOrder(lbServiceProvider, lbDao);
diff --git 
a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java 
b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
index b8e4835f589..99f319d6de7 100644
--- 
a/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
+++ 
b/server/src/test/java/org/apache/cloudstack/service/NetrisServiceMockTest.java
@@ -105,4 +105,14 @@ public class NetrisServiceMockTest implements 
NetrisService {
     public boolean releaseNatIp(long zoneId, String publicIp) {
         return true;
     }
+
+    @Override
+    public boolean createLbRule(NetrisNetworkRule rule) {
+        return true;
+    }
+
+    @Override
+    public boolean deleteLbRule(NetrisNetworkRule rule) {
+        return true;
+    }
 }
diff --git a/ui/src/views/network/LoadBalancing.vue 
b/ui/src/views/network/LoadBalancing.vue
index 09f9a39c4cf..8e3d1e4c90f 100644
--- a/ui/src/views/network/LoadBalancing.vue
+++ b/ui/src/views/network/LoadBalancing.vue
@@ -40,7 +40,7 @@
           <tooltip-label :title="$t('label.sourcecidrlist')" bold 
:tooltip="createLoadBalancerRuleParams.cidrlist.description" 
:tooltip-placement="'right'"/>
           <a-input v-model:value="newRule.cidrlist"></a-input>
         </div>
-        <div class="form__item">
+        <div class="form__item" v-if="lbProvider !== 'Netris'">
           <div class="form__label">{{ $t('label.algorithm') }}</div>
           <a-select
             v-model:value="newRule.algorithm"
@@ -64,7 +64,7 @@
             :filterOption="(input, option) => {
               return option.label.toLowerCase().indexOf(input.toLowerCase()) 
>= 0
             }" >
-            <a-select-option value="tcp-proxy" 
:label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
+            <a-select-option v-if="lbProvider !== 'Netris'" value="tcp-proxy" 
:label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option>
             <a-select-option value="tcp" :label="$t('label.tcp')">{{ 
$t('label.tcp') }}</a-select-option>
             <a-select-option value="udp" :label="$t('label.udp')">{{ 
$t('label.udp') }}</a-select-option>
           </a-select>
@@ -409,7 +409,7 @@
           <p class="edit-rule__label">{{ $t('label.name') }}</p>
           <a-input v-focus="true" v-model:value="editRuleDetails.name" />
         </div>
-        <div class="edit-rule__item">
+        <div v-if="lbProvider !== 'Netris'" class="edit-rule__item">
           <p class="edit-rule__label">{{ $t('label.algorithm') }}</p>
           <a-select
             v-model:value="editRuleDetails.algorithm"
@@ -423,7 +423,7 @@
             <a-select-option value="source" 
:label="$t('label.lb.algorithm.source')">{{ $t('label.lb.algorithm.source') 
}}</a-select-option>
           </a-select>
         </div>
-        <div class="edit-rule__item">
+        <div v-if="lbProvider !== 'Netris'" class="edit-rule__item">
           <p class="edit-rule__label">{{ $t('label.protocol') }}</p>
           <a-select
             v-model:value="editRuleDetails.protocol"
@@ -781,9 +781,11 @@ export default {
         vmguestip: [],
         cidrlist: ''
       },
+      lbProvider: null,
       addVmModalVisible: false,
       addVmModalLoading: false,
       addVmModalNicLoading: false,
+      zoneloading: false,
       vms: [],
       nics: [],
       totalCount: 0,
@@ -802,10 +804,6 @@ export default {
           title: this.$t('label.privateport'),
           dataIndex: 'privateport'
         },
-        {
-          key: 'algorithm',
-          title: this.$t('label.algorithm')
-        },
         {
           key: 'cidrlist',
           title: this.$t('label.sourcecidrlist')
@@ -979,6 +977,7 @@ export default {
     fetchData () {
       this.fetchListTiers()
       this.fetchLBRules()
+      this.fetchZone()
     },
     fetchListTiers () {
       this.tiers.loading = true
@@ -1073,6 +1072,22 @@ export default {
         })
       })
     },
+    fetchZone () {
+      this.zoneloading = true
+      api('listZones', {
+        id: this.resource.zoneid
+      }).then(response => {
+        this.lbProvider = response?.listzonesresponse?.zone?.[0]?.provider || 
null
+      }).finally(() => {
+        this.zoneloading = false
+        if (this.lbProvider !== 'Netris') {
+          this.column.push({
+            key: 'algorithm',
+            title: this.$t('label.algorithm')
+          })
+        }
+      })
+    },
     returnAlgorithmName (name) {
       switch (name) {
         case 'leastconn':
@@ -1377,7 +1392,7 @@ export default {
       this.selectedRule = rule
       this.editRuleModalVisible = true
       this.editRuleDetails.name = this.selectedRule.name
-      this.editRuleDetails.algorithm = this.selectedRule.algorithm
+      this.editRuleDetails.algorithm = this.lbProvider !== 'Netris' ? 
this.selectedRule.algorithm : undefined
       this.editRuleDetails.protocol = this.selectedRule.protocol
     },
     handleSubmitEditForm () {

Reply via email to