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

dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 86827f871d2 Update CIDR/Gateway of the Shared Networks from Guest IP 
ranges (#11249)
86827f871d2 is described below

commit 86827f871d24def41c5c8cf6d8b7bad910979a55
Author: Suresh Kumar Anaparti <[email protected]>
AuthorDate: Tue Jul 29 14:00:14 2025 +0530

    Update CIDR/Gateway of the Shared Networks from Guest IP ranges (#11249)
---
 .../resources/META-INF/db/schema-42010to42100.sql  |  5 ++
 .../cloud/network/element/BigSwitchBcfElement.java | 54 ++++++++---------
 .../main/java/com/cloud/api/ApiResponseHelper.java | 17 +++---
 .../configuration/ConfigurationManagerImpl.java    | 68 ++++++++++++++++++++++
 .../src/main/java/com/cloud/utils/StringUtils.java | 52 +++++++++++++++++
 5 files changed, 161 insertions(+), 35 deletions(-)

diff --git 
a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql 
b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql
index 103b9363d6c..4c7fe74cbcd 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql
@@ -659,3 +659,8 @@ CALL 
`cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`(
     'Resume',
     '[]'
 );
+
+ALTER TABLE `cloud`.`networks` MODIFY COLUMN `cidr` varchar(255) DEFAULT NULL 
COMMENT 'CloudStack managed vms get IP address from cidr.In general this cidr 
also serves as the network CIDR. But in case IP reservation feature is being 
used by a Guest network, networkcidr is the Effective network CIDR for that 
network';
+ALTER TABLE `cloud`.`networks` MODIFY COLUMN `gateway` varchar(255) DEFAULT 
NULL COMMENT 'gateway(s) for this network configuration';
+ALTER TABLE `cloud`.`networks` MODIFY COLUMN `ip6_cidr` varchar(1024) DEFAULT 
NULL COMMENT 'IPv6 cidr(s) for this network';
+ALTER TABLE `cloud`.`networks` MODIFY COLUMN `ip6_gateway` varchar(1024) 
DEFAULT NULL COMMENT 'IPv6 gateway(s) for this network';
diff --git 
a/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java
 
b/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java
index adb957c3b5e..db0efd71b1d 100644
--- 
a/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java
+++ 
b/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java
@@ -237,7 +237,7 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         // get arguments for CreateBcfAttachmentCommand
         // determine whether this is VPC network or stand-alone network
         Vpc vpc = null;
-        if(network.getVpcId()!=null){
+        if (network.getVpcId() != null) {
             vpc = _vpcDao.acquireInLockTable(network.getVpcId());
         }
 
@@ -264,7 +264,7 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         String vmwareVswitchLabel = 
_networkModel.getDefaultGuestTrafficLabel(zoneId, HypervisorType.VMware);
         String[] labelArray = null;
         String vswitchName = null;
-        if(vmwareVswitchLabel!=null){
+        if (vmwareVswitchLabel != null) {
             labelArray=vmwareVswitchLabel.split(",");
             vswitchName = labelArray[0];
         }
@@ -273,9 +273,9 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         //   kvm: ivs port name
         //   vmware: specific portgroup naming convention
         String pgName = "";
-        if (dest.getHost().getHypervisorType() == HypervisorType.KVM){
+        if (dest.getHost().getHypervisorType() == HypervisorType.KVM) {
             pgName = hostname;
-        } else if (dest.getHost().getHypervisorType() == 
HypervisorType.VMware){
+        } else if (dest.getHost().getHypervisorType() == 
HypervisorType.VMware) {
             pgName = hostname + "-" + vswitchName;
         }
 
@@ -306,7 +306,7 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         String nicId = nic.getUuid();
 
         String tenantId;
-        if(network.getVpcId()!=null) {
+        if (network.getVpcId() != null) {
             tenantId = network.getNetworkDomain();
         } else {
             tenantId = networkId;
@@ -439,16 +439,16 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
 
         DataCenterVO zone = 
_zoneDao.findById(physicalNetwork.getDataCenterId());
         String zoneName;
-        if(zone!= null){
+        if (zone != null) {
             zoneName = zone.getName();
         } else {
             zoneName = String.valueOf(zoneId);
         }
 
         Boolean natNow =  _bcfUtils.isNatEnabled();
-        if (!nat && natNow){
+        if (!nat && natNow) {
             throw new CloudRuntimeException("NAT is enabled in existing 
controller. Enable NAT for new controller or remove existing controller 
first.");
-        } else if (nat && !natNow){
+        } else if (nat && !natNow) {
             throw new CloudRuntimeException("NAT is disabled in existing 
controller. Disable NAT for new controller or remove existing controller 
first.");
         }
 
@@ -582,7 +582,7 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         BigSwitchBcfResource bcfResource = (BigSwitchBcfResource) resource;
         bcfUtilsInit();
 
-        if(_bcfUtils.getTopology()!=null){
+        if (_bcfUtils.getTopology() != null) {
             bcfResource.setTopology(_bcfUtils.getTopology());
         }
 
@@ -621,7 +621,7 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         _bcfUtils.listACLbyNetwork(network);
 
         Vpc vpc = null;
-        if(network.getVpcId()!=null){
+        if (network.getVpcId() != null) {
             vpc = _vpcDao.acquireInLockTable(network.getVpcId());
         }
 
@@ -635,11 +635,11 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
             tenantId = network.getUuid();
         }
 
-        for (StaticNat rule: rules){
+        for (StaticNat rule: rules) {
             String srcIp = 
_ipAddressDao.findById(rule.getSourceIpAddressId()).getAddress().addr();
             String dstIp = rule.getDestIpAddress();
             String mac = rule.getSourceMacAddress();
-            if(!rule.isForRevoke()) {
+            if (!rule.isForRevoke()) {
                 logger.debug("BCF enables static NAT for public IP: " + srcIp 
+ " private IP " + dstIp
                         + " mac " + mac);
                 CreateBcfStaticNatCommand cmd = new CreateBcfStaticNatCommand(
@@ -671,13 +671,13 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         SubnetUtils utils;
         String cidr = null;
         List<String> cidrList;
-        for(NetworkACLItem r: rules){
-            if(r.getState()==NetworkACLItem.State.Revoke){
+        for (NetworkACLItem r: rules) {
+            if (r.getState() == NetworkACLItem.State.Revoke) {
                 continue;
             }
             cidrList = r.getSourceCidrList();
-            if(cidrList != null){
-                if(cidrList.size()>1 || 
!r.getSourcePortEnd().equals(r.getSourcePortStart())){
+            if (cidrList != null) {
+                if (cidrList.size() > 1 || 
!r.getSourcePortEnd().equals(r.getSourcePortStart())) {
                     throw new ResourceUnavailableException("One CIDR and one 
port only please.",
                             Network.class, network.getId());
                 } else {
@@ -688,7 +688,7 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
                 cidr = "";
             } else {
                 utils = new SubnetUtils(cidr);
-                
if(!utils.getInfo().getNetworkAddress().equals(utils.getInfo().getAddress())){
+                if 
(!utils.getInfo().getNetworkAddress().equals(utils.getInfo().getAddress())) {
                     throw new ResourceUnavailableException("Invalid CIDR in 
Network ACL rule.",
                             Network.class, network.getId());
                 }
@@ -710,13 +710,13 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         SubnetUtils utils;
         String cidr = null;
         List<String> cidrList;
-        for(FirewallRule r: rules){
-            if(r.getState()==FirewallRule.State.Revoke){
+        for (FirewallRule r: rules) {
+            if (r.getState() == FirewallRule.State.Revoke) {
                 continue;
             }
             cidrList = r.getSourceCidrList();
-            if(cidrList != null){
-                if(cidrList.size()>1 || 
!r.getSourcePortEnd().equals(r.getSourcePortStart())){
+            if (cidrList != null) {
+                if (cidrList.size()>1 || 
!r.getSourcePortEnd().equals(r.getSourcePortStart())) {
                     throw new ResourceUnavailableException("One CIDR and one 
port only please.",
                             Network.class, network.getId());
                 } else {
@@ -727,7 +727,7 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
                 cidr = "";
             } else {
                 utils = new SubnetUtils(cidr);
-                
if(!utils.getInfo().getNetworkAddress().equals(utils.getInfo().getAddress())){
+                if 
(!utils.getInfo().getNetworkAddress().equals(utils.getInfo().getAddress())) {
                     throw new ResourceUnavailableException("Invalid CIDR in 
Firewall rule.",
                             Network.class, network.getId());
                 }
@@ -741,7 +741,7 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         bcfUtilsInit();
 
         Vpc vpc = null;
-        if(network.getVpcId()!=null){
+        if (network.getVpcId() != null) {
             vpc = _vpcDao.acquireInLockTable(network.getVpcId());
         }
 
@@ -756,23 +756,23 @@ NetworkACLServiceProvider, FirewallServiceProvider, 
ResourceStateAdapter {
         UpdateBcfRouterCommand cmd = new UpdateBcfRouterCommand(tenantId);
 
         List<AclData> aclList = _bcfUtils.listACLbyNetwork(network);
-        for(AclData acl: aclList){
+        for (AclData acl: aclList) {
             cmd.addAcl(acl);
         }
 
-        if(vpc != null){
+        if (vpc != null) {
             cmd.setPublicIp(_bcfUtils.getPublicIpByVpc(vpc));
         } else {
             cmd.setPublicIp(_bcfUtils.getPublicIpByNetwork(network));
         }
 
         BcfAnswer answer = _bcfUtils.sendBcfCommandWithNetworkSyncCheck(cmd, 
network);
-        if(answer != null && !answer.getResult()){
+        if (answer != null && !answer.getResult()) {
             throw new IllegalArgumentException("Illegal router update 
arguments");
         }
     }
 
-    private void bcfUtilsInit(){
+    private void bcfUtilsInit() {
         if (_bcfUtils == null) {
             _bcfUtils = new BigSwitchBcfUtils(_networkDao, _nicDao,
                     _vmDao, _hostDao, _vpcDao, _bigswitchBcfDao,
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java 
b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index a4dafd60562..0293e0d08fb 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -2539,10 +2539,11 @@ public class ApiResponseHelper implements 
ResponseGenerator {
             response.setType(network.getGuestType().toString());
         }
 
-        response.setGateway(network.getGateway());
+        
response.setGateway(com.cloud.utils.StringUtils.getFirstValueFromCommaSeparatedString(network.getGateway()));
+        String cidr = 
com.cloud.utils.StringUtils.getFirstValueFromCommaSeparatedString(network.getCidr());
 
         // FIXME - either set netmask or cidr
-        response.setCidr(network.getCidr());
+        response.setCidr(cidr);
         if (network.getNetworkCidr() != null) {
             response.setNetworkCidr((network.getNetworkCidr()));
         }
@@ -2553,18 +2554,18 @@ public class ApiResponseHelper implements 
ResponseGenerator {
         if (network.getNetworkCidr() != null) {
             
response.setNetmask(NetUtils.cidr2Netmask(network.getNetworkCidr()));
         }
-        if (((network.getCidr()) != null) && (network.getNetworkCidr() == 
null)) {
-            response.setNetmask(NetUtils.cidr2Netmask(network.getCidr()));
+        if ((cidr != null) && (network.getNetworkCidr() == null)) {
+            response.setNetmask(NetUtils.cidr2Netmask(cidr));
         }
 
-        response.setIp6Gateway(network.getIp6Gateway());
-        response.setIp6Cidr(network.getIp6Cidr());
+        
response.setIp6Gateway(com.cloud.utils.StringUtils.getFirstValueFromCommaSeparatedString(network.getIp6Gateway()));
+        
response.setIp6Cidr(com.cloud.utils.StringUtils.getFirstValueFromCommaSeparatedString(network.getIp6Cidr()));
 
         // create response for reserved IP ranges that can be used for
         // non-cloudstack purposes
         String reservation = null;
-        if ((network.getCidr() != null) && 
(NetUtils.isNetworkAWithinNetworkB(network.getCidr(), 
network.getNetworkCidr()))) {
-            String[] guestVmCidrPair = network.getCidr().split("\\/");
+        if ((cidr != null) && (NetUtils.isNetworkAWithinNetworkB(cidr, 
network.getNetworkCidr()))) {
+            String[] guestVmCidrPair = cidr.split("\\/");
             String[] guestCidrPair = network.getNetworkCidr().split("\\/");
 
             Long guestVmCidrSize = Long.valueOf(guestVmCidrPair[1]);
diff --git 
a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java 
b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index a60b84bf2b6..03b97af4e10 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -5397,9 +5397,42 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
         final VlanVO vlan = commitVlanAndIpRange(zoneId, networkId, 
physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, 
domain, vlanOwner, vlanIp6Gateway, vlanIp6Cidr,
                 ipv4, zone, vlanType, ipv6Range, ipRange, forSystemVms, 
provider);
 
+        if (vlan != null) {
+            if (ipv4) {
+                addCidrAndGatewayForIpv4(networkId, vlanGateway, vlanNetmask);
+            } else if (ipv6) {
+                addCidrAndGatewayForIpv6(networkId, vlanIp6Gateway, 
vlanIp6Cidr);
+            }
+        }
+
         return vlan;
     }
 
+    private void addCidrAndGatewayForIpv4(final long networkId, final String 
vlanGateway, final String vlanNetmask) {
+        final NetworkVO networkVO = _networkDao.findById(networkId);
+        String networkCidr = networkVO.getCidr();
+        String newCidr = NetUtils.getCidrFromGatewayAndNetmask(vlanGateway, 
vlanNetmask);
+        String newNetworkCidr = 
com.cloud.utils.StringUtils.updateCommaSeparatedStringWithValue(networkCidr, 
newCidr, true);
+        networkVO.setCidr(newNetworkCidr);
+
+        String networkGateway = networkVO.getGateway();
+        String newNetworkGateway = 
com.cloud.utils.StringUtils.updateCommaSeparatedStringWithValue(networkGateway, 
vlanGateway, true);
+        networkVO.setGateway(newNetworkGateway);
+        _networkDao.update(networkId, networkVO);
+    }
+
+    private void addCidrAndGatewayForIpv6(final long networkId, final String 
vlanIp6Gateway, final String vlanIp6Cidr) {
+        final NetworkVO networkVO = _networkDao.findById(networkId);
+        String networkIp6Cidr = networkVO.getIp6Cidr();
+        String newNetworkIp6Cidr = 
com.cloud.utils.StringUtils.updateCommaSeparatedStringWithValue(networkIp6Cidr, 
vlanIp6Cidr, true);
+        networkVO.setIp6Cidr(newNetworkIp6Cidr);
+
+        String networkIp6Gateway = networkVO.getIp6Gateway();
+        String newNetworkIp6Gateway = 
com.cloud.utils.StringUtils.updateCommaSeparatedStringWithValue(networkIp6Gateway,
 vlanIp6Gateway, true);
+        networkVO.setIp6Gateway(newNetworkIp6Gateway);
+        _networkDao.update(networkId, networkVO);
+    }
+
     private boolean isConnectivityWithoutVlan(Network network) {
         boolean connectivityWithoutVlan = false;
         if (_networkModel.areServicesSupportedInNetwork(network.getId(), 
Service.Connectivity)) {
@@ -6440,12 +6473,47 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
     private boolean deleteAndPublishVlanAndPublicIpRange(final long userId, 
final long vlanDbId, final Account caller) {
         VlanVO deletedVlan = deleteVlanAndPublicIpRange(userId, vlanDbId, 
caller);
         if (deletedVlan != null) {
+            final boolean ipv4 = deletedVlan.getVlanGateway() != null;
+            final boolean ipv6 = deletedVlan.getIp6Gateway() != null;
+            final long networkId = deletedVlan.getNetworkId();
+
+            if (ipv4) {
+                removeCidrAndGatewayForIpv4(networkId, deletedVlan);
+            } else if (ipv6) {
+                removeCidrAndGatewayForIpv6(networkId, deletedVlan);
+            }
+
             messageBus.publish(_name, MESSAGE_DELETE_VLAN_IP_RANGE_EVENT, 
PublishScope.LOCAL, deletedVlan);
             return true;
         }
         return false;
     }
 
+    private void removeCidrAndGatewayForIpv4(final long networkId, VlanVO 
deletedVlan) {
+        final NetworkVO networkVO = _networkDao.findById(networkId);
+        String networkCidr = networkVO.getCidr();
+        String cidrToRemove = 
NetUtils.getCidrFromGatewayAndNetmask(deletedVlan.getVlanGateway(), 
deletedVlan.getVlanNetmask());
+        String newNetworkCidr = 
com.cloud.utils.StringUtils.updateCommaSeparatedStringWithValue(networkCidr, 
cidrToRemove, false);
+        networkVO.setCidr(newNetworkCidr);
+
+        String networkGateway = networkVO.getGateway();
+        String newNetworkGateway = 
com.cloud.utils.StringUtils.updateCommaSeparatedStringWithValue(networkGateway, 
deletedVlan.getVlanGateway(), false);
+        networkVO.setGateway(newNetworkGateway);
+        _networkDao.update(networkId, networkVO);
+    }
+
+    private void removeCidrAndGatewayForIpv6(final long networkId, VlanVO 
deletedVlan) {
+        final NetworkVO networkVO = _networkDao.findById(networkId);
+        String networkIp6Cidr = networkVO.getIp6Cidr();
+        String newNetworkIp6Cidr = 
com.cloud.utils.StringUtils.updateCommaSeparatedStringWithValue(networkIp6Cidr, 
deletedVlan.getIp6Cidr(), false);
+        networkVO.setIp6Cidr(newNetworkIp6Cidr);
+
+        String networkIp6Gateway = networkVO.getIp6Gateway();
+        String newNetworkIp6Gateway = 
com.cloud.utils.StringUtils.updateCommaSeparatedStringWithValue(networkIp6Gateway,
 deletedVlan.getIp6Gateway(), false);
+        networkVO.setIp6Gateway(newNetworkIp6Gateway);
+        _networkDao.update(networkId, networkVO);
+    }
+
     @Override
     public void checkDiskOfferingAccess(final Account caller, final 
DiskOffering dof, DataCenter zone) {
         for (final SecurityChecker checker : _secChecker) {
diff --git a/utils/src/main/java/com/cloud/utils/StringUtils.java 
b/utils/src/main/java/com/cloud/utils/StringUtils.java
index 3e4e970af2e..94295e3bb2e 100644
--- a/utils/src/main/java/com/cloud/utils/StringUtils.java
+++ b/utils/src/main/java/com/cloud/utils/StringUtils.java
@@ -24,14 +24,17 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
 import java.nio.charset.Charset;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 public class StringUtils extends org.apache.commons.lang3.StringUtils {
     private static final char[] hexChar = {'0', '1', '2', '3', '4', '5', '6', 
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
@@ -409,4 +412,53 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
         String[] finalMergedTagsArray = appendedTags.split(",");
         return finalMergedTagsArray;
     }
+
+
+    /**
+     * Converts the comma separated numbers and ranges to numbers
+     * @param originalString the original string (can be null or empty) 
containing list of comma separated values that has to be updated
+     * @param value the value to add to, or remove from the original string
+     * @param add if true, adds the input value; if false, removes it
+     * @return String containing the modified original string (or null if 
empty)
+     */
+    public static String updateCommaSeparatedStringWithValue(String 
originalString, String value, boolean add) {
+        if (org.apache.commons.lang3.StringUtils.isEmpty(value)) {
+            return originalString;
+        }
+
+        Set<String> values = new LinkedHashSet<>();
+
+        if (org.apache.commons.lang3.StringUtils.isNotEmpty(originalString)) {
+            values.addAll(Arrays.stream(originalString.split(","))
+                    .map(String::trim)
+                    .filter(s -> !s.isEmpty())
+                    .collect(Collectors.toList()));
+        }
+
+        if (add) {
+            values.add(value);
+        } else {
+            values.remove(value);
+        }
+
+        return values.isEmpty() ? null : String.join(",", values);
+    }
+
+    /**
+     * Returns the first value from a comma-separated string.
+     * @param inputString the input string (can be null or empty) containing 
list of comma separated values
+     * @return the first value, or null if none found
+     */
+    public static String getFirstValueFromCommaSeparatedString(String 
inputString) {
+        if (org.apache.commons.lang3.StringUtils.isEmpty(inputString)) {
+            return inputString;
+        }
+
+        String[] values = inputString.split(",");
+        if (values.length > 0) {
+            return values[0].trim();
+        }
+
+        return null;
+    }
 }

Reply via email to