mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	Update CIDR/Gateway of the Shared Networks from Guest IP ranges (#11249)
This commit is contained in:
		
							parent
							
								
									f6ad184ea2
								
							
						
					
					
						commit
						86827f871d
					
				| @ -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'; | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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]); | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user