You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pe...@apache.org on 2025/03/05 22:52:08 UTC
(cloudstack) 38/44: Phase5 - Support for LB - create, delete and Update operations (#49)
This is an automated email from the ASF dual-hosted git repository.
pearl11594 pushed a commit to branch netris-update-vpc-and-tier-names
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit ca4c13c4a092275666751b5a635bf28dd262e248
Author: Pearl Dsilva <pe...@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 320f064a31e..bb1f4ec1b9b 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;
@@ -2248,6 +2249,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)) {
@@ -2297,6 +2299,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 eeec232cee5..f20ccc0e29f 100644
--- a/ui/src/views/network/LoadBalancing.vue
+++ b/ui/src/views/network/LoadBalancing.vue
@@ -40,7 +40,7 @@
<tooltip-label :title="$t('label.cidrlist')" 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.cidrlist')
@@ -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 () {