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 <pearl1...@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 <nicovazque...@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')

Reply via email to