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

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

commit 9db630932e0c711043c409cce4328d63bc4e7a3a
Author: Fabricio Duarte <[email protected]>
AuthorDate: Tue Mar 10 18:33:00 2026 -0300

    Address public IP limit validations
---
 .../java/com/cloud/dc/dao/AccountVlanMapDao.java   |   2 +-
 .../com/cloud/dc/dao/AccountVlanMapDaoImpl.java    |   4 +-
 .../java/com/cloud/dc/dao/DomainVlanMapDao.java    |   2 +-
 .../com/cloud/dc/dao/DomainVlanMapDaoImpl.java     |   4 +-
 server/src/main/java/com/cloud/api/ApiDBUtils.java |   4 +
 .../configuration/ConfigurationManagerImpl.java    | 184 ++++++++++++---------
 .../java/com/cloud/network/NetworkServiceImpl.java |  24 +--
 .../cloud/resourcelimit/CheckedReservation.java    |   6 +
 8 files changed, 135 insertions(+), 95 deletions(-)

diff --git 
a/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDao.java 
b/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDao.java
index 01afd0780f7..e4047cf7973 100644
--- a/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDao.java
+++ b/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDao.java
@@ -27,6 +27,6 @@ public interface AccountVlanMapDao extends 
GenericDao<AccountVlanMapVO, Long> {
 
     public List<AccountVlanMapVO> listAccountVlanMapsByVlan(long vlanDbId);
 
-    public AccountVlanMapVO findAccountVlanMap(long accountId, long vlanDbId);
+    public AccountVlanMapVO findAccountVlanMap(Long accountId, long vlanDbId);
 
 }
diff --git 
a/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDaoImpl.java 
b/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDaoImpl.java
index 12114770f11..0844bb77caa 100644
--- a/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/dc/dao/AccountVlanMapDaoImpl.java
@@ -48,9 +48,9 @@ public class AccountVlanMapDaoImpl extends 
GenericDaoBase<AccountVlanMapVO, Long
     }
 
     @Override
-    public AccountVlanMapVO findAccountVlanMap(long accountId, long vlanDbId) {
+    public AccountVlanMapVO findAccountVlanMap(Long accountId, long vlanDbId) {
         SearchCriteria<AccountVlanMapVO> sc = AccountVlanSearch.create();
-        sc.setParameters("accountId", accountId);
+        sc.setParametersIfNotNull("accountId", accountId);
         sc.setParameters("vlanDbId", vlanDbId);
         return findOneIncludingRemovedBy(sc);
     }
diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDao.java 
b/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDao.java
index 6af16bbace9..d14ccbe86ca 100644
--- a/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDao.java
+++ b/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDao.java
@@ -24,5 +24,5 @@ import com.cloud.utils.db.GenericDao;
 public interface DomainVlanMapDao extends GenericDao<DomainVlanMapVO, Long> {
     public List<DomainVlanMapVO> listDomainVlanMapsByDomain(long domainId);
     public List<DomainVlanMapVO> listDomainVlanMapsByVlan(long vlanDbId);
-    public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId);
+    public DomainVlanMapVO findDomainVlanMap(Long domainId, long vlanDbId);
 }
diff --git 
a/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDaoImpl.java 
b/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDaoImpl.java
index f789721d5fd..0b4c781349f 100644
--- a/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/dc/dao/DomainVlanMapDaoImpl.java
@@ -46,9 +46,9 @@ public class DomainVlanMapDaoImpl extends 
GenericDaoBase<DomainVlanMapVO, Long>
     }
 
     @Override
-    public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId) {
+    public DomainVlanMapVO findDomainVlanMap(Long domainId, long vlanDbId) {
         SearchCriteria<DomainVlanMapVO> sc = DomainVlanSearch.create();
-        sc.setParameters("domainId", domainId);
+        sc.setParametersIfNotNull("domainId", domainId);
         sc.setParameters("vlanDbId", vlanDbId);
         return findOneIncludingRemovedBy(sc);
     }
diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java 
b/server/src/main/java/com/cloud/api/ApiDBUtils.java
index f7ffb039801..2d359b87d2e 100644
--- a/server/src/main/java/com/cloud/api/ApiDBUtils.java
+++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java
@@ -2291,6 +2291,10 @@ public class ApiDBUtils {
         return s_accountService.isAdmin(account.getId());
     }
 
+    public static Account getSystemAccount() {
+        return s_accountService.getSystemAccount();
+    }
+
     public static List<ResourceTagJoinVO> 
listResourceTagViewByResourceUUID(String resourceUUID, ResourceObjectType 
resourceType) {
         return s_tagJoinDao.listBy(resourceUUID, resourceType);
     }
diff --git 
a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java 
b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 81970b0f8a7..cc062baa4bd 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -49,10 +49,6 @@ import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
-import com.cloud.consoleproxy.ConsoleProxyManager;
-import com.cloud.network.router.VirtualNetworkApplianceManager;
-import com.cloud.storage.secondary.SecondaryStorageVmManager;
-import com.cloud.vm.VirtualMachineManager;
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.acl.SecurityChecker;
 import org.apache.cloudstack.affinity.AffinityGroup;
@@ -126,6 +122,7 @@ import org.apache.cloudstack.region.PortableIpVO;
 import org.apache.cloudstack.region.Region;
 import org.apache.cloudstack.region.RegionVO;
 import org.apache.cloudstack.region.dao.RegionDao;
+import org.apache.cloudstack.reservation.dao.ReservationDao;
 import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO;
 import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
@@ -154,6 +151,7 @@ import com.cloud.api.query.vo.NetworkOfferingJoinVO;
 import com.cloud.capacity.CapacityManager;
 import com.cloud.capacity.dao.CapacityDao;
 import com.cloud.configuration.Resource.ResourceType;
+import com.cloud.consoleproxy.ConsoleProxyManager;
 import com.cloud.dc.AccountVlanMapVO;
 import com.cloud.dc.ClusterDetailsDao;
 import com.cloud.dc.ClusterDetailsVO;
@@ -247,6 +245,7 @@ import com.cloud.network.dao.UserIpv6AddressDao;
 import com.cloud.network.element.NetrisProviderVO;
 import com.cloud.network.element.NsxProviderVO;
 import com.cloud.network.netris.NetrisService;
+import com.cloud.network.router.VirtualNetworkApplianceManager;
 import com.cloud.network.rules.LoadBalancerContainer.Scheme;
 import com.cloud.network.vpc.VpcManager;
 import com.cloud.offering.DiskOffering;
@@ -264,6 +263,7 @@ import com.cloud.org.Grouping;
 import com.cloud.org.Grouping.AllocationState;
 import com.cloud.projects.Project;
 import com.cloud.projects.ProjectManager;
+import com.cloud.resourcelimit.CheckedReservation;
 import com.cloud.server.ManagementService;
 import com.cloud.service.ServiceOfferingDetailsVO;
 import com.cloud.service.ServiceOfferingVO;
@@ -281,6 +281,7 @@ import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.StoragePoolTagsDao;
 import com.cloud.storage.dao.VMTemplateZoneDao;
 import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.secondary.SecondaryStorageVmManager;
 import com.cloud.test.IPRangeConfig;
 import com.cloud.user.Account;
 import com.cloud.user.AccountDetailVO;
@@ -314,6 +315,7 @@ import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.NicIpAlias;
 import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.VmDetailConstants;
 import com.cloud.vm.dao.NicIpAliasDao;
 import com.cloud.vm.dao.NicIpAliasVO;
@@ -406,6 +408,8 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
     @Inject
     ResourceLimitService _resourceLimitMgr;
     @Inject
+    ReservationDao reservationDao;
+    @Inject
     ProjectManager _projectMgr;
     @Inject
     NetworkOfferingServiceMapDao _ntwkOffServiceMapDao;
@@ -537,7 +541,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
     public static final ConfigKey<Long> DELETE_QUERY_BATCH_SIZE = new 
ConfigKey<>("Advanced", Long.class, "delete.query.batch.size", "0",
             "Indicates the limit applied while deleting entries in bulk. With 
this, the delete query will apply the limit as many times as necessary," +
                     " to delete all the entries. This is advised when 
retaining several days of records, which can lead to slowness. <= 0 means that 
no limit will " +
-                    "be applied. Default value is 0. For now, this is used for 
deletion of vm & volume stats only.", true);
+                    "be applied. Default value is 0. For now, this is used for 
deletion of VM stats, volume stats, and usage records.", true);
 
     private static final String IOPS_READ_RATE = "IOPS Read";
     private static final String IOPS_WRITE_RATE = "IOPS Write";
@@ -641,7 +645,6 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
     protected void populateConfigKeysAllowedOnlyForDefaultAdmin() {
         
configKeysAllowedOnlyForDefaultAdmin.add(AccountManagerImpl.listOfRoleTypesAllowedForOperationsOfSameRoleType.key());
         
configKeysAllowedOnlyForDefaultAdmin.add(AccountManagerImpl.allowOperationsOnUsersInSameAccount.key());
-
         
configKeysAllowedOnlyForDefaultAdmin.add(VirtualMachineManager.SystemVmEnableUserData.key());
         
configKeysAllowedOnlyForDefaultAdmin.add(ConsoleProxyManager.ConsoleProxyVmUserData.key());
         
configKeysAllowedOnlyForDefaultAdmin.add(SecondaryStorageVmManager.SecondaryStorageVmUserData.key());
@@ -828,7 +831,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
             case Account:
                 final AccountVO account = _accountDao.findById(resourceId);
                 if (account == null) {
-                    throw new InvalidParameterValueException("unable to find 
account by id " + resourceId);
+                    throw new InvalidParameterValueException("Unable to find 
Account by id " + resourceId);
                 }
                 resourceType = ApiCommandResourceType.Account;
                 AccountDetailVO accountDetailVO = 
_accountDetailsDao.findDetail(resourceId, name);
@@ -984,7 +987,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
         String hypervisors = _configDao.getValue(hypervisorListConfigName);
         if (Arrays.asList(hypervisors.split(",")).contains(previousValue)) {
             hypervisors = hypervisors.replace(previousValue, newValue);
-            logger.info("Updating the hypervisor list configuration '{}}' to 
match the new custom hypervisor display name",
+            logger.info("Updating the hypervisor list configuration '{}' to 
match the new custom hypervisor display name",
                     hypervisorListConfigName);
             _configDao.update(hypervisorListConfigName, hypervisors);
         }
@@ -1753,7 +1756,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
 
         // Check if there are any non-removed vms in the pod.
         if (!_vmInstanceDao.listByPodId(podId).isEmpty()) {
-            throw new CloudRuntimeException(errorMsg + "there are virtual 
machines in this pod.");
+            throw new CloudRuntimeException(errorMsg + "there are Instances in 
this pod.");
         }
 
         // Check if there are any non-removed clusters in the pod.
@@ -1797,7 +1800,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
         // Don't allow gateway to overlap with start/endIp
         if (!skipGatewayOverlapCheck) {
             if (NetUtils.ipRangesOverlap(startIp, endIp, gateway, gateway)) {
-                throw new InvalidParameterValueException("The gateway 
shouldn't overlap start/end ip addresses");
+                throw new InvalidParameterValueException("The gateway 
shouldn't overlap start/end IP addresses");
             }
         }
 
@@ -1851,7 +1854,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                 final List<DataCenterIpAddressVO> privateIps = 
_privateIpAddressDao.listByPodIdDcId(podId, pod.getDataCenterId());
                 if (!privateIps.isEmpty()) {
                     if (!_privateIpAddressDao.deleteIpAddressByPod(podId)) {
-                        throw new CloudRuntimeException(String.format("Failed 
to cleanup private ip addresses for pod %s", pod));
+                        throw new CloudRuntimeException(String.format("Failed 
to cleanup private IP addresses for pod %s", pod));
                     }
                 }
 
@@ -1859,7 +1862,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                 final List<DataCenterLinkLocalIpAddressVO> localIps = 
_linkLocalIpAllocDao.listByPodIdDcId(podId, pod.getDataCenterId());
                 if (!localIps.isEmpty()) {
                     if (!_linkLocalIpAllocDao.deleteIpAddressByPod(podId)) {
-                        throw new CloudRuntimeException(String.format("Failed 
to cleanup private ip addresses for pod %s", pod));
+                        throw new CloudRuntimeException(String.format("Failed 
to cleanup private IP addresses for pod %s", pod));
                     }
                 }
 
@@ -1921,7 +1924,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
         final Account account = CallContext.current().getCallingAccount();
 
         if(!_accountMgr.isRootAdmin(account.getId())) {
-            throw new PermissionDeniedException(String.format("Cannot perform 
this operation, Calling account is not root admin: %s", account));
+            throw new PermissionDeniedException(String.format("Cannot perform 
this operation, Calling Account is not root admin: %s", account));
         }
 
         final long podId = cmd.getPodId();
@@ -1988,7 +1991,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
         }
 
         if (NetUtils.ipRangesOverlap(startIp, endIp, gateway, gateway)) {
-            throw new InvalidParameterValueException("The gateway shouldn't 
overlap start/end ip addresses");
+            throw new InvalidParameterValueException("The gateway shouldn't 
overlap start/end IP addresses");
         }
 
         final String[] existingPodIpRanges = pod.getDescription().split(",");
@@ -2032,7 +2035,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                         lock = _podDao.acquireInLockTable(podId);
 
                         if (lock == null) {
-                            String msg = String.format("Unable to acquire lock 
on table to update the ip range of POD: %s, Creation failed.", pod);
+                            String msg = String.format("Unable to acquire lock 
on table to update the IP range of POD: %s, Creation failed.", pod);
                             logger.warn(msg);
                             throw new CloudRuntimeException(msg);
                         }
@@ -2145,7 +2148,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                         lock = _podDao.acquireInLockTable(podId);
 
                         if (lock == null) {
-                            String msg = String.format("Unable to acquire lock 
on table to update the ip range of POD: %s, Deletion failed.", pod);
+                            String msg = String.format("Unable to acquire lock 
on table to update the IP range of POD: %s, Deletion failed.", pod);
                             logger.warn(msg);
                             throw new CloudRuntimeException(msg);
                         }
@@ -2159,7 +2162,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
 
                     for(long ipAddr = NetUtils.ip2Long(startIp); ipAddr <= 
NetUtils.ip2Long(endIp); ipAddr++) {
                         if 
(!_privateIpAddressDao.deleteIpAddressByPodDc(NetUtils.long2Ip(ipAddr), podId, 
pod.getDataCenterId())) {
-                            throw new 
CloudRuntimeException(String.format("Failed to cleanup private ip address: %s 
of Pod: %s DC: %s", NetUtils.long2Ip(ipAddr), pod, 
_zoneDao.findById(pod.getDataCenterId())));
+                            throw new 
CloudRuntimeException(String.format("Failed to cleanup private IP address: %s 
of Pod: %s DC: %s", NetUtils.long2Ip(ipAddr), pod, 
_zoneDao.findById(pod.getDataCenterId())));
                         }
                     }
                 }
@@ -2266,7 +2269,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
         try {
             lock = _podDao.acquireInLockTable(podId);
             if (lock == null) {
-                String msg = String.format("Unable to acquire lock on table to 
update the ip range of POD: %s, Update failed.", pod);
+                String msg = String.format("Unable to acquire lock on table to 
update the IP range of POD: %s, Update failed.", pod);
                 logger.warn(msg);
                 throw new CloudRuntimeException(msg);
             }
@@ -2281,7 +2284,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                 if (currentIpRange.size() > 0) {
                     for (Long startIP: currentIpRange) {
                         if 
(!_privateIpAddressDao.deleteIpAddressByPodDc(NetUtils.long2Ip(startIP),podId,zoneId))
 {
-                            throw new 
CloudRuntimeException(String.format("Failed to remove private ip address: %s of 
Pod: %s DC: %s", NetUtils.long2Ip(startIP), pod, 
_zoneDao.findById(pod.getDataCenterId())));
+                            throw new 
CloudRuntimeException(String.format("Failed to remove private IP address: %s of 
Pod: %s DC: %s", NetUtils.long2Ip(startIP), pod, 
_zoneDao.findById(pod.getDataCenterId())));
                         }
                     }
                 }
@@ -2499,7 +2502,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                 }
 
                 if (NetUtils.ipRangesOverlap(existingPodIpRange[0], 
existingPodIpRange[1], gateway, gateway)) {
-                    throw new InvalidParameterValueException("The gateway 
shouldn't overlap some start/end ip addresses");
+                    throw new InvalidParameterValueException("The gateway 
shouldn't overlap some start/end IP addresses");
                 }
             }
         }
@@ -3419,7 +3422,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
             isCustomizedIops = false;
 
             if (cmd.getHypervisorSnapshotReserve() != null) {
-                throw new InvalidParameterValueException("Hypervisor snapshot 
reserve is not a valid parameter for a system VM.");
+                throw new InvalidParameterValueException("Hypervisor Snapshot 
reserve is not a valid parameter for a system VM.");
             }
         } else {
             allowNetworkRate = true;
@@ -4794,7 +4797,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
 
     @Override
     @DB
-    @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_CREATE, 
eventDescription = "creating vlan ip range", async = false)
+    @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_CREATE, 
eventDescription = "Creating VLAN IP range", async = false)
     public Vlan createVlanAndPublicIpRange(final CreateVlanIpRangeCmd cmd) 
throws InsufficientCapacityException, ConcurrentOperationException, 
ResourceUnavailableException,
     ResourceAllocationException {
         Long zoneId = cmd.getZoneId();
@@ -4957,7 +4960,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
         }
 
         if (zone.isSecurityGroupEnabled() && zone.getNetworkType() != 
DataCenter.NetworkType.Basic && forVirtualNetwork) {
-            throw new InvalidParameterValueException("Can't add virtual ip 
range into a zone with security group enabled");
+            throw new InvalidParameterValueException("Can't add virtual IP 
range into a zone with security group enabled");
         }
 
         // If networkId is not specified, and vlan is Virtual or Direct
@@ -4993,13 +4996,13 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
         Pair<Boolean, Pair<String, String>> sameSubnet = null;
         // Can add vlan range only to the network which allows it
         if (!network.getSpecifyIpRanges()) {
-            throw new InvalidParameterValueException("Network " + network + " 
doesn't support adding ip ranges");
+            throw new InvalidParameterValueException("Network " + network + " 
doesn't support adding IP ranges");
         }
 
         if (zone.getNetworkType() == DataCenter.NetworkType.Advanced) {
             if (network.getTrafficType() == TrafficType.Guest) {
                 if (network.getGuestType() != GuestType.Shared) {
-                    throw new 
InvalidParameterValueException(String.format("Can execute createVLANIpRanges on 
shared guest network, but type of this guest network %s is %s", network, 
network.getGuestType()));
+                    throw new 
InvalidParameterValueException(String.format("Can execute createVLANIpRanges on 
shared guest Network, but type of this guest Network %s is %s", network, 
network.getGuestType()));
                 }
 
                 final List<VlanVO> vlans = 
_vlanDao.listVlansByNetworkId(network.getId());
@@ -5026,22 +5029,20 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
             throw new InvalidParameterValueException("Gateway, netmask and 
zoneId have to be passed in for virtual and direct untagged networks");
         }
 
-        if (forVirtualNetwork) {
-            if (vlanOwner != null) {
-
-                final long accountIpRange = NetUtils.ip2Long(endIP) - 
NetUtils.ip2Long(startIP) + 1;
-
-                // check resource limits
-                _resourceLimitMgr.checkResourceLimit(vlanOwner, 
ResourceType.public_ip, accountIpRange);
-            }
-        }
         // Check if the IP range overlaps with the private ip
         if (ipv4) {
             checkOverlapPrivateIpRange(zoneId, startIP, endIP);
         }
 
-        return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, 
newVlanNetmask, vlanId, forVirtualNetwork, forSystemVms, networkId, 
physicalNetworkId, startIPv6, endIPv6, ip6Gateway,
-                ip6Cidr, domain, vlanOwner, network, sameSubnet, 
cmd.getProvider());
+        long reservedIpAddressesAmount = 0L;
+        if (forVirtualNetwork && vlanOwner != null) {
+            reservedIpAddressesAmount = NetUtils.ip2Long(endIP) - 
NetUtils.ip2Long(startIP) + 1;
+        }
+
+        try (CheckedReservation publicIpReservation = new 
CheckedReservation(vlanOwner, ResourceType.public_ip, null, null, null, 
reservedIpAddressesAmount, null, reservationDao, _resourceLimitMgr)) {
+            return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, 
newVlanNetmask, vlanId, forVirtualNetwork, forSystemVms, networkId, 
physicalNetworkId, startIPv6, endIPv6, ip6Gateway,
+                    ip6Cidr, domain, vlanOwner, network, sameSubnet, 
cmd.getProvider());
+        }
     }
 
     private Network getNetwork(Long networkId) {
@@ -5137,10 +5138,10 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
                     "either both netmask and gateway should be passed or both 
should me omited.");
         } else {
             if (!NetUtils.sameSubnet(newStartIP, newVlanGateway, 
newVlanNetmask)) {
-                throw new InvalidParameterValueException("The start ip and 
gateway do not belong to the same subnet");
+                throw new InvalidParameterValueException("The start IP and 
gateway do not belong to the same subnet");
             }
             if (!NetUtils.sameSubnet(newEndIP, newVlanGateway, 
newVlanNetmask)) {
-                throw new InvalidParameterValueException("The end ip and 
gateway do not belong to the same subnet");
+                throw new InvalidParameterValueException("The end IP and 
gateway do not belong to the same subnet");
             }
         }
         final String cidrnew = 
NetUtils.getCidrFromGatewayAndNetmask(newVlanGateway, newVlanNetmask);
@@ -5169,7 +5170,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                     ipv6, ip6Gateway, ip6Cidr, startIPv6, endIPv6, network);
         }
         if (newVlanGateway == null && newVlanNetmask == null && !sameSubnet) {
-            throw new InvalidParameterValueException("The ip range dose not 
belong to any of the existing subnets, Provide the netmask and gateway if you 
want to add new subnet");
+            throw new InvalidParameterValueException("The IP range dose not 
belong to any of the existing subnets, Provide the netmask and gateway if you 
want to add new subnet");
         }
         Pair<String, String> vlanDetails = null;
 
@@ -5577,7 +5578,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                                 ip.isSourceNat(), 
vlan.getVlanType().toString(), ip.getSystem(), usageHidden, 
ip.getClass().getName(), ip.getUuid());
                     }
                     // increment resource count for dedicated public ip's
-                    
_resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), 
ResourceType.public_ip, new Long(ips.size()));
+                    
_resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), 
ResourceType.public_ip, (long)ips.size());
                 } else if (domain != null && !forSystemVms) {
                     // This VLAN is domain-wide, so create a DomainVlanMapVO 
entry
                     final DomainVlanMapVO domainVlanMapVO = new 
DomainVlanMapVO(domain.getId(), vlan.getId());
@@ -5621,7 +5622,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                                            String endIpv6,
                                            String ip6Gateway,
                                            String ip6Cidr,
-                                           Boolean forSystemVms) throws 
ConcurrentOperationException {
+                                           Boolean forSystemVms) throws 
ConcurrentOperationException, ResourceAllocationException {
 
         VlanVO vlanRange = _vlanDao.findById(id);
         if (vlanRange == null) {
@@ -5641,24 +5642,49 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
             }
         }
 
+        AccountVlanMapVO accountMap = 
_accountVlanMapDao.findAccountVlanMap(null, id);
+        Account account = accountMap != null ? 
_accountDao.findById(accountMap.getAccountId()) : null;
+
+        DomainVlanMapVO domainMap = _domainVlanMapDao.findDomainVlanMap(null, 
id);
+        Long domainId = domainMap != null ? domainMap.getDomainId() : null;
+
         final Boolean isRangeForSystemVM = checkIfVlanRangeIsForSystemVM(id);
         if (forSystemVms != null && isRangeForSystemVM != forSystemVms) {
             if (VlanType.DirectAttached.equals(vlanRange.getVlanType())) {
                 throw new InvalidParameterValueException("forSystemVms is not 
available for this IP range with vlan type: " + VlanType.DirectAttached);
             }
             // Check if range has already been dedicated
-            final List<AccountVlanMapVO> maps = 
_accountVlanMapDao.listAccountVlanMapsByVlan(id);
-            if (maps != null && !maps.isEmpty()) {
+            if (account != null) {
                 throw new InvalidParameterValueException("Specified Public IP 
range has already been dedicated to an account");
             }
-
-            List<DomainVlanMapVO> domainmaps = 
_domainVlanMapDao.listDomainVlanMapsByVlan(id);
-            if (domainmaps != null && !domainmaps.isEmpty()) {
+            if (domainId != null) {
                 throw new InvalidParameterValueException("Specified Public IP 
range has already been dedicated to a domain");
             }
         }
         if (ipv4) {
-            updateVlanAndIpv4Range(id, vlanRange, startIp, endIp, gateway, 
netmask, isRangeForSystemVM, forSystemVms);
+            long existingIpAddressAmount = 0L;
+            long newIpAddressAmount = 0L;
+
+            if (account != null) {
+                // IPv4 public range is dedicated to an account (IPv6 cannot 
be dedicated at the moment).
+                // We need to update the resource count.
+                existingIpAddressAmount = 
_publicIpAddressDao.countIPs(vlanRange.getDataCenterId(), id, false);
+                newIpAddressAmount = NetUtils.ip2Long(endIp) - 
NetUtils.ip2Long(startIp) + 1;
+            }
+
+            try (CheckedReservation publicIpReservation = new 
CheckedReservation(account, ResourceType.public_ip, null, null, null, 
newIpAddressAmount, existingIpAddressAmount, reservationDao, 
_resourceLimitMgr)) {
+
+                updateVlanAndIpv4Range(id, vlanRange, startIp, endIp, gateway, 
netmask, isRangeForSystemVM, forSystemVms);
+
+                if (account != null) {
+                    long countDiff = newIpAddressAmount - 
existingIpAddressAmount;
+                    if (countDiff > 0) {
+                        
_resourceLimitMgr.incrementResourceCount(account.getId(), 
ResourceType.public_ip, countDiff);
+                    } else if (countDiff < 0) {
+                        
_resourceLimitMgr.decrementResourceCount(account.getId(), 
ResourceType.public_ip, Math.abs(countDiff));
+                    }
+                }
+            }
         }
         if (ipv6) {
             updateVlanAndIpv6Range(id, vlanRange, startIpv6, endIpv6, 
ip6Gateway, ip6Cidr, isRangeForSystemVM, forSystemVms);
@@ -5738,10 +5764,10 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
         final List<UserIpv6AddressVO> listAllocatedIPs = 
_ipv6Dao.listByVlanIdAndState(id, IpAddress.State.Allocated);
 
         if (ip6Gateway != null && 
!ip6Gateway.equals(vlanRange.getIp6Gateway()) && 
(CollectionUtils.isNotEmpty(listAllocatedIPs) || 
CollectionUtils.isNotEmpty(ipv6Service.getAllocatedIpv6FromVlanRange(vlanRange))))
 {
-            throw new InvalidParameterValueException(String.format("Unable to 
change ipv6 gateway to %s because some IPs are in use", ip6Gateway));
+            throw new InvalidParameterValueException(String.format("Unable to 
change IPv6 gateway to %s because some IPs are in use", ip6Gateway));
         }
         if (ip6Cidr != null && !ip6Cidr.equals(vlanRange.getIp6Cidr()) && 
(CollectionUtils.isNotEmpty(listAllocatedIPs) || 
CollectionUtils.isNotEmpty(ipv6Service.getAllocatedIpv6FromVlanRange(vlanRange))))
 {
-            throw new InvalidParameterValueException(String.format("Unable to 
change ipv6 cidr to %s because some IPs are in use", ip6Cidr));
+            throw new InvalidParameterValueException(String.format("Unable to 
change IPv6 CIDR to %s because some IPs are in use", ip6Cidr));
         }
         ip6Gateway = MoreObjects.firstNonNull(ip6Gateway, 
vlanRange.getIp6Gateway());
         ip6Cidr = MoreObjects.firstNonNull(ip6Cidr, vlanRange.getIp6Cidr());
@@ -5937,7 +5963,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
             } finally {
                 _vlanDao.releaseFromLockTable(vlanDbId);
                 if (resourceCountToBeDecrement > 0) {  //Making sure to 
decrement the count of only success operations above. For any reaason if 
disassociation fails then this number will vary from original range length.
-                    
_resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), 
ResourceType.public_ip, new Long(resourceCountToBeDecrement));
+                    
_resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), 
ResourceType.public_ip, (long)resourceCountToBeDecrement);
                 }
             }
         } else {   // !isAccountSpecific
@@ -6045,12 +6071,6 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
             throw new InvalidParameterValueException("Public IP range can be 
dedicated to an account only in the zone of type " + NetworkType.Advanced);
         }
 
-        // Check Public IP resource limits
-        if (vlanOwner != null) {
-            final int accountPublicIpRange = 
_publicIpAddressDao.countIPs(zoneId, vlanDbId, false);
-            _resourceLimitMgr.checkResourceLimit(vlanOwner, 
ResourceType.public_ip, accountPublicIpRange);
-        }
-
         // Check if any of the Public IP addresses is allocated to another
         // account
         final List<IPAddressVO> ips = 
_publicIpAddressDao.listByVlanId(vlanDbId);
@@ -6071,29 +6091,35 @@ public class ConfigurationManagerImpl extends 
ManagerBase implements Configurati
             }
         }
 
-        if (vlanOwner != null) {
-            // Create an AccountVlanMapVO entry
-            final AccountVlanMapVO accountVlanMapVO = new 
AccountVlanMapVO(vlanOwner.getId(), vlan.getId());
-            _accountVlanMapDao.persist(accountVlanMapVO);
+        // Check Public IP resource limits
+        long reservedIpAddressesAmount = vlanOwner != null ? 
_publicIpAddressDao.countIPs(zoneId, vlanDbId, false) : 0L;
+        try (CheckedReservation publicIpReservation = new 
CheckedReservation(vlanOwner, ResourceType.public_ip, null, null, null, 
reservedIpAddressesAmount, null, reservationDao, _resourceLimitMgr)) {
+
+            if (vlanOwner != null) {
+                // Create an AccountVlanMapVO entry
+                final AccountVlanMapVO accountVlanMapVO = new 
AccountVlanMapVO(vlanOwner.getId(), vlan.getId());
+                _accountVlanMapDao.persist(accountVlanMapVO);
 
-           // generate usage event for dedication of every ip address in the 
range
-            for (final IPAddressVO ip : ips) {
-                final boolean usageHidden = _ipAddrMgr.isUsageHidden(ip);
-                
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, 
vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), 
ip.getAddress().toString(), ip.isSourceNat(),
-                        vlan.getVlanType().toString(), ip.getSystem(), 
usageHidden, ip.getClass().getName(), ip.getUuid());
+               // generate usage event for dedication of every ip address in 
the range
+                for (final IPAddressVO ip : ips) {
+                    final boolean usageHidden = _ipAddrMgr.isUsageHidden(ip);
+                    
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, 
vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), 
ip.getAddress().toString(), ip.isSourceNat(),
+                            vlan.getVlanType().toString(), ip.getSystem(), 
usageHidden, ip.getClass().getName(), ip.getUuid());
+                }
+            } else if (domain != null) {
+                // Create an DomainVlanMapVO entry
+                DomainVlanMapVO domainVlanMapVO = new 
DomainVlanMapVO(domain.getId(), vlan.getId());
+                _domainVlanMapDao.persist(domainVlanMapVO);
             }
-        } else if (domain != null) {
-            // Create an DomainVlanMapVO entry
-            DomainVlanMapVO domainVlanMapVO = new 
DomainVlanMapVO(domain.getId(), vlan.getId());
-            _domainVlanMapDao.persist(domainVlanMapVO);
-        }
 
-        // increment resource count for dedicated public ip's
-        if (vlanOwner != null) {
-            _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), 
ResourceType.public_ip, new Long(ips.size()));
-        }
+            // increment resource count for dedicated public ip's
+            if (vlanOwner != null) {
+                _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), 
ResourceType.public_ip, (long)ips.size());
+            }
 
-        return vlan;
+            return vlan;
+
+        }
     }
 
     @Override
@@ -6182,7 +6208,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
                 }
             }
             // decrement resource count for dedicated public ip's
-            
_resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), 
ResourceType.public_ip, new Long(ips.size()));
+            
_resourceLimitMgr.decrementResourceCount(acctVln.get(0).getAccountId(), 
ResourceType.public_ip, (long)ips.size());
             success = true;
         } else if (isDomainSpecific && 
_domainVlanMapDao.remove(domainVlan.get(0).getId())) {
             logger.debug("Remove the vlan from domain_vlan_map successfully.");
@@ -6351,7 +6377,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
         final List<Object> newCidrPair = new ArrayList<>();
         newCidrPair.add(0, getCidrAddress(cidr));
         newCidrPair.add(1, (long)getCidrSize(cidr));
-        currentPodCidrSubnets.put(new Long(-1), newCidrPair);
+        currentPodCidrSubnets.put(-1L, newCidrPair);
 
         final DataCenterVO dcVo = _zoneDao.findById(dcId);
         final String guestNetworkCidr = dcVo.getGuestNetworkCidr();
@@ -6451,7 +6477,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
     }
 
     private String getPodName(final long podId) {
-        return _podDao.findById(new Long(podId)).getName();
+        return _podDao.findById(podId).getName();
     }
 
     private boolean validZone(final String zoneName) {
@@ -6463,7 +6489,7 @@ public class ConfigurationManagerImpl extends ManagerBase 
implements Configurati
     }
 
     private String getZoneName(final long zoneId) {
-        final DataCenterVO zone = _zoneDao.findById(new Long(zoneId));
+        final DataCenterVO zone = _zoneDao.findById(zoneId);
         if (zone != null) {
             return zone.getName();
         } else {
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java 
b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 8b5e2c5a5ef..d872317fa28 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -41,6 +41,7 @@ import java.util.UUID;
 import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
+import com.cloud.resourcelimit.CheckedReservation;
 import org.apache.cloudstack.acl.ControlledEntity.ACLType;
 import org.apache.cloudstack.acl.SecurityChecker.AccessType;
 import org.apache.cloudstack.alert.AlertService;
@@ -76,6 +77,7 @@ import org.apache.cloudstack.network.NetworkPermissionVO;
 import org.apache.cloudstack.network.RoutedIpv4Manager;
 import org.apache.cloudstack.network.dao.NetworkPermissionDao;
 import 
org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
+import org.apache.cloudstack.reservation.dao.ReservationDao;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.BooleanUtils;
@@ -335,6 +337,8 @@ public class NetworkServiceImpl extends ManagerBase 
implements NetworkService, C
     @Inject
     ResourceLimitService _resourceLimitMgr;
     @Inject
+    ReservationDao reservationDao;
+    @Inject
     DomainManager _domainMgr;
     @Inject
     ProjectManager _projectMgr;
@@ -1152,15 +1156,10 @@ public class NetworkServiceImpl extends ManagerBase 
implements NetworkService, C
         if (ipDedicatedAccountId != null && 
!ipDedicatedAccountId.equals(account.getAccountId())) {
             throw new InvalidParameterValueException("Unable to reserve a IP 
because it is dedicated to another account.");
         }
-        if (ipDedicatedAccountId == null) {
-            // Check that the maximum number of public IPs for the given 
accountId will not be exceeded
-            try {
-                _resourceLimitMgr.checkResourceLimit(account, 
Resource.ResourceType.public_ip);
-            } catch (ResourceAllocationException ex) {
-                logger.warn("Failed to allocate resource of type " + 
ex.getResourceType() + " for account " + account);
-                throw new AccountLimitException("Maximum number of public IP 
addresses for account: " + account.getAccountName() + " has been exceeded.");
-            }
-        }
+
+        long reservedIpAddressesAmount = ipDedicatedAccountId == null ? 1L : 
0L;
+        try (CheckedReservation publicIpAddressReservation = new 
CheckedReservation(account, Resource.ResourceType.public_ip, 
reservedIpAddressesAmount, reservationDao, _resourceLimitMgr)) {
+
         List<AccountVlanMapVO> maps = 
_accountVlanMapDao.listAccountVlanMapsByVlan(ipVO.getVlanId());
         ipVO.setAllocatedTime(new Date());
         ipVO.setAllocatedToAccountId(account.getAccountId());
@@ -1170,10 +1169,15 @@ public class NetworkServiceImpl extends ManagerBase 
implements NetworkService, C
             ipVO.setDisplay(displayIp);
         }
         ipVO = _ipAddressDao.persist(ipVO);
-        if (ipDedicatedAccountId == null) {
+        if (reservedIpAddressesAmount > 0) {
             _resourceLimitMgr.incrementResourceCount(account.getId(), 
Resource.ResourceType.public_ip);
         }
         return ipVO;
+
+        } catch (ResourceAllocationException ex) {
+            logger.warn("Failed to allocate resource of type " + 
ex.getResourceType() + " for account " + account);
+            throw new AccountLimitException("Maximum number of public IP 
addresses for account: " + account.getAccountName() + " has been exceeded.");
+        }
     }
 
     @Override
diff --git 
a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java 
b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java
index 5f9913e2ee5..cab77ccf16a 100644
--- a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java
+++ b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
+import com.cloud.api.ApiDBUtils;
 import org.apache.cloudstack.context.CallContext;
 import org.apache.cloudstack.reservation.ReservationVO;
 import org.apache.cloudstack.reservation.dao.ReservationDao;
@@ -146,6 +147,11 @@ public class CheckedReservation implements Reserver {
 
         this.reservationDao = reservationDao;
         this.resourceLimitService = resourceLimitService;
+
+        // When allocating to a domain instead of a specific account, consider 
the system account as the owner for the validations here.
+        if (account == null) {
+            account = ApiDBUtils.getSystemAccount();
+        }
         this.account = account;
 
         if (domainId == null) {


Reply via email to