diff --git a/plugins/network-elements/netris/pom.xml b/plugins/network-elements/netris/pom.xml
index 881d8195a93..21aeadec935 100644
--- a/plugins/network-elements/netris/pom.xml
+++ b/plugins/network-elements/netris/pom.xml
@@ -35,5 +35,10 @@
netris-java-sdk
1.0.0
+
+ com.github.seancfoley
+ ipaddress
+ 5.5.1
+
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
index 440892e6e87..463001452c1 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/NetrisCommand.java
@@ -20,13 +20,13 @@ import com.cloud.agent.api.Command;
public class NetrisCommand extends Command {
private final long zoneId;
- private final long accountId;
- private final long domainId;
+ private final Long accountId;
+ private final Long domainId;
private final String name;
private final long id;
private final boolean isVpc;
- public NetrisCommand(long zoneId, long accountId, long domainId, String name, long id, boolean isVpc) {
+ public NetrisCommand(long zoneId, Long accountId, Long domainId, String name, long id, boolean isVpc) {
this.zoneId = zoneId;
this.accountId = accountId;
this.domainId = domainId;
@@ -48,11 +48,11 @@ public class NetrisCommand extends Command {
return zoneId;
}
- public long getAccountId() {
+ public Long getAccountId() {
return accountId;
}
- public long getDomainId() {
+ public Long getDomainId() {
return domainId;
}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/SetupNetrisPublicRangeCommand.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/SetupNetrisPublicRangeCommand.java
new file mode 100644
index 00000000000..5cbdd96e76a
--- /dev/null
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/agent/api/SetupNetrisPublicRangeCommand.java
@@ -0,0 +1,37 @@
+// 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 SetupNetrisPublicRangeCommand extends NetrisCommand {
+
+ private final String superCidr;
+ private final String exactCidr;
+
+ public SetupNetrisPublicRangeCommand(long zoneId, String superCidr, String exactCidr) {
+ super(zoneId, null, null, null, zoneId, false);
+ this.superCidr = superCidr;
+ this.exactCidr = exactCidr;
+ }
+
+ public String getSuperCidr() {
+ return superCidr;
+ }
+
+ public String getExactCidr() {
+ return exactCidr;
+ }
+}
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 0fdb0913053..dc922aef963 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
@@ -34,6 +34,7 @@ import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
import org.apache.cloudstack.agent.api.NetrisAnswer;
import org.apache.cloudstack.StartupNetrisCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
import org.apache.cloudstack.service.NetrisApiClient;
import org.apache.cloudstack.service.NetrisApiClientImpl;
import org.apache.logging.log4j.LogManager;
@@ -94,6 +95,8 @@ public class NetrisResource implements ServerResource {
return executeRequest((CreateNetrisVnetCommand) cmd);
} else if (cmd instanceof DeleteNetrisVnetCommand) {
return executeRequest((DeleteNetrisVnetCommand) cmd);
+ } else if (cmd instanceof SetupNetrisPublicRangeCommand) {
+ return executeRequest((SetupNetrisPublicRangeCommand) cmd);
} else {
return Answer.createUnsupportedCommandAnswer(cmd);
}
@@ -250,6 +253,14 @@ public class NetrisResource implements ServerResource {
return new NetrisAnswer(cmd, true, "OK");
}
+ private Answer executeRequest(SetupNetrisPublicRangeCommand cmd) {
+ boolean result = netrisApiClient.setupZoneLevelPublicRange(cmd);
+ if (!result) {
+ return new NetrisAnswer(cmd, false, "Netris Setup for Public Range failed");
+ }
+ return new NetrisAnswer(cmd, true, "OK");
+ }
+
@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 127636dd7d5..5b19aa43199 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
@@ -27,14 +27,19 @@ public class NetrisResourceObjectUtils {
public static String retrieveNetrisResourceObjectName(NetrisCommand cmd, NetrisObjectType netrisObjectType, String... suffixes) {
long zoneId = cmd.getZoneId();
- long accountId = cmd.getAccountId();
- long domainId = cmd.getDomainId();
+ Long accountId = cmd.getAccountId();
+ Long domainId = cmd.getDomainId();
long objectId = cmd.getId();
String objectName = cmd.getName();
boolean isVpc = cmd.isVpc();
+ boolean isZoneLevel = accountId == null && domainId == null;
StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.append(String.format("D%s-A%s-Z%s", domainId, accountId, zoneId));
+ if (isZoneLevel) {
+ stringBuilder.append(String.format("Z%s", zoneId));
+ } else {
+ stringBuilder.append(String.format("D%s-A%s-Z%s", domainId, accountId, zoneId));
+ }
String prefix = isVpc ? "-V" : "-N";
switch (netrisObjectType) {
case VPC:
@@ -46,10 +51,14 @@ public class NetrisResourceObjectUtils {
}
break;
case IPAM_ALLOCATION:
- stringBuilder.append(String.format("%s%s", prefix, objectId));
+ if (!isZoneLevel) {
+ stringBuilder.append(String.format("%s%s", prefix, objectId));
+ }
break;
case IPAM_SUBNET:
- stringBuilder.append(String.format("-N%s", objectId));
+ if (!isZoneLevel) {
+ stringBuilder.append(String.format("-N%s", objectId));
+ }
break;
case VNET:
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 42cb9a772fc..0a00583df52 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,6 +24,7 @@ import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
import java.util.List;
@@ -50,4 +51,11 @@ public interface NetrisApiClient {
boolean createVnet(CreateNetrisVnetCommand cmd);
boolean deleteVnet(DeleteNetrisVnetCommand cmd);
+
+ /**
+ * Check and create zone level Netris Public range in the following manner:
+ * - Check the IPAM allocation for the zone super CIDR. In case it doesn't exist, create it
+ * - Check the IPAM subnet for NAT purpose for the range start-end. In case it doesn't exist, create it
+ */
+ boolean setupZoneLevelPublicRange(SetupNetrisPublicRangeCommand 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 b2cc9d47bdf..b61c84e0110 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
@@ -33,6 +33,9 @@ import io.netris.model.FilterBySites;
import io.netris.model.FilterByVpc;
import io.netris.model.GetSiteBody;
import io.netris.model.InlineResponse2004;
+import io.netris.model.InlineResponse2004Data;
+import io.netris.model.IpTree;
+import io.netris.model.IpTreeAllocation;
import io.netris.model.IpTreeAllocationTenant;
import io.netris.model.IpTreeSubnet;
import io.netris.model.IpTreeSubnetSites;
@@ -63,6 +66,7 @@ import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
import org.apache.cloudstack.agent.api.DeleteNetrisVnetCommand;
import org.apache.cloudstack.agent.api.DeleteNetrisVpcCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.LogManager;
@@ -196,25 +200,31 @@ public class NetrisApiClientImpl implements NetrisApiClient {
return response;
}
- private InlineResponse2004 createVpcAllocationInternal(VPCResponseObjectOK createdVpc, String cidr, int adminTenantId,
- String adminTenantName, String netrisIpamAllocationName) {
- logger.debug(String.format("Creating Netris VPC Allocation %s for VPC %s", cidr, createdVpc.getData().getName()));
+ private InlineResponse2004Data createIpamAllocationInternal(String ipamName, String ipamPrefix, VPCListing vpc) {
+ logger.debug(String.format("Creating Netris IPAM Allocation %s for VPC %s", ipamPrefix, vpc.getName()));
try {
IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
AllocationBody body = new AllocationBody();
AllocationBodyVpc allocationBodyVpc = new AllocationBodyVpc();
- allocationBodyVpc.setId(createdVpc.getData().getId());
- allocationBodyVpc.setName(createdVpc.getData().getName());
+ allocationBodyVpc.setId(vpc.getId());
+ allocationBodyVpc.setName(vpc.getName());
body.setVpc(allocationBodyVpc);
- body.setName(netrisIpamAllocationName);
- body.setPrefix(cidr);
+ body.setName(ipamName);
+ body.setPrefix(ipamPrefix);
IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
- allocationTenant.setId(new BigDecimal(adminTenantId));
- allocationTenant.setName(adminTenantName);
+ allocationTenant.setId(new BigDecimal(tenantId));
+ allocationTenant.setName(tenantName);
body.setTenant(allocationTenant);
- return ipamApi.apiV2IpamAllocationPost(body);
+ InlineResponse2004 ipamResponse = ipamApi.apiV2IpamAllocationPost(body);
+ if (ipamResponse == null || !ipamResponse.isIsSuccess()) {
+ String reason = ipamResponse == null ? "Empty response" : "Operation failed on Netris";
+ logger.debug("The Netris Allocation {} for VPC {} creation failed: {}", ipamPrefix, vpc.getName(), reason);
+ return null;
+ }
+ logger.debug(String.format("Successfully created VPC %s and its IPAM Allocation %s on Netris", vpc.getName(), ipamPrefix));
+ return ipamResponse.getData();
} catch (ApiException e) {
- logAndThrowException(String.format("Error creating Netris Allocation %s for VPC %s", cidr, createdVpc.getData().getName()), e);
+ logAndThrowException(String.format("Error creating Netris IPAM Allocation %s for VPC %s", ipamPrefix, vpc.getName()), e);
return null;
}
}
@@ -231,14 +241,8 @@ public class NetrisApiClientImpl implements NetrisApiClient {
String netrisIpamAllocationName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, cmd.getCidr());
String vpcCidr = cmd.getCidr();
- InlineResponse2004 ipamResponse = createVpcAllocationInternal(createdVpc, vpcCidr, tenantId, tenantName, netrisIpamAllocationName);
- if (ipamResponse == null || !ipamResponse.isIsSuccess()) {
- String reason = ipamResponse == null ? "Empty response" : "Operation failed on Netris";
- logger.debug("The Netris Allocation {} for VPC {} creation failed: {}", vpcCidr, cmd.getName(), reason);
- return false;
- }
- logger.debug(String.format("Successfully created VPC %s and its IPAM Allocation %s on Netris", cmd.getName(), vpcCidr));
- return true;
+ InlineResponse2004Data createdIpamAllocation = createIpamAllocationInternal(netrisIpamAllocationName, vpcCidr, createdVpc.getData());
+ return createdIpamAllocation != null;
}
private void deleteVpcIpamAllocationInternal(VPCListing vpcResource, String vpcCidr) {
@@ -334,12 +338,7 @@ public class NetrisApiClientImpl implements NetrisApiClient {
String netrisVnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.VNET, vNetName) ;
String netrisSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, vnetCidr) ;
- InlineResponse2004 subnetResponse = createVpcSubnetInternal(associatedVpc, vNetName, vnetCidr, netrisSubnetName);
- if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
- String reason = subnetResponse == null ? "Empty response" : "Operation failed on Netris";
- logger.debug("The Netris Subnet {} for network {} creation failed: {}", vnetCidr, networkName, reason);
- return false;
- }
+ createIpamSubnetInternal(netrisSubnetName, vnetCidr, SubnetBody.PurposeEnum.COMMON, associatedVpc);
logger.debug("Successfully created IPAM Subnet {} for network {} on Netris", netrisSubnetName, networkName);
VnetResAddBody vnetResponse = createVnetInternal(associatedVpc, netrisVnetName, vnetCidr);
@@ -392,6 +391,71 @@ public class NetrisApiClientImpl implements NetrisApiClient {
return true;
}
+ protected VPCListing getSystemVpc() throws ApiException {
+ List systemVpcList = listVPCs().stream().filter(VPCListing::isIsSystem).collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(systemVpcList)) {
+ String msg = "Cannot find any system VPC";
+ logger.error(msg);
+ throw new CloudRuntimeException(msg);
+ }
+ return systemVpcList.get(0);
+ }
+
+ private BigDecimal getIpamAllocationIdByPrefixAndVpc(String superCidrPrefix, VPCListing vpc) throws ApiException {
+ IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+ FilterBySites filterBySites = new FilterBySites();
+ filterBySites.add(siteId);
+ FilterByVpc filterByVpc = new FilterByVpc();
+ filterByVpc.add(vpc.getId());
+ IpTree ipamTree = ipamApi.apiV2IpamGet(filterBySites, filterByVpc);
+ List superCidrList = ipamTree.getData().stream()
+ .filter(x -> x.getPrefix().equals(superCidrPrefix))
+ .collect(Collectors.toList());
+ return CollectionUtils.isEmpty(superCidrList) ? null : superCidrList.get(0).getId();
+ }
+
+ private IpTreeSubnet getIpamSubnetByAllocationAndPrefixAndPurposeAndVpc(BigDecimal ipamAllocationId, String exactCidr, IpTreeSubnet.PurposeEnum purpose, VPCListing vpc) throws ApiException {
+ IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
+ FilterByVpc filterByVpc = new FilterByVpc();
+ filterByVpc.add(vpc.getId());
+ SubnetResBody subnetResBody = ipamApi.apiV2IpamSubnetsGet(filterByVpc);
+ List exactSubnetList = subnetResBody.getData().stream()
+ .filter(x -> x.getAllocationID().equals(ipamAllocationId) && x.getPrefix().equals(exactCidr) && x.getPurpose() == purpose)
+ .collect(Collectors.toList());
+ return CollectionUtils.isEmpty(exactSubnetList) ? null : exactSubnetList.get(0);
+ }
+
+ @Override
+ public boolean setupZoneLevelPublicRange(SetupNetrisPublicRangeCommand cmd) {
+ String superCidr = cmd.getSuperCidr();
+ String exactCidr = cmd.getExactCidr();
+ try {
+ VPCListing systemVpc = getSystemVpc();
+ logger.debug("Checking if the Netris Public Super CIDR {} exists", superCidr);
+ BigDecimal ipamAllocationId = getIpamAllocationIdByPrefixAndVpc(superCidr, systemVpc);
+ if (ipamAllocationId == null) {
+ String ipamName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_ALLOCATION, superCidr);
+ InlineResponse2004Data ipamAllocation = createIpamAllocationInternal(ipamName, superCidr, systemVpc);
+ if (ipamAllocation == null) {
+ String msg = String.format("Could not create the zone level super CIDR %s for the system VPC", superCidr);
+ logger.error(msg);
+ throw new CloudRuntimeException(msg);
+ }
+ ipamAllocationId = new BigDecimal(ipamAllocation.getId());
+ }
+ IpTreeSubnet exactSubnet = getIpamSubnetByAllocationAndPrefixAndPurposeAndVpc(ipamAllocationId, exactCidr, IpTreeSubnet.PurposeEnum.NAT, systemVpc);
+ if (exactSubnet == null) {
+ String ipamSubnetName = NetrisResourceObjectUtils.retrieveNetrisResourceObjectName(cmd, NetrisResourceObjectUtils.NetrisObjectType.IPAM_SUBNET, exactCidr);
+ createIpamSubnetInternal(ipamSubnetName, exactCidr, SubnetBody.PurposeEnum.NAT, systemVpc);
+ }
+ } catch (ApiException e) {
+ String msg = String.format("Error setting up the Netris Public Range %s on super CIDR %s", exactCidr, superCidr);
+ logAndThrowException(msg, e);
+ return false;
+ }
+ return true;
+ }
+
private void deleteVnetInternal(VPCListing associatedVpc, FilterBySites siteFilter, FilterByVpc vpcFilter, String netrisVnetName, String vNetName) {
try {
VNetApi vNetApi = apiClient.getApiStubForMethod(VNetApi.class);
@@ -433,16 +497,16 @@ public class NetrisApiClientImpl implements NetrisApiClient {
}
}
- private InlineResponse2004 createVpcSubnetInternal(VPCListing associatedVpc, String vNetName, String vNetCidr, String netrisSubnetName) {
- logger.debug("Creating Netris VPC Subnet {} for VPC {} for vNet {}", vNetCidr, associatedVpc.getName(), vNetName);
+ private InlineResponse2004Data createIpamSubnetInternal(String subnetName, String subnetPrefix, SubnetBody.PurposeEnum purpose, VPCListing vpc) {
+ logger.debug("Creating Netris IPAM Subnet {} for VPC {}", subnetPrefix, vpc.getName());
try {
SubnetBody subnetBody = new SubnetBody();
- subnetBody.setName(netrisSubnetName);
+ subnetBody.setName(subnetName);
AllocationBodyVpc vpcAllocationBody = new AllocationBodyVpc();
- vpcAllocationBody.setName(associatedVpc.getName());
- vpcAllocationBody.setId(associatedVpc.getId());
+ vpcAllocationBody.setName(vpc.getName());
+ vpcAllocationBody.setId(vpc.getId());
subnetBody.setVpc(vpcAllocationBody);
IpTreeAllocationTenant allocationTenant = new IpTreeAllocationTenant();
@@ -455,12 +519,18 @@ public class NetrisApiClientImpl implements NetrisApiClient {
subnetSites.setName(siteName);
subnetBody.setSites(List.of(subnetSites));
- subnetBody.setPurpose(SubnetBody.PurposeEnum.COMMON);
- subnetBody.setPrefix(vNetCidr);
+ subnetBody.setPurpose(purpose);
+ subnetBody.setPrefix(subnetPrefix);
IpamApi ipamApi = apiClient.getApiStubForMethod(IpamApi.class);
- return ipamApi.apiV2IpamSubnetPost(subnetBody);
+ InlineResponse2004 subnetResponse = ipamApi.apiV2IpamSubnetPost(subnetBody);
+ if (subnetResponse == null || !subnetResponse.isIsSuccess()) {
+ String reason = subnetResponse == null ? "Empty response" : "Operation failed on Netris";
+ logger.debug("The Netris IPAM Subnet {} creation failed: {}", subnetName, reason);
+ throw new CloudRuntimeException(reason);
+ }
+ return subnetResponse.getData();
} catch (ApiException e) {
- logAndThrowException(String.format("Error creating Netris Subnet %s for VPC %s", vNetCidr, associatedVpc.getName()), e);
+ logAndThrowException(String.format("Error creating Netris IPAM Subnet %s for VPC %s", subnetPrefix, vpc.getName()), e);
return null;
}
}
diff --git a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
index ef4cd265d8c..2887e03a1f5 100644
--- a/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
+++ b/plugins/network-elements/netris/src/main/java/org/apache/cloudstack/service/NetrisProviderServiceImpl.java
@@ -16,15 +16,19 @@
// under the License.
package org.apache.cloudstack.service;
-import com.amazonaws.util.CollectionUtils;
+import com.cloud.agent.api.Answer;
import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.VlanDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.DetailVO;
import com.cloud.host.Host;
import com.cloud.host.dao.HostDetailsDao;
import com.cloud.network.Network;
import com.cloud.network.Networks;
+import com.cloud.network.dao.IPAddressDao;
+import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetrisProviderDao;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
@@ -36,7 +40,11 @@ import com.cloud.resource.ResourceManager;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
import com.google.common.annotations.VisibleForTesting;
+import inet.ipaddr.IPAddress;
+import inet.ipaddr.IPAddressString;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
import org.apache.cloudstack.api.command.DeleteNetrisProviderCmd;
@@ -44,6 +52,10 @@ import org.apache.cloudstack.api.command.ListNetrisProvidersCmd;
import org.apache.cloudstack.api.response.NetrisProviderResponse;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.resource.NetrisResource;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
@@ -53,9 +65,12 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
+import java.util.stream.Collectors;
public class NetrisProviderServiceImpl implements NetrisProviderService {
+ protected Logger logger = LogManager.getLogger(getClass());
+
@Inject
DataCenterDao dataCenterDao;
@Inject
@@ -68,6 +83,10 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
PhysicalNetworkDao physicalNetworkDao;
@Inject
NetworkDao networkDao;
+ @Inject
+ private IPAddressDao ipAddressDao;
+ @Inject
+ private VlanDao vlanDao;
@Override
public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
@@ -123,12 +142,77 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
} else {
throw new CloudRuntimeException("Failed to add Netris controller due to internal error.");
}
+ createNetrisPublicIpRangesOnNetrisProvider(zoneId, netrisResource);
} catch (ConfigurationException e) {
throw new CloudRuntimeException(e.getMessage());
}
return netrisProvider;
}
+ /**
+ * Calculate the minimum CIDR subnet containing the IP range (using the library: IPAddress)
+ * From: Example
+ * @param ipRange format: startIP-endIP
+ * @return the minimum CIDR containing the IP range
+ */
+ protected String calculateSubnetCidrFromIpRange(String ipRange) {
+ if (StringUtils.isBlank(ipRange) || !ipRange.contains("-")) {
+ return null;
+ }
+ String[] rangeArray = ipRange.split("-");
+ String startIp = rangeArray[0];
+ String endIp = rangeArray[1];
+ IPAddress startIpAddress = new IPAddressString(startIp).getAddress();
+ IPAddress endIpAddress = new IPAddressString(endIp).getAddress();
+ return startIpAddress.coverWithPrefixBlock(endIpAddress).toPrefixLengthString();
+ }
+
+ /**
+ * Prepare the Netris Public Range to be used by CloudStack after the zone is created and the Netris provider is added
+ */
+ public SetupNetrisPublicRangeCommand createSetupPublicRangeCommand(long zoneId, String gateway, String netmask, String ipRange) {
+ String superCidr = NetUtils.getCidrFromGatewayAndNetmask(gateway, netmask);
+ String subnetNatCidr = calculateSubnetCidrFromIpRange(ipRange);
+ return new SetupNetrisPublicRangeCommand(zoneId, superCidr, subnetNatCidr);
+ }
+
+ protected void createNetrisPublicIpRangesOnNetrisProvider(long zoneId, NetrisResource netrisResource) {
+ List physicalNetworks = physicalNetworkDao.listByZoneAndTrafficType(zoneId, Networks.TrafficType.Public);
+ physicalNetworks = physicalNetworks.stream().filter(x -> x.getIsolationMethods().contains(Network.Provider.Netris.getName())).collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(physicalNetworks)) {
+ return;
+ }
+ for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
+ List publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
+ List vlanDbIds = publicIps.stream()
+ .filter(x -> !x.isForSystemVms())
+ .map(IPAddressVO::getVlanId)
+ .collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(vlanDbIds)) {
+ String msg = "Cannot find a public IP range VLAN range for the Netris Public traffic";
+ logger.error(msg);
+ throw new CloudRuntimeException(msg);
+ }
+ if (vlanDbIds.size() > 1) {
+ logger.warn("Expected one Netris Public IP range but found {}. Using the first one to create the Netris IPAM allocation and NAT subnet", vlanDbIds.size());
+ }
+ VlanVO vlanRecord = vlanDao.findById(vlanDbIds.get(0));
+ if (vlanRecord == null) {
+ logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
+ return;
+ }
+ String gateway = vlanRecord.getVlanGateway();
+ String netmask = vlanRecord.getVlanNetmask();
+ String ipRange = vlanRecord.getIpRange();
+ SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
+ Answer answer = netrisResource.executeRequest(cmd);
+ boolean result = answer != null && answer.getResult();
+ if (!result) {
+ throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
+ }
+ }
+ }
+
@Override
public List listNetrisProviders(Long zoneId) {
List netrisControllersResponseList = new ArrayList<>();
@@ -158,7 +242,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
List physicalNetworks = physicalNetworkDao.listByZone(zoneId);
for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
List networkList = networkDao.listByPhysicalNetwork(physicalNetwork.getId());
- if (!CollectionUtils.isNullOrEmpty(networkList)) {
+ if (!CollectionUtils.isEmpty(networkList)) {
validateNetworkState(networkList);
}
}
diff --git a/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java
new file mode 100644
index 00000000000..233787d9004
--- /dev/null
+++ b/plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java
@@ -0,0 +1,33 @@
+// 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.service;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NetrisProviderServiceImplTest {
+
+ private NetrisProviderServiceImpl service = new NetrisProviderServiceImpl();
+
+ @Test
+ public void testCalculateSubnetCidrFromIpRange() {
+ String ipRange = "100.99.18.140-100.99.18.159";
+ String expectedCidr = "100.99.18.128/27";
+ String cidr = service.calculateSubnetCidrFromIpRange(ipRange);
+ Assert.assertEquals(expectedCidr, cidr);
+ }
+}
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 05be4ea6307..00b21ec385a 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -4450,7 +4450,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
if (network.getIsolationMethods().contains("GRE")) {
minVnet = MIN_GRE_KEY;
maxVnet = MAX_GRE_KEY;
- } else if (network.getIsolationMethods().contains("VXLAN")) {
+ } else if (network.getIsolationMethods().contains("VXLAN") || network.getIsolationMethods().contains("Netris")) {
minVnet = MIN_VXLAN_VNI;
maxVnet = MAX_VXLAN_VNI;
// fail if zone already contains VNI, need to be unique per zone.
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 9ac5d54f98e..8365853ad64 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1772,7 +1772,7 @@
"label.public.ips": "Public IP addresses",
"label.public.lb": "Public LB",
"label.public.traffic": "Public traffic",
-"label.public.traffic.netris": "Netris Public traffic",
+"label.public.traffic.netris": "Netris Public IP Pool",
"label.public.traffic.nsx": "NSX Public traffic",
"label.publicinterface": "Public interface",
"label.publicip": "IP address",