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/02/24 22:36:49 UTC
(cloudstack) 26/46: Support to pass provider when creating public ip range and create IPAM on Netris (#28)
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 b9400836d9382f8f48dd634d6a4c318efd461b3e
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Fri Dec 6 11:48:45 2024 -0500
Support to pass provider when creating public ip range and create IPAM on Netris (#28)
* UI: support to pass provider when creating public ip range
* prevent adding public ip range for a provider that isnt supported in zone
* Create public range on Netris when created on CloudStack
---------
Co-authored-by: nvazquez <ni...@gmail.com>
---
.../com/cloud/network/netris/NetrisService.java | 1 +
.../service/NetrisProviderServiceImpl.java | 91 +-------------------
.../cloudstack/service/NetrisServiceImpl.java | 96 ++++++++++++++++++++++
...iceImplTest.java => NetrisServiceImplTest.java} | 4 +-
.../configuration/ConfigurationManagerImpl.java | 17 ++++
ui/src/views/infra/network/IpRangesTabPublic.vue | 11 +++
6 files changed, 130 insertions(+), 90 deletions(-)
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 ea2f73383e0..d989a53fcdb 100644
--- a/api/src/main/java/com/cloud/network/netris/NetrisService.java
+++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java
@@ -21,6 +21,7 @@ import com.cloud.network.SDNProviderNetworkRule;
import com.cloud.network.vpc.Vpc;
public interface NetrisService {
+ boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId);
boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork);
boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc);
boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr);
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 119a4c80f94..a18251cee11 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,21 +16,14 @@
// under the License.
package org.apache.cloudstack.service;
-import com.cloud.agent.api.Answer;
import com.cloud.dc.DataCenterVO;
-import com.cloud.dc.VlanDetailsVO;
-import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.DataCenterDao;
-import com.cloud.dc.dao.VlanDao;
-import com.cloud.dc.dao.VlanDetailsDao;
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;
@@ -38,16 +31,12 @@ import com.cloud.network.dao.PhysicalNetworkDao;
import com.cloud.network.dao.PhysicalNetworkVO;
import com.cloud.network.element.NetrisProviderVO;
import com.cloud.network.netris.NetrisProvider;
+import com.cloud.network.netris.NetrisService;
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.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.command.AddNetrisProviderCmd;
import org.apache.cloudstack.api.command.DeleteNetrisProviderCmd;
@@ -56,7 +45,6 @@ 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;
@@ -68,7 +56,6 @@ 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 {
@@ -87,11 +74,7 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
@Inject
NetworkDao networkDao;
@Inject
- private IPAddressDao ipAddressDao;
- @Inject
- private VlanDao vlanDao;
- @Inject
- private VlanDetailsDao vlanDetailsDao;
+ private NetrisService netrisService;
@Override
public NetrisProvider addProvider(AddNetrisProviderCmd cmd) {
@@ -147,81 +130,13 @@ public class NetrisProviderServiceImpl implements NetrisProviderService {
} else {
throw new CloudRuntimeException("Failed to add Netris controller due to internal error.");
}
- createNetrisPublicIpRangesOnNetrisProvider(zoneId, netrisResource);
+ netrisService.createIPAMAllocationsForZoneLevelPublicRanges(zoneId);
} catch (ConfigurationException e) {
throw new CloudRuntimeException(e.getMessage());
}
return netrisProvider;
}
- /**
- * Calculate the minimum CIDR subnet containing the IP range (using the library: <a href="https://github.com/seancfoley/IPAddress">IPAddress</a>)
- * From: <a href="https://github.com/seancfoley/IPAddress/wiki/Code-Examples-3:-Subnetting-and-Other-Subnet-Operations#from-start-and-end-address-get-single-cidr-block-covering-both">Example</a>
- * @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<PhysicalNetworkVO> 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<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
- List<Long> 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);
- }
- for (Long vlanDbId : vlanDbIds) {
- VlanVO vlanRecord = vlanDao.findById(vlanDbId);
- if (vlanRecord == null) {
- logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
- return;
- }
- VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
- if (vlanDetail == null) {
- logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
- continue;
- }
- 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<BaseResponse> listNetrisProviders(Long zoneId) {
List<BaseResponse> netrisControllersResponseList = new ArrayList<>();
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 b1e00f771cd..a8fe3ae2054 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
@@ -18,16 +18,29 @@ package org.apache.cloudstack.service;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
+import com.cloud.dc.VlanDetailsVO;
+import com.cloud.dc.VlanVO;
+import com.cloud.dc.dao.VlanDao;
+import com.cloud.dc.dao.VlanDetailsDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.network.IpAddress;
+import com.cloud.network.Network;
import com.cloud.network.Networks;
import com.cloud.network.SDNProviderNetworkRule;
+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;
+import com.cloud.network.dao.PhysicalNetworkDao;
+import com.cloud.network.dao.PhysicalNetworkVO;
import com.cloud.network.element.NetrisProviderVO;
import com.cloud.network.netris.NetrisService;
import com.cloud.network.vpc.Vpc;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
+import inet.ipaddr.IPAddress;
+import inet.ipaddr.IPAddressString;
import io.netris.model.NatPostBody;
import org.apache.cloudstack.agent.api.CreateNetrisVnetCommand;
import org.apache.cloudstack.agent.api.CreateNetrisVpcCommand;
@@ -37,15 +50,21 @@ 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.agent.api.NetrisCommand;
+import org.apache.cloudstack.agent.api.SetupNetrisPublicRangeCommand;
+import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.resource.NetrisResourceObjectUtils;
+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 java.util.List;
import java.util.Locale;
import java.util.Objects;
+import java.util.stream.Collectors;
public class NetrisServiceImpl implements NetrisService, Configurable {
@@ -57,6 +76,14 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
private NetworkDao networkDao;
@Inject
private AgentManager agentMgr;
+ @Inject
+ private PhysicalNetworkDao physicalNetworkDao;
+ @Inject
+ private IPAddressDao ipAddressDao;
+ @Inject
+ private VlanDao vlanDao;
+ @Inject
+ private VlanDetailsDao vlanDetailsDao;
@Override
public String getConfigComponentName() {
@@ -87,6 +114,75 @@ public class NetrisServiceImpl implements NetrisService, Configurable {
return (NetrisAnswer) answer;
}
+ /**
+ * Calculate the minimum CIDR subnet containing the IP range (using the library: <a href="https://github.com/seancfoley/IPAddress">IPAddress</a>)
+ * From: <a href="https://github.com/seancfoley/IPAddress/wiki/Code-Examples-3:-Subnetting-and-Other-Subnet-Operations#from-start-and-end-address-get-single-cidr-block-covering-both">Example</a>
+ * @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);
+ }
+
+ @Override
+ public boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId) {
+ List<PhysicalNetworkVO> 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 false;
+ }
+ for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
+ List<IPAddressVO> publicIps = ipAddressDao.listByPhysicalNetworkId(physicalNetwork.getId());
+ List<Long> 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);
+ }
+ for (Long vlanDbId : vlanDbIds) {
+ VlanVO vlanRecord = vlanDao.findById(vlanDbId);
+ if (vlanRecord == null) {
+ logger.error("Cannot set up the Netris Public IP range as it cannot find the public range on database");
+ return false;
+ }
+ VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlanDbId, ApiConstants.NETRIS_DETAIL_KEY);
+ if (vlanDetail == null) {
+ logger.debug("Skipping the Public IP range {} creation on Netris as it does not belong to the Netris Public IP Pool", vlanRecord.getIpRange());
+ continue;
+ }
+ String gateway = vlanRecord.getVlanGateway();
+ String netmask = vlanRecord.getVlanNetmask();
+ String ipRange = vlanRecord.getIpRange();
+ SetupNetrisPublicRangeCommand cmd = createSetupPublicRangeCommand(zoneId, gateway, netmask, ipRange);
+ NetrisAnswer answer = sendNetrisCommand(cmd, zoneId);
+ if (!answer.getResult()) {
+ throw new CloudRuntimeException("Netris Public IP Range setup failed, please check the logs");
+ }
+ }
+ }
+ return true;
+ }
+
@Override
public boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpc) {
CreateNetrisVpcCommand cmd = new CreateNetrisVpcCommand(zoneId, accountId, domainId, vpcName, cidr, vpcId, isVpc);
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/NetrisServiceImplTest.java
similarity index 90%
rename from plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisProviderServiceImplTest.java
rename to plugins/network-elements/netris/src/test/java/org/apache/cloudstack/service/NetrisServiceImplTest.java
index 233787d9004..807324f404a 100644
--- 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/NetrisServiceImplTest.java
@@ -19,9 +19,9 @@ package org.apache.cloudstack.service;
import org.junit.Assert;
import org.junit.Test;
-public class NetrisProviderServiceImplTest {
+public class NetrisServiceImplTest {
- private NetrisProviderServiceImpl service = new NetrisProviderServiceImpl();
+ private NetrisServiceImpl service = new NetrisServiceImpl();
@Test
public void testCalculateSubnetCidrFromIpRange() {
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 17c2385cdaa..9b00a53e969 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -52,6 +52,7 @@ import javax.naming.ConfigurationException;
import com.cloud.network.dao.NetrisProviderDao;
import com.cloud.network.element.NetrisProviderVO;
+import com.cloud.network.netris.NetrisService;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.affinity.AffinityGroup;
import org.apache.cloudstack.affinity.AffinityGroupService;
@@ -477,6 +478,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
NsxProviderDao nsxProviderDao;
@Inject
NetrisProviderDao netrisProviderDao;
+ @Inject
+ NetrisService netrisService;
// FIXME - why don't we have interface for DataCenterLinkLocalIpAddressDao?
@Inject
@@ -4664,6 +4667,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
}
+ // If external provider is provided, verify zone has that provider enabled
+ Provider provider = cmd.getProvider();
+ if (Objects.nonNull(provider)) {
+ boolean unsupported =
+ (Provider.Nsx == provider && nsxProviderDao.findByZoneId(zoneId) == null) ||
+ (Provider.Netris == provider && netrisProviderDao.findByZoneId(zoneId) == null);
+ if (unsupported) {
+ throw new InvalidParameterValueException(String.format("Cannot add public IP range as the zone does not support provider: %s", provider.getName()));
+ }
+ }
+
// verify that physical network exists
PhysicalNetworkVO pNtwk = null;
if (physicalNetworkId != null) {
@@ -4833,6 +4847,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
});
+ if (provider == Provider.Netris) {
+ netrisService.createIPAMAllocationsForZoneLevelPublicRanges(zoneId);
+ }
messageBus.publish(_name, MESSAGE_CREATE_VLAN_IP_RANGE_EVENT, PublishScope.LOCAL, vlan);
return vlan;
diff --git a/ui/src/views/infra/network/IpRangesTabPublic.vue b/ui/src/views/infra/network/IpRangesTabPublic.vue
index 3955b8c0359..0f3610f5e05 100644
--- a/ui/src/views/infra/network/IpRangesTabPublic.vue
+++ b/ui/src/views/infra/network/IpRangesTabPublic.vue
@@ -244,6 +244,16 @@
<a-form-item name="endip" ref="endip" :label="$t('label.endip')" class="form__item">
<a-input v-model:value="form.endip" />
</a-form-item>
+ <a-form-item name="provider" ref="provider">
+ <template #label>
+ <tooltip-label :title="$t('label.provider')"/>
+ </template>
+ <a-select v-model:value="form.provider">
+ <a-select-option value=""></a-select-option>
+ <a-select-option value="NSX">{{ $t('label.nsx') }}</a-select-option>
+ <a-select-option value="Netris">{{ $t('label.netris') }}</a-select-option>
+ </a-select>
+ </a-form-item>
</div>
<div class="form__item" v-if="!basicGuestNetwork && form.iptype != 'ip6'">
<tooltip-label :title="$t('label.set.reservation')" :tooltip="$t('label.set.reservation.desc')" class="tooltip-label-wrapper"/>
@@ -633,6 +643,7 @@ export default {
params.podid = values.podid
params.networkid = this.network.id
}
+ params.provider = values.provider
api('createVlanIpRange', params).then(() => {
this.$notification.success({
message: this.$t('message.success.add.iprange')