From 89df31816480d07d5cade1989929e83f7cffbbe8 Mon Sep 17 00:00:00 2001 From: dahn Date: Fri, 20 Feb 2026 16:20:14 +0100 Subject: [PATCH 01/43] [20.3] resource allocation --- .../com/cloud/user/ResourceLimitService.java | 2 + .../service/NetworkOrchestrationService.java | 2 +- .../orchestration/NetworkOrchestrator.java | 49 +++++++++++++------ .../com/cloud/network/NetworkServiceImpl.java | 2 +- .../resourcelimit/CheckedReservation.java | 32 ++++++++---- .../ResourceLimitManagerImpl.java | 36 +++++++++----- .../resourcelimit/CheckedReservationTest.java | 17 +------ .../ResourceLimitManagerImplTest.java | 6 ++- .../com/cloud/vpc/MockNetworkManagerImpl.java | 2 +- .../vpc/MockResourceLimitManagerImpl.java | 10 ++++ 10 files changed, 101 insertions(+), 57 deletions(-) diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java index 666529808bf..fc486601d16 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -185,6 +185,7 @@ public interface ResourceLimitService { */ public void checkResourceLimit(Account account, ResourceCount.ResourceType type, long... count) throws ResourceAllocationException; public void checkResourceLimitWithTag(Account account, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException; + public void checkResourceLimitWithTag(Account account, Long domainId, boolean considerSystemAccount, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException; /** * Gets the count of resources for a resource type and account @@ -284,4 +285,5 @@ public interface ResourceLimitService { void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); + long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag); } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index dbab3320316..d1f391cf625 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -302,7 +302,7 @@ public interface NetworkOrchestrationService { void removeDhcpServiceInSubnet(Nic nic); - boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType); + boolean isResourceCountUpdateNeeded(NetworkOffering networkOffering); void prepareAllNicsForMigration(VirtualMachineProfile vm, DeployDestination dest); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 899ce51022b..f8bf613d3e7 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -39,12 +39,14 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.configuration.Resource; import com.cloud.dc.ASNumberVO; import com.cloud.bgp.BGPService; import com.cloud.dc.VlanDetailsVO; import com.cloud.dc.dao.ASNumberDao; import com.cloud.dc.dao.VlanDetailsDao; import com.cloud.network.dao.NsxProviderDao; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -62,6 +64,7 @@ import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.dao.NetworkPermissionDao; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; @@ -441,6 +444,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra ClusterDao clusterDao; @Inject RoutedIpv4Manager routedIpv4Manager; + @Inject + private ReservationDao reservationDao; protected StateMachine2 _stateMachine; ScheduledExecutorService _executor; @@ -2721,12 +2726,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return null; } - final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, aclType); - //check resource limits - if (updateResourceCount) { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.network, isDisplayNetworkEnabled); - } - // Validate network offering if (ntwkOff.getState() != NetworkOffering.State.Enabled) { // see NetworkOfferingVO @@ -2745,6 +2744,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra boolean ipv6 = false; + try (CheckedReservation networkReservation = new CheckedReservation(owner, domainId, Resource.ResourceType.network, null, null, 1L, reservationDao, _resourceLimitMgr)) { + if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { ipv6 = true; } @@ -3084,8 +3085,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } } - if (updateResourceCount) { - _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.network, isDisplayNetworkEnabled); + if (isResourceCountUpdateNeeded(ntwkOff)) { + changeAccountResourceCountOrRecalculateDomainResourceCount(owner.getAccountId(), domainId, isDisplayNetworkEnabled, true); } UsageEventUtils.publishNetworkCreation(network); @@ -3096,6 +3097,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra CallContext.current().setEventDetails("Network Id: " + network.getId()); CallContext.current().putContextParameter(Network.class, network.getUuid()); return network; + } catch (Exception e) { + logger.error(e); + throw new RuntimeException(e); + } } @Override @@ -3460,9 +3465,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } final NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, networkFinal.getNetworkOfferingId()); - final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, networkFinal.getAclType()); - if (updateResourceCount) { - _resourceLimitMgr.decrementResourceCount(networkFinal.getAccountId(), ResourceType.network, networkFinal.getDisplayNetwork()); + if (isResourceCountUpdateNeeded(ntwkOff)) { + changeAccountResourceCountOrRecalculateDomainResourceCount(networkFinal.getAccountId(), networkFinal.getDomainId(), networkFinal.getDisplayNetwork(), false); } } return deletedVlans.second(); @@ -3485,6 +3489,23 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return success; } + /** + * If it is a shared network with {@link ACLType#Domain}, it will belong to account {@link Account#ACCOUNT_ID_SYSTEM} and the resources will be not incremented for the + * domain. Therefore, we force the recalculation of the domain's resource count in this case. Otherwise, it will change the count for the account owner. + * @param incrementAccountResourceCount If true, the account resource count will be incremented by 1; otherwise, it will decremented by 1. + */ + private void changeAccountResourceCountOrRecalculateDomainResourceCount(Long accountId, Long domainId, boolean displayNetwork, boolean incrementAccountResourceCount) { + if (Account.ACCOUNT_ID_SYSTEM == accountId && ObjectUtils.isNotEmpty(domainId)) { + _resourceLimitMgr.recalculateDomainResourceCount(domainId, ResourceType.network, null); + } else { + if (incrementAccountResourceCount) { + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.network, displayNetwork); + } else { + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.network, displayNetwork); + } + } + } + private void publishDeletedVlanRanges(List deletedVlanRangeToPublish) { if (CollectionUtils.isNotEmpty(deletedVlanRangeToPublish)) { for (VlanVO vlan : deletedVlanRangeToPublish) { @@ -3494,10 +3515,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } @Override - public boolean resourceCountNeedsUpdate(final NetworkOffering ntwkOff, final ACLType aclType) { - //Update resource count only for Isolated account specific non-system networks - final boolean updateResourceCount = ntwkOff.getGuestType() == GuestType.Isolated && !ntwkOff.isSystemOnly() && aclType == ACLType.Account; - return updateResourceCount; + public boolean isResourceCountUpdateNeeded(NetworkOffering networkOffering) { + return !networkOffering.isSystemOnly(); } protected Pair> deleteVlansInNetwork(final NetworkVO network, final long userId, final Account callerAccount) { diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index cfa4574d996..54c1d133943 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -3168,7 +3168,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C if (displayNetwork != null && displayNetwork != network.getDisplayNetwork()) { // Update resource count if it needs to be updated NetworkOffering networkOffering = _networkOfferingDao.findById(network.getNetworkOfferingId()); - if (_networkMgr.resourceCountNeedsUpdate(networkOffering, network.getAclType())) { + if (_networkMgr.isResourceCountUpdateNeeded(networkOffering)) { _resourceLimitMgr.changeResourceCount(network.getAccountId(), Resource.ResourceType.network, displayNetwork); } diff --git a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java index d66e1eb912a..211ca65a9d8 100644 --- a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java +++ b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java @@ -49,6 +49,7 @@ public class CheckedReservation implements AutoCloseable { ResourceLimitService resourceLimitService; private final Account account; + private Long domainId; private final ResourceType resourceType; private Long amount; private List reservations; @@ -73,12 +74,12 @@ public class CheckedReservation implements AutoCloseable { this.reservations = null; } - protected void checkLimitAndPersistReservations(Account account, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount) throws ResourceAllocationException { + protected void checkLimitAndPersistReservations(Account account, Long domainId, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount) throws ResourceAllocationException { try { - checkLimitAndPersistReservation(account, resourceType, resourceId, null, amount); + checkLimitAndPersistReservation(account, domainId, resourceType, resourceId, null, amount); if (CollectionUtils.isNotEmpty(resourceLimitTags)) { for (String tag : resourceLimitTags) { - checkLimitAndPersistReservation(account, resourceType, resourceId, tag, amount); + checkLimitAndPersistReservation(account, domainId, resourceType, resourceId, tag, amount); } } } catch (ResourceAllocationException rae) { @@ -87,11 +88,11 @@ public class CheckedReservation implements AutoCloseable { } } - protected void checkLimitAndPersistReservation(Account account, ResourceType resourceType, Long resourceId, String tag, Long amount) throws ResourceAllocationException { + protected void checkLimitAndPersistReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, String tag, Long amount) throws ResourceAllocationException { if (amount > 0) { - resourceLimitService.checkResourceLimitWithTag(account, resourceType, tag, amount); + resourceLimitService.checkResourceLimitWithTag(account, domainId, true, resourceType, tag, amount); } - ReservationVO reservationVO = new ReservationVO(account.getAccountId(), account.getDomainId(), resourceType, tag, amount); + ReservationVO reservationVO = new ReservationVO(account.getAccountId(), domainId, resourceType, tag, amount); if (resourceId != null) { reservationVO.setResourceId(resourceId); } @@ -114,9 +115,20 @@ public class CheckedReservation implements AutoCloseable { */ public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, account.getDomainId(), resourceType, resourceId, resourceLimitTags, amount, reservationDao, resourceLimitService); + } + + public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount, + ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { this.reservationDao = reservationDao; this.resourceLimitService = resourceLimitService; this.account = account; + + this.domainId = domainId; + if (domainId == null) { + this.domainId = account.getDomainId(); + } + this.resourceType = resourceType; this.amount = amount; this.reservations = new ArrayList<>(); @@ -127,7 +139,7 @@ public class CheckedReservation implements AutoCloseable { setGlobalLock(); if (quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) { try { - checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount); + checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, resourceLimitTags, amount); CallContext.current().putContextParameter(getContextParameterKey(), getIds()); } catch (NullPointerException npe) { throw new CloudRuntimeException("not enough means to check limits", npe); @@ -138,11 +150,11 @@ public class CheckedReservation implements AutoCloseable { throw new ResourceAllocationException(String.format("unable to acquire resource reservation \"%s\"", quotaLimitLock.getName()), resourceType); } } else { - checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount); + checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, resourceLimitTags, amount); } } else { logger.debug("not reserving any amount of resources for {} in domain {}, type: {}, tag: {}", - account.getAccountName(), account.getDomainId(), resourceType, getResourceLimitTagsAsString()); + account.getAccountName(), this.domainId, resourceType, getResourceLimitTagsAsString()); } } @@ -153,7 +165,7 @@ public class CheckedReservation implements AutoCloseable { @NotNull private void setGlobalLock() { - String lockName = String.format("CheckedReservation-%s/%d", account.getDomainId(), resourceType.getOrdinal()); + String lockName = String.format("CheckedReservation-%s/%d", this.domainId, resourceType.getOrdinal()); setQuotaLimitLock(GlobalLock.getInternLock(lockName)); } diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index d4d91b6de7b..8db204cf177 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -36,6 +36,7 @@ import java.util.stream.Stream; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.network.dao.NetworkDomainDao; import com.cloud.utils.Ternary; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.response.AccountResponse; @@ -192,6 +193,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim ServiceOfferingDao serviceOfferingDao; @Inject DiskOfferingDao diskOfferingDao; + @Inject + private NetworkDomainDao networkDomainDao; protected GenericSearchBuilder templateSizeSearch; protected GenericSearchBuilder snapshotSizeSearch; @@ -488,15 +491,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim return max; } - protected void checkDomainResourceLimit(final Account account, final Project project, final ResourceType type, String tag, long numResources) throws ResourceAllocationException { - // check all domains in the account's domain hierarchy - Long domainId; - if (project != null) { - domainId = project.getDomainId(); - } else { - domainId = account.getDomainId(); - } - + protected void checkDomainResourceLimit(Long domainId, final ResourceType type, String tag, long numResources) throws ResourceAllocationException { while (domainId != null) { DomainVO domain = _domainDao.findById(domainId); // no limit check if it is ROOT domain @@ -618,11 +613,16 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim @Override public void checkResourceLimitWithTag(final Account account, final ResourceType type, String tag, long... count) throws ResourceAllocationException { + checkResourceLimitWithTag(account, null, false, type, tag, count); + } + + @Override + public void checkResourceLimitWithTag(final Account account, Long domainId, boolean considerSystemAccount, final ResourceType type, String tag, long... count) throws ResourceAllocationException { final long numResources = ((count.length == 0) ? 1 : count[0]); Project project = null; // Don't place any limits on system or root admin accounts - if (_accountMgr.isRootAdmin(account.getId())) { + if (_accountMgr.isRootAdmin(account.getId()) && !(considerSystemAccount && Account.ACCOUNT_ID_SYSTEM == account.getId())) { return; } @@ -630,6 +630,14 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim project = _projectDao.findByProjectAccountId(account.getId()); } + if (domainId == null) { + if (project != null) { + domainId = project.getDomainId(); + } else { + domainId = account.getDomainId(); + } + } + Long domainIdFinal = domainId; final Project projectFinal = project; Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override @@ -639,7 +647,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim // Check account limits checkAccountResourceLimit(account, projectFinal, type, tag, numResources); // check all domains in the account's domain hierarchy - checkDomainResourceLimit(account, projectFinal, type, tag, numResources); + checkDomainResourceLimit(domainIdFinal, type, tag, numResources); } }); } @@ -1155,7 +1163,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim * @param type the resource type to do the recalculation for * @return the resulting new resource count */ - protected long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag) { + public long recalculateDomainResourceCount(final long domainId, final ResourceType type, String tag) { List accounts = _accountDao.findActiveAccountsForDomain(domainId); List childDomains = _domainDao.findImmediateChildrenForParent(domainId); @@ -1196,6 +1204,10 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim newResourceCount += _projectDao.countProjectsForDomain(domainId); } + if (type == ResourceType.network) { + newResourceCount += networkDomainDao.listDomainNetworkMapByDomain(domainId).size(); + } + // TODO make sure that the resource counts are not null for (ResourceCountVO resourceCount : resourceCounts) { if (resourceCount.getResourceOwnerType() == ResourceOwnerType.Domain && resourceCount.getDomainId() == domainId) { diff --git a/server/src/test/java/com/cloud/resourcelimit/CheckedReservationTest.java b/server/src/test/java/com/cloud/resourcelimit/CheckedReservationTest.java index 247647dd010..5e72f204493 100644 --- a/server/src/test/java/com/cloud/resourcelimit/CheckedReservationTest.java +++ b/server/src/test/java/com/cloud/resourcelimit/CheckedReservationTest.java @@ -149,23 +149,10 @@ public class CheckedReservationTest { @Test public void testMultipleReservationsWithOneFailing() { List tags = List.of("abc", "xyz"); - when(account.getAccountId()).thenReturn(1L); - when(account.getDomainId()).thenReturn(4L); Map persistedReservations = new HashMap<>(); - Mockito.when(reservationDao.persist(Mockito.any(ReservationVO.class))).thenAnswer((Answer) invocation -> { - ReservationVO reservationVO = (ReservationVO) invocation.getArguments()[0]; - Long id = (long) (persistedReservations.size() + 1); - ReflectionTestUtils.setField(reservationVO, "id", id); - persistedReservations.put(id, reservationVO); - return reservationVO; - }); - Mockito.when(reservationDao.remove(Mockito.anyLong())).thenAnswer((Answer) invocation -> { - Long id = (Long) invocation.getArguments()[0]; - persistedReservations.remove(id); - return true; - }); + try { - Mockito.doThrow(ResourceAllocationException.class).when(resourceLimitService).checkResourceLimitWithTag(account, Resource.ResourceType.cpu, "xyz", 1L); + Mockito.doThrow(ResourceAllocationException.class).when(resourceLimitService).checkResourceLimitWithTag(account, account.getDomainId(), true, Resource.ResourceType.cpu, "xyz", 1L); try (CheckedReservation vmReservation = new CheckedReservation(account, Resource.ResourceType.user_vm, tags, 1L, reservationDao, resourceLimitService); CheckedReservation cpuReservation = new CheckedReservation(account, Resource.ResourceType.cpu, tags, 1L, reservationDao, resourceLimitService); CheckedReservation memReservation = new CheckedReservation(account, Resource.ResourceType.memory, tags, 256L, reservationDao, resourceLimitService); diff --git a/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java b/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java index 53ccc830dd2..e3bfdc63635 100644 --- a/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java +++ b/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java @@ -587,10 +587,11 @@ public class ResourceLimitManagerImplTest extends TestCase { public void testCheckResourceLimitWithTagNonAdmin() throws ResourceAllocationException { AccountVO account = Mockito.mock(AccountVO.class); Mockito.when(account.getId()).thenReturn(1L); + Mockito.when(account.getDomainId()).thenReturn(1L); Mockito.when(accountManager.isRootAdmin(1L)).thenReturn(false); Mockito.doReturn(new ArrayList()).when(resourceLimitManager).lockAccountAndOwnerDomainRows(Mockito.anyLong(), Mockito.any(Resource.ResourceType.class), Mockito.anyString()); Mockito.doNothing().when(resourceLimitManager).checkAccountResourceLimit(account, null, Resource.ResourceType.cpu, hostTags.get(0), 1); - Mockito.doNothing().when(resourceLimitManager).checkDomainResourceLimit(account, null, Resource.ResourceType.cpu, hostTags.get(0), 1); + Mockito.doNothing().when(resourceLimitManager).checkDomainResourceLimit(1L, Resource.ResourceType.cpu, hostTags.get(0), 1); try { resourceLimitManager.checkResourceLimitWithTag(account, Resource.ResourceType.cpu, hostTags.get(0), 1); } catch (ResourceAllocationException e) { @@ -606,9 +607,10 @@ public class ResourceLimitManagerImplTest extends TestCase { Mockito.when(accountManager.isRootAdmin(1L)).thenReturn(false); ProjectVO projectVO = Mockito.mock(ProjectVO.class); Mockito.when(projectDao.findByProjectAccountId(Mockito.anyLong())).thenReturn(projectVO); + Mockito.when(projectVO.getDomainId()).thenReturn(1L); Mockito.doReturn(new ArrayList()).when(resourceLimitManager).lockAccountAndOwnerDomainRows(Mockito.anyLong(), Mockito.any(Resource.ResourceType.class), Mockito.anyString()); Mockito.doNothing().when(resourceLimitManager).checkAccountResourceLimit(account, projectVO, Resource.ResourceType.cpu, hostTags.get(0), 1); - Mockito.doNothing().when(resourceLimitManager).checkDomainResourceLimit(account, projectVO, Resource.ResourceType.cpu, hostTags.get(0), 1); + Mockito.doNothing().when(resourceLimitManager).checkDomainResourceLimit(1L, Resource.ResourceType.cpu, hostTags.get(0), 1); try { resourceLimitManager.checkResourceLimitWithTag(account, Resource.ResourceType.cpu, hostTags.get(0), 1); } catch (ResourceAllocationException e) { diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index 673d0b7a48c..371181ae882 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -951,7 +951,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches } @Override - public boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType) { + public boolean isResourceCountUpdateNeeded(NetworkOffering ntwkOff) { return false; //To change body of implemented methods use File | Settings | File Templates. } diff --git a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java index 3f3220d0934..7e8dbbf04c6 100644 --- a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java @@ -237,6 +237,11 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } + @Override + public void checkResourceLimitWithTag(Account account, Long domainId, boolean considerSystemAccount, ResourceType type, String tag, long... count) throws ResourceAllocationException { + + } + @Override public List getResourceLimitHostTags() { return null; @@ -381,4 +386,9 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc public void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) { } + + @Override + public long recalculateDomainResourceCount(long domainId, ResourceType type, String tag) { + return 0; + } } From 4dd91feb277a5a529b261dd5fa78d84ac31f9edc Mon Sep 17 00:00:00 2001 From: dahn Date: Fri, 20 Feb 2026 16:37:24 +0100 Subject: [PATCH 02/43] [20.3] resource instance limits --- .../java/com/cloud/vm/UserVmManagerImpl.java | 281 +++++++++--------- 1 file changed, 142 insertions(+), 139 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 815ac4f70fe..0e1c3bb9194 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -54,7 +54,6 @@ import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; -import com.cloud.network.NetworkService; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -249,6 +248,7 @@ import com.cloud.hypervisor.kvm.dpdk.DpdkHelper; import com.cloud.kubernetes.cluster.KubernetesServiceHelper; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; +import com.cloud.network.NetworkService; import com.cloud.network.Network.GuestType; import com.cloud.network.Network.IpAddresses; import com.cloud.network.Network.Provider; @@ -655,19 +655,19 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject VnfTemplateManager vnfTemplateManager; - private static final ConfigKey VmIpFetchWaitInterval = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmip.retrieval.interval", "180", + private static final ConfigKey VmIpFetchWaitInterval = new ConfigKey<>("Advanced", Integer.class, "externaldhcp.vmip.retrieval.interval", "180", "Wait Interval (in seconds) for shared network vm dhcp ip addr fetch for next iteration ", true); - private static final ConfigKey VmIpFetchTrialMax = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmip.max.retry", "10", + private static final ConfigKey VmIpFetchTrialMax = new ConfigKey<>("Advanced", Integer.class, "externaldhcp.vmip.max.retry", "10", "The max number of retrieval times for shared network vm dhcp ip fetch, in case of failures", true); - private static final ConfigKey VmIpFetchThreadPoolMax = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmipFetch.threadPool.max", "10", + private static final ConfigKey VmIpFetchThreadPoolMax = new ConfigKey<>("Advanced", Integer.class, "externaldhcp.vmipFetch.threadPool.max", "10", "number of threads for fetching vms ip address", true); - private static final ConfigKey VmIpFetchTaskWorkers = new ConfigKey("Advanced", Integer.class, "externaldhcp.vmipfetchtask.workers", "10", + private static final ConfigKey VmIpFetchTaskWorkers = new ConfigKey<>("Advanced", Integer.class, "externaldhcp.vmipfetchtask.workers", "10", "number of worker threads for vm ip fetch task ", true); - private static final ConfigKey AllowDeployVmIfGivenHostFails = new ConfigKey("Advanced", Boolean.class, "allow.deploy.vm.if.deploy.on.given.host.fails", "false", + private static final ConfigKey AllowDeployVmIfGivenHostFails = new ConfigKey<>("Advanced", Boolean.class, "allow.deploy.vm.if.deploy.on.given.host.fails", "false", "allow vm to deploy on different host if vm fails to deploy on the given host ", true); private static final ConfigKey KvmAdditionalConfigAllowList = new ConfigKey<>(String.class, @@ -679,7 +679,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private static final ConfigKey VmwareAdditionalConfigAllowList = new ConfigKey<>(String.class, "allow.additional.vm.configuration.list.vmware", "Advanced", "", "Comma separated list of allowed additional configuration options.", true, ConfigKey.Scope.Global, null, null, EnableAdditionalVmConfig.key(), null, null, ConfigKey.Kind.CSV, null); - private static final ConfigKey VmDestroyForcestop = new ConfigKey("Advanced", Boolean.class, "vm.destroy.forcestop", "false", + private static final ConfigKey VmDestroyForcestop = new ConfigKey<>("Advanced", Boolean.class, "vm.destroy.forcestop", "false", "On destroy, force-stop takes this value ", true); @Override @@ -1150,7 +1150,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (dc.getNetworkType() == DataCenter.NetworkType.Advanced) { //List all networks of vm List vmNetworks = _vmNetworkMapDao.getNetworks(vmId); - List routers = new ArrayList(); + List routers = new ArrayList<>(); //List the stopped routers for(long vmNetworkId : vmNetworks) { List router = _routerDao.listStopped(vmNetworkId); @@ -3126,7 +3126,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // Verify that vm's hostName is unique - List vmNtwks = new ArrayList(nics.size()); + List vmNtwks = new ArrayList<>(nics.size()); for (Nic nic : nics) { vmNtwks.add(_networkDao.findById(nic.getNetworkId())); } @@ -3692,7 +3692,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); - List networkList = new ArrayList(); + List networkList = new ArrayList<>(); // Verify that caller can perform actions in behalf of vm owner _accountMgr.checkAccess(caller, null, true, owner); @@ -3718,7 +3718,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir //add the default securityGroup only if no security group is specified if (securityGroupIdList == null || securityGroupIdList.isEmpty()) { if (securityGroupIdList == null) { - securityGroupIdList = new ArrayList(); + securityGroupIdList = new ArrayList<>(); } SecurityGroup defaultGroup = _securityGroupMgr.getDefaultSecurityGroup(owner.getId()); if (defaultGroup != null) { @@ -3750,7 +3750,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); - List networkList = new ArrayList(); + List networkList = new ArrayList<>(); boolean isSecurityGroupEnabledNetworkUsed = false; boolean isVmWare = (template.getHypervisorType() == HypervisorType.VMware || (hypervisor != null && hypervisor == HypervisorType.VMware)); @@ -3828,7 +3828,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir //add the default securityGroup only if no security group is specified if (securityGroupIdList == null || securityGroupIdList.isEmpty()) { if (securityGroupIdList == null) { - securityGroupIdList = new ArrayList(); + securityGroupIdList = new ArrayList<>(); } SecurityGroup defaultGroup = _securityGroupMgr.getDefaultSecurityGroup(owner.getId()); @@ -3863,7 +3863,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); - List networkList = new ArrayList(); + List networkList = new ArrayList<>(); // Verify that caller can perform actions in behalf of vm owner _accountMgr.checkAccess(caller, null, true, owner); @@ -4573,11 +4573,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private void checkIfHostNameUniqueInNtwkDomain(String hostName, List networkList) { // Check that hostName is unique in the network domain - Map> ntwkDomains = new HashMap>(); + Map> ntwkDomains = new HashMap<>(); for (Network network : networkList) { String ntwkDomain = network.getNetworkDomain(); if (!ntwkDomains.containsKey(ntwkDomain)) { - List ntwkIds = new ArrayList(); + List ntwkIds = new ArrayList<>(); ntwkIds.add(network.getId()); ntwkDomains.put(ntwkDomain, ntwkIds); } else { @@ -4718,10 +4718,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir logger.debug("Allocating in the DB for vm"); DataCenterDeployment plan = new DataCenterDeployment(zone.getId()); - List computeTags = new ArrayList(); + List computeTags = new ArrayList<>(); computeTags.add(offering.getHostTag()); - List rootDiskTags = new ArrayList(); + List rootDiskTags = new ArrayList<>(); DiskOfferingVO rootDiskOfferingVO = _diskOfferingDao.findById(rootDiskOfferingId); rootDiskTags.add(rootDiskOfferingVO.getTags()); @@ -4934,7 +4934,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir VirtualMachine.class.getName(), vm.getUuid(), isDisplay); } else { - Map customParameters = new HashMap(); + Map customParameters = new HashMap<>(); customParameters.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString()); customParameters.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString()); customParameters.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString()); @@ -4951,7 +4951,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } logger.debug("Collect vm network statistics from host before stopping Vm"); long hostId = userVm.getHostId(); - List vmNames = new ArrayList(); + List vmNames = new ArrayList<>(); vmNames.add(userVm.getInstanceName()); final HostVO host = _hostDao.findById(hostId); Account account = _accountMgr.getAccount(userVm.getAccountId()); @@ -5527,132 +5527,137 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (owner.getState() == Account.State.DISABLED) { throw new PermissionDeniedException(String.format("The owner of %s is disabled: %s", vm, owner)); } - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - // check if account/domain is with in resource limits to start a new vm - ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - resourceLimitService.checkVmResourceLimit(owner, vm.isDisplayVm(), offering, template); - } - // check if vm is security group enabled - if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() - && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkModel.canAddDefaultSecurityGroup()) { - // if vm is not mapped to security group, create a mapping - if (logger.isDebugEnabled()) { - logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); + Pair> vmParamPair; + try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, vm.getId(), null, 1L, reservationDao, _resourceLimitMgr)) { + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + // check if account/domain is with in resource limits to start a new vm + ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); + resourceLimitService.checkVmResourceLimit(owner, vm.isDisplayVm(), offering, template); } - - SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId()); - if (defaultSecurityGroup != null) { - List groupList = new ArrayList(); - groupList.add(defaultSecurityGroup.getId()); - _securityGroupMgr.addInstanceToGroups(vm, groupList); - } - } - // Choose deployment planner - // Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd - // Default behaviour is invoked when host, cluster or pod are not specified - boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId()); - Pod destinationPod = getDestinationPod(podId, isRootAdmin); - Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin); - HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost); - DataCenterDeployment plan = null; - boolean deployOnGivenHost = false; - if (destinationHost != null) { - logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - _hostDao.loadHostTags(destinationHost); - validateStrictHostTagCheck(vm, destinationHost); - - final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - Pair cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false); - if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) { - String errorMsg; - if (!cpuCapabilityAndCapacity.first()) { - errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost); - } else { - errorMsg = String.format("Cannot deploy the VM to specified host %s, host does not have enough free CPU or RAM, please check the logs", destinationHost); + // check if vm is security group enabled + if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() + && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkModel.canAddDefaultSecurityGroup()) { + // if vm is not mapped to security group, create a mapping + if (logger.isDebugEnabled()) { + logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); } - logger.info(errorMsg); + + SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId()); + if (defaultSecurityGroup != null) { + List groupList = new ArrayList<>(); + groupList.add(defaultSecurityGroup.getId()); + _securityGroupMgr.addInstanceToGroups(vm, groupList); + } + } + // Choose deployment planner + // Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd + // Default behaviour is invoked when host, cluster or pod are not specified + boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId()); + Pod destinationPod = getDestinationPod(podId, isRootAdmin); + Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin); + HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost); + DataCenterDeployment plan = null; + boolean deployOnGivenHost = false; + if (destinationHost != null) { + logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + _hostDao.loadHostTags(destinationHost); + validateStrictHostTagCheck(vm, destinationHost); + + final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); + Pair cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false); + if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) { + String errorMsg; + if (!cpuCapabilityAndCapacity.first()) { + errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost); + } else { + errorMsg = String.format("Cannot deploy the VM to specified host %s, host does not have enough free CPU or RAM, please check the logs", destinationHost); + } + logger.info(errorMsg); + if (!AllowDeployVmIfGivenHostFails.value()) { + throw new InvalidParameterValueException(errorMsg); + } + } else { + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); + if (!AllowDeployVmIfGivenHostFails.value()) { + deployOnGivenHost = true; + } + } + } else if (destinationCluster != null) { + logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null); if (!AllowDeployVmIfGivenHostFails.value()) { - throw new InvalidParameterValueException(errorMsg); - }; - } else { - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); + deployOnGivenHost = true; + } + } else if (destinationPod != null) { + logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null); if (!AllowDeployVmIfGivenHostFails.value()) { deployOnGivenHost = true; } } - } else if (destinationCluster != null) { - logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null); - if (!AllowDeployVmIfGivenHostFails.value()) { - deployOnGivenHost = true; - } - } else if (destinationPod != null) { - logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null); - if (!AllowDeployVmIfGivenHostFails.value()) { - deployOnGivenHost = true; - } - } - // Set parameters - Map params = null; - if (vm.isUpdateParameters()) { - _vmDao.loadDetails(vm); + // Set parameters + Map params = null; + if (vm.isUpdateParameters()) { + _vmDao.loadDetails(vm); - String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template); + String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template); - if (!validPassword(password)) { - throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); - } - - // Check if an SSH key pair was selected for the instance and if so - // use it to encrypt & save the vm password - encryptAndStorePassword(vm, password); - - params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password); - } - - if(additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { - if (! HypervisorType.VMware.equals(vm.getHypervisorType())) { - throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType()); - } - Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup); - if (logger.isTraceEnabled()) { - logger.trace("It was specified whether to enter setup mode: " + paramValue.toString()); - } - params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue); - } - - VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); - - DeploymentPlanner planner = null; - if (deploymentPlannerToUse != null) { - // if set to null, the deployment planner would be later figured out either from global config var, or from - // the service offering - planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse); - if (planner == null) { - throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse); - } - } - vmEntity.setParamsToEntity(additionalParams); - - String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId())); - vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost); - - Pair> vmParamPair = new Pair(vm, params); - if (vm != null && vm.isUpdateParameters()) { - // this value is not being sent to the backend; need only for api - // display purposes - if (template.isEnablePassword()) { - if (vm.getDetail(VmDetailConstants.PASSWORD) != null) { - userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD); + if (!validPassword(password)) { + throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); } - vm.setUpdateParameters(false); - _vmDao.update(vm.getId(), vm); - } - } + // Check if an SSH key pair was selected for the instance and if so + // use it to encrypt & save the vm password + encryptAndStorePassword(vm, password); + + params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password); + } + + if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { + if (!HypervisorType.VMware.equals(vm.getHypervisorType())) { + throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType()); + } + Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup); + if (logger.isTraceEnabled()) { + logger.trace("It was specified whether to enter setup mode: " + paramValue.toString()); + } + params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue); + } + + VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); + + DeploymentPlanner planner = null; + if (deploymentPlannerToUse != null) { + // if set to null, the deployment planner would be later figured out either from global config var, or from + // the service offering + planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse); + if (planner == null) { + throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse); + } + } + vmEntity.setParamsToEntity(additionalParams); + + String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId())); + vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost); + + vmParamPair = new Pair(vm, params); + if (vm != null && vm.isUpdateParameters()) { + // this value is not being sent to the backend; need only for api + // display purposes + if (template.isEnablePassword()) { + if (vm.getDetail(VmDetailConstants.PASSWORD) != null) { + userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD); + } + vm.setUpdateParameters(false); + _vmDao.update(vm.getId(), vm); + } + } + } catch (Exception e) { + logger.error("Failed to start VM {}", vm, e); + throw new CloudRuntimeException("Failed to start VM " + vm, e); + } return vmParamPair; } @@ -5830,7 +5835,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return; } long hostId = userVm.getHostId(); - List vmNames = new ArrayList(); + List vmNames = new ArrayList<>(); vmNames.add(userVm.getInstanceName()); final HostVO host = _hostDao.findById(hostId); Account account = _accountMgr.getAccount(userVm.getAccountId()); @@ -6494,7 +6499,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir //transform group names to ids here if (cmd.getSecurityGroupNameList() != null) { - List securityGroupIds = new ArrayList(); + List securityGroupIds = new ArrayList<>(); for (String groupName : cmd.getSecurityGroupNameList()) { SecurityGroup sg = _securityGroupMgr.getSecurityGroup(groupName, cmd.getEntityOwnerId()); if (sg == null) { @@ -7232,7 +7237,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } private Map getVolumePoolMappingForMigrateVmWithStorage(VMInstanceVO vm, Map volumeToPool) { - Map volToPoolObjectMap = new HashMap(); + Map volToPoolObjectMap = new HashMap<>(); List vmVolumes = getVmVolumesForMigrateVmWithStorage(vm); @@ -8149,10 +8154,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir /** * Attempts to create a network suitable for the creation of a VM ({@link NetworkOrchestrationService#createGuestNetwork}). * If no physical network is found, throws a {@link InvalidParameterValueException}. - * @param caller The account which calls for the network creation. * @param newAccount The account to which the network will be created. * @param zone The zone where the network will be created. - * @param requiredOffering The network offering required to create the network. * @return The NetworkVO for the network created. * @throws InsufficientCapacityException * @throws ResourceAllocationException From 1593944553fb92431a1efd3f500574f120363fa6 Mon Sep 17 00:00:00 2001 From: dahn Date: Fri, 20 Feb 2026 17:19:58 +0100 Subject: [PATCH 03/43] [20.3] Implement/fix limit validation for projects --- .../cloud/projects/ProjectManagerImpl.java | 51 +++++++++++++++---- .../resourcelimit/CheckedReservation.java | 4 +- .../com/cloud/resourcelimit/Reserver.java | 24 +++++++++ .../ResourceLimitManagerImpl.java | 5 -- 4 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 server/src/main/java/com/cloud/resourcelimit/Reserver.java diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index 7a743e3ce76..8302f0ddf15 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -36,6 +36,7 @@ import javax.inject.Inject; import javax.mail.MessagingException; import javax.naming.ConfigurationException; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ProjectRole; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -47,6 +48,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.utils.mailing.MailAddress; import org.apache.cloudstack.utils.mailing.SMTPMailProperties; import org.apache.cloudstack.utils.mailing.SMTPMailSender; @@ -159,6 +161,8 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C private VpcManager _vpcMgr; @Inject MessageBus messageBus; + @Inject + private ReservationDao reservationDao; protected boolean _invitationRequired = false; protected long _invitationTimeOut = 86400000; @@ -272,8 +276,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C owner = _accountDao.findById(user.getAccountId()); } - //do resource limit check - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.project); + try (CheckedReservation projectReservation = new CheckedReservation(owner, ResourceType.project, null, null, 1L, reservationDao, _resourceLimitMgr)) { final Account ownerFinal = owner; User finalUser = user; @@ -308,6 +311,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C messageBus.publish(_name, ProjectManager.MESSAGE_CREATE_TUNGSTEN_PROJECT_EVENT, PublishScope.LOCAL, project); return project; + } } @Override @@ -491,6 +495,9 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C //remove account ProjectAccountVO projectAccount = _projectAccountDao.findByProjectIdAccountId(projectId, account.getId()); success = _projectAccountDao.remove(projectAccount.getId()); + if (projectAccount.getAccountRole() == Role.Admin) { + _resourceLimitMgr.decrementResourceCount(account.getId(), ResourceType.project); + } //remove all invitations for account if (success) { @@ -594,12 +601,22 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C if (username == null) { throw new InvalidParameterValueException("User information (ID) is required to add user to the project"); } + + boolean shouldIncrementResourceCount = projectRole != null && Role.Admin == projectRole; + try (CheckedReservation cr = new CheckedReservation(userAccount, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { if (assignUserToProject(project, user.getId(), user.getAccountId(), projectRole, Optional.ofNullable(role).map(ProjectRole::getId).orElse(null)) != null) { + if (shouldIncrementResourceCount) { + _resourceLimitMgr.incrementResourceCount(userAccount.getId(), ResourceType.project); + } return true; + } else { + logger.warn("Failed to add user to project: {}", project); + return false; + } + } catch (ResourceAllocationException e) { + throw new RuntimeException(e); } - logger.warn("Failed to add user to project: {}", project); - return false; } } @@ -652,14 +669,18 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C } private void updateProjectAccount(ProjectAccountVO futureOwner, Role newAccRole, Long accountId) throws ResourceAllocationException { - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(accountId), ResourceType.project); + Account account = _accountMgr.getAccount(accountId); + boolean shouldIncrementResourceCount = Role.Admin == newAccRole; + + try (CheckedReservation checkedReservation = new CheckedReservation(account, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { futureOwner.setAccountRole(newAccRole); _projectAccountDao.update(futureOwner.getId(), futureOwner); - if (newAccRole != null && Role.Admin == newAccRole) { + if (shouldIncrementResourceCount) { _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.project); } else { _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.project); } + } } @Override @@ -701,7 +722,8 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C " doesn't belong to the project. Add it to the project first and then change the project's ownership"); } - //do resource limit check + try (CheckedReservation checkedReservation = new CheckedReservation(futureOwnerAccount, ResourceType.project, null, null, 1L, reservationDao, _resourceLimitMgr)) { + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(futureOwnerAccount.getId()), ResourceType.project); //unset the role for the old owner @@ -714,7 +736,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C futureOwner.setAccountRole(Role.Admin); _projectAccountDao.update(futureOwner.getId(), futureOwner); _resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project); - + } } else { logger.trace("Future owner {}is already the owner of the project {}", newOwnerName, project); } @@ -857,13 +879,22 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C if (account == null) { throw new InvalidParameterValueException("Account information is required for assigning account to the project"); } + + boolean shouldIncrementResourceCount = projectRoleType != null && Role.Admin == projectRoleType; + try (CheckedReservation cr = new CheckedReservation(account, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { if (assignAccountToProject(project, account.getId(), projectRoleType, null, Optional.ofNullable(projectRole).map(ProjectRole::getId).orElse(null)) != null) { + if (shouldIncrementResourceCount) { + _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.project); + } return true; } else { logger.warn("Failed to add account {} to project {}", accountName, project); return false; } + } catch (ResourceAllocationException e) { + throw new RuntimeException(e); + } } } @@ -1042,7 +1073,9 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C boolean success = true; ProjectAccountVO projectAccount = _projectAccountDao.findByProjectIdUserId(projectId, user.getAccountId(), user.getId()); success = _projectAccountDao.remove(projectAccount.getId()); - + if (projectAccount.getAccountRole() == Role.Admin) { + _resourceLimitMgr.decrementResourceCount(user.getAccountId(), ResourceType.project); + } if (success) { logger.debug("Removed user {} from project. Removing any invite sent to the user", user); ProjectInvitation invite = _projectInvitationDao.findByUserIdProjectId(user.getId(), user.getAccountId(), projectId); diff --git a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java index 211ca65a9d8..8d2e19d475e 100644 --- a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java +++ b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java @@ -40,7 +40,7 @@ import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; -public class CheckedReservation implements AutoCloseable { +public class CheckedReservation implements Reserver { protected Logger logger = LogManager.getLogger(getClass()); private static final int TRY_TO_GET_LOCK_TIME = 120; @@ -174,7 +174,7 @@ public class CheckedReservation implements AutoCloseable { } @Override - public void close() throws Exception { + public void close() { removeAllReservations(); } diff --git a/server/src/main/java/com/cloud/resourcelimit/Reserver.java b/server/src/main/java/com/cloud/resourcelimit/Reserver.java new file mode 100644 index 00000000000..4d5e3ac30c5 --- /dev/null +++ b/server/src/main/java/com/cloud/resourcelimit/Reserver.java @@ -0,0 +1,24 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.resourcelimit; + +public interface Reserver extends AutoCloseable { + + void close(); + +} diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 8db204cf177..01dcd1125da 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -1199,11 +1199,6 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim long newResourceCount = 0L; ResourceCountVO domainRC = null; - // calculate project count here - if (type == ResourceType.project) { - newResourceCount += _projectDao.countProjectsForDomain(domainId); - } - if (type == ResourceType.network) { newResourceCount += networkDomainDao.listDomainNetworkMapByDomain(domainId).size(); } From 7faa1b650b9258d9ebe2eaf7f64868dc40fc6c4d Mon Sep 17 00:00:00 2001 From: dahn Date: Fri, 20 Feb 2026 17:26:25 +0100 Subject: [PATCH 04/43] [20.3] resource allocation vpc --- .../main/java/com/cloud/network/vpc/VpcManagerImpl.java | 3 +++ .../java/com/cloud/network/vpc/VpcManagerImplTest.java | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java index 7fcdf5ce56e..4a952bb582d 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -50,6 +50,7 @@ import com.cloud.dc.dao.ASNumberDao; import com.cloud.dc.Vlan; import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.element.NsxProviderVO; +import com.cloud.resourcelimit.CheckedReservation; import com.google.common.collect.Sets; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.alert.AlertService; @@ -1246,6 +1247,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis vpc.setPublicMtu(publicMtu); vpc.setDisplay(Boolean.TRUE.equals(displayVpc)); + try (CheckedReservation vpcReservation = new CheckedReservation(owner, ResourceType.vpc, null, null, 1L, reservationDao, _resourceLimitMgr)) { if (vpc.getCidr() == null && cidrSize != null) { // Allocate a CIDR for VPC Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForVpc(vpc, cidrSize); @@ -1265,6 +1267,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis routedIpv4Manager.persistBgpPeersForVpc(newVpc.getId(), bgpPeerIds); } return newVpc; + } } private void validateVpcCidrSize(Account caller, long accountId, VpcOffering vpcOffering, String cidr, Integer cidrSize, long zoneId) { diff --git a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java index ee56a092dd1..92d3baa8ac2 100644 --- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java @@ -54,6 +54,7 @@ import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingServiceMapVO; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; +import com.cloud.resourcelimit.CheckedReservation; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; @@ -81,6 +82,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; @@ -516,7 +518,7 @@ public class VpcManagerImplTest { VpcVO vpc = Mockito.mock(VpcVO.class); Mockito.when(vpcDao.persist(any(), anyMap())).thenReturn(vpc); Mockito.when(vpc.getUuid()).thenReturn("uuid"); - try { + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain, ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null, null); @@ -533,7 +535,7 @@ public class VpcManagerImplTest { Mockito.when(vpc.getUuid()).thenReturn("uuid"); doReturn(true).when(routedIpv4Manager).isRoutedVpc(any()); doNothing().when(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(), anyString()); - try { + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain, ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null, null); @@ -556,7 +558,7 @@ public class VpcManagerImplTest { Ipv4GuestSubnetNetworkMap ipv4GuestSubnetNetworkMap = Mockito.mock(Ipv4GuestSubnetNetworkMap.class); doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(), anyInt()); List bgpPeerIds = Arrays.asList(11L, 12L); - try { + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, null, vpcDomain, ip4Dns[0], ip4Dns[1], null, null, true, 1500, 24, null, bgpPeerIds); From b025e85fc57b46f4b01b5dd3720fe2c031a2ed81 Mon Sep 17 00:00:00 2001 From: dahn Date: Fri, 20 Feb 2026 17:27:15 +0100 Subject: [PATCH 05/43] Check resource reservation on volume creation --- .../com/cloud/user/ResourceLimitService.java | 2 +- .../resourcelimit/ResourceLimitManagerImpl.java | 3 ++- .../com/cloud/storage/VolumeApiServiceImpl.java | 16 ++++++++++++++-- .../cloud/vpc/MockResourceLimitManagerImpl.java | 5 +++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java index fc486601d16..93609555122 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -247,7 +247,7 @@ public interface ResourceLimitService { void updateTaggedResourceLimitsAndCountsForAccounts(List responses, String tag); void updateTaggedResourceLimitsAndCountsForDomains(List responses, String tag); void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException; - + List getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering); void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException; diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 01dcd1125da..09a0dda3aaa 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -1647,7 +1647,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim return tags; } - protected List getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering) { + @Override + public List getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering) { if (Boolean.FALSE.equals(display)) { return new ArrayList<>(); } diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 4f8b55d16fb..38102619be5 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -35,6 +35,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.InternalIdentity; @@ -87,6 +88,7 @@ import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.framework.jobs.impl.OutcomeImpl; import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; import org.apache.cloudstack.jobs.JobInfo; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; @@ -354,6 +356,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic private BackupDao backupDao; @Inject HostPodDao podDao; + @Inject + private ReservationDao reservationDao; protected Gson _gson; @@ -918,8 +922,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Storage.ProvisioningType provisioningType = diskOffering.getProvisioningType(); - // Check that the resource limit for volume & primary storage won't be exceeded - _resourceLimitMgr.checkVolumeResourceLimit(owner,displayVolume, size, diskOffering); + List tags = _resourceLimitMgr.getResourceLimitStorageTagsForResourceCountOperation(displayVolume, diskOffering); + if (tags.size() == 1 && tags.get(0) == null) { + tags = new ArrayList<>(); + } + try (CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, null, tags, 1L, reservationDao, _resourceLimitMgr); + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, null, tags, size, reservationDao, _resourceLimitMgr)) { // Verify that zone exists DataCenterVO zone = _dcDao.findById(zoneId); @@ -942,6 +950,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return commitVolume(cmd, caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName, _uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details); + } catch (Exception e) { + logger.error(e); + throw new RuntimeException(e); + } } @Override diff --git a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java index 7e8dbbf04c6..045f2178529 100644 --- a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java @@ -277,6 +277,11 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } + @Override + public List getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering) { + return null; + } + @Override public void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException { From 0edd577f4bb8a071cbc8c8bb066fa26e6b634101 Mon Sep 17 00:00:00 2001 From: dahn Date: Fri, 20 Feb 2026 17:27:34 +0100 Subject: [PATCH 06/43] Fix: KVM Direct Download URL injection --- .../direct/download/DirectTemplateDownloaderImpl.java | 11 ++++++----- .../download/MetalinkDirectTemplateDownloader.java | 2 +- .../direct/download/NfsDirectTemplateDownloader.java | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/DirectTemplateDownloaderImpl.java b/core/src/main/java/org/apache/cloudstack/direct/download/DirectTemplateDownloaderImpl.java index a1485463eaa..05619e5632b 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/DirectTemplateDownloaderImpl.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/DirectTemplateDownloaderImpl.java @@ -21,6 +21,7 @@ package org.apache.cloudstack.direct.download; import com.cloud.utils.UriUtils; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.utils.security.DigestHelper; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -33,6 +34,7 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; public abstract class DirectTemplateDownloaderImpl implements DirectTemplateDownloader { @@ -128,15 +130,14 @@ public abstract class DirectTemplateDownloaderImpl implements DirectTemplateDown */ protected File createTemporaryDirectoryAndFile(String downloadDir) { createFolder(downloadDir); - return new File(downloadDir + File.separator + getFileNameFromUrl()); + return new File(downloadDir + File.separator + getTemporaryFileName()); } /** - * Return filename from url + * Return filename from the temporary download file */ - public String getFileNameFromUrl() { - String[] urlParts = url.split("/"); - return urlParts[urlParts.length - 1]; + public String getTemporaryFileName() { + return String.format("%s.%s", UUID.randomUUID(), FilenameUtils.getExtension(url)); } @Override diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java index 2050b9ef09f..854c310cde9 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/MetalinkDirectTemplateDownloader.java @@ -97,7 +97,7 @@ public class MetalinkDirectTemplateDownloader extends DirectTemplateDownloaderIm DirectTemplateDownloader urlDownloader = createDownloaderForMetalinks(getUrl(), getTemplateId(), getDestPoolPath(), getChecksum(), headers, connectTimeout, soTimeout, null, temporaryDownloadPath); try { - setDownloadedFilePath(downloadDir + File.separator + getFileNameFromUrl()); + setDownloadedFilePath(downloadDir + File.separator + getTemporaryFileName()); File f = new File(getDownloadedFilePath()); if (f.exists()) { f.delete(); diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/NfsDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/NfsDirectTemplateDownloader.java index 21184ef07fe..6b0959b78ff 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/NfsDirectTemplateDownloader.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/NfsDirectTemplateDownloader.java @@ -69,7 +69,7 @@ public class NfsDirectTemplateDownloader extends DirectTemplateDownloaderImpl { String mount = String.format(mountCommand, srcHost + ":" + srcPath, "/mnt/" + mountSrcUuid); Script.runSimpleBashScript(mount); String downloadDir = getDestPoolPath() + File.separator + getDirectDownloadTempPath(getTemplateId()); - setDownloadedFilePath(downloadDir + File.separator + getFileNameFromUrl()); + setDownloadedFilePath(downloadDir + File.separator + getTemporaryFileName()); Script.runSimpleBashScript("cp /mnt/" + mountSrcUuid + srcPath + " " + getDownloadedFilePath()); Script.runSimpleBashScript("umount /mnt/" + mountSrcUuid); return new Pair<>(true, getDownloadedFilePath()); From 3b987f21afbf02dcdd376a94835e3e277628a4e6 Mon Sep 17 00:00:00 2001 From: dahn Date: Fri, 20 Feb 2026 17:28:48 +0100 Subject: [PATCH 07/43] [20.3] handle user's canned policy when a bucket is deleted --- .../java/com/cloud/agent/api/to/BucketTO.java | 7 ++ .../driver/MinIOObjectStoreDriverImpl.java | 79 +++++++++++++------ .../MinIOObjectStoreDriverImplTest.java | 7 +- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/api/src/main/java/com/cloud/agent/api/to/BucketTO.java b/api/src/main/java/com/cloud/agent/api/to/BucketTO.java index f7e4bfea80f..fd8237998a7 100644 --- a/api/src/main/java/com/cloud/agent/api/to/BucketTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/BucketTO.java @@ -26,10 +26,13 @@ public final class BucketTO { private String secretKey; + private long accountId; + public BucketTO(Bucket bucket) { this.name = bucket.getName(); this.accessKey = bucket.getAccessKey(); this.secretKey = bucket.getSecretKey(); + this.accountId = bucket.getAccountId(); } public BucketTO(String name) { @@ -47,4 +50,8 @@ public final class BucketTO { public String getSecretKey() { return this.secretKey; } + + public long getAccountId() { + return this.accountId; + } } diff --git a/plugins/storage/object/minio/src/main/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImpl.java b/plugins/storage/object/minio/src/main/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImpl.java index 9dc4b30414e..28e3b85e1a5 100644 --- a/plugins/storage/object/minio/src/main/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImpl.java +++ b/plugins/storage/object/minio/src/main/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImpl.java @@ -24,6 +24,8 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; @@ -98,6 +100,51 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { return String.format("%s-%s", ACS_PREFIX, account.getUuid()); } + private void updateCannedPolicy(long storeId, Account account, String excludeBucket) { + List buckets = _bucketDao.listByObjectStoreIdAndAccountId(storeId, account.getId()); + + String resources = buckets.stream() + .map(BucketVO::getName) + .filter(name -> !Objects.equals(name, excludeBucket)) + .map(name -> "\"arn:aws:s3:::" + name + "/*\"") + .collect(Collectors.joining(",\n")); + String policy; + if (resources.isEmpty()) { + // Resource cannot be empty in a canned Policy so deny access to all resources if the user has no buckets + policy = " {\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Action\": \"s3:*\",\n" + + " \"Effect\": \"Deny\",\n" + + " \"Resource\": [\"arn:aws:s3:::*\", \"arn:aws:s3:::*/*\"]\n" + + " }\n" + + " ],\n" + + " \"Version\": \"2012-10-17\"\n" + + " }"; + } else { + policy = " {\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Action\": \"s3:*\",\n" + + " \"Effect\": \"Allow\",\n" + + " \"Resource\": [" + resources + "]\n" + + " }\n" + + " ],\n" + + " \"Version\": \"2012-10-17\"\n" + + " }"; + } + + MinioAdminClient minioAdminClient = getMinIOAdminClient(storeId); + String policyName = getUserOrAccessKeyForAccount(account) + "-policy"; + String userName = getUserOrAccessKeyForAccount(account); + try { + minioAdminClient.addCannedPolicy(policyName, policy); + minioAdminClient.setPolicy(userName, false, policyName); + } catch (NoSuchAlgorithmException | IOException | InvalidKeyException e) { + throw new CloudRuntimeException(e); + } + } + @Override public Bucket createBucket(Bucket bucket, boolean objectLock) { //ToDo Client pool mgmt @@ -125,33 +172,8 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { throw new CloudRuntimeException(e); } - List buckets = _bucketDao.listByObjectStoreIdAndAccountId(storeId, accountId); - StringBuilder resources_builder = new StringBuilder(); - for(BucketVO exitingBucket : buckets) { - resources_builder.append("\"arn:aws:s3:::"+exitingBucket.getName()+"/*\",\n"); - } - resources_builder.append("\"arn:aws:s3:::"+bucketName+"/*\"\n"); + updateCannedPolicy(storeId, account,null); - String policy = " {\n" + - " \"Statement\": [\n" + - " {\n" + - " \"Action\": \"s3:*\",\n" + - " \"Effect\": \"Allow\",\n" + - " \"Principal\": \"*\",\n" + - " \"Resource\": ["+resources_builder+"]" + - " }\n" + - " ],\n" + - " \"Version\": \"2012-10-17\"\n" + - " }"; - MinioAdminClient minioAdminClient = getMinIOAdminClient(storeId); - String policyName = getUserOrAccessKeyForAccount(account) + "-policy"; - String userName = getUserOrAccessKeyForAccount(account); - try { - minioAdminClient.addCannedPolicy(policyName, policy); - minioAdminClient.setPolicy(userName, false, policyName); - } catch (Exception e) { - throw new CloudRuntimeException(e); - } String accessKey = _accountDetailsDao.findDetail(accountId, MINIO_ACCESS_KEY).getValue(); String secretKey = _accountDetailsDao.findDetail(accountId, MINIO_SECRET_KEY).getValue(); ObjectStoreVO store = _storeDao.findById(storeId); @@ -183,6 +205,8 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { @Override public boolean deleteBucket(BucketTO bucket, long storeId) { String bucketName = bucket.getName(); + long accountId = bucket.getAccountId(); + Account account = _accountDao.findById(accountId); MinioClient minioClient = getMinIOClient(storeId); try { if(!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { @@ -197,6 +221,9 @@ public class MinIOObjectStoreDriverImpl extends BaseObjectStoreDriverImpl { } catch (Exception e) { throw new CloudRuntimeException(e); } + + updateCannedPolicy(storeId, account, bucketName); + return true; } diff --git a/plugins/storage/object/minio/src/test/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImplTest.java b/plugins/storage/object/minio/src/test/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImplTest.java index 1a8b3d9663a..d3298a235ca 100644 --- a/plugins/storage/object/minio/src/test/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImplTest.java +++ b/plugins/storage/object/minio/src/test/java/org/apache/cloudstack/storage/datastore/driver/MinIOObjectStoreDriverImplTest.java @@ -129,10 +129,15 @@ public class MinIOObjectStoreDriverImplTest { @Test public void testDeleteBucket() throws Exception { String bucketName = "test-bucket"; - BucketTO bucket = new BucketTO(bucketName); + BucketVO bucketVO = new BucketVO(1L, 1L, 1L, bucketName, 1, false, false, false, null); + BucketTO bucket = new BucketTO(bucketVO); + when(accountDao.findById(1L)).thenReturn(account); + when(account.getUuid()).thenReturn(UUID.randomUUID().toString()); + when(bucketDao.listByObjectStoreIdAndAccountId(anyLong(), anyLong())).thenReturn(new ArrayList()); doReturn(minioClient).when(minioObjectStoreDriverImpl).getMinIOClient(anyLong()); when(minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())).thenReturn(true); doNothing().when(minioClient).removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + doReturn(minioAdminClient).when(minioObjectStoreDriverImpl).getMinIOAdminClient(anyLong()); boolean success = minioObjectStoreDriverImpl.deleteBucket(bucket, 1L); assertTrue(success); verify(minioClient, times(1)).bucketExists(any()); From e0ef3a694723b644f1baeca138d2e2bba80244da Mon Sep 17 00:00:00 2001 From: dahn Date: Fri, 20 Feb 2026 17:29:25 +0100 Subject: [PATCH 08/43] Check resource reservation on volume snapshot creation --- .../storage/snapshot/SnapshotManagerImpl.java | 37 ++++++++++++------- .../storage/snapshot/SnapshotManagerTest.java | 11 ++++-- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 19cde4da0f1..d5475948c59 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.acl.SecurityChecker; import com.cloud.api.ApiDBUtils; import org.apache.cloudstack.annotation.AnnotationService; @@ -67,6 +68,7 @@ import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO; import org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDao; import org.apache.cloudstack.snapshot.SnapshotHelper; @@ -240,6 +242,8 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement @Inject private AnnotationDao annotationDao; + @Inject + private ReservationDao reservationDao; @Inject protected SnapshotHelper snapshotHelper; @Inject @@ -1705,20 +1709,6 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement Type snapshotType = getSnapshotType(policyId); Account owner = _accountMgr.getAccount(volume.getAccountId()); - ResourceType storeResourceType = getStoreResourceType(volume.getDataCenterId(), locationType); - try { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot); - _resourceLimitMgr.checkResourceLimit(owner, storeResourceType, volume.getSize()); - } catch (ResourceAllocationException e) { - if (snapshotType != Type.MANUAL) { - String msg = String.format("Snapshot resource limit exceeded for account %s. Failed to create recurring snapshots", owner); - logger.warn(msg); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, "Snapshot resource limit exceeded for account id : " + owner.getId() - + ". Failed to create recurring snapshots; please use updateResourceLimit to increase the limit"); - } - throw e; - } - // Determine the name for this snapshot // Snapshot Name: VMInstancename + volumeName + timeString String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT); @@ -1750,6 +1740,14 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement hypervisorType = volume.getHypervisorType(); } + ResourceType storeResourceType = ResourceType.secondary_storage; + if (!isBackupSnapshotToSecondaryForZone(volume.getDataCenterId()) || + Snapshot.LocationType.PRIMARY.equals(locationType)) { + storeResourceType = ResourceType.primary_storage; + } + + try (CheckedReservation volumeSnapshotReservation = new CheckedReservation(owner, ResourceType.snapshot, null, null, 1L, reservationDao, _resourceLimitMgr); + CheckedReservation storageReservation = new CheckedReservation(owner, storeResourceType, null, null, volume.getSize(), reservationDao, _resourceLimitMgr)) { SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName, (short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), volume.getMinIops(), volume.getMaxIops(), hypervisorType, locationType); @@ -1761,6 +1759,17 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot); _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), storeResourceType, volume.getSize()); return snapshot; + } catch (Exception e) { + if (e instanceof ResourceAllocationException) { + if (snapshotType != Type.MANUAL) { + String msg = String.format("Snapshot resource limit exceeded for account id : %s. Failed to create recurring snapshots", owner.getId()); + logger.warn(msg); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, msg + ". Please, use updateResourceLimit to increase the limit"); + } + throw (ResourceAllocationException) e; + } + throw new CloudRuntimeException(e); + } } @Override diff --git a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerTest.java b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerTest.java index 28903c72cc3..5513536ab75 100755 --- a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerTest.java +++ b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerTest.java @@ -36,6 +36,7 @@ import java.util.UUID; import com.cloud.api.ApiDBUtils; import com.cloud.exception.PermissionDeniedException; +import com.cloud.resourcelimit.CheckedReservation; import com.cloud.storage.Storage; import org.apache.cloudstack.api.command.user.snapshot.ExtractSnapshotCmd; import org.apache.cloudstack.context.CallContext; @@ -65,6 +66,7 @@ import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; @@ -233,8 +235,6 @@ public class SnapshotManagerTest { when(_storageStrategyFactory.getSnapshotStrategy(Mockito.any(SnapshotVO.class), Mockito.eq(SnapshotOperation.BACKUP))).thenReturn(snapshotStrategy); when(_storageStrategyFactory.getSnapshotStrategy(Mockito.any(SnapshotVO.class), Mockito.eq(SnapshotOperation.REVERT))).thenReturn(snapshotStrategy); - doNothing().when(_resourceLimitMgr).checkResourceLimit(any(Account.class), any(ResourceType.class)); - doNothing().when(_resourceLimitMgr).checkResourceLimit(any(Account.class), any(ResourceType.class), anyLong()); doNothing().when(_resourceLimitMgr).decrementResourceCount(anyLong(), any(ResourceType.class), anyLong()); doNothing().when(_resourceLimitMgr).incrementResourceCount(anyLong(), any(ResourceType.class)); doNothing().when(_resourceLimitMgr).incrementResourceCount(anyLong(), any(ResourceType.class), anyLong()); @@ -317,7 +317,12 @@ public class SnapshotManagerTest { when(mockList2.size()).thenReturn(0); when(_vmSnapshotDao.listByInstanceId(TEST_VM_ID, VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging)).thenReturn(mockList2); when(_snapshotDao.persist(any(SnapshotVO.class))).thenReturn(snapshotMock); - _snapshotMgr.allocSnapshot(TEST_VOLUME_ID, Snapshot.MANUAL_POLICY_ID, null, null); + + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + _snapshotMgr.allocSnapshot(TEST_VOLUME_ID, Snapshot.MANUAL_POLICY_ID, null, null); + } catch (ResourceAllocationException e) { + Assert.fail(String.format("Failure with exception: %s", e.getMessage())); + } } @Test(expected = InvalidParameterValueException.class) From 06ee2fea76d1b5f4fced86793e18e365bd68b7c2 Mon Sep 17 00:00:00 2001 From: Daniel Augusto Veronezi Salvador <38945620+GutoVeronezi@users.noreply.github.com> Date: Sun, 22 Feb 2026 10:44:24 -0300 Subject: [PATCH 09/43] Implement/fix limit validation for secondary storage --- .../manager/BareMetalTemplateAdapter.java | 1 - .../storage/ImageStoreUploadMonitorImpl.java | 26 +- .../cloud/storage/VolumeApiServiceImpl.java | 3 +- .../template/HypervisorTemplateAdapter.java | 35 +- .../cloud/template/TemplateAdapterBase.java | 3 - .../cloud/template/TemplateManagerImpl.java | 111 +++-- .../template/TemplateManagerImplTest.java | 402 +++--------------- 7 files changed, 158 insertions(+), 423 deletions(-) diff --git a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java index 940897de3c9..c6c38a39809 100644 --- a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java +++ b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java @@ -106,7 +106,6 @@ public class BareMetalTemplateAdapter extends TemplateAdapterBase implements Tem } } - _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); return template; } diff --git a/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java b/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java index 334e9f10835..408eb69917a 100755 --- a/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java +++ b/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java @@ -26,6 +26,10 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.agent.api.to.OVFInformationTO; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.user.Account; +import com.cloud.user.dao.AccountDao; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; @@ -37,6 +41,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.storage.command.UploadStatusAnswer; import org.apache.cloudstack.storage.command.UploadStatusAnswer.UploadStatus; import org.apache.cloudstack.storage.command.UploadStatusCommand; @@ -117,6 +122,10 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto private TemplateJoinDao templateJoinDao; @Inject private DeployAsIsHelper deployAsIsHelper; + @Inject + private ReservationDao reservationDao; + @Inject + private AccountDao accountDao; private long _nodeId; private ScheduledExecutorService _executor = null; @@ -436,8 +445,23 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto break; } } + + Account owner = accountDao.findById(template.getAccountId()); + long templateSize = answer.getVirtualSize(); + + try (CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.secondary_storage, null, null, templateSize, reservationDao, _resourceLimitMgr)) { + _resourceLimitMgr.incrementResourceCount(owner.getId(), Resource.ResourceType.secondary_storage, templateSize); + } catch (ResourceAllocationException e) { + tmpTemplateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.UPLOAD_ERROR); + tmpTemplateDataStore.setState(State.Failed); + stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao); + msg = String.format("Upload of template [%s] failed because its owner [%s] does not have enough secondary storage space available.", template.getUuid(), owner.getUuid()); + logger.warn(msg); + sendAlert = true; + break; + } + stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationSucceeded, null, _templateDao); - _resourceLimitMgr.incrementResourceCount(template.getAccountId(), Resource.ResourceType.secondary_storage, answer.getVirtualSize()); //publish usage event String etype = EventTypes.EVENT_TEMPLATE_CREATE; if (tmpTemplate.getFormat() == Storage.ImageFormat.ISO) { diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 38102619be5..a975bd62408 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -107,6 +107,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity; +import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.identity.ManagementServerNode; import org.apache.cloudstack.utils.imagestore.ImageStoreUtil; import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; @@ -522,7 +523,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Account account = _accountDao.findById(accountId); Domain domain = domainDao.findById(account.getDomainId()); - command.setDefaultMaxSecondaryStorageInGB(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null)); + command.setDefaultMaxSecondaryStorageInGB(ByteScaleUtils.bytesToGibibytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null))); command.setAccountId(accountId); Gson gson = new GsonBuilder().create(); String metadata = EncryptionUtil.encodeData(gson.toJson(command), key); diff --git a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java index 1422e788e24..c096ef0eb1d 100644 --- a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java +++ b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java @@ -37,10 +37,8 @@ import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; -import org.apache.cloudstack.api.command.user.iso.GetUploadParamsForIsoCmd; import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd; import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd; -import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd; import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.direct.download.DirectDownloadManager; @@ -66,6 +64,7 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.heuristics.HeuristicRuleHelper; import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity; +import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.security.DigestHelper; import org.apache.commons.collections.CollectionUtils; @@ -217,19 +216,6 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { profile.setSize(templateSize); } profile.setUrl(url); - // Check that the resource limit for secondary storage won't be exceeded - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), - ResourceType.secondary_storage, - UriUtils.getRemoteSize(url, followRedirects)); - return profile; - } - - @Override - public TemplateProfile prepare(GetUploadParamsForIsoCmd cmd) throws ResourceAllocationException { - TemplateProfile profile = super.prepare(cmd); - - // Check that the resource limit for secondary storage won't be exceeded - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), ResourceType.secondary_storage); return profile; } @@ -247,19 +233,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { profile.setSize(templateSize); } profile.setUrl(url); - // Check that the resource limit for secondary storage won't be exceeded - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), - ResourceType.secondary_storage, - UriUtils.getRemoteSize(url, followRedirects)); - return profile; - } - @Override - public TemplateProfile prepare(GetUploadParamsForTemplateCmd cmd) throws ResourceAllocationException { - TemplateProfile profile = super.prepare(cmd); - - // Check that the resource limit for secondary storage won't be exceeded - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), ResourceType.secondary_storage); return profile; } @@ -287,7 +261,6 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { persistDirectDownloadTemplate(template.getId(), profile.getSize()); } - _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); return template; } @@ -434,7 +407,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { if(payloads.isEmpty()) { throw new CloudRuntimeException("unable to find zone or an image store with enough capacity"); } - _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + return payloads; } }); @@ -477,7 +450,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { Account account = _accountDao.findById(accountId); Domain domain = _domainDao.findById(account.getDomainId()); - payload.setDefaultMaxSecondaryStorageInGB(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null)); + payload.setDefaultMaxSecondaryStorageInGB(ByteScaleUtils.bytesToGibibytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null))); payload.setAccountId(accountId); payload.setRemoteEndPoint(ep.getPublicAddr()); payload.setRequiresHvm(template.requiresHvm()); @@ -543,8 +516,8 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { UsageEventUtils.publishUsageEvent(etype, template.getAccountId(), -1, template.getId(), template.getName(), null, null, physicalSize, template.getSize(), VirtualMachineTemplate.class.getName(), template.getUuid()); } - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.secondary_storage, template.getSize()); } + _resourceLimitMgr.recalculateResourceCount(accountId, tmplt.getDomainId(), ResourceType.secondary_storage.getOrdinal()); } return null; diff --git a/server/src/main/java/com/cloud/template/TemplateAdapterBase.java b/server/src/main/java/com/cloud/template/TemplateAdapterBase.java index d932a9e018b..e63097916ce 100644 --- a/server/src/main/java/com/cloud/template/TemplateAdapterBase.java +++ b/server/src/main/java/com/cloud/template/TemplateAdapterBase.java @@ -51,7 +51,6 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import com.cloud.api.ApiDBUtils; import com.cloud.configuration.Config; -import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.dao.DomainDao; @@ -227,8 +226,6 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat throw new IllegalArgumentException("Unable to find user with id " + userId); } - _resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.template); - // If a zoneId is specified, make sure it is valid if (zoneIdList != null) { for (Long zoneId :zoneIdList) { diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index bf470205107..5c34e2a7473 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -35,6 +35,8 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.cpu.CPU; +import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.utils.UriUtils; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; @@ -87,6 +89,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.secstorage.dao.SecondaryStorageHeuristicDao; import org.apache.cloudstack.secstorage.heuristics.HeuristicType; import org.apache.cloudstack.snapshot.SnapshotHelper; @@ -323,6 +326,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, @Inject private HeuristicRuleHelper heuristicRuleHelper; + @Inject + private ReservationDao reservationDao; + private TemplateAdapter getAdapter(HypervisorType type) { TemplateAdapter adapter = null; if (type == HypervisorType.BareMetal) { @@ -347,15 +353,30 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, @ActionEvent(eventType = EventTypes.EVENT_ISO_CREATE, eventDescription = "Creating ISO") public VirtualMachineTemplate registerIso(RegisterIsoCmd cmd) throws ResourceAllocationException { TemplateAdapter adapter = getAdapter(HypervisorType.None); - TemplateProfile profile = adapter.prepare(cmd); - VMTemplateVO template = adapter.create(profile); + Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId()); - if (template != null) { - CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); - return template; - } else { - throw new CloudRuntimeException("Failed to create ISO"); + // Secondary storage resource count is not incremented for BareMetalTemplateAdapter + // Note: checking the file size before registering will require the Management Server host to have access to the Internet and a DNS server + long secondaryStorageUsage = adapter instanceof HypervisorTemplateAdapter && !cmd.isDirectDownload() ? + UriUtils.getRemoteSize(cmd.getUrl(), StorageManager.DataStoreDownloadFollowRedirects.value()) : 0L; + + try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr); + CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, ResourceType.secondary_storage, null, null, secondaryStorageUsage, reservationDao, _resourceLimitMgr)) { + TemplateProfile profile = adapter.prepare(cmd); + VMTemplateVO template = adapter.create(profile); + + // Secondary storage resource usage will be recalculated in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + if (secondaryStorageUsage > 0) { + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, secondaryStorageUsage); + } + if (template != null) { + CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); + return template; + } } + + throw new CloudRuntimeException("Failed to create ISO"); } @Override @@ -374,18 +395,32 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } TemplateAdapter adapter = getAdapter(HypervisorType.getType(cmd.getHypervisor())); + Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId()); + + long secondaryStorageUsage = adapter instanceof HypervisorTemplateAdapter && !cmd.isDirectDownload() ? + UriUtils.getRemoteSize(cmd.getUrl(), StorageManager.DataStoreDownloadFollowRedirects.value()) : 0L; + + try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr); + CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, ResourceType.secondary_storage, null, null, secondaryStorageUsage, reservationDao, _resourceLimitMgr)) { TemplateProfile profile = adapter.prepare(cmd); VMTemplateVO template = adapter.create(profile); + // Secondary storage resource usage will be recalculated in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack + // for HypervisorTemplateAdapter + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + if (secondaryStorageUsage > 0) { + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, secondaryStorageUsage); + } + if (template != null) { CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); if (cmd instanceof RegisterVnfTemplateCmd) { vnfTemplateManager.persistVnfTemplate(template.getId(), (RegisterVnfTemplateCmd) cmd); } return template; - } else { - throw new CloudRuntimeException("Failed to create a Template"); } + } + throw new CloudRuntimeException("Failed to create a Template"); } /** @@ -449,17 +484,35 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, @Override @ActionEvent(eventType = EventTypes.EVENT_ISO_CREATE, eventDescription = "Creating post upload ISO") public GetUploadParamsResponse registerIsoForPostUpload(GetUploadParamsForIsoCmd cmd) throws ResourceAllocationException, MalformedURLException { - TemplateAdapter adapter = getAdapter(HypervisorType.None); - TemplateProfile profile = adapter.prepare(cmd); - return registerPostUploadInternal(adapter, profile); + Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId()); + + try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr)) { + TemplateAdapter adapter = getAdapter(HypervisorType.None); + TemplateProfile profile = adapter.prepare(cmd); + + GetUploadParamsResponse response = registerPostUploadInternal(adapter, profile); + + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + + return response; + } } @Override @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "Creating post upload Template") public GetUploadParamsResponse registerTemplateForPostUpload(GetUploadParamsForTemplateCmd cmd) throws ResourceAllocationException, MalformedURLException { - TemplateAdapter adapter = getAdapter(HypervisorType.getType(cmd.getHypervisor())); - TemplateProfile profile = adapter.prepare(cmd); - return registerPostUploadInternal(adapter, profile); + Account owner = _accountMgr.getAccount(cmd.getEntityOwnerId()); + + try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr)) { + TemplateAdapter adapter = getAdapter(HypervisorType.getType(cmd.getHypervisor())); + TemplateProfile profile = adapter.prepare(cmd); + + GetUploadParamsResponse response = registerPostUploadInternal(adapter, profile); + + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + + return response; + } } @Override @@ -826,9 +879,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, // find the size of the template to be copied TemplateDataStoreVO srcTmpltStore = _tmplStoreDao.findByStoreTemplate(srcSecStore.getId(), tmpltId); - _resourceLimitMgr.checkResourceLimit(account, ResourceType.template); - _resourceLimitMgr.checkResourceLimit(account, ResourceType.secondary_storage, new Long(srcTmpltStore.getSize()).longValue()); - // Event details String copyEventType; if (template.getFormat().equals(ImageFormat.ISO)) { @@ -975,21 +1025,21 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, // sync template from cache store to region store if it is not there, for cases where we are going to migrate existing NFS to S3. _tmpltSvr.syncTemplateToRegionStore(template, srcSecStore); } + + AccountVO templateOwner = _accountDao.findById(template.getAccountId()); + for (Long destZoneId : destZoneIds) { DataStore dstSecStore = getImageStore(destZoneId, templateId); if (dstSecStore != null) { logger.debug("There is Template {} in secondary storage {} in zone {} , don't need to copy", template, dstSecStore, dataCenterVOs.get(destZoneId)); continue; } + try (CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, template.getSize(), reservationDao, _resourceLimitMgr)) { if (!copy(userId, template, srcSecStore, dataCenterVOs.get(destZoneId))) { failedZones.add(dataCenterVOs.get(destZoneId).getName()); + continue; } - else{ - if (template.getSize() != null) { - // increase resource count - long accountId = template.getAccountId(); - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.secondary_storage, template.getSize()); - } + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, template.getSize()); } } } @@ -1012,9 +1062,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, AccountVO account = _accountDao.findById(template.getAccountId()); - - _resourceLimitMgr.checkResourceLimit(account, ResourceType.template); - try { _tmpltDao.addTemplateToZone(template, dstZoneId); return true; @@ -1946,8 +1993,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } } - _resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.template); - _resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.secondary_storage, new Long(volume != null ? volume.getSize() : snapshot.getSize()).longValue()); + long templateSize = volume != null ? volume.getSize() : snapshot.getSize(); + try (CheckedReservation templateReservation = new CheckedReservation(templateOwner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr); + CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, templateSize, reservationDao, _resourceLimitMgr)) { if (!isAdmin || featured == null) { featured = Boolean.FALSE; @@ -2036,8 +2084,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.template); - _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, - new Long(volume != null ? volume.getSize() : snapshot.getSize())); + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, templateSize); } if (template != null) { @@ -2046,7 +2093,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } else { throw new CloudRuntimeException("Failed to create a Template"); } - + } } @Override diff --git a/server/src/test/java/com/cloud/template/TemplateManagerImplTest.java b/server/src/test/java/com/cloud/template/TemplateManagerImplTest.java index 576930e46f4..7893d28d9ca 100755 --- a/server/src/test/java/com/cloud/template/TemplateManagerImplTest.java +++ b/server/src/test/java/com/cloud/template/TemplateManagerImplTest.java @@ -19,26 +19,17 @@ package com.cloud.template; -import com.cloud.agent.AgentManager; -import com.cloud.api.query.dao.UserVmJoinDao; -import com.cloud.configuration.Resource; -import com.cloud.dc.dao.DataCenterDao; -import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao; -import com.cloud.domain.dao.DomainDao; -import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.host.Status; -import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.HypervisorGuruManager; -import com.cloud.projects.ProjectManager; +import com.cloud.resourcelimit.CheckedReservation; import com.cloud.storage.DataStoreRole; import com.cloud.storage.GuestOSVO; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; -import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.TemplateProfile; @@ -47,13 +38,11 @@ import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.GuestOSDao; -import com.cloud.storage.dao.LaunchPermissionDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateDetailsDao; import com.cloud.storage.dao.VMTemplatePoolDao; -import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; @@ -62,13 +51,11 @@ import com.cloud.user.ResourceLimitService; import com.cloud.user.User; import com.cloud.user.UserData; import com.cloud.user.UserVO; -import com.cloud.user.dao.AccountDao; -import com.cloud.utils.component.ComponentContext; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import junit.framework.TestCase; import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd; import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd; import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; @@ -77,54 +64,34 @@ import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd; import org.apache.cloudstack.api.command.user.template.UpdateVnfTemplateCmd; import org.apache.cloudstack.api.command.user.userdata.LinkUserDataToTemplateCmd; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; -import org.apache.cloudstack.engine.subsystem.api.storage.StorageCacheManager; import org.apache.cloudstack.engine.subsystem.api.storage.StorageStrategyFactory; -import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; -import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.messagebus.MessageBus; -import org.apache.cloudstack.secstorage.dao.SecondaryStorageHeuristicDao; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.secstorage.heuristics.HeuristicType; -import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.heuristics.HeuristicRuleHelper; import org.apache.cloudstack.storage.template.VnfTemplateManager; -import org.apache.cloudstack.test.utils.SpringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.Mockito; +import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.core.type.classreading.MetadataReader; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.TypeFilter; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.support.AnnotationConfigContextLoader; -import javax.inject.Inject; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -136,79 +103,77 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(loader = AnnotationConfigContextLoader.class) -public class TemplateManagerImplTest { +@RunWith(MockitoJUnitRunner.class) +public class TemplateManagerImplTest extends TestCase { - @Inject - TemplateManagerImpl templateManager = new TemplateManagerImpl(); + @Spy + @InjectMocks + TemplateManagerImpl templateManager; - @Inject + @Mock DataStoreManager dataStoreManager; - @Inject + @Mock VMTemplateDao vmTemplateDao; - @Inject + @Mock VMTemplatePoolDao vmTemplatePoolDao; - @Inject + @Mock TemplateDataStoreDao templateDataStoreDao; - @Inject + @Mock StoragePoolHostDao storagePoolHostDao; - @Inject + @Mock PrimaryDataStoreDao primaryDataStoreDao; - @Inject + @Mock ResourceLimitService resourceLimitMgr; - @Inject + @Mock ImageStoreDao imgStoreDao; - @Inject + @Mock GuestOSDao guestOSDao; - @Inject - VMTemplateDao tmpltDao; - - @Inject + @Mock SnapshotDao snapshotDao; - @Inject + @Mock + VolumeDao volumeDao; + + @Mock VMTemplateDetailsDao tmpltDetailsDao; - @Inject + @Mock StorageStrategyFactory storageStrategyFactory; - @Inject + @Mock VMInstanceDao _vmInstanceDao; - @Inject - private VMTemplateDao _tmpltDao; + @Mock + ReservationDao reservationDao; - @Inject + @Mock HypervisorGuruManager _hvGuruMgr; - @Inject + @Mock AccountManager _accountMgr; - @Inject - VnfTemplateManager vnfTemplateManager; - @Inject - TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao; - @Inject + @Mock + VnfTemplateManager vnfTemplateManager; + + @Mock HeuristicRuleHelper heuristicRuleHelperMock; public class CustomThreadPoolExecutor extends ThreadPoolExecutor { @@ -238,7 +203,6 @@ public class TemplateManagerImplTest { @Before public void setUp() { - ComponentContext.initComponentsLifeCycle(); AccountVO account = new AccountVO("admin", 1L, "networkDomain", Account.Type.NORMAL, "uuid"); UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN); CallContext.register(user, account); @@ -272,7 +236,7 @@ public class TemplateManagerImplTest { List adapters = new ArrayList(); adapters.add(templateAdapter); when(cmd.getId()).thenReturn(0L); - when(_tmpltDao.findById(cmd.getId())).thenReturn(template); + when(vmTemplateDao.findById(cmd.getId())).thenReturn(template); when(cmd.getZoneId()).thenReturn(null); when(template.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.None); @@ -293,7 +257,6 @@ public class TemplateManagerImplTest { //case 2.2: When Force delete flag is 'false' and VM instance VO list is non empty. when(cmd.isForced()).thenReturn(false); VMInstanceVO vmInstanceVO = mock(VMInstanceVO.class); - when(vmInstanceVO.getInstanceName()).thenReturn("mydDummyVM"); vmInstanceVOList.add(vmInstanceVO); when(_vmInstanceDao.listNonExpungedByTemplate(anyLong())).thenReturn(vmInstanceVOList); try { @@ -308,7 +271,6 @@ public class TemplateManagerImplTest { when(mockTemplate.getId()).thenReturn(202l); StoragePoolVO mockPool = mock(StoragePoolVO.class); - when(mockPool.getId()).thenReturn(2l); PrimaryDataStore mockPrimaryDataStore = mock(PrimaryDataStore.class); when(mockPrimaryDataStore.getId()).thenReturn(2l); @@ -316,7 +278,6 @@ public class TemplateManagerImplTest { VMTemplateStoragePoolVO mockTemplateStore = mock(VMTemplateStoragePoolVO.class); when(mockTemplateStore.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOADED); - when(dataStoreManager.getPrimaryDataStore(anyLong())).thenReturn(mockPrimaryDataStore); when(vmTemplateDao.findById(anyLong(), anyBoolean())).thenReturn(mockTemplate); when(vmTemplatePoolDao.findByPoolTemplate(anyLong(), anyLong(), nullable(String.class))).thenReturn(mockTemplateStore); @@ -332,13 +293,11 @@ public class TemplateManagerImplTest { when(mockTemplate.getId()).thenReturn(202l); StoragePoolVO mockPool = mock(StoragePoolVO.class); - when(mockPool.getId()).thenReturn(2l); PrimaryDataStore mockPrimaryDataStore = mock(PrimaryDataStore.class); when(mockPrimaryDataStore.getId()).thenReturn(2l); when(mockPrimaryDataStore.getDataCenterId()).thenReturn(1l); - when(dataStoreManager.getPrimaryDataStore(anyLong())).thenReturn(mockPrimaryDataStore); when(vmTemplateDao.findById(anyLong(), anyBoolean())).thenReturn(mockTemplate); when(vmTemplatePoolDao.findByPoolTemplate(anyLong(), anyLong(), nullable(String.class))).thenReturn(null); when(templateDataStoreDao.findByTemplateZoneDownloadStatus(202l, 1l, VMTemplateStorageResourceAssoc.Status.DOWNLOADED)).thenReturn(null); @@ -353,7 +312,6 @@ public class TemplateManagerImplTest { when(mockTemplate.getId()).thenReturn(202l); StoragePoolVO mockPool = mock(StoragePoolVO.class); - when(mockPool.getId()).thenReturn(2l); PrimaryDataStore mockPrimaryDataStore = mock(PrimaryDataStore.class); when(mockPrimaryDataStore.getId()).thenReturn(2l); @@ -361,7 +319,6 @@ public class TemplateManagerImplTest { TemplateDataStoreVO mockTemplateDataStore = mock(TemplateDataStoreVO.class); - when(dataStoreManager.getPrimaryDataStore(anyLong())).thenReturn(mockPrimaryDataStore); when(vmTemplateDao.findById(anyLong(), anyBoolean())).thenReturn(mockTemplate); when(vmTemplatePoolDao.findByPoolTemplate(anyLong(), anyLong(), nullable(String.class))).thenReturn(null); when(templateDataStoreDao.findByTemplateZoneDownloadStatus(202l, 1l, VMTemplateStorageResourceAssoc.Status.DOWNLOADED)).thenReturn(mockTemplateDataStore); @@ -412,20 +369,10 @@ public class TemplateManagerImplTest { PrimaryDataStore mockPrimaryDataStore = mock(PrimaryDataStore.class); VMTemplateStoragePoolVO mockTemplateStore = mock(VMTemplateStoragePoolVO.class); - when(mockPrimaryDataStore.getId()).thenReturn(2l); - when(mockPool.getId()).thenReturn(2l); when(mockPool.getStatus()).thenReturn(StoragePoolStatus.Disabled); - when(mockPool.getDataCenterId()).thenReturn(1l); - when(mockTemplate.getId()).thenReturn(202l); - when(mockTemplateStore.getDownloadState()).thenReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOADED); when(vmTemplateDao.findById(anyLong())).thenReturn(mockTemplate); - when(dataStoreManager.getPrimaryDataStore(anyLong())).thenReturn(mockPrimaryDataStore); - when(vmTemplateDao.findById(anyLong(), anyBoolean())).thenReturn(mockTemplate); - when(vmTemplatePoolDao.findByPoolTemplate(anyLong(), anyLong(), nullable(String.class))).thenReturn(mockTemplateStore); when(primaryDataStoreDao.findById(anyLong())).thenReturn(mockPool); - doNothing().when(mockTemplateStore).setMarkedForGC(anyBoolean()); - ExecutorService preloadExecutor = new CustomThreadPoolExecutor(8, 8, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new NamedThreadFactory("Template-Preloader")); templateManager._preloadExecutor = preloadExecutor; @@ -443,15 +390,10 @@ public class TemplateManagerImplTest { StoragePoolVO mockPool1 = mock(StoragePoolVO.class); when(mockPool1.getId()).thenReturn(2l); - when(mockPool1.getStatus()).thenReturn(StoragePoolStatus.Up); when(mockPool1.getDataCenterId()).thenReturn(1l); StoragePoolVO mockPool2 = mock(StoragePoolVO.class); - when(mockPool2.getId()).thenReturn(3l); - when(mockPool2.getStatus()).thenReturn(StoragePoolStatus.Up); when(mockPool2.getDataCenterId()).thenReturn(1l); StoragePoolVO mockPool3 = mock(StoragePoolVO.class); - when(mockPool3.getId()).thenReturn(4l); - when(mockPool3.getStatus()).thenReturn(StoragePoolStatus.Up); when(mockPool3.getDataCenterId()).thenReturn(2l); pools.add(mockPool1); pools.add(mockPool2); @@ -464,9 +406,6 @@ public class TemplateManagerImplTest { when(dataStoreManager.getPrimaryDataStore(anyLong())).thenReturn(mockPrimaryDataStore); when(vmTemplateDao.findById(anyLong(), anyBoolean())).thenReturn(mockTemplate); when(vmTemplatePoolDao.findByPoolTemplate(anyLong(), anyLong(), nullable(String.class))).thenReturn(mockTemplateStore); - when(primaryDataStoreDao.findById(2l)).thenReturn(mockPool1); - when(primaryDataStoreDao.findById(3l)).thenReturn(mockPool2); - when(primaryDataStoreDao.findById(4l)).thenReturn(mockPool3); when(primaryDataStoreDao.listByStatus(StoragePoolStatus.Up)).thenReturn(pools); doNothing().when(mockTemplateStore).setMarkedForGC(anyBoolean()); @@ -494,7 +433,6 @@ public class TemplateManagerImplTest { when(mockCreateCmd.getVolumeId()).thenReturn(null); when(mockCreateCmd.getSnapshotId()).thenReturn(1L); when(mockCreateCmd.getOsTypeId()).thenReturn(1L); - when(mockCreateCmd.getEventDescription()).thenReturn("test"); when(mockCreateCmd.getDetails()).thenReturn(null); when(mockCreateCmd.getZoneId()).thenReturn(null); @@ -507,20 +445,17 @@ public class TemplateManagerImplTest { when(mockSnapshot.getState()).thenReturn(Snapshot.State.BackedUp); when(mockSnapshot.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); - doNothing().when(resourceLimitMgr).checkResourceLimit(any(Account.class), eq(Resource.ResourceType.template)); - doNothing().when(resourceLimitMgr).checkResourceLimit(any(Account.class), eq(Resource.ResourceType.secondary_storage), anyLong()); - GuestOSVO mockGuestOS = mock(GuestOSVO.class); when(guestOSDao.findById(anyLong())).thenReturn(mockGuestOS); - when(tmpltDao.getNextInSequence(eq(Long.class), eq("id"))).thenReturn(1L); + when(vmTemplateDao.getNextInSequence(eq(Long.class), eq("id"))).thenReturn(1L); List mockRegionStores = new ArrayList<>(); ImageStoreVO mockRegionStore = mock(ImageStoreVO.class); mockRegionStores.add(mockRegionStore); when(imgStoreDao.findRegionImageStores()).thenReturn(mockRegionStores); - when(tmpltDao.persist(any(VMTemplateVO.class))).thenAnswer(new Answer() { + when(vmTemplateDao.persist(any(VMTemplateVO.class))).thenAnswer(new Answer() { @Override public VMTemplateVO answer(InvocationOnMock invocationOnMock) throws Throwable { Object[] args = invocationOnMock.getArguments(); @@ -528,8 +463,10 @@ public class TemplateManagerImplTest { } }); - VMTemplateVO template = templateManager.createPrivateTemplateRecord(mockCreateCmd, mockTemplateOwner); - assertTrue("Template in a region store should have cross zones set", template.isCrossZones()); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + VMTemplateVO template = templateManager.createPrivateTemplateRecord(mockCreateCmd, mockTemplateOwner); + assertTrue("Template in a region store should have cross zones set", template.isCrossZones()); + } } @Test @@ -541,7 +478,7 @@ public class TemplateManagerImplTest { when(cmd.getUserdataPolicy()).thenReturn(UserData.UserDataOverridePolicy.ALLOWOVERRIDE); VMTemplateVO template = Mockito.mock(VMTemplateVO.class); - when(_tmpltDao.findById(anyLong())).thenReturn(template); + when(vmTemplateDao.findById(anyLong())).thenReturn(template); VirtualMachineTemplate resultTemplate = templateManager.linkUserDataToTemplate(cmd); @@ -557,7 +494,6 @@ public class TemplateManagerImplTest { when(cmd.getUserdataPolicy()).thenReturn(UserData.UserDataOverridePolicy.ALLOWOVERRIDE); VMTemplateVO template = Mockito.mock(VMTemplateVO.class); - when(_tmpltDao.findById(1L)).thenReturn(template); templateManager.linkUserDataToTemplate(cmd); } @@ -571,7 +507,6 @@ public class TemplateManagerImplTest { when(cmd.getUserdataPolicy()).thenReturn(UserData.UserDataOverridePolicy.ALLOWOVERRIDE); VMTemplateVO template = Mockito.mock(VMTemplateVO.class); - when(_tmpltDao.findById(1L)).thenReturn(template); templateManager.linkUserDataToTemplate(cmd); } @@ -584,7 +519,7 @@ public class TemplateManagerImplTest { when(cmd.getUserdataId()).thenReturn(2L); when(cmd.getUserdataPolicy()).thenReturn(UserData.UserDataOverridePolicy.ALLOWOVERRIDE); - when(_tmpltDao.findById(anyLong())).thenReturn(null); + when(vmTemplateDao.findById(anyLong())).thenReturn(null); templateManager.linkUserDataToTemplate(cmd); } @@ -599,7 +534,7 @@ public class TemplateManagerImplTest { VMTemplateVO template = Mockito.mock(VMTemplateVO.class); when(template.getId()).thenReturn(1L); - when(_tmpltDao.findById(1L)).thenReturn(template); + when(vmTemplateDao.findById(1L)).thenReturn(template); VirtualMachineTemplate resultTemplate = templateManager.linkUserDataToTemplate(cmd); @@ -630,7 +565,6 @@ public class TemplateManagerImplTest { DataStore dataStore = Mockito.mock(DataStore.class); VolumeVO volumeVO = Mockito.mock(VolumeVO.class); - Mockito.when(dataStoreManager.getDataStore(Mockito.anyString(), Mockito.any(DataStoreRole.class))).thenReturn(null); Mockito.when(heuristicRuleHelperMock.getImageStoreIfThereIsHeuristicRule(Mockito.anyLong(), Mockito.any(HeuristicType.class), Mockito.any(VolumeVO.class))).thenReturn(null); Mockito.when(dataStoreManager.getImageStoreWithFreeCapacity(Mockito.anyLong())).thenReturn(dataStore); @@ -643,7 +577,6 @@ public class TemplateManagerImplTest { DataStore dataStore = Mockito.mock(DataStore.class); VolumeVO volumeVO = Mockito.mock(VolumeVO.class); - Mockito.when(dataStoreManager.getDataStore(Mockito.anyString(), Mockito.any(DataStoreRole.class))).thenReturn(null); Mockito.when(heuristicRuleHelperMock.getImageStoreIfThereIsHeuristicRule(Mockito.anyLong(), Mockito.any(HeuristicType.class), Mockito.any(VolumeVO.class))).thenReturn(dataStore); templateManager.getImageStore(null, 1L, volumeVO); @@ -773,243 +706,4 @@ public class TemplateManagerImplTest { Mockito.verify(heuristicRuleHelperMock, Mockito.times(1)).getImageStoreIfThereIsHeuristicRule(1L, HeuristicType.TEMPLATE, vmTemplateVOMock); } - @Configuration - @ComponentScan(basePackageClasses = {TemplateManagerImpl.class}, - includeFilters = {@ComponentScan.Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, - useDefaultFilters = false) - public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { - - @Bean - public DataStoreManager dataStoreManager() { - return Mockito.mock(DataStoreManager.class); - } - - @Bean - public VMTemplateDao vmTemplateDao() { - return Mockito.mock(VMTemplateDao.class); - } - - @Bean - public StorageStrategyFactory storageStrategyFactory() { - return Mockito.mock(StorageStrategyFactory.class); - } - - @Bean - public VMTemplatePoolDao vmTemplatePoolDao() { - return Mockito.mock(VMTemplatePoolDao.class); - } - - @Bean - public TemplateDataStoreDao templateDataStoreDao() { - return Mockito.mock(TemplateDataStoreDao.class); - } - - @Bean - public VMTemplateZoneDao vmTemplateZoneDao() { - return Mockito.mock(VMTemplateZoneDao.class); - } - - @Bean - public VMInstanceDao vmInstanceDao() { - return Mockito.mock(VMInstanceDao.class); - } - - @Bean - public PrimaryDataStoreDao primaryDataStoreDao() { - return Mockito.mock(PrimaryDataStoreDao.class); - } - - @Bean - public StoragePoolHostDao storagePoolHostDao() { - return Mockito.mock(StoragePoolHostDao.class); - } - - @Bean - public AccountDao accountDao() { - return Mockito.mock(AccountDao.class); - } - - @Bean - public AgentManager agentMgr() { - return Mockito.mock(AgentManager.class); - } - - @Bean - public AccountManager accountManager() { - return Mockito.mock(AccountManager.class); - } - - @Bean - public HostDao hostDao() { - return Mockito.mock(HostDao.class); - } - - @Bean - public DataCenterDao dcDao() { - return Mockito.mock(DataCenterDao.class); - } - - @Bean - public UserVmDao userVmDao() { - return Mockito.mock(UserVmDao.class); - } - - @Bean - public VolumeDao volumeDao() { - return Mockito.mock(VolumeDao.class); - } - - @Bean - public SnapshotDao snapshotDao() { - return Mockito.mock(SnapshotDao.class); - } - - @Bean - public ConfigurationDao configDao() { - return Mockito.mock(ConfigurationDao.class); - } - - @Bean - public DomainDao domainDao() { - return Mockito.mock(DomainDao.class); - } - - @Bean - public GuestOSDao guestOSDao() { - return Mockito.mock(GuestOSDao.class); - } - - @Bean - public StorageManager storageManager() { - return Mockito.mock(StorageManager.class); - } - - @Bean - public UsageEventDao usageEventDao() { - return Mockito.mock(UsageEventDao.class); - } - - @Bean - public ResourceLimitService resourceLimitMgr() { - return Mockito.mock(ResourceLimitService.class); - } - - @Bean - public LaunchPermissionDao launchPermissionDao() { - return Mockito.mock(LaunchPermissionDao.class); - } - - @Bean - public ProjectManager projectMgr() { - return Mockito.mock(ProjectManager.class); - } - - @Bean - public VolumeDataFactory volFactory() { - return Mockito.mock(VolumeDataFactory.class); - } - - @Bean - public TemplateDataFactory tmplFactory() { - return Mockito.mock(TemplateDataFactory.class); - } - - @Bean - public SnapshotDataFactory snapshotFactory() { - return Mockito.mock(SnapshotDataFactory.class); - } - - @Bean - public TemplateService tmpltSvr() { - return Mockito.mock(TemplateService.class); - } - - @Bean - public VolumeOrchestrationService volumeMgr() { - return Mockito.mock(VolumeOrchestrationService.class); - } - - @Bean - public EndPointSelector epSelector() { - return Mockito.mock(EndPointSelector.class); - } - - @Bean - public UserVmJoinDao userVmJoinDao() { - return Mockito.mock(UserVmJoinDao.class); - } - - @Bean - public SnapshotDataStoreDao snapshotStoreDao() { - return Mockito.mock(SnapshotDataStoreDao.class); - } - - @Bean - public ImageStoreDao imageStoreDao() { - return Mockito.mock(ImageStoreDao.class); - } - - @Bean - public MessageBus messageBus() { - return Mockito.mock(MessageBus.class); - } - - @Bean - public StorageCacheManager cacheMgr() { - return Mockito.mock(StorageCacheManager.class); - } - - @Bean - public TemplateAdapter templateAdapter() { - return Mockito.mock(TemplateAdapter.class); - } - - @Bean - public VMTemplateDetailsDao vmTemplateDetailsDao() { - return Mockito.mock(VMTemplateDetailsDao.class); - } - - @Bean - public HypervisorGuruManager hypervisorGuruManager() { - return Mockito.mock(HypervisorGuruManager.class); - } - - @Bean - public VnfTemplateManager vnfTemplateManager() { - return Mockito.mock(VnfTemplateManager.class); - } - - @Bean - public TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao() { - return Mockito.mock(TemplateDeployAsIsDetailsDao.class); - } - - @Bean - public SnapshotHelper snapshotHelper() { - return Mockito.mock(SnapshotHelper.class); - } - - @Bean - public SnapshotService snapshotService() { - return Mockito.mock(SnapshotService.class); - } - - @Bean - public SecondaryStorageHeuristicDao secondaryStorageHeuristicDao() { - return Mockito.mock(SecondaryStorageHeuristicDao.class); - } - - @Bean - public HeuristicRuleHelper heuristicRuleHelper() { - return Mockito.mock(HeuristicRuleHelper.class); - } - - public static class Library implements TypeFilter { - @Override - public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { - ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); - return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); - } - } - } } From 4bcd509193fcbcfdbeb88743b029584882aa7e30 Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:54:08 +0530 Subject: [PATCH 10/43] Fix resource limit reservation and check during StartVirtualMachine --- .../java/com/cloud/vm/UserVmManagerImpl.java | 272 +++++++++--------- 1 file changed, 141 insertions(+), 131 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 0e1c3bb9194..7fcf242ea20 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5493,13 +5493,135 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return startVirtualMachine(vmId, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, true); } + private Pair> startVirtualMachineUnchecked(UserVmVO vm, VMTemplateVO template, Long podId, + Long clusterId, Long hostId, @NotNull Map additionalParams, String deploymentPlannerToUse, + boolean isExplicitHost, boolean isRootAdmin) throws ResourceUnavailableException, InsufficientCapacityException { + + // check if vm is security group enabled + if (_securityGroupMgr.isVmSecurityGroupEnabled(vm.getId()) && _securityGroupMgr.getSecurityGroupsForVm(vm.getId()).isEmpty() + && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vm.getId()) && _networkModel.canAddDefaultSecurityGroup()) { + // if vm is not mapped to security group, create a mapping + if (logger.isDebugEnabled()) { + logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); + } + + SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId()); + if (defaultSecurityGroup != null) { + List groupList = new ArrayList<>(); + groupList.add(defaultSecurityGroup.getId()); + _securityGroupMgr.addInstanceToGroups(vm, groupList); + } + } + + // Choose deployment planner + // Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd + // Default behaviour is invoked when host, cluster or pod are not specified + Pod destinationPod = getDestinationPod(podId, isRootAdmin); + Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin); + HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost); + DataCenterDeployment plan = null; + boolean deployOnGivenHost = false; + if (destinationHost != null) { + logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + _hostDao.loadHostTags(destinationHost); + validateStrictHostTagCheck(vm, destinationHost); + + final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); + Pair cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false); + if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) { + String errorMsg; + if (!cpuCapabilityAndCapacity.first()) { + errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost); + } else { + errorMsg = String.format("Cannot deploy the VM to specified host %s, host does not have enough free CPU or RAM, please check the logs", destinationHost); + } + logger.info(errorMsg); + if (!AllowDeployVmIfGivenHostFails.value()) { + throw new InvalidParameterValueException(errorMsg); + } + } else { + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); + if (!AllowDeployVmIfGivenHostFails.value()) { + deployOnGivenHost = true; + } + } + } else if (destinationCluster != null) { + logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null); + if (!AllowDeployVmIfGivenHostFails.value()) { + deployOnGivenHost = true; + } + } else if (destinationPod != null) { + logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM"); + plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null); + if (!AllowDeployVmIfGivenHostFails.value()) { + deployOnGivenHost = true; + } + } + + // Set parameters + Map params = null; + if (vm.isUpdateParameters()) { + _vmDao.loadDetails(vm); + String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template); + if (!validPassword(password)) { + throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); + } + // Check if an SSH key pair was selected for the instance and if so + // use it to encrypt & save the vm password + encryptAndStorePassword(vm, password); + params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password); + } + + if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { + if (!HypervisorType.VMware.equals(vm.getHypervisorType())) { + throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType()); + } + Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup); + if (logger.isTraceEnabled()) { + logger.trace("It was specified whether to enter setup mode: " + paramValue.toString()); + } + params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue); + } + + VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); + + DeploymentPlanner planner = null; + if (deploymentPlannerToUse != null) { + // if set to null, the deployment planner would be later figured out either from global config var, or from + // the service offering + planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse); + if (planner == null) { + throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse); + } + } + vmEntity.setParamsToEntity(additionalParams); + + UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId()); + String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId())); + vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost); + + Pair> vmParamPair = new Pair(vm, params); + if (vm.isUpdateParameters()) { + // this value is not being sent to the backend; need only for api + // display purposes + if (template.isEnablePassword()) { + if (vm.getDetail(VmDetailConstants.PASSWORD) != null) { + userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD); + } + vm.setUpdateParameters(false); + _vmDao.update(vm.getId(), vm); + } + } + return vmParamPair; + } + @Override public Pair> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId, @NotNull Map additionalParams, String deploymentPlannerToUse, boolean isExplicitHost) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException { // Input validation final Account callerAccount = CallContext.current().getCallingAccount(); - UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId()); // if account is removed, return error if (callerAccount == null || callerAccount.getRemoved() != null) { @@ -5527,138 +5649,26 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (owner.getState() == Account.State.DISABLED) { throw new PermissionDeniedException(String.format("The owner of %s is disabled: %s", vm, owner)); } - Pair> vmParamPair; - try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, vm.getId(), null, 1L, reservationDao, _resourceLimitMgr)) { - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - // check if account/domain is with in resource limits to start a new vm - ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - resourceLimitService.checkVmResourceLimit(owner, vm.isDisplayVm(), offering, template); + boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId()); + + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(offering, template); + try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, resourceLimitHostTags, 1l, reservationDao, resourceLimitService); + CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, resourceLimitHostTags, Long.valueOf(offering.getCpu()), reservationDao, resourceLimitService); + CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, resourceLimitHostTags, Long.valueOf(offering.getRamSize()), reservationDao, resourceLimitService); + ) { + return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin); + } catch (ResourceAllocationException | CloudRuntimeException e) { + throw e; + } catch (Exception e) { + logger.error("Failed to start VM {} : error during resource reservation and allocation", e); + throw new CloudRuntimeException(e); } - // check if vm is security group enabled - if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() - && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkModel.canAddDefaultSecurityGroup()) { - // if vm is not mapped to security group, create a mapping - if (logger.isDebugEnabled()) { - logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); - } - - SecurityGroup defaultSecurityGroup = _securityGroupMgr.getDefaultSecurityGroup(vm.getAccountId()); - if (defaultSecurityGroup != null) { - List groupList = new ArrayList<>(); - groupList.add(defaultSecurityGroup.getId()); - _securityGroupMgr.addInstanceToGroups(vm, groupList); - } - } - // Choose deployment planner - // Host takes 1st preference, Cluster takes 2nd preference and Pod takes 3rd - // Default behaviour is invoked when host, cluster or pod are not specified - boolean isRootAdmin = _accountService.isRootAdmin(callerAccount.getId()); - Pod destinationPod = getDestinationPod(podId, isRootAdmin); - Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin); - HostVO destinationHost = getDestinationHost(hostId, isRootAdmin, isExplicitHost); - DataCenterDeployment plan = null; - boolean deployOnGivenHost = false; - if (destinationHost != null) { - logger.debug("Destination Host to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - _hostDao.loadHostTags(destinationHost); - validateStrictHostTagCheck(vm, destinationHost); - - final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - Pair cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(destinationHost, offering, false); - if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) { - String errorMsg; - if (!cpuCapabilityAndCapacity.first()) { - errorMsg = String.format("Cannot deploy the VM to specified host %s, requested CPU and speed is more than the host capability", destinationHost); - } else { - errorMsg = String.format("Cannot deploy the VM to specified host %s, host does not have enough free CPU or RAM, please check the logs", destinationHost); - } - logger.info(errorMsg); - if (!AllowDeployVmIfGivenHostFails.value()) { - throw new InvalidParameterValueException(errorMsg); - } - } else { - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationHost.getPodId(), destinationHost.getClusterId(), destinationHost.getId(), null, null); - if (!AllowDeployVmIfGivenHostFails.value()) { - deployOnGivenHost = true; - } - } - } else if (destinationCluster != null) { - logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null); - if (!AllowDeployVmIfGivenHostFails.value()) { - deployOnGivenHost = true; - } - } else if (destinationPod != null) { - logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM"); - plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null); - if (!AllowDeployVmIfGivenHostFails.value()) { - deployOnGivenHost = true; - } - } - - // Set parameters - Map params = null; - if (vm.isUpdateParameters()) { - _vmDao.loadDetails(vm); - - String password = getCurrentVmPasswordOrDefineNewPassword(String.valueOf(additionalParams.getOrDefault(VirtualMachineProfile.Param.VmPassword, "")), vm, template); - - if (!validPassword(password)) { - throw new InvalidParameterValueException("A valid password for this virtual machine was not provided."); - } - - // Check if an SSH key pair was selected for the instance and if so - // use it to encrypt & save the vm password - encryptAndStorePassword(vm, password); - - params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.VmPassword, password); - } - - if (additionalParams.containsKey(VirtualMachineProfile.Param.BootIntoSetup)) { - if (!HypervisorType.VMware.equals(vm.getHypervisorType())) { - throw new InvalidParameterValueException(ApiConstants.BOOT_INTO_SETUP + " makes no sense for " + vm.getHypervisorType()); - } - Object paramValue = additionalParams.get(VirtualMachineProfile.Param.BootIntoSetup); - if (logger.isTraceEnabled()) { - logger.trace("It was specified whether to enter setup mode: " + paramValue.toString()); - } - params = createParameterInParameterMap(params, additionalParams, VirtualMachineProfile.Param.BootIntoSetup, paramValue); - } - - VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); - - DeploymentPlanner planner = null; - if (deploymentPlannerToUse != null) { - // if set to null, the deployment planner would be later figured out either from global config var, or from - // the service offering - planner = _planningMgr.getDeploymentPlannerByName(deploymentPlannerToUse); - if (planner == null) { - throw new InvalidParameterValueException("Can't find a planner by name " + deploymentPlannerToUse); - } - } - vmEntity.setParamsToEntity(additionalParams); - - String reservationId = vmEntity.reserve(planner, plan, new ExcludeList(), Long.toString(callerUser.getId())); - vmEntity.deploy(reservationId, Long.toString(callerUser.getId()), params, deployOnGivenHost); - - vmParamPair = new Pair(vm, params); - if (vm != null && vm.isUpdateParameters()) { - // this value is not being sent to the backend; need only for api - // display purposes - if (template.isEnablePassword()) { - if (vm.getDetail(VmDetailConstants.PASSWORD) != null) { - userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.PASSWORD); - } - vm.setUpdateParameters(false); - _vmDao.update(vm.getId(), vm); - } - } - } catch (Exception e) { - logger.error("Failed to start VM {}", vm, e); - throw new CloudRuntimeException("Failed to start VM " + vm, e); + } else { + return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin); } - return vmParamPair; } /** From 03dfe4d1f3e165b943346437b421ce72f87ae249 Mon Sep 17 00:00:00 2001 From: abh1sar Date: Mon, 2 Mar 2026 11:06:13 +0530 Subject: [PATCH 11/43] secondary storage resource limit for download --- .../VMTemplateStorageResourceAssoc.java | 3 +- .../agent/api/storage/DownloadAnswer.java | 2 +- .../image/BaseImageStoreDriverImpl.java | 9 +- .../ResourceLimitManagerImpl.java | 4 +- .../storage/download/DownloadActiveState.java | 5 ++ .../storage/download/DownloadErrorState.java | 5 ++ .../download/DownloadInactiveState.java | 6 ++ .../download/DownloadLimitReachedState.java | 54 +++++++++++ .../storage/download/DownloadListener.java | 89 +++++++++++++++++-- .../cloud/storage/download/DownloadState.java | 6 +- .../template/HypervisorTemplateAdapter.java | 9 +- .../storage/template/DownloadManagerImpl.java | 2 +- 12 files changed, 173 insertions(+), 21 deletions(-) create mode 100644 server/src/main/java/com/cloud/storage/download/DownloadLimitReachedState.java diff --git a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java index db702a61f2b..7d5b2d7c57d 100644 --- a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java +++ b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java @@ -23,9 +23,10 @@ import org.apache.cloudstack.api.InternalIdentity; public interface VMTemplateStorageResourceAssoc extends InternalIdentity { public static enum Status { - UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED + UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, LIMIT_REACHED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED } + List ERROR_DOWNLOAD_STATES = List.of(Status.DOWNLOAD_ERROR, Status.ABANDONED, Status.LIMIT_REACHED, Status.UNKNOWN); List PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS); String getInstallPath(); diff --git a/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java index 0c6373134b1..1c5eb7b9a9a 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/storage/DownloadAnswer.java @@ -140,7 +140,7 @@ public class DownloadAnswer extends Answer { } public Long getTemplateSize() { - return templateSize; + return templateSize == 0 ? templatePhySicalSize : templateSize; } public void setTemplatePhySicalSize(long templatePhySicalSize) { diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java index a2e9eff2a08..61b1a84cdc6 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java @@ -230,8 +230,10 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver { updateBuilder.setJobId(answer.getJobId()); updateBuilder.setLocalDownloadPath(answer.getDownloadPath()); updateBuilder.setInstallPath(answer.getInstallPath()); - updateBuilder.setSize(answer.getTemplateSize()); - updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize()); + if (!VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) { + updateBuilder.setSize(answer.getTemplateSize()); + updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize()); + } _templateStoreDao.update(tmpltStoreVO.getId(), updateBuilder); // update size in vm_template table VMTemplateVO tmlptUpdater = _templateDao.createForUpdate(); @@ -241,8 +243,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver { AsyncCompletionCallback caller = context.getParentCallback(); - if (answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR || - answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.ABANDONED || answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.UNKNOWN) { + if (VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) { CreateCmdResult result = new CreateCmdResult(null, null); result.setSuccess(false); result.setResult(answer.getErrorString()); diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 09a0dda3aaa..43c3b383258 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -258,7 +258,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim templateSizeSearch = _vmTemplateStoreDao.createSearchBuilder(SumCount.class); templateSizeSearch.select("sum", Func.SUM, templateSizeSearch.entity().getSize()); - templateSizeSearch.and("downloadState", templateSizeSearch.entity().getDownloadState(), Op.EQ); + templateSizeSearch.and("downloadState", templateSizeSearch.entity().getDownloadState(), Op.IN); templateSizeSearch.and("destroyed", templateSizeSearch.entity().getDestroyed(), Op.EQ); SearchBuilder join1 = _vmTemplateDao.createSearchBuilder(); join1.and("accountId", join1.entity().getAccountId(), Op.EQ); @@ -1410,7 +1410,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim long totalTemplatesSize = 0; SearchCriteria sc = templateSizeSearch.create(); - sc.setParameters("downloadState", Status.DOWNLOADED); + sc.setParameters("downloadState", Status.DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS); sc.setParameters("destroyed", false); sc.setJoinParameters("templates", "accountId", accountId); List templates = _vmTemplateStoreDao.customSearch(sc, null); diff --git a/server/src/main/java/com/cloud/storage/download/DownloadActiveState.java b/server/src/main/java/com/cloud/storage/download/DownloadActiveState.java index 889ffbc0b1c..35b23985dd2 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadActiveState.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadActiveState.java @@ -95,6 +95,11 @@ public abstract class DownloadActiveState extends DownloadState { return Status.ABANDONED.toString(); } + @Override + public String handleLimitReached() { + return Status.LIMIT_REACHED.toString(); + } + @Override public String handleDisconnect() { diff --git a/server/src/main/java/com/cloud/storage/download/DownloadErrorState.java b/server/src/main/java/com/cloud/storage/download/DownloadErrorState.java index a0834456a2d..5dddefb0192 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadErrorState.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadErrorState.java @@ -60,6 +60,11 @@ public class DownloadErrorState extends DownloadInactiveState { return Status.ABANDONED.toString(); } + @Override + public String handleLimitReached() { + return Status.LIMIT_REACHED.toString(); + } + @Override public String getName() { return Status.DOWNLOAD_ERROR.toString(); diff --git a/server/src/main/java/com/cloud/storage/download/DownloadInactiveState.java b/server/src/main/java/com/cloud/storage/download/DownloadInactiveState.java index 8fee3d0437c..69d46879ebe 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadInactiveState.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadInactiveState.java @@ -36,6 +36,12 @@ public abstract class DownloadInactiveState extends DownloadState { return getName(); } + @Override + public String handleLimitReached() { + // ignore and stay put + return getName(); + } + @Override public String handleDisconnect() { //ignore and stay put diff --git a/server/src/main/java/com/cloud/storage/download/DownloadLimitReachedState.java b/server/src/main/java/com/cloud/storage/download/DownloadLimitReachedState.java new file mode 100644 index 00000000000..8ce5668299e --- /dev/null +++ b/server/src/main/java/com/cloud/storage/download/DownloadLimitReachedState.java @@ -0,0 +1,54 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage.download; + +import org.apache.cloudstack.storage.command.DownloadProgressCommand.RequestType; +import org.apache.logging.log4j.Level; + +import com.cloud.agent.api.storage.DownloadAnswer; +import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; + +public class DownloadLimitReachedState extends DownloadInactiveState { + + public DownloadLimitReachedState(DownloadListener dl) { + super(dl); + } + + @Override + public String getName() { + return Status.LIMIT_REACHED.toString(); + } + + @Override + public String handleEvent(DownloadEvent event, Object eventObj) { + if (logger.isTraceEnabled()) { + getDownloadListener().log("handleEvent, event type=" + event + ", curr state=" + getName(), Level.TRACE); + } + return Status.LIMIT_REACHED.toString(); + } + + @Override + public void onEntry(String prevState, DownloadEvent event, Object evtObj) { + if (!prevState.equalsIgnoreCase(getName())) { + DownloadAnswer answer = new DownloadAnswer("Storage Limit Reached", Status.LIMIT_REACHED); + getDownloadListener().callback(answer); + getDownloadListener().cancelStatusTask(); + getDownloadListener().cancelTimeoutTask(); + getDownloadListener().scheduleImmediateStatusCheck(RequestType.PURGE); + } + } +} diff --git a/server/src/main/java/com/cloud/storage/download/DownloadListener.java b/server/src/main/java/com/cloud/storage/download/DownloadListener.java index 42b0e394db4..058881fdb54 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadListener.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadListener.java @@ -25,6 +25,15 @@ import java.util.Timer; import javax.inject.Inject; +import com.cloud.configuration.Resource; +import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.ResourceLimitService; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -34,10 +43,13 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.managed.context.ManagedContextTimerTask; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.storage.command.DownloadCommand; import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType; import org.apache.cloudstack.storage.command.DownloadProgressCommand; import org.apache.cloudstack.storage.command.DownloadProgressCommand.RequestType; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.utils.cache.LazyCache; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -107,6 +119,7 @@ public class DownloadListener implements Listener { public static final String DOWNLOAD_ERROR = Status.DOWNLOAD_ERROR.toString(); public static final String DOWNLOAD_IN_PROGRESS = Status.DOWNLOAD_IN_PROGRESS.toString(); public static final String DOWNLOAD_ABANDONED = Status.ABANDONED.toString(); + public static final String DOWNLOAD_LIMIT_REACHED = Status.LIMIT_REACHED.toString(); private EndPoint _ssAgent; @@ -137,6 +150,18 @@ public class DownloadListener implements Listener { private DataStoreManager _storeMgr; @Inject private VolumeService _volumeSrv; + @Inject + private VMTemplateDao _templateDao; + @Inject + private TemplateDataStoreDao _templateDataStoreDao; + @Inject + private VolumeDao _volumeDao; + @Inject + private ResourceLimitService _resourceLimitMgr; + @Inject + private AccountManager _accountMgr; + @Inject + ReservationDao _reservationDao; private LazyCache> zoneHypervisorsCache; @@ -160,7 +185,7 @@ public class DownloadListener implements Listener { _downloadMonitor = downloadMonitor; _cmd = cmd; initStateMachine(); - _currState = getState(Status.NOT_DOWNLOADED.toString()); + _currState = getState(NOT_DOWNLOADED); this._timer = timer; _timeoutTask = new TimeoutTask(this); this._timer.schedule(_timeoutTask, 3 * STATUS_POLL_INTERVAL); @@ -184,11 +209,12 @@ public class DownloadListener implements Listener { } private void initStateMachine() { - _stateMap.put(Status.NOT_DOWNLOADED.toString(), new NotDownloadedState(this)); - _stateMap.put(Status.DOWNLOADED.toString(), new DownloadCompleteState(this)); - _stateMap.put(Status.DOWNLOAD_ERROR.toString(), new DownloadErrorState(this)); - _stateMap.put(Status.DOWNLOAD_IN_PROGRESS.toString(), new DownloadInProgressState(this)); - _stateMap.put(Status.ABANDONED.toString(), new DownloadAbandonedState(this)); + _stateMap.put(NOT_DOWNLOADED, new NotDownloadedState(this)); + _stateMap.put(DOWNLOADED, new DownloadCompleteState(this)); + _stateMap.put(DOWNLOAD_ERROR, new DownloadErrorState(this)); + _stateMap.put(DOWNLOAD_IN_PROGRESS, new DownloadInProgressState(this)); + _stateMap.put(DOWNLOAD_ABANDONED, new DownloadAbandonedState(this)); + _stateMap.put(DOWNLOAD_LIMIT_REACHED, new DownloadLimitReachedState(this)); } private DownloadState getState(String stateName) { @@ -239,10 +265,53 @@ public class DownloadListener implements Listener { return false; } + private Long getAccountIdForDataObject() { + if (object == null) { + return null; + } + if (DataObjectType.TEMPLATE.equals(object.getType())) { + VMTemplateVO t = _templateDao.findById(object.getId()); + return t != null ? t.getAccountId() : null; + } else if (DataObjectType.VOLUME.equals(object.getType())) { + VolumeVO v = _volumeDao.findById(object.getId()); + return v != null ? v.getAccountId() : null; + } + return null; + } + + private Long getSizeFromDB() { + Long lastSize = 0L; + if (DataObjectType.TEMPLATE.equals(object.getType())) { + TemplateDataStoreVO t = _templateDataStoreDao.findByStoreTemplate(object.getDataStore().getId(), object.getId()); + lastSize = t.getSize(); + } else if (DataObjectType.VOLUME.equals(object.getType())) { + VolumeVO v = _volumeDao.findById(object.getId()); + lastSize = v.getSize(); + } + return lastSize; + } + + private Boolean checkAndUpdateResourceLimits(DownloadAnswer answer) { + Long lastSize = getSizeFromDB(); + Long currentSize = answer.getTemplateSize(); + + if (currentSize > lastSize) { + Long accountId = getAccountIdForDataObject(); + Account account = _accountMgr.getAccount(accountId); + Long usage = currentSize - lastSize; + try (CheckedReservation secStorageReservation = new CheckedReservation(account, Resource.ResourceType.secondary_storage, usage, _reservationDao, _resourceLimitMgr)) { + _resourceLimitMgr.incrementResourceCount(accountId, Resource.ResourceType.secondary_storage, usage); + } catch (Exception e) { + return false; + } + } + return true; + } + @Override public boolean processAnswers(long agentId, long seq, Answer[] answers) { boolean processed = false; - if (answers != null & answers.length > 0) { + if (answers != null && answers.length > 0) { if (answers[0] instanceof DownloadAnswer) { final DownloadAnswer answer = (DownloadAnswer)answers[0]; if (getJobId() == null) { @@ -250,7 +319,11 @@ public class DownloadListener implements Listener { } else if (!getJobId().equalsIgnoreCase(answer.getJobId())) { return false;//TODO } - transition(DownloadEvent.DOWNLOAD_ANSWER, answer); + if (!checkAndUpdateResourceLimits(answer)) { + transition(DownloadEvent.LIMIT_REACHED, answer); + } else { + transition(DownloadEvent.DOWNLOAD_ANSWER, answer); + } processed = true; } } diff --git a/server/src/main/java/com/cloud/storage/download/DownloadState.java b/server/src/main/java/com/cloud/storage/download/DownloadState.java index 68723b53e35..e58d1f3f39f 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadState.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadState.java @@ -26,7 +26,7 @@ import com.cloud.agent.api.storage.DownloadAnswer; public abstract class DownloadState { public static enum DownloadEvent { - DOWNLOAD_ANSWER, ABANDON_DOWNLOAD, TIMEOUT_CHECK, DISCONNECT + DOWNLOAD_ANSWER, ABANDON_DOWNLOAD, LIMIT_REACHED, TIMEOUT_CHECK, DISCONNECT }; protected Logger logger = LogManager.getLogger(getClass()); @@ -51,6 +51,8 @@ public abstract class DownloadState { return handleAnswer(answer); case ABANDON_DOWNLOAD: return handleAbort(); + case LIMIT_REACHED: + return handleLimitReached(); case TIMEOUT_CHECK: Date now = new Date(); long update = now.getTime() - dl.getLastUpdated().getTime(); @@ -78,6 +80,8 @@ public abstract class DownloadState { public abstract String handleAbort(); + public abstract String handleLimitReached(); + public abstract String handleDisconnect(); public abstract String handleAnswer(DownloadAnswer answer); diff --git a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java index c096ef0eb1d..7e5811a2ece 100644 --- a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java +++ b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java @@ -481,13 +481,12 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { CreateTemplateContext context) { TemplateApiResult result = callback.getResult(); TemplateInfo template = context.template; + VMTemplateVO tmplt = _tmpltDao.findById(template.getId()); if (result.isSuccess()) { - VMTemplateVO tmplt = _tmpltDao.findById(template.getId()); // need to grant permission for public templates if (tmplt.isPublicTemplate()) { _messageBus.publish(_name, TemplateManager.MESSAGE_REGISTER_PUBLIC_TEMPLATE_EVENT, PublishScope.LOCAL, tmplt.getId()); } - long accountId = tmplt.getAccountId(); if (template.getSize() != null) { // publish usage event String etype = EventTypes.EVENT_TEMPLATE_CREATE; @@ -517,7 +516,11 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { template.getSize(), VirtualMachineTemplate.class.getName(), template.getUuid()); } } - _resourceLimitMgr.recalculateResourceCount(accountId, tmplt.getDomainId(), ResourceType.secondary_storage.getOrdinal()); + } + if (tmplt != null) { + long accountId = tmplt.getAccountId(); + Account account = _accountDao.findById(accountId); + _resourceLimitMgr.recalculateResourceCount(accountId, account.getDomainId(), ResourceType.secondary_storage.getOrdinal()); } return null; diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/DownloadManagerImpl.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/DownloadManagerImpl.java index 43a9422e8ad..c695b3d4b44 100644 --- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/DownloadManagerImpl.java +++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/template/DownloadManagerImpl.java @@ -996,7 +996,7 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager break; // TODO } return new DownloadAnswer(jobId, getDownloadPct(jobId), getDownloadError(jobId), getDownloadStatus2(jobId), getDownloadLocalPath(jobId), getInstallPath(jobId), - getDownloadTemplateSize(jobId), getDownloadTemplatePhysicalSize(jobId), getDownloadCheckSum(jobId)); + getDownloadTemplateSize(jobId), td.getDownloadedBytes(), getDownloadCheckSum(jobId)); } private String getInstallPath(String jobId) { From d0f673015798ce4d34fefdf5301fed9ecf9615e1 Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Fri, 6 Mar 2026 10:45:36 +0530 Subject: [PATCH 12/43] volume download fix --- .../storage/image/BaseImageStoreDriverImpl.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java index 61b1a84cdc6..26b39e30776 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/image/BaseImageStoreDriverImpl.java @@ -286,19 +286,22 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver { updateBuilder.setJobId(answer.getJobId()); updateBuilder.setLocalDownloadPath(answer.getDownloadPath()); updateBuilder.setInstallPath(answer.getInstallPath()); - updateBuilder.setSize(answer.getTemplateSize()); - updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize()); + if (!VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) { + updateBuilder.setSize(answer.getTemplateSize()); + updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize()); + } _volumeStoreDao.update(volStoreVO.getId(), updateBuilder); // update size in volume table - VolumeVO volUpdater = volumeDao.createForUpdate(); - volUpdater.setSize(answer.getTemplateSize()); - volumeDao.update(obj.getId(), volUpdater); + if (!VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) { + VolumeVO volUpdater = volumeDao.createForUpdate(); + volUpdater.setSize(answer.getTemplateSize()); + volumeDao.update(obj.getId(), volUpdater); + } } AsyncCompletionCallback caller = context.getParentCallback(); - if (answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR || - answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.ABANDONED || answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.UNKNOWN) { + if (VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) { CreateCmdResult result = new CreateCmdResult(null, null); result.setSuccess(false); result.setResult(answer.getErrorString()); From 81a8ac8e1ffa4438349eb5901e798428a6a744dd Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Mon, 9 Mar 2026 10:19:56 +0530 Subject: [PATCH 13/43] secondary storage resource limit for upload --- .../storage/command/UploadStatusCommand.java | 10 ++ .../storage/ImageStoreUploadMonitorImpl.java | 169 +++++++++++++----- .../resource/HttpUploadServerHandler.java | 2 + .../resource/NfsSecondaryStorageResource.java | 33 +++- 4 files changed, 173 insertions(+), 41 deletions(-) diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/UploadStatusCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/UploadStatusCommand.java index 9e6b76e467f..f78744046f7 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/UploadStatusCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/UploadStatusCommand.java @@ -28,6 +28,7 @@ public class UploadStatusCommand extends Command { } private String entityUuid; private EntityType entityType; + private Boolean abort; protected UploadStatusCommand() { } @@ -37,6 +38,11 @@ public class UploadStatusCommand extends Command { this.entityType = entityType; } + public UploadStatusCommand(String entityUuid, EntityType entityType, Boolean abort) { + this(entityUuid, entityType); + this.abort = abort; + } + public String getEntityUuid() { return entityUuid; } @@ -45,6 +51,10 @@ public class UploadStatusCommand extends Command { return entityType; } + public Boolean getAbort() { + return abort; + } + @Override public boolean executeInSequence() { return false; diff --git a/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java b/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java index 408eb69917a..b56e5b56213 100755 --- a/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java +++ b/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java @@ -26,10 +26,10 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.agent.api.to.OVFInformationTO; -import com.cloud.exception.ResourceAllocationException; import com.cloud.resourcelimit.CheckedReservation; import com.cloud.user.Account; import com.cloud.user.dao.AccountDao; +import com.cloud.user.AccountManager; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; @@ -126,6 +126,8 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto private ReservationDao reservationDao; @Inject private AccountDao accountDao; + @Inject + private AccountManager _accountMgr; private long _nodeId; private ScheduledExecutorService _executor = null; @@ -214,6 +216,36 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto public UploadStatusCheck() { } + private Answer sendUploadStatusCommandForVolume(EndPoint ep, UploadStatusCommand cmd, VolumeVO volume) { + Answer answer = null; + try { + answer = ep.sendMessage(cmd); + } catch (CloudRuntimeException e) { + logger.warn("Unable to get upload status for volume {}. Error details: {}", volume, e.getMessage()); + answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage()); + } + if (answer == null || !(answer instanceof UploadStatusAnswer)) { + logger.warn("No or invalid answer corresponding to UploadStatusCommand for volume {}", volume); + return null; + } + return answer; + } + + private Answer sendUploadStatusCommandForTemplate(EndPoint ep, UploadStatusCommand cmd, VMTemplateVO template) { + Answer answer = null; + try { + answer = ep.sendMessage(cmd); + } catch (CloudRuntimeException e) { + logger.warn("Unable to get upload status for template {}. Error details: {}", template, e.getMessage()); + answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage()); + } + if (answer == null || !(answer instanceof UploadStatusAnswer)) { + logger.warn("No or invalid answer corresponding to UploadStatusCommand for template {}", template); + return null; + } + return answer; + } + @Override protected void runInContext() { // 1. Select all entries with download_state = Not_Downloaded or Download_In_Progress @@ -240,18 +272,17 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto UploadStatusCommand cmd = new UploadStatusCommand(volume.getUuid(), EntityType.Volume); if (host != null && host.getManagementServerId() != null) { if (_nodeId == host.getManagementServerId().longValue()) { - Answer answer = null; - try { - answer = ep.sendMessage(cmd); - } catch (CloudRuntimeException e) { - logger.warn("Unable to get upload status for volume {}. Error details: {}", volume, e.getMessage()); - answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage()); - } - if (answer == null || !(answer instanceof UploadStatusAnswer)) { - logger.warn("No or invalid answer corresponding to UploadStatusCommand for volume {}", volume); + Answer answer = sendUploadStatusCommandForVolume(ep, cmd, volume); + if (answer == null) { continue; } - handleVolumeStatusResponse((UploadStatusAnswer)answer, volume, volumeDataStore); + if (!handleVolumeStatusResponse((UploadStatusAnswer)answer, volume, volumeDataStore)) { + cmd = new UploadStatusCommand(volume.getUuid(), EntityType.Volume, true); + answer = sendUploadStatusCommandForVolume(ep, cmd, volume); + if (answer == null) { + logger.warn("Unable to abort upload for volume {}", volume); + } + } } } else { String error = "Volume " + volume.getUuid() + " failed to upload as SSVM is either destroyed or SSVM agent not in 'Up' state"; @@ -284,18 +315,17 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto UploadStatusCommand cmd = new UploadStatusCommand(template.getUuid(), EntityType.Template); if (host != null && host.getManagementServerId() != null) { if (_nodeId == host.getManagementServerId().longValue()) { - Answer answer = null; - try { - answer = ep.sendMessage(cmd); - } catch (CloudRuntimeException e) { - logger.warn("Unable to get upload status for template {}. Error details: {}", template, e.getMessage()); - answer = new UploadStatusAnswer(cmd, UploadStatus.UNKNOWN, e.getMessage()); - } - if (answer == null || !(answer instanceof UploadStatusAnswer)) { - logger.warn("No or invalid answer corresponding to UploadStatusCommand for template {}", template); + Answer answer = sendUploadStatusCommandForTemplate(ep, cmd, template); + if (answer == null) { continue; } - handleTemplateStatusResponse((UploadStatusAnswer)answer, template, templateDataStore); + if (!handleTemplateStatusResponse((UploadStatusAnswer) answer, template, templateDataStore)) { + cmd = new UploadStatusCommand(template.getUuid(), EntityType.Template, true); + answer = sendUploadStatusCommandForTemplate(ep, cmd, template); + if (answer == null) { + logger.warn("Unable to abort upload for template {}", template); + } + } } } else { String error = String.format( @@ -312,7 +342,41 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto } } - private void handleVolumeStatusResponse(final UploadStatusAnswer answer, final VolumeVO volume, final VolumeDataStoreVO volumeDataStore) { + private Boolean checkAndUpdateSecondaryStorageResourceLimit(Long accountId, Long lastSize, Long currentSize) { + if (lastSize >= currentSize) { + return true; + } + Long usage = currentSize - lastSize; + try (CheckedReservation secStorageReservation = new CheckedReservation(_accountMgr.getAccount(accountId), Resource.ResourceType.secondary_storage, null, null, usage, reservationDao, _resourceLimitMgr)) { + _resourceLimitMgr.incrementResourceCount(accountId, Resource.ResourceType.secondary_storage, usage); + return true; + } catch (Exception e) { + _resourceLimitMgr.decrementResourceCount(accountId, Resource.ResourceType.secondary_storage, lastSize); + return false; + } + } + + private Boolean checkAndUpdateVolumeResourceLimit(VolumeVO volume, VolumeDataStoreVO volumeDataStore, UploadStatusAnswer answer) { + boolean success = true; + Long currentSize = answer.getVirtualSize() != 0 ? answer.getVirtualSize() : answer.getPhysicalSize(); + Long lastSize = volume.getSize() != null ? volume.getSize() : 0L; + if (!checkAndUpdateSecondaryStorageResourceLimit(volume.getAccountId(), volume.getSize(), currentSize)) { + volumeDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR); + volumeDataStore.setState(State.Failed); + volumeDataStore.setErrorString("Storage Limit Reached"); + Account owner = accountDao.findById(volume.getAccountId()); + String msg = String.format("Upload of volume [%s] failed because its owner [%s] does not have enough secondary storage space available.", volume.getUuid(), owner.getUuid()); + logger.error(msg); + success = false; + } + VolumeVO volumeUpdate = _volumeDao.findById(volume.getId()); + volumeUpdate.setSize(currentSize); + _volumeDao.update(volumeUpdate.getId(), volumeUpdate); + return success; + } + + private boolean handleVolumeStatusResponse(final UploadStatusAnswer answer, final VolumeVO volume, final VolumeDataStoreVO volumeDataStore) { + final boolean[] needAbort = new boolean[]{false}; final StateMachine2 stateMachine = Volume.State.getStateMachine(); Transaction.execute(new TransactionCallbackNoReturn() { @Override @@ -324,6 +388,11 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto try { switch (answer.getStatus()) { case COMPLETED: + if (!checkAndUpdateVolumeResourceLimit(tmpVolume, tmpVolumeDataStore, answer)) { + stateMachine.transitTo(tmpVolume, Event.OperationFailed, null, _volumeDao); + sendAlert = true; + break; + } tmpVolumeDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOADED); tmpVolumeDataStore.setState(State.Ready); tmpVolumeDataStore.setInstallPath(answer.getInstallPath()); @@ -335,7 +404,6 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto volumeUpdate.setSize(answer.getVirtualSize()); _volumeDao.update(tmpVolume.getId(), volumeUpdate); stateMachine.transitTo(tmpVolume, Event.OperationSucceeded, null, _volumeDao); - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), Resource.ResourceType.secondary_storage, answer.getVirtualSize()); // publish usage events UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_UPLOAD, tmpVolume.getAccountId(), @@ -348,6 +416,12 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto } break; case IN_PROGRESS: + if (!checkAndUpdateVolumeResourceLimit(tmpVolume, tmpVolumeDataStore, answer)) { + stateMachine.transitTo(tmpVolume, Event.OperationFailed, null, _volumeDao); + sendAlert = true; + needAbort[0] = true; + break; + } if (tmpVolume.getState() == Volume.State.NotUploaded) { tmpVolumeDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS); tmpVolumeDataStore.setDownloadPercent(answer.getDownloadPercent()); @@ -396,10 +470,29 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto } } }); + return !needAbort[0]; } - private void handleTemplateStatusResponse(final UploadStatusAnswer answer, final VMTemplateVO template, final TemplateDataStoreVO templateDataStore) { + private Boolean checkAndUpdateTemplateResourceLimit(VMTemplateVO template, TemplateDataStoreVO templateDataStore, UploadStatusAnswer answer) { + boolean success = true; + Long currentSize = answer.getVirtualSize() != 0 ? answer.getVirtualSize() : answer.getPhysicalSize(); + Long lastSize = template.getSize() != null ? template.getSize() : 0L; + if (!checkAndUpdateSecondaryStorageResourceLimit(template.getAccountId(), lastSize, currentSize)) { + templateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR); + templateDataStore.setErrorString("Storage Limit Reached"); + templateDataStore.setState(State.Failed); + Account owner = accountDao.findById(template.getAccountId()); + String msg = String.format("Upload of template [%s] failed because its owner [%s] does not have enough secondary storage space available.", template.getUuid(), owner.getUuid()); + logger.error(msg); + success = false; + } + templateDataStore.setSize(currentSize); + return success; + } + + private boolean handleTemplateStatusResponse(final UploadStatusAnswer answer, final VMTemplateVO template, final TemplateDataStoreVO templateDataStore) { final StateMachine2 stateMachine = VirtualMachineTemplate.State.getStateMachine(); + final boolean[] needAbort = new boolean[]{false}; Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { @@ -410,6 +503,11 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto try { switch (answer.getStatus()) { case COMPLETED: + if (!checkAndUpdateTemplateResourceLimit(tmpTemplate, tmpTemplateDataStore, answer)) { + stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao); + sendAlert = true; + break; + } tmpTemplateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOADED); tmpTemplateDataStore.setState(State.Ready); tmpTemplateDataStore.setInstallPath(answer.getInstallPath()); @@ -445,22 +543,6 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto break; } } - - Account owner = accountDao.findById(template.getAccountId()); - long templateSize = answer.getVirtualSize(); - - try (CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.secondary_storage, null, null, templateSize, reservationDao, _resourceLimitMgr)) { - _resourceLimitMgr.incrementResourceCount(owner.getId(), Resource.ResourceType.secondary_storage, templateSize); - } catch (ResourceAllocationException e) { - tmpTemplateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.UPLOAD_ERROR); - tmpTemplateDataStore.setState(State.Failed); - stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao); - msg = String.format("Upload of template [%s] failed because its owner [%s] does not have enough secondary storage space available.", template.getUuid(), owner.getUuid()); - logger.warn(msg); - sendAlert = true; - break; - } - stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationSucceeded, null, _templateDao); //publish usage event String etype = EventTypes.EVENT_TEMPLATE_CREATE; @@ -477,6 +559,12 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto } break; case IN_PROGRESS: + if (!checkAndUpdateTemplateResourceLimit(tmpTemplate, tmpTemplateDataStore, answer)) { + stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao); + sendAlert = true; + needAbort[0] = true; + break; + } if (tmpTemplate.getState() == VirtualMachineTemplate.State.NotUploaded) { tmpTemplateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS); stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.UploadRequested, null, _templateDao); @@ -526,6 +614,7 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto } } }); + return !needAbort[0]; } } diff --git a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java index a580105d52a..605749649bb 100644 --- a/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java +++ b/services/secondary-storage/server/src/main/java/org/apache/cloudstack/storage/resource/HttpUploadServerHandler.java @@ -130,6 +130,7 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler uploadEntityStateMap = new HashMap<>(); + private final Map uploadEntityStateMap = new ConcurrentHashMap<>(); + private final Map uploadChannelMap = new ConcurrentHashMap<>(); private String _ssvmPSK = null; private long processTimeout; @@ -2374,6 +2376,20 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S String entityUuid = cmd.getEntityUuid(); if (uploadEntityStateMap.containsKey(entityUuid)) { UploadEntity uploadEntity = uploadEntityStateMap.get(entityUuid); + if (Boolean.TRUE.equals(cmd.getAbort())) { + updateStateMapWithError(entityUuid, "Upload Entity aborted"); + String errorMsg = uploadEntity.getErrorMessage(); + if (errorMsg == null) { + errorMsg = "Upload aborted by management server"; + } + Channel channel = uploadChannelMap.remove(entityUuid); + if (channel != null && channel.isActive()) { + logger.info("Closing upload channel for entity {}", entityUuid); + channel.close(); + } + uploadEntityStateMap.remove(entityUuid); + return new UploadStatusAnswer(cmd, UploadStatus.ERROR, errorMsg); + } if (uploadEntity.getUploadState() == UploadEntity.Status.ERROR) { uploadEntityStateMap.remove(entityUuid); return new UploadStatusAnswer(cmd, UploadStatus.ERROR, uploadEntity.getErrorMessage()); @@ -2392,6 +2408,7 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S UploadStatusAnswer answer = new UploadStatusAnswer(cmd, UploadStatus.IN_PROGRESS); long downloadedSize = FileUtils.sizeOfDirectory(new File(uploadEntity.getInstallPathPrefix())); int downloadPercent = (int)(100 * downloadedSize / uploadEntity.getContentLength()); + answer.setPhysicalSize(downloadedSize); answer.setDownloadPercent(Math.min(downloadPercent, 100)); return answer; } @@ -3421,6 +3438,10 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S public String postUpload(String uuid, String filename, long processTimeout) { UploadEntity uploadEntity = uploadEntityStateMap.get(uuid); + if (uploadEntity == null) { + logger.warn("Upload entity not found for uuid: {}. Upload may have been aborted.", uuid); + return "Upload entity not found. Upload may have been aborted."; + } int installTimeoutPerGig = 180 * 60 * 1000; String resourcePath = uploadEntity.getInstallPathPrefix(); @@ -3571,6 +3592,16 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S return _ssvmPSK; } + public void registerUploadChannel(String uuid, Channel channel) { + uploadChannelMap.put(uuid, channel); + } + + public void deregisterUploadChannel(String uuid) { + if (uuid != null) { + uploadChannelMap.remove(uuid); + } + } + public void updateStateMapWithError(String uuid, String errorMessage) { UploadEntity uploadEntity = null; if (uploadEntityStateMap.get(uuid) != null) { From 360b64ce1e35623273ecbb955e6be7b8156b257c Mon Sep 17 00:00:00 2001 From: Fabricio Duarte Date: Mon, 9 Mar 2026 08:23:58 -0300 Subject: [PATCH 14/43] Consider infinite resources when calculating secondary storage limit for upload operations --- .../command/TemplateOrVolumePostUploadCommand.java | 8 ++++++++ .../main/java/com/cloud/storage/VolumeApiServiceImpl.java | 3 +-- .../com/cloud/template/HypervisorTemplateAdapter.java | 3 +-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/TemplateOrVolumePostUploadCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/TemplateOrVolumePostUploadCommand.java index 3ac83031eaf..9acfe30bf43 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/TemplateOrVolumePostUploadCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/TemplateOrVolumePostUploadCommand.java @@ -19,6 +19,9 @@ package org.apache.cloudstack.storage.command; +import com.cloud.configuration.Resource; +import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; + public class TemplateOrVolumePostUploadCommand { long entityId; @@ -185,6 +188,11 @@ public class TemplateOrVolumePostUploadCommand { this.description = description; } + public void setDefaultMaxSecondaryStorageInBytes(long defaultMaxSecondaryStorageInBytes) { + this.defaultMaxSecondaryStorageInGB = defaultMaxSecondaryStorageInBytes != Resource.RESOURCE_UNLIMITED ? + ByteScaleUtils.bytesToGibibytes(defaultMaxSecondaryStorageInBytes) : Resource.RESOURCE_UNLIMITED; + } + public void setDefaultMaxSecondaryStorageInGB(long defaultMaxSecondaryStorageInGB) { this.defaultMaxSecondaryStorageInGB = defaultMaxSecondaryStorageInGB; } diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index a975bd62408..884ece90b1c 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -107,7 +107,6 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity; -import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.identity.ManagementServerNode; import org.apache.cloudstack.utils.imagestore.ImageStoreUtil; import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; @@ -523,7 +522,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Account account = _accountDao.findById(accountId); Domain domain = domainDao.findById(account.getDomainId()); - command.setDefaultMaxSecondaryStorageInGB(ByteScaleUtils.bytesToGibibytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null))); + command.setDefaultMaxSecondaryStorageInBytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null)); command.setAccountId(accountId); Gson gson = new GsonBuilder().create(); String metadata = EncryptionUtil.encodeData(gson.toJson(command), key); diff --git a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java index 7e5811a2ece..632add684d7 100644 --- a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java +++ b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java @@ -64,7 +64,6 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.heuristics.HeuristicRuleHelper; import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity; -import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.security.DigestHelper; import org.apache.commons.collections.CollectionUtils; @@ -450,7 +449,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { Account account = _accountDao.findById(accountId); Domain domain = _domainDao.findById(account.getDomainId()); - payload.setDefaultMaxSecondaryStorageInGB(ByteScaleUtils.bytesToGibibytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null))); + payload.setDefaultMaxSecondaryStorageInBytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null)); payload.setAccountId(accountId); payload.setRemoteEndPoint(ep.getPublicAddr()); payload.setRequiresHvm(template.requiresHvm()); From 0a4b4c6af05c20e00a1eba8c0454780378cd6e8e Mon Sep 17 00:00:00 2001 From: Fabricio Duarte Date: Fri, 6 Mar 2026 10:17:37 -0300 Subject: [PATCH 15/43] [20.3] Address limit checks for VM, CPU, memory, volume, and primary storage --- .../com/cloud/projects/ProjectService.java | 4 +- .../com/cloud/user/ResourceLimitService.java | 15 +- .../user/account/AddAccountToProjectCmd.java | 3 +- .../user/account/AddUserToProjectCmd.java | 3 +- .../cloudstack}/resourcelimit/Reserver.java | 8 +- .../test/AddAccountToProjectCmdTest.java | 3 + .../cloud/vm/VirtualMachineManagerImpl.java | 17 +- .../orchestration/NetworkOrchestrator.java | 3 - .../orchestration/VolumeOrchestrator.java | 20 ++- .../cloud/projects/ProjectManagerImpl.java | 8 +- .../resourcelimit/CheckedReservation.java | 91 +++++++--- .../resourcelimit/ReservationHelper.java | 35 ++++ .../ResourceLimitManagerImpl.java | 142 ++++++--------- .../cloud/storage/VolumeApiServiceImpl.java | 118 ++++++++---- .../storage/snapshot/SnapshotManagerImpl.java | 15 +- .../cloud/template/TemplateManagerImpl.java | 5 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 135 +++++++------- .../vm/snapshot/VMSnapshotManagerImpl.java | 22 ++- .../VolumeImportUnmanageManagerImpl.java | 21 ++- .../vm/UnmanagedVMsManagerImpl.java | 168 ++++++++++-------- .../ResourceLimitManagerImplTest.java | 83 ++------- .../storage/VolumeApiServiceImplTest.java | 52 ++++-- .../com/cloud/vm/UserVmManagerImplTest.java | 44 ++--- .../vm/snapshot/VMSnapshotManagerTest.java | 8 +- .../vpc/MockResourceLimitManagerImpl.java | 23 +-- .../VolumeImportUnmanageManagerImplTest.java | 3 - .../vm/UnmanagedVMsManagerImplTest.java | 53 ++---- 27 files changed, 595 insertions(+), 507 deletions(-) rename {server/src/main/java/com/cloud => api/src/main/java/org/apache/cloudstack}/resourcelimit/Reserver.java (73%) create mode 100644 server/src/main/java/com/cloud/resourcelimit/ReservationHelper.java diff --git a/api/src/main/java/com/cloud/projects/ProjectService.java b/api/src/main/java/com/cloud/projects/ProjectService.java index 5080cb5a781..d11e9ae0446 100644 --- a/api/src/main/java/com/cloud/projects/ProjectService.java +++ b/api/src/main/java/com/cloud/projects/ProjectService.java @@ -82,7 +82,7 @@ public interface ProjectService { Project updateProject(long id, String name, String displayText, String newOwnerName, Long userId, Role newRole) throws ResourceAllocationException; - boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType); + boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) throws ResourceAllocationException; boolean deleteAccountFromProject(long projectId, String accountName); @@ -100,6 +100,6 @@ public interface ProjectService { Project findByProjectAccountIdIncludingRemoved(long projectAccountId); - boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole); + boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException; } diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java index 93609555122..d725c4a967b 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -30,6 +30,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; import com.cloud.template.VirtualMachineTemplate; +import org.apache.cloudstack.resourcelimit.Reserver; public interface ResourceLimitService { @@ -246,12 +247,12 @@ public interface ResourceLimitService { List getResourceLimitStorageTags(DiskOffering diskOffering); void updateTaggedResourceLimitsAndCountsForAccounts(List responses, String tag); void updateTaggedResourceLimitsAndCountsForDomains(List responses, String tag); - void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException; + void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException; List getResourceLimitStorageTagsForResourceCountOperation(Boolean display, DiskOffering diskOffering); void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, - DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException; + DiskOffering currentOffering, DiskOffering newOffering, List reservations) throws ResourceAllocationException; - void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException; + void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException; void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); @@ -268,20 +269,18 @@ public interface ResourceLimitService { void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); void decrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering); - void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException; + void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException; void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template); void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template); void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu, - Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template) throws ResourceAllocationException; + Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException; void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering, - VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException; + VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List reservations) throws ResourceAllocationException; - void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException; void incrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu); void decrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu); - void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException; void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java index 93021487040..6342709280a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddAccountToProjectCmd.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.user.account; import java.util.List; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.BaseCmd; @@ -106,7 +107,7 @@ public class AddAccountToProjectCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Override - public void execute() { + public void execute() throws ResourceAllocationException { if (accountName == null && email == null) { throw new InvalidParameterValueException("Either accountName or email is required"); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java index 9bdc85bc5c7..0a2d8824a5b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/account/AddUserToProjectCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.account; +import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiArgValidator; @@ -111,7 +112,7 @@ public class AddUserToProjectCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// @Override - public void execute() { + public void execute() throws ResourceAllocationException { validateInput(); boolean result = _projectService.addUserToProject(getProjectId(), getUsername(), getEmail(), getProjectRoleId(), getRoleType()); if (result) { diff --git a/server/src/main/java/com/cloud/resourcelimit/Reserver.java b/api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.java similarity index 73% rename from server/src/main/java/com/cloud/resourcelimit/Reserver.java rename to api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.java index 4d5e3ac30c5..6b3f57b6aa5 100644 --- a/server/src/main/java/com/cloud/resourcelimit/Reserver.java +++ b/api/src/main/java/org/apache/cloudstack/resourcelimit/Reserver.java @@ -15,8 +15,14 @@ // specific language governing permissions and limitations // under the License. -package com.cloud.resourcelimit; +package org.apache.cloudstack.resourcelimit; +/** + * Interface implemented by CheckedReservation. + *

+ * This is defined in cloud-api to allow methods declared in modules that do not depend on cloud-server + * to receive CheckedReservations as parameters. + */ public interface Reserver extends AutoCloseable { void close(); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java index f100822b8c7..cd0390aa268 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/AddAccountToProjectCmdTest.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.test; +import com.cloud.exception.ResourceAllocationException; import junit.framework.Assert; import junit.framework.TestCase; @@ -149,6 +150,8 @@ public class AddAccountToProjectCmdTest extends TestCase { addAccountToProjectCmd.execute(); } catch (InvalidParameterValueException exception) { Assert.assertEquals("Either accountName or email is required", exception.getLocalizedMessage()); + } catch (ResourceAllocationException exception) { + Assert.fail(); } } diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index c0976fe137e..2dcb8fa2059 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -4757,9 +4757,20 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac private void saveCustomOfferingDetails(long vmId, ServiceOffering serviceOffering) { Map details = userVmDetailsDao.listDetailsKeyPairs(vmId); - details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString()); - details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString()); - details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString()); + + // We need to restore only the customizable parameters. If we save a parameter that is not customizable and attempt + // to restore a VM snapshot, com.cloud.vm.UserVmManagerImpl.validateCustomParameters will fail. + ServiceOffering unfilledOffering = _serviceOfferingDao.findByIdIncludingRemoved(serviceOffering.getId()); + if (unfilledOffering.getCpu() == null) { + details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString()); + } + if (unfilledOffering.getSpeed() == null) { + details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString()); + } + if (unfilledOffering.getRamSize() == null) { + details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString()); + } + List detailList = new ArrayList<>(); for (Map.Entry entry: details.entrySet()) { UserVmDetailVO detailVO = new UserVmDetailVO(vmId, entry.getKey(), entry.getValue(), true); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index f8bf613d3e7..4fd5cbd1949 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -3097,9 +3097,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra CallContext.current().setEventDetails("Network Id: " + network.getId()); CallContext.current().putContextParameter(Network.class, network.getUuid()); return network; - } catch (Exception e) { - logger.error(e); - throw new RuntimeException(e); } } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index fdaec016cf5..0fc61d81588 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -39,6 +39,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import com.cloud.exception.ResourceAllocationException; +import com.cloud.resourcelimit.ReservationHelper; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; @@ -77,6 +78,7 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.secret.PassphraseVO; import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.cloudstack.snapshot.SnapshotHelper; @@ -1867,14 +1869,20 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati template == null ? null : template.getSize(), vol.getPassphraseId() != null); - if (newSize != vol.getSize()) { - DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId()); + if (newSize == vol.getSize()) { + return; + } + + DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId()); + + List reservations = new ArrayList<>(); + try { VMInstanceVO vm = vol.getInstanceId() != null ? vmInstanceDao.findById(vol.getInstanceId()) : null; if (vm == null || vm.getType() == VirtualMachine.Type.User) { // Update resource count for user vm volumes when volume is attached if (newSize > vol.getSize()) { _resourceLimitMgr.checkPrimaryStorageResourceLimit(_accountMgr.getActiveAccountById(vol.getAccountId()), - vol.isDisplay(), newSize - vol.getSize(), diskOffering); + vol.isDisplay(), newSize - vol.getSize(), diskOffering, reservations); _resourceLimitMgr.incrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(), newSize - vol.getSize(), diskOffering); } else { @@ -1882,9 +1890,11 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati vol.getSize() - newSize, diskOffering); } } - vol.setSize(newSize); - _volsDao.persist(vol); + } finally { + ReservationHelper.closeAll(reservations); } + vol.setSize(newSize); + _volsDao.persist(vol); } @Override diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index 8302f0ddf15..43efccd04f9 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -544,7 +544,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C @Override @ActionEvent(eventType = EventTypes.EVENT_PROJECT_USER_ADD, eventDescription = "adding user to project", async = true) - public boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) { + public boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); Project project = getProject(projectId); @@ -614,8 +614,6 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C logger.warn("Failed to add user to project: {}", project); return false; } - } catch (ResourceAllocationException e) { - throw new RuntimeException(e); } } } @@ -814,7 +812,7 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C @Override @ActionEvent(eventType = EventTypes.EVENT_PROJECT_ACCOUNT_ADD, eventDescription = "adding account to project", async = true) - public boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) { + public boolean addAccountToProject(long projectId, String accountName, String email, Long projectRoleId, Role projectRoleType) throws ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); //check that the project exists @@ -892,8 +890,6 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C logger.warn("Failed to add account {} to project {}", accountName, project); return false; } - } catch (ResourceAllocationException e) { - throw new RuntimeException(e); } } } diff --git a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java index 8d2e19d475e..5f9913e2ee5 100644 --- a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java +++ b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java @@ -20,13 +20,16 @@ package com.cloud.resourcelimit; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.reservation.ReservationVO; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.user.ResourceReservation; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -48,12 +51,15 @@ public class CheckedReservation implements Reserver { ReservationDao reservationDao; ResourceLimitService resourceLimitService; - private final Account account; + private Account account; private Long domainId; - private final ResourceType resourceType; - private Long amount; + private ResourceType resourceType; + private Long resourceId; + private Long reservationAmount; + private List reservationTags; + private Long existingAmount; + private List existingLimitTags; private List reservations; - private List resourceLimitTags; private String getContextParameterKey() { return getResourceReservationContextParameterKey(resourceType); @@ -100,9 +106,25 @@ public class CheckedReservation implements Reserver { this.reservations.add(reservation); } - public CheckedReservation(Account account, ResourceType resourceType, List resourceLimitTags, Long amount, - ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { - this(account, resourceType, null, resourceLimitTags, amount, reservationDao, resourceLimitService); + // TODO: refactor these into a Builder to avoid having so many constructors + public CheckedReservation(Account account, ResourceType resourceType, List resourceLimitTags, Long reservationAmount, + ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, resourceType, null, resourceLimitTags, null, reservationAmount, null, reservationDao, resourceLimitService); + } + + public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List reservedTags, + List existingTags, Long reservationAmount, Long existingAmount, ReservationDao reservationDao, + ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, null, resourceType, resourceId, reservedTags, existingTags, reservationAmount, existingAmount, reservationDao, resourceLimitService); + } + + public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List reservedTags, + Long reservationAmount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, domainId, resourceType, resourceId, reservedTags, null, reservationAmount, null, reservationDao, resourceLimitService); + } + + public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List reservedTags, Long reservationAmount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, null, resourceType, resourceId, reservedTags, null, reservationAmount, null, reservationDao, resourceLimitService); } /** @@ -110,36 +132,43 @@ public class CheckedReservation implements Reserver { * - create DB entry for reservation * - hold the id of this record as a ticket for implementation * - * @param amount positive number of the resource type to reserve + * @param reservationAmount positive number of the resource type to reserve * @throws ResourceAllocationException */ - public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount, - ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { - this(account, account.getDomainId(), resourceType, resourceId, resourceLimitTags, amount, reservationDao, resourceLimitService); - } + public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List reservedTags, + List existingTags, Long reservationAmount, Long existingAmount, ReservationDao reservationDao, + ResourceLimitService resourceLimitService) throws ResourceAllocationException { + + if (ObjectUtils.allNull(account, domainId)) { + logger.debug("Not reserving any {} resources, as no account/domain was provided.", resourceType); + return; + } - public CheckedReservation(Account account, Long domainId, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount, - ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { this.reservationDao = reservationDao; this.resourceLimitService = resourceLimitService; this.account = account; - this.domainId = domainId; if (domainId == null) { - this.domainId = account.getDomainId(); + domainId = account.getDomainId(); } + this.domainId = domainId; this.resourceType = resourceType; - this.amount = amount; + this.reservationAmount = reservationAmount; + this.existingAmount = existingAmount; this.reservations = new ArrayList<>(); - this.resourceLimitTags = resourceLimitTags; - if (this.amount != null && this.amount != 0) { - if (amount > 0) { + this.reservationTags = getTagsWithoutNull(reservedTags); + this.existingLimitTags = getTagsWithoutNull(existingTags); + + // TODO: refactor me + if (this.reservationAmount != null && this.reservationAmount != 0) { + if (reservationAmount > 0) { setGlobalLock(); if (quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) { try { - checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, resourceLimitTags, amount); + adjustCountToNotConsiderExistingAmount(); + checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, reservationTags, reservationAmount); CallContext.current().putContextParameter(getContextParameterKey(), getIds()); } catch (NullPointerException npe) { throw new CloudRuntimeException("not enough means to check limits", npe); @@ -150,7 +179,7 @@ public class CheckedReservation implements Reserver { throw new ResourceAllocationException(String.format("unable to acquire resource reservation \"%s\"", quotaLimitLock.getName()), resourceType); } } else { - checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, resourceLimitTags, amount); + checkLimitAndPersistReservations(account, this.domainId, resourceType, resourceId, reservationTags, reservationAmount); } } else { logger.debug("not reserving any amount of resources for {} in domain {}, type: {}, tag: {}", @@ -158,6 +187,20 @@ public class CheckedReservation implements Reserver { } } + protected List getTagsWithoutNull(List tags) { + if (tags == null) { + return null; + } + return tags.stream().filter(Objects::nonNull).collect(Collectors.toList()); + } + + protected void adjustCountToNotConsiderExistingAmount() throws ResourceAllocationException { + if (existingAmount == null || existingAmount == 0) { + return; + } + checkLimitAndPersistReservations(account, domainId, resourceType, resourceId, existingLimitTags, -1 * existingAmount); + } + public CheckedReservation(Account account, ResourceType resourceType, Long amount, ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { this(account, resourceType, null, amount, reservationDao, resourceLimitService); @@ -183,11 +226,11 @@ public class CheckedReservation implements Reserver { } public String getResourceLimitTagsAsString() { - return CollectionUtils.isNotEmpty(resourceLimitTags) ? StringUtils.join(resourceLimitTags) : null; + return CollectionUtils.isNotEmpty(reservationTags) ? StringUtils.join(reservationTags) : null; } public Long getReservedAmount() { - return amount; + return reservationAmount; } public List getReservations() { diff --git a/server/src/main/java/com/cloud/resourcelimit/ReservationHelper.java b/server/src/main/java/com/cloud/resourcelimit/ReservationHelper.java new file mode 100644 index 00000000000..cffa17176fa --- /dev/null +++ b/server/src/main/java/com/cloud/resourcelimit/ReservationHelper.java @@ -0,0 +1,35 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.resourcelimit; + +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.resourcelimit.Reserver; + +import java.util.List; + +public class ReservationHelper { + + public static void closeAll(List reservations) throws CloudRuntimeException { + for (Reserver reservation : reservations) { + reservation.close(); + } + } + +} diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 43c3b383258..6d2ec103ca2 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -52,6 +52,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.reservation.ReservationVO; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -168,6 +169,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim @Inject private ReservationDao reservationDao; @Inject + private ResourceLimitService resourceLimitService; + @Inject protected SnapshotDao _snapshotDao; @Inject private SnapshotDataStoreDao _snapshotDataStoreDao; @@ -1662,54 +1665,53 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } @Override - public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException { + public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException { List tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering); if (CollectionUtils.isEmpty(tags)) { return; } - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.volume, tag); - if (size != null) { - checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size); - } + + CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, tags, 1L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + + if (size != null) { + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); } } @Override - public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException { + public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException { List tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering); if (CollectionUtils.isEmpty(tags)) { return; } - if (size != null) { - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size); - } - } + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); } @Override public void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, - DiskOffering currentOffering, DiskOffering newOffering + DiskOffering currentOffering, DiskOffering newOffering, List reservations ) throws ResourceAllocationException { Ternary, Set, Set> updatedResourceLimitStorageTags = getResourceLimitStorageTagsForDiskOfferingChange(display, currentOffering, newOffering); if (updatedResourceLimitStorageTags == null) { return; } - Set sameTags = updatedResourceLimitStorageTags.first(); - Set newTags = updatedResourceLimitStorageTags.second(); - - if (newSize > currentSize) { - for (String tag : sameTags) { - checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, newSize - currentSize); - } + List currentTags = getResourceLimitStorageTagsForResourceCountOperation(true, currentOffering); + List tagsAfterUpdate = getResourceLimitStorageTagsForResourceCountOperation(true, newOffering); + if (currentTags.isEmpty() && tagsAfterUpdate.isEmpty()) { + return; } - for (String tag : newTags) { - checkResourceLimitWithTag(owner, ResourceType.volume, tag, 1L); - checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, newSize); - } + CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, null, tagsAfterUpdate, + currentTags, 1L, 1L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, null, + tagsAfterUpdate, currentTags, newSize, currentSize, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); } @DB @@ -1932,18 +1934,23 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } @Override - public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException { + public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException { List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); if (CollectionUtils.isEmpty(tags)) { return; } + + CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, tags, 1L, reservationDao, resourceLimitService); + reservations.add(vmReservation); + Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L; + CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, tags, cpu, reservationDao, resourceLimitService); + reservations.add(cpuReservation); + Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L; - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.user_vm, tag); - checkResourceLimitWithTag(owner, ResourceType.cpu, tag, cpu); - checkResourceLimitWithTag(owner, ResourceType.memory, tag, ram); - } + CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, tags, ram, reservationDao, resourceLimitService); + reservations.add(memReservation); + } @Override @@ -1989,76 +1996,53 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim @Override public void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering, - VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate) throws ResourceAllocationException { + VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List reservations) throws ResourceAllocationException { checkVmResourceLimitsForServiceOfferingAndTemplateChange(owner, display, null, null, - null, null, offering, offering, currentTemplate, newTemplate); + null, null, offering, offering, currentTemplate, newTemplate, reservations); } @Override public void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu, Long currentMemory, Long newMemory, - ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template + ServiceOffering currentOffering, ServiceOffering newOffering, VirtualMachineTemplate template, List reservations ) throws ResourceAllocationException { checkVmResourceLimitsForServiceOfferingAndTemplateChange(owner, display, currentCpu, newCpu, currentMemory, newMemory, currentOffering, - newOffering != null ? newOffering : currentOffering, template, template); + newOffering != null ? newOffering : currentOffering, template, template, reservations); } private void checkVmResourceLimitsForServiceOfferingAndTemplateChange(Account owner, Boolean display, Long currentCpu, Long newCpu, Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, - VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate + VirtualMachineTemplate currentTemplate, VirtualMachineTemplate newTemplate, List reservations ) throws ResourceAllocationException { - Ternary, Set, Set> updatedResourceLimitHostTags = getResourceLimitHostTagsForVmServiceOfferingAndTemplateChange(display, currentOffering, newOffering, currentTemplate, newTemplate); - if (updatedResourceLimitHostTags == null) { + List currentTags = getResourceLimitHostTagsForResourceCountOperation(true, currentOffering, currentTemplate); + List tagsAfterUpdate = getResourceLimitHostTagsForResourceCountOperation(true, newOffering, newTemplate); + if (currentTags.isEmpty() && tagsAfterUpdate.isEmpty()) { return; } + CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, null, tagsAfterUpdate, + currentTags, 1L, 1L, reservationDao, resourceLimitService); + reservations.add(vmReservation); + if (currentCpu == null) { currentCpu = currentOffering.getCpu() != null ? Long.valueOf(currentOffering.getCpu()) : 0L; } if (newCpu == null) { newCpu = newOffering.getCpu() != null ? Long.valueOf(newOffering.getCpu()) : 0L; } + CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, null, tagsAfterUpdate, + currentTags, newCpu, currentCpu, reservationDao, resourceLimitService); + reservations.add(cpuReservation); + if (currentMemory == null) { currentMemory = currentOffering.getRamSize() != null ? Long.valueOf(currentOffering.getRamSize()) : 0L; } if (newMemory == null) { newMemory = newOffering.getRamSize() != null ? Long.valueOf(newOffering.getRamSize()) : 0L; } - - Set sameTags = updatedResourceLimitHostTags.first(); - Set newTags = updatedResourceLimitHostTags.second(); - - if (newCpu - currentCpu > 0 || newMemory - currentMemory > 0) { - for (String tag : sameTags) { - if (newCpu - currentCpu > 0) { - checkResourceLimitWithTag(owner, ResourceType.cpu, tag, newCpu - currentCpu); - } - - if (newMemory - currentMemory > 0) { - checkResourceLimitWithTag(owner, ResourceType.memory, tag, newMemory - currentMemory); - } - } - } - - for (String tag : newTags) { - checkResourceLimitWithTag(owner, ResourceType.user_vm, tag, 1L); - checkResourceLimitWithTag(owner, ResourceType.cpu, tag, newCpu); - checkResourceLimitWithTag(owner, ResourceType.memory, tag, newMemory); - } - } - - @Override - public void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException { - List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); - if (CollectionUtils.isEmpty(tags)) { - return; - } - if (cpu == null) { - cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L; - } - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.cpu, tag, cpu); - } + CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, null, tagsAfterUpdate, + currentTags, newMemory, currentMemory, reservationDao, resourceLimitService); + reservations.add(memReservation); } @Override @@ -2089,20 +2073,6 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } } - @Override - public void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException { - List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); - if (CollectionUtils.isEmpty(tags)) { - return; - } - if (memory == null) { - memory = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L; - } - for (String tag : tags) { - checkResourceLimitWithTag(owner, ResourceType.memory, tag, memory); - } - } - @Override public void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) { List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 884ece90b1c..7186b07334d 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -36,6 +36,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.resourcelimit.ReservationHelper; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.InternalIdentity; @@ -93,6 +94,7 @@ import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO; import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.command.AttachAnswer; import org.apache.cloudstack.storage.command.AttachCommand; @@ -425,9 +427,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Long diskOfferingId = cmd.getDiskOfferingId(); String imageStoreUuid = cmd.getImageStoreUuid(); - validateVolume(caller, ownerId, zoneId, volumeName, url, format, diskOfferingId); + VolumeVO volume; - VolumeVO volume = persistVolume(owner, zoneId, volumeName, url, format, diskOfferingId, Volume.State.Allocated); + List reservations = new ArrayList<>(); + try { + + validateVolume(caller, ownerId, zoneId, volumeName, url, format, diskOfferingId, reservations); + volume = persistVolume(owner, zoneId, volumeName, url, format, diskOfferingId, Volume.State.Allocated); + + } finally { + ReservationHelper.closeAll(reservations); + } VolumeInfo vol = volFactory.getVolume(volume.getId()); @@ -467,7 +477,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic final Long diskOfferingId = cmd.getDiskOfferingId(); String imageStoreUuid = cmd.getImageStoreUuid(); - validateVolume(caller, ownerId, zoneId, volumeName, null, format, diskOfferingId); + List reservations = new ArrayList<>(); + try { + validateVolume(caller, ownerId, zoneId, volumeName, null, format, diskOfferingId, reservations); return Transaction.execute(new TransactionCallbackWithException() { @Override @@ -535,9 +547,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return response; } }); + + } finally { + ReservationHelper.closeAll(reservations); + } } - private boolean validateVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format, Long diskOfferingId) throws ResourceAllocationException { + private boolean validateVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format, Long diskOfferingId, List reservations) throws ResourceAllocationException { // permission check Account volumeOwner = _accountMgr.getActiveAccountById(ownerId); @@ -548,7 +564,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _accountMgr.checkAccess(caller, null, true, volumeOwner); // Check that the resource limit for volumes won't be exceeded - _resourceLimitMgr.checkVolumeResourceLimit(volumeOwner, true, null, diskOffering); + _resourceLimitMgr.checkVolumeResourceLimit(volumeOwner, true, null, diskOffering, reservations); // Verify that zone exists DataCenterVO zone = _dcDao.findById(zoneId); @@ -926,8 +942,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (tags.size() == 1 && tags.get(0) == null) { tags = new ArrayList<>(); } - try (CheckedReservation volumeReservation = new CheckedReservation(owner, ResourceType.volume, null, tags, 1L, reservationDao, _resourceLimitMgr); - CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, null, tags, size, reservationDao, _resourceLimitMgr)) { + + List reservations = new ArrayList<>(); + try { + _resourceLimitMgr.checkVolumeResourceLimit(owner, displayVolume, size, diskOffering, reservations); // Verify that zone exists DataCenterVO zone = _dcDao.findById(zoneId); @@ -950,9 +968,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return commitVolume(cmd, caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName, _uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details); - } catch (Exception e) { - logger.error(e); - throw new RuntimeException(e); + } finally { + ReservationHelper.closeAll(reservations); } } @@ -1278,7 +1295,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (dataStore != null && dataStore.getDriver() instanceof PrimaryDataStoreDriver) { newSize = ((PrimaryDataStoreDriver) dataStore.getDriver()).getVolumeSizeRequiredOnPool(newSize, null, isEncryptionRequired); } - validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, diskOffering, newDiskOffering); + + List reservations = new ArrayList<>(); + try { + validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, diskOffering, newDiskOffering, reservations); // Note: The storage plug-in in question should perform validation on the IOPS to check if a sufficient number of IOPS is available to perform // the requested change @@ -1406,6 +1426,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + + } finally { + ReservationHelper.closeAll(reservations); + } } /** @@ -1839,12 +1863,11 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Please specify a volume in Destroy state."); } + DiskOffering diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + + List reservations = new ArrayList<>(); try { - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize()); - } catch (ResourceAllocationException e) { - logger.error("primary storage resource limit check failed", e); - throw new InvalidParameterValueException(e.getMessage()); - } + _resourceLimitMgr.checkVolumeResourceLimit(_accountMgr.getAccount(volume.getAccountId()), volume.isDisplayVolume(), volume.getSize(), diskOffering, reservations); try { _volsDao.detachVolume(volume.getId()); @@ -1856,6 +1879,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _resourceLimitMgr.incrementVolumeResourceCount(volume.getAccountId(), volume.isDisplay(), volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId())); + } catch (ResourceAllocationException e) { + logger.error("primary storage resource limit check failed", e); + throw new InvalidParameterValueException(e.getMessage()); + } finally { + ReservationHelper.closeAll(reservations); + } publishVolumeCreationUsageEvent(volume); @@ -2085,7 +2114,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic newSize = ((PrimaryDataStoreDriver) dataStore.getDriver()).getVolumeSizeRequiredOnPool(newSize, null, newDiskOffering.getEncrypt()); } - validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, existingDiskOffering, newDiskOffering); + List reservations = new ArrayList<>(); + try { + validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, existingDiskOffering, newDiskOffering, reservations); /* If this volume has never been beyond allocated state, short circuit everything and simply update the database. */ // We need to publish this event to usage_volume table @@ -2175,6 +2206,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } return volume; + + } finally { + ReservationHelper.closeAll(reservations); + } } private void updateStorageWithTheNewDiskOffering(VolumeVO volume, DiskOfferingVO newDiskOffering) { @@ -2380,7 +2415,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } private void validateVolumeResizeWithSize(VolumeVO volume, long currentSize, Long newSize, boolean shrinkOk, - DiskOfferingVO existingDiskOffering, DiskOfferingVO newDiskOffering) throws ResourceAllocationException { + DiskOfferingVO existingDiskOffering, DiskOfferingVO newDiskOffering, List reservations) throws ResourceAllocationException { // if the caller is looking to change the size of the volume if (newSize != null && currentSize != newSize) { @@ -2450,7 +2485,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic /* Check resource limit for this account */ _resourceLimitMgr.checkVolumeResourceLimitForDiskOfferingChange(_accountMgr.getAccount(volume.getAccountId()), volume.isDisplayVolume(), currentSize, newSize != null ? newSize : currentSize, - existingDiskOffering, newDiskOffering); + existingDiskOffering, newDiskOffering, reservations); } @Override @@ -2622,7 +2657,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic checkForBackups(vm, true); - checkRightsToAttach(caller, volumeToAttach, vm); + _accountMgr.checkAccess(caller, null, true, volumeToAttach, vm); HypervisorType rootDiskHyperType = vm.getHypervisorType(); HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId()); @@ -2649,6 +2684,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Volume's disk offering has encryption enabled, but volume encryption is not supported for hypervisor type " + rootDiskHyperType); } + Account owner = _accountDao.findById(volumeToAttach.getAccountId()); + List resourceLimitStorageTags = _resourceLimitMgr.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); + Long requiredPrimaryStorageSpace = getRequiredPrimaryStorageSizeForVolumeAttach(resourceLimitStorageTags, volumeToAttach); + + try (CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, resourceLimitStorageTags, requiredPrimaryStorageSpace, reservationDao, _resourceLimitMgr)) { + _jobMgr.updateAsyncJobAttachment(job.getId(), "Volume", volumeId); if (asyncExecutionContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { @@ -2656,9 +2697,21 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } else { return getVolumeAttachJobResult(vmId, volumeId, deviceId); } + + } catch (ResourceAllocationException e) { + logger.error("primary storage resource limit check failed", e); + throw new InvalidParameterValueException(e.getMessage()); + } } - @Nullable private Volume getVolumeAttachJobResult(Long vmId, Long volumeId, Long deviceId) { + protected Long getRequiredPrimaryStorageSizeForVolumeAttach(List resourceLimitStorageTags, VolumeInfo volumeToAttach) { + if (CollectionUtils.isEmpty(resourceLimitStorageTags) || Arrays.asList(Volume.State.Allocated, Volume.State.Ready).contains(volumeToAttach.getState())) { + return 0L; + } + return volumeToAttach.getSize(); + } + + @Nullable protected Volume getVolumeAttachJobResult(Long vmId, Long volumeId, Long deviceId) { Outcome outcome = attachVolumeToVmThroughJobQueue(vmId, volumeId, deviceId); Volume vol = null; @@ -2707,21 +2760,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } - private void checkRightsToAttach(Account caller, VolumeInfo volumeToAttach, UserVmVO vm) { - _accountMgr.checkAccess(caller, null, true, volumeToAttach, vm); - - Account owner = _accountDao.findById(volumeToAttach.getAccountId()); - - if (!Arrays.asList(Volume.State.Allocated, Volume.State.Ready).contains(volumeToAttach.getState())) { - try { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, volumeToAttach.getSize()); - } catch (ResourceAllocationException e) { - logger.error("primary storage resource limit check failed", e); - throw new InvalidParameterValueException(e.getMessage()); - } - } - } - private void checkForVMSnapshots(Long vmId, UserVmVO vm) { // if target VM has associated VM snapshots List vmSnapshots = _vmSnapshotDao.findByVm(vmId); @@ -4207,7 +4245,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _accountMgr.checkAccess(caller, null, true, oldAccount); _accountMgr.checkAccess(caller, null, true, newAccount); - _resourceLimitMgr.checkVolumeResourceLimit(newAccount, true, volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId())); + List reservations = new ArrayList<>(); + try { + _resourceLimitMgr.checkVolumeResourceLimit(newAccount, true, volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId()), reservations); Transaction.execute(new TransactionCallbackNoReturn() { @Override @@ -4217,6 +4257,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic }); return volume; + + } finally { + ReservationHelper.closeAll(reservations); + } } protected void updateVolumeAccount(Account oldAccount, VolumeVO volume, Account newAccount) { diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index d5475948c59..4a4a7544ce7 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -1759,16 +1759,13 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot); _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), storeResourceType, volume.getSize()); return snapshot; - } catch (Exception e) { - if (e instanceof ResourceAllocationException) { - if (snapshotType != Type.MANUAL) { - String msg = String.format("Snapshot resource limit exceeded for account id : %s. Failed to create recurring snapshots", owner.getId()); - logger.warn(msg); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, msg + ". Please, use updateResourceLimit to increase the limit"); - } - throw (ResourceAllocationException) e; + } catch (ResourceAllocationException e) { + if (snapshotType != Type.MANUAL) { + String msg = String.format("Snapshot resource limit exceeded for account id : %s. Failed to create recurring snapshots", owner.getId()); + logger.warn(msg); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, msg + ". Please, use updateResourceLimit to increase the limit"); } - throw new CloudRuntimeException(e); + throw e; } } diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index 5c34e2a7473..1f3eb567c6f 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -357,6 +357,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, // Secondary storage resource count is not incremented for BareMetalTemplateAdapter // Note: checking the file size before registering will require the Management Server host to have access to the Internet and a DNS server + // If it does not, UriUtils.getRemoteSize will return 0L. long secondaryStorageUsage = adapter instanceof HypervisorTemplateAdapter && !cmd.isDirectDownload() ? UriUtils.getRemoteSize(cmd.getUrl(), StorageManager.DataStoreDownloadFollowRedirects.value()) : 0L; @@ -365,7 +366,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, TemplateProfile profile = adapter.prepare(cmd); VMTemplateVO template = adapter.create(profile); - // Secondary storage resource usage will be recalculated in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack + // Secondary storage resource usage will be incremented in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); if (secondaryStorageUsage > 0) { _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, secondaryStorageUsage); @@ -405,7 +406,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, TemplateProfile profile = adapter.prepare(cmd); VMTemplateVO template = adapter.create(profile); - // Secondary storage resource usage will be recalculated in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack + // Secondary storage resource usage will be incremented in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack // for HypervisorTemplateAdapter _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); if (secondaryStorageUsage > 0) { diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 7fcf242ea20..8b77cb506a8 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -54,6 +54,7 @@ import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; +import com.cloud.resourcelimit.ReservationHelper; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -122,6 +123,7 @@ import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.query.QueryService; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.DettachCommand; @@ -1351,9 +1353,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Account owner = _accountMgr.getActiveAccountById(vmInstance.getAccountId()); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); + + List reservations = new ArrayList<>(); + try { if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, - (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template); + (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations); } // Check that the specified service offering ID is valid @@ -1376,6 +1381,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return _vmDao.findById(vmInstance.getId()); + } finally { + ReservationHelper.closeAll(reservations); + } } /** @@ -2063,9 +2071,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); + List reservations = new ArrayList<>(); + try { // Check resource limits _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, - (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template); + (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations); // Dynamically upgrade the running vms boolean success = false; @@ -2137,6 +2147,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } return success; + + } finally { + ReservationHelper.closeAll(reservations); + } } protected void validateDiskOfferingChecks(ServiceOfferingVO currentServiceOffering, ServiceOfferingVO newServiceOffering) { @@ -2322,10 +2336,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + List reservations = new ArrayList<>(); + try { // First check that the maximum number of UserVMs, CPU and Memory limit for the given // accountId will not be exceeded if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template); + resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template, reservations); } _haMgr.cancelDestroy(vm, vm.getHostId()); @@ -2350,6 +2366,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir //Update Resource Count for the given account resourceCountIncrement(account.getId(), vm.isDisplayVm(), serviceOffering, template); + + } finally { + ReservationHelper.closeAll(reservations); + } } }); @@ -2776,27 +2796,25 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir long currentCpu = currentServiceOffering.getCpu(); long currentMemory = currentServiceOffering.getRamSize(); VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vmInstance.getTemplateId()); + List reservations = new ArrayList<>(); try { + _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), currentCpu, newCpu, + currentMemory, newMemory, currentServiceOffering, svcOffering, template, reservations); if (newCpu > currentCpu) { - _resourceLimitMgr.checkVmCpuResourceLimit(owner, vmInstance.isDisplay(), svcOffering, template, newCpu - currentCpu); + _resourceLimitMgr.incrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newCpu - currentCpu); + } else if (newCpu > 0 && currentCpu > newCpu){ + _resourceLimitMgr.decrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentCpu - newCpu); } if (newMemory > currentMemory) { - _resourceLimitMgr.checkVmMemoryResourceLimit(owner, vmInstance.isDisplay(), svcOffering, template, newMemory - currentMemory); + _resourceLimitMgr.incrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newMemory - currentMemory); + } else if (newMemory > 0 && currentMemory > newMemory){ + _resourceLimitMgr.decrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentMemory - newMemory); } } catch (ResourceAllocationException e) { logger.error(String.format("Failed to updated VM due to: %s", e.getLocalizedMessage())); throw new InvalidParameterValueException(e.getLocalizedMessage()); - } - - if (newCpu > currentCpu) { - _resourceLimitMgr.incrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newCpu - currentCpu); - } else if (newCpu > 0 && currentCpu > newCpu){ - _resourceLimitMgr.decrementVmCpuResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentCpu - newCpu); - } - if (newMemory > currentMemory) { - _resourceLimitMgr.incrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, newMemory - currentMemory); - } else if (newMemory > 0 && currentMemory > newMemory){ - _resourceLimitMgr.decrementVmMemoryResourceCount(owner.getAccountId(), vmInstance.isDisplay(), svcOffering, template, currentMemory - newMemory); + } finally { + ReservationHelper.closeAll(reservations); } } @@ -4236,7 +4254,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException(String.format("Invalid disk offering %s specified for datadisk Template %s. Disk offering size should be greater than or equal to the Template size", dataDiskOffering, dataDiskTemplate)); } _templateDao.loadDetails(dataDiskTemplate); - resourceLimitService.checkVolumeResourceLimit(owner, true, dataDiskOffering.getDiskSize(), dataDiskOffering); } } @@ -5660,11 +5677,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, resourceLimitHostTags, Long.valueOf(offering.getRamSize()), reservationDao, resourceLimitService); ) { return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin); - } catch (ResourceAllocationException | CloudRuntimeException e) { - throw e; - } catch (Exception e) { - logger.error("Failed to start VM {} : error during resource reservation and allocation", e); - throw new CloudRuntimeException(e); } } else { return startVirtualMachineUnchecked(vm, template, podId, clusterId, hostId, additionalParams, deploymentPlannerToUse, isExplicitHost, isRootAdmin); @@ -7387,38 +7399,29 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return findMigratedVm(vm.getId(), vm.getType()); } - protected void checkVolumesLimits(Account account, List volumes) throws ResourceAllocationException { - Long totalVolumes = 0L; - Long totalVolumesSize = 0L; + protected void checkVolumesLimits(Account account, List volumes, List reservations) throws ResourceAllocationException { Map> diskOfferingTagsMap = new HashMap<>(); - Map tagVolumeCountMap = new HashMap<>(); - Map tagSizeMap = new HashMap<>(); + for (VolumeVO volume : volumes) { if (!volume.isDisplay()) { continue; } - totalVolumes++; - totalVolumesSize += volume.getSize(); - if (!diskOfferingTagsMap.containsKey(volume.getDiskOfferingId())) { - diskOfferingTagsMap.put(volume.getDiskOfferingId(), _resourceLimitMgr.getResourceLimitStorageTags( - _diskOfferingDao.findById(volume.getDiskOfferingId()))); + + Long diskOfferingId = volume.getDiskOfferingId(); + if (!diskOfferingTagsMap.containsKey(diskOfferingId)) { + DiskOffering diskOffering = _diskOfferingDao.findById(diskOfferingId); + List tagsForDiskOffering = _resourceLimitMgr.getResourceLimitStorageTags(diskOffering); + diskOfferingTagsMap.put(diskOfferingId, tagsForDiskOffering); } - List tags = diskOfferingTagsMap.get(volume.getDiskOfferingId()); - for (String tag : tags) { - if (tagVolumeCountMap.containsKey(tag)) { - tagVolumeCountMap.put(tag, tagVolumeCountMap.get(tag) + 1); - tagSizeMap.put(tag, tagSizeMap.get(tag) + volume.getSize()); - } else { - tagVolumeCountMap.put(tag, 1L); - tagSizeMap.put(tag, volume.getSize()); - } - } - } - _resourceLimitMgr.checkResourceLimit(account, ResourceType.volume, totalVolumes); - _resourceLimitMgr.checkResourceLimit(account, ResourceType.primary_storage, totalVolumesSize); - for (String tag : tagVolumeCountMap.keySet()) { - resourceLimitService.checkResourceLimitWithTag(account, ResourceType.volume, tag, tagVolumeCountMap.get(tag)); - resourceLimitService.checkResourceLimitWithTag(account, ResourceType.primary_storage, tag, tagSizeMap.get(tag)); + + List tags = diskOfferingTagsMap.get(diskOfferingId); + + CheckedReservation volumeReservation = new CheckedReservation(account, ResourceType.volume, tags, 1L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + + long size = ObjectUtils.defaultIfNull(volume.getSize(), 0L); + CheckedReservation primaryStorageReservation = new CheckedReservation(account, ResourceType.primary_storage, tags, size, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); } } @@ -7460,14 +7463,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir final ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()); VirtualMachineTemplate template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - verifyResourceLimitsForAccountAndStorage(newAccount, vm, offering, volumes, template); - validateIfNewOwnerHasAccessToTemplate(vm, newAccount, template); DomainVO domain = _domainDao.findById(domainId); logger.trace("Verifying if the new account [{}] has access to the specified domain [{}].", newAccount, domain); _accountMgr.checkAccess(newAccount, domain); + List reservations = new ArrayList<>(); + try { + verifyResourceLimitsForAccountAndStorage(newAccount, vm, offering, volumes, template, reservations); + Network newNetwork = ensureDestinationNetwork(cmd, vm, newAccount); try { Transaction.execute(new TransactionCallbackNoReturn() { @@ -7484,6 +7489,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw e; } + } finally { + ReservationHelper.closeAll(reservations); + } + logger.info("VM [{}] now belongs to account [{}].", vm.getInstanceName(), newAccountName); return vm; } @@ -7554,18 +7563,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir * @param volumes The volumes whose total size can exceed resource limits. * @throws ResourceAllocationException */ - protected void verifyResourceLimitsForAccountAndStorage(Account account, UserVmVO vm, ServiceOfferingVO offering, List volumes, VirtualMachineTemplate template) + protected void verifyResourceLimitsForAccountAndStorage(Account account, UserVmVO vm, ServiceOfferingVO offering, List volumes, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException { logger.trace("Verifying if CPU and RAM for VM [{}] do not exceed account [{}] limit.", vm, account); if (!countOnlyRunningVmsInResourceLimitation()) { - resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), offering, template); + resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), offering, template, reservations); } logger.trace("Verifying if volume size for VM [{}] does not exceed account [{}] limit.", vm, account); - checkVolumesLimits(account, volumes); + checkVolumesLimits(account, volumes, reservations); } protected boolean countOnlyRunningVmsInResourceLimitation() { @@ -8415,12 +8424,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir VMTemplateVO template = getRestoreVirtualMachineTemplate(caller, newTemplateId, rootVols, vm); DiskOffering diskOffering = rootDiskOfferingId != null ? _diskOfferingDao.findById(rootDiskOfferingId) : null; + + List reservations = new ArrayList<>(); try { - checkRestoreVmFromTemplate(vm, template, rootVols, diskOffering, details); - } catch (ResourceAllocationException e) { - logger.error("Failed to restore VM {} due to {}", vm, e.getMessage(), e); - throw new CloudRuntimeException("Failed to restore VM " + vm.getUuid() + " due to " + e.getMessage(), e); - } + checkRestoreVmFromTemplate(vm, template, rootVols, diskOffering, details, reservations); if (needRestart) { try { @@ -8565,6 +8572,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir logger.debug("Restore VM {} done successfully", vm); return vm; + } catch (ResourceAllocationException e) { + logger.error("Failed to restore VM {} due to {}", vm, e.getMessage(), e); + throw new CloudRuntimeException("Failed to restore VM " + vm.getUuid() + " due to " + e.getMessage(), e); + } finally { + ReservationHelper.closeAll(reservations); + } } Long getRootVolumeSizeForVmRestore(Volume vol, VMTemplateVO template, UserVmVO userVm, DiskOffering diskOffering, Map details, boolean update) { @@ -8664,7 +8677,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir * @param template template * @throws InvalidParameterValueException if restore is not possible */ - private void checkRestoreVmFromTemplate(UserVmVO vm, VMTemplateVO template, List rootVolumes, DiskOffering newDiskOffering, Map details) throws ResourceAllocationException { + private void checkRestoreVmFromTemplate(UserVmVO vm, VMTemplateVO template, List rootVolumes, DiskOffering newDiskOffering, Map details, List reservations) throws ResourceAllocationException { TemplateDataStoreVO tmplStore; if (!template.isDirectDownload()) { tmplStore = _templateStoreDao.findByTemplateZoneReady(template.getId(), vm.getDataCenterId()); @@ -8682,7 +8695,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (vm.getTemplateId() != template.getId()) { ServiceOfferingVO serviceOffering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); VMTemplateVO currentTemplate = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - _resourceLimitMgr.checkVmResourceLimitsForTemplateChange(owner, vm.isDisplay(), serviceOffering, currentTemplate, template); + _resourceLimitMgr.checkVmResourceLimitsForTemplateChange(owner, vm.isDisplay(), serviceOffering, currentTemplate, template, reservations); } for (Volume vol : rootVolumes) { @@ -8693,7 +8706,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (newDiskOffering != null || !vol.getSize().equals(newSize)) { DiskOffering currentOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); _resourceLimitMgr.checkVolumeResourceLimitForDiskOfferingChange(owner, vol.isDisplay(), - vol.getSize(), newSize, currentOffering, newDiskOffering); + vol.getSize(), newSize, currentOffering, newDiskOffering, reservations); } } } diff --git a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java index cdbb7119e9e..8e7ce351246 100644 --- a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -811,25 +811,31 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme } /** - * If snapshot was taken with a different service offering than actual used in vm, should change it back to it - * @param userVm vm to change service offering (if necessary) + * If snapshot was taken with a different service offering than actual used in vm, should change it back to it. + * We also call changeUserVmServiceOffering in case the service offering is dynamic in order to + * perform resource limit validation, as the amount of CPUs or memory may have been changed. * @param vmSnapshotVo vm snapshot */ protected void updateUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) { if (vmSnapshotVo.getServiceOfferingId() != userVm.getServiceOfferingId()) { changeUserVmServiceOffering(userVm, vmSnapshotVo); + return; + } + ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(userVm.getServiceOfferingId()); + if (serviceOffering.isDynamic()) { + changeUserVmServiceOffering(userVm, vmSnapshotVo); } } /** * Get user vm details as a map - * @param userVm user vm + * @param vmSnapshotVo snapshot to get the details from * @return map */ - protected Map getVmMapDetails(UserVm userVm) { - List userVmDetails = _userVmDetailsDao.listDetails(userVm.getId()); + protected Map getVmMapDetails(VMSnapshotVO vmSnapshotVo) { + List vmSnapshotDetails = _vmSnapshotDetailsDao.listDetails(vmSnapshotVo.getId()); Map details = new HashMap(); - for (UserVmDetailVO detail : userVmDetails) { + for (VMSnapshotDetailsVO detail : vmSnapshotDetails) { details.put(detail.getName(), detail.getValue()); } return details; @@ -841,7 +847,7 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme * @param vmSnapshotVo vm snapshot */ protected void changeUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) { - Map vmDetails = getVmMapDetails(userVm); + Map vmDetails = getVmMapDetails(vmSnapshotVo); boolean result = upgradeUserVmServiceOffering(userVm, vmSnapshotVo.getServiceOfferingId(), vmDetails); if (! result){ throw new CloudRuntimeException("Instance Snapshot reverting failed because the Instance service offering couldn't be changed to the one used when Snapshot was taken"); @@ -938,8 +944,8 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException { - revertCustomServiceOfferingDetailsFromVmSnapshot(userVm, vmSnapshotVo); updateUserVmServiceOffering(userVm, vmSnapshotVo); + revertCustomServiceOfferingDetailsFromVmSnapshot(userVm, vmSnapshotVo); } }); return userVm; diff --git a/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java b/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java index 9e1fc46e02e..47aa57f728c 100644 --- a/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java @@ -35,6 +35,7 @@ import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.offering.DiskOffering; +import com.cloud.resourcelimit.ReservationHelper; import com.cloud.storage.DataStoreRole; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage; @@ -68,6 +69,7 @@ import org.apache.cloudstack.api.response.VolumeForImportResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; @@ -198,10 +200,7 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ logFailureAndThrowException("Volume is a reference of snapshot on primary: " + volume.getFullPath()); } - // 5. check resource limitation - checkResourceLimitForImportVolume(owner, volume); - - // 6. get disk offering + // 5. get disk offering DiskOfferingVO diskOffering = getOrCreateDiskOffering(owner, cmd.getDiskOfferingId(), pool.getDataCenterId(), pool.isLocal()); if (diskOffering.isCustomized()) { volumeApiService.validateCustomDiskOfferingSizeRange(volume.getVirtualSize() / ByteScaleUtils.GiB); @@ -210,6 +209,11 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ logFailureAndThrowException(String.format("Disk offering: %s storage tags are not compatible with selected storage pool: %s", diskOffering, pool)); } + List reservations = new ArrayList<>(); + try { + // 6. check resource limitation + checkResourceLimitForImportVolume(owner, volume, diskOffering, reservations); + // 7. create records String volumeName = StringUtils.isNotBlank(cmd.getName()) ? cmd.getName().trim() : volumePath; VolumeVO volumeVO = importVolumeInternal(volume, diskOffering, owner, pool, volumeName); @@ -221,6 +225,10 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ publicUsageEventForVolumeImportAndUnmanage(volumeVO, true); return responseGenerator.createVolumeResponse(ResponseObject.ResponseView.Full, volumeVO); + + } finally { + ReservationHelper.closeAll(reservations); + } } protected VolumeOnStorageTO getVolumeOnStorageAndCheck(StoragePoolVO pool, String volumePath) { @@ -456,11 +464,10 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ return volumeDao.findById(diskProfile.getVolumeId()); } - protected void checkResourceLimitForImportVolume(Account owner, VolumeOnStorageTO volume) { + protected void checkResourceLimitForImportVolume(Account owner, VolumeOnStorageTO volume, DiskOfferingVO diskOffering, List reservations) { Long volumeSize = volume.getVirtualSize(); try { - resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.volume); - resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.primary_storage, volumeSize); + resourceLimitService.checkVolumeResourceLimit(owner, true, volumeSize, diskOffering, reservations); } catch (ResourceAllocationException e) { logger.error("VM resource allocation error for account: {}", owner, e); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 0cf921f36be..1917804fb8d 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -86,6 +86,8 @@ import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.org.Cluster; import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceState; +import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.resourcelimit.ReservationHelper; import com.cloud.serializer.GsonHelper; import com.cloud.server.ManagementService; import com.cloud.service.ServiceOfferingVO; @@ -164,6 +166,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -222,6 +226,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { @Inject private ResourceLimitService resourceLimitService; @Inject + private ReservationDao reservationDao; + @Inject private UserVmDetailsDao userVmDetailsDao; @Inject private UserVmManager userVmManager; @@ -604,7 +610,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { return new Pair<>(rootDisk, dataDisks); } - private void checkUnmanagedDiskAndOfferingForImport(String instanceName, UnmanagedInstanceTO.Disk disk, DiskOffering diskOffering, ServiceOffering serviceOffering, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed) + private void checkUnmanagedDiskAndOfferingForImport(String instanceName, UnmanagedInstanceTO.Disk disk, DiskOffering diskOffering, ServiceOffering serviceOffering, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed, List reservations) throws ServerApiException, PermissionDeniedException, ResourceAllocationException { if (serviceOffering == null && diskOffering == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Disk offering for disk ID [%s] not found during VM [%s] import.", disk.getDiskId(), instanceName)); @@ -612,7 +618,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (diskOffering != null) { accountService.checkAccess(owner, diskOffering, zone); } - resourceLimitService.checkVolumeResourceLimit(owner, true, null, diskOffering); if (disk.getCapacity() == null || disk.getCapacity() == 0) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Size of disk(ID: %s) is found invalid during VM import", disk.getDiskId())); } @@ -627,9 +632,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (diskOffering != null && !migrateAllowed && !storagePoolSupportsDiskOffering(storagePool, diskOffering)) { throw new InvalidParameterValueException(String.format("Disk offering: %s is not compatible with storage pool: %s of unmanaged disk: %s", diskOffering.getUuid(), storagePool.getUuid(), disk.getDiskId())); } + resourceLimitService.checkVolumeResourceLimit(owner, true, disk.getCapacity(), diskOffering, reservations); } - private void checkUnmanagedDiskAndOfferingForImport(String intanceName, List disks, final Map diskOfferingMap, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed) + private void checkUnmanagedDiskAndOfferingForImport(String intanceName, List disks, final Map diskOfferingMap, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed, List reservations) throws ServerApiException, PermissionDeniedException, ResourceAllocationException { String diskController = null; for (UnmanagedInstanceTO.Disk disk : disks) { @@ -646,7 +652,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Multiple data disk controllers of different type (%s, %s) are not supported for import. Please make sure that all data disk controllers are of the same type", diskController, disk.getController())); } } - checkUnmanagedDiskAndOfferingForImport(intanceName, disk, diskOfferingDao.findById(diskOfferingMap.get(disk.getDiskId())), null, owner, zone, cluster, migrateAllowed); + checkUnmanagedDiskAndOfferingForImport(intanceName, disk, diskOfferingDao.findById(diskOfferingMap.get(disk.getDiskId())), null, owner, zone, cluster, migrateAllowed, reservations); } } @@ -1073,40 +1079,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } } - protected void checkUnmanagedDiskLimits(Account account, UnmanagedInstanceTO.Disk rootDisk, ServiceOffering serviceOffering, - List dataDisks, Map dataDiskOfferingMap) throws ResourceAllocationException { - Long totalVolumes = 0L; - Long totalVolumesSize = 0L; - List disks = new ArrayList<>(); - disks.add(rootDisk); - disks.addAll(dataDisks); - Map diskOfferingMap = new HashMap<>(dataDiskOfferingMap); - diskOfferingMap.put(rootDisk.getDiskId(), serviceOffering.getDiskOfferingId()); - Map diskOfferingVolumeCountMap = new HashMap<>(); - Map diskOfferingSizeMap = new HashMap<>(); - for (UnmanagedInstanceTO.Disk disk : disks) { - totalVolumes++; - totalVolumesSize += disk.getCapacity(); - Long diskOfferingId = diskOfferingMap.get(disk.getDiskId()); - if (diskOfferingVolumeCountMap.containsKey(diskOfferingId)) { - diskOfferingVolumeCountMap.put(diskOfferingId, diskOfferingVolumeCountMap.get(diskOfferingId) + 1); - diskOfferingSizeMap.put(diskOfferingId, diskOfferingSizeMap.get(diskOfferingId) + disk.getCapacity()); - } else { - diskOfferingVolumeCountMap.put(diskOfferingId, 1L); - diskOfferingSizeMap.put(diskOfferingId, disk.getCapacity()); - } - } - resourceLimitService.checkResourceLimit(account, Resource.ResourceType.volume, totalVolumes); - resourceLimitService.checkResourceLimit(account, Resource.ResourceType.primary_storage, totalVolumesSize); - for (Long diskOfferingId : diskOfferingVolumeCountMap.keySet()) { - List tags = resourceLimitService.getResourceLimitStorageTags(diskOfferingDao.findById(diskOfferingId)); - for (String tag : tags) { - resourceLimitService.checkResourceLimitWithTag(account, Resource.ResourceType.volume, tag, diskOfferingVolumeCountMap.get(diskOfferingId)); - resourceLimitService.checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, tag, diskOfferingSizeMap.get(diskOfferingId)); - } - } - } - private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedInstance, final String instanceNameInternal, final DataCenter zone, final Cluster cluster, final HostVO host, final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId, final ServiceOfferingVO serviceOffering, final Map dataDiskOfferingMap, @@ -1164,17 +1136,14 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { allDetails.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(size)); } + List reservations = new ArrayList<>(); try { - checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), rootDisk, null, validatedServiceOffering, owner, zone, cluster, migrateAllowed); - if (CollectionUtils.isNotEmpty(dataDisks)) { // Data disk(s) present - checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), dataDisks, dataDiskOfferingMap, owner, zone, cluster, migrateAllowed); - allDetails.put(VmDetailConstants.DATA_DISK_CONTROLLER, dataDisks.get(0).getController()); - } - checkUnmanagedDiskLimits(owner, rootDisk, serviceOffering, dataDisks, dataDiskOfferingMap); - } catch (ResourceAllocationException e) { - logger.error("Volume resource allocation error for owner: {}", owner, e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resource allocation error for owner: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), rootDisk, null, validatedServiceOffering, owner, zone, cluster, migrateAllowed, reservations); + if (CollectionUtils.isNotEmpty(dataDisks)) { // Data disk(s) present + checkUnmanagedDiskAndOfferingForImport(unmanagedInstance.getName(), dataDisks, dataDiskOfferingMap, owner, zone, cluster, migrateAllowed, reservations); + allDetails.put(VmDetailConstants.DATA_DISK_CONTROLLER, dataDisks.get(0).getController()); } + // Check NICs and supplied networks Map nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap); Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, cluster.getHypervisorType()); @@ -1257,6 +1226,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } publishVMUsageUpdateResourceCount(userVm, validatedServiceOffering, template); return userVm; + + } catch (ResourceAllocationException e) { // This will be thrown by checkUnmanagedDiskAndOfferingForImport, so the VM was not imported yet + logger.error("Volume resource allocation error for owner: {}", owner, e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resource allocation error for owner: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + } finally { + ReservationHelper.closeAll(reservations); + } } private void addImportingVMBootTypeAndModeDetails(String bootType, String bootMode, Map allDetails) { @@ -1361,8 +1337,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { VMTemplateVO template = getTemplateForImportInstance(cmd.getTemplateId(), cluster.getHypervisorType()); ServiceOfferingVO serviceOffering = getServiceOfferingForImportInstance(cmd.getServiceOfferingId(), owner, zone); - checkResourceLimitForImportInstance(owner); - String displayName = getDisplayNameForImportInstance(cmd.getDisplayName(), instanceName); String hostName = getHostNameForImportInstance(cmd.getHostName(), cluster.getHypervisorType(), instanceName, displayName); @@ -1378,6 +1352,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List managedVms = new ArrayList<>(additionalNameFilters); managedVms.addAll(getHostsManagedVms(hosts)); + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); + try (CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); + CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, Long.valueOf(serviceOffering.getCpu()), reservationDao, resourceLimitService); + CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, Long.valueOf(serviceOffering.getRamSize()), reservationDao, resourceLimitService)) { + ActionEventUtils.onStartedActionEvent(userId, owner.getId(), EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, true, 0); @@ -1405,6 +1384,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } } + } catch (ResourceAllocationException e) { + logger.error(String.format("VM resource allocation error for account: %s", owner.getUuid()), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + } + if (userVm == null) { ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, 0); @@ -1464,15 +1448,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { return StringUtils.isEmpty(displayName) ? instanceName : displayName; } - private void checkResourceLimitForImportInstance(Account owner) { - try { - resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.user_vm, 1); - } catch (ResourceAllocationException e) { - logger.error("VM resource allocation error for account: {}", owner, e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); - } - } - private ServiceOfferingVO getServiceOfferingForImportInstance(Long serviceOfferingId, Account owner, DataCenter zone) { if (serviceOfferingId == null) { throw new InvalidParameterValueException("Service offering ID cannot be null"); @@ -2338,12 +2313,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new InvalidParameterValueException(String.format("Service offering ID: %d cannot be found", serviceOfferingId)); } accountService.checkAccess(owner, serviceOffering, zone); - try { - resourceLimitService.checkResourceLimit(owner, Resource.ResourceType.user_vm, 1); - } catch (ResourceAllocationException e) { - logger.error("VM resource allocation error for account: {}", owner, e); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); - } String displayName = cmd.getDisplayName(); if (StringUtils.isEmpty(displayName)) { displayName = instanceName; @@ -2431,6 +2400,11 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { UserVm userVm = null; + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); + try (CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); + CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, Long.valueOf(serviceOffering.getCpu()), reservationDao, resourceLimitService); + CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, Long.valueOf(serviceOffering.getRamSize()), reservationDao, resourceLimitService)) { + if (ImportSource.EXTERNAL == importSource) { String username = cmd.getUsername(); String password = cmd.getPassword(); @@ -2451,6 +2425,12 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new RuntimeException(e); } } + + } catch (ResourceAllocationException e) { + logger.error(String.format("VM resource allocation error for account: %s", owner.getUuid()), e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM resource allocation error for account: %s. %s", owner.getUuid(), StringUtils.defaultString(e.getMessage()))); + } + if (userVm == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import Vm with name: %s ", instanceName)); } @@ -2464,7 +2444,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { final VirtualMachineTemplate template, final String displayName, final String hostName, final Account caller, final Account owner, final Long userId, final ServiceOfferingVO serviceOffering, final Map dataDiskOfferingMap, final Map nicNetworkMap, final Map callerNicIpAddressMap, - final String remoteUrl, String username, String password, String tmpPath, final Map details) { + final String remoteUrl, String username, String password, String tmpPath, final Map details) throws ResourceAllocationException { UserVm userVm = null; Map allDetails = new HashMap<>(details); @@ -2475,6 +2455,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("No attached disks found for the unmanaged VM: %s", instanceName)); } + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); Pair> rootAndDataDisksPair = getRootAndDataDisks(unmanagedInstanceDisks, dataDiskOfferingMap); final UnmanagedInstanceTO.Disk rootDisk = rootAndDataDisksPair.first(); final List dataDisks = rootAndDataDisksPair.second(); @@ -2483,6 +2464,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } allDetails.put(VmDetailConstants.ROOT_DISK_CONTROLLER, rootDisk.getController()); + List reservations = new ArrayList<>(); + try { + checkVolumeResourceLimitsForExternalKvmVmImport(owner, rootDisk, dataDisks, diskOffering, dataDiskOfferingMap, reservations); + // Check NICs and supplied networks Map nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap); Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, Hypervisor.HypervisorType.KVM); @@ -2503,16 +2488,12 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (userVm == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); } - DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); String rootVolumeName = String.format("ROOT-%s", userVm.getId()); DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null); DiskProfile[] dataDiskProfiles = new DiskProfile[dataDisks.size()]; int diskSeq = 0; for (UnmanagedInstanceTO.Disk disk : dataDisks) { - if (disk.getCapacity() == null || disk.getCapacity() == 0) { - throw new InvalidParameterValueException(String.format("Disk ID: %s size is invalid", disk.getDiskId())); - } DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); DiskProfile dataDiskProfile = volumeManager.allocateRawVolume(Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), offering, null, null, null, userVm, template, owner, null); dataDiskProfiles[diskSeq++] = dataDiskProfile; @@ -2537,10 +2518,6 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List> diskProfileStoragePoolList = new ArrayList<>(); try { - if (rootDisk.getCapacity() == null || rootDisk.getCapacity() == 0) { - throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId())); - } - diskProfileStoragePoolList.add(importExternalDisk(rootDisk, userVm, dest, diskOffering, Volume.Type.ROOT, template, null, remoteUrl, username, password, tmpPath, diskProfile)); @@ -2574,6 +2551,30 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); return userVm; + + } finally { + ReservationHelper.closeAll(reservations); + } + } + + protected void checkVolumeResourceLimitsForExternalKvmVmImport(Account owner, UnmanagedInstanceTO.Disk rootDisk, + List dataDisks, DiskOfferingVO rootDiskOffering, + Map dataDiskOfferingMap, List reservations) throws ResourceAllocationException { + if (rootDisk.getCapacity() == null || rootDisk.getCapacity() == 0) { + throw new InvalidParameterValueException(String.format("Root disk ID: %s size is invalid", rootDisk.getDiskId())); + } + resourceLimitService.checkVolumeResourceLimit(owner, true, rootDisk.getCapacity(), rootDiskOffering, reservations); + + if (CollectionUtils.isEmpty(dataDisks)) { + return; + } + for (UnmanagedInstanceTO.Disk disk : dataDisks) { + if (disk.getCapacity() == null || disk.getCapacity() == 0) { + throw new InvalidParameterValueException(String.format("Data disk ID: %s size is invalid", disk.getDiskId())); + } + DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); + resourceLimitService.checkVolumeResourceLimit(owner, true, disk.getCapacity(), offering, reservations); + } } private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, final String instanceName, final DataCenter zone, @@ -2641,7 +2642,16 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (userVm == null) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); } + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + + List reservations = new ArrayList<>(); + List resourceLimitStorageTags = resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); + try { + CheckedReservation volumeReservation = new CheckedReservation(owner, Resource.ResourceType.volume, resourceLimitStorageTags, + CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? 1L : 0L, reservationDao, resourceLimitService); + reservations.add(volumeReservation); + String rootVolumeName = String.format("ROOT-%s", userVm.getId()); DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null); @@ -2686,6 +2696,14 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new CloudRuntimeException("Disk not found or is invalid"); } diskProfile.setSize(checkVolumeAnswer.getSize()); + try { + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.primary_storage, resourceLimitStorageTags, + CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? diskProfile.getSize() : 0L, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); + } catch (ResourceAllocationException e) { + cleanupFailedImportVM(userVm); + throw e; + } List> diskProfileStoragePoolList = new ArrayList<>(); try { @@ -2705,6 +2723,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { networkOrchestrationService.importNic(macAddress, 0, network, true, userVm, requestedIpPair, zone, true); publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); return userVm; + + } finally { + ReservationHelper.closeAll(reservations); + } } private void checkVolume(Map volumeDetails) { diff --git a/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java b/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java index e3bfdc63635..bdc6620c490 100644 --- a/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java +++ b/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.TaggedResourceLimitAndCountResponse; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.reservation.dao.ReservationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -40,6 +41,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; @@ -264,6 +266,7 @@ public class ResourceLimitManagerImplTest extends TestCase { @Test public void testCheckVmResourceLimit() { + List reservations = new ArrayList<>(); ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class); VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); Mockito.when(serviceOffering.getHostTag()).thenReturn(hostTags.get(0)); @@ -271,53 +274,12 @@ public class ResourceLimitManagerImplTest extends TestCase { Mockito.when(serviceOffering.getRamSize()).thenReturn(256); Mockito.when(template.getTemplateTag()).thenReturn(hostTags.get(0)); Account account = Mockito.mock(Account.class); - try { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); - resourceLimitManager.checkVmResourceLimit(account, true, serviceOffering, template); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + resourceLimitManager.checkVmResourceLimit(account, true, serviceOffering, template, reservations); List tags = new ArrayList<>(); tags.add(null); tags.add(hostTags.get(0)); - for (String tag: tags) { - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.user_vm, tag); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.cpu, tag, 2L); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.memory, tag, 256L); - } - } catch (ResourceAllocationException e) { - Assert.fail("Exception encountered: " + e.getMessage()); - } - } - - @Test - public void testCheckVmCpuResourceLimit() { - ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class); - VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); - Mockito.when(serviceOffering.getHostTag()).thenReturn(hostTags.get(0)); - Mockito.when(template.getTemplateTag()).thenReturn(hostTags.get(0)); - Account account = Mockito.mock(Account.class); - long cpu = 2L; - try { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); - resourceLimitManager.checkVmCpuResourceLimit(account, true, serviceOffering, template, cpu); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.cpu, null, cpu); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.cpu, hostTags.get(0), cpu); - } catch (ResourceAllocationException e) { - Assert.fail("Exception encountered: " + e.getMessage()); - } - } - - @Test - public void testCheckVmMemoryResourceLimit() { - ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class); - VirtualMachineTemplate template = Mockito.mock(VirtualMachineTemplate.class); - Mockito.when(serviceOffering.getHostTag()).thenReturn(hostTags.get(0)); - Mockito.when(template.getTemplateTag()).thenReturn(hostTags.get(0)); - Account account = Mockito.mock(Account.class); - long delta = 256L; - try { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); - resourceLimitManager.checkVmMemoryResourceLimit(account, true, serviceOffering, template, delta); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.memory, null, delta); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.memory, hostTags.get(0), delta); + Assert.assertEquals(3, mockCheckedReservation.constructed().size()); } catch (ResourceAllocationException e) { Assert.fail("Exception encountered: " + e.getMessage()); } @@ -325,21 +287,15 @@ public class ResourceLimitManagerImplTest extends TestCase { @Test public void testCheckVolumeResourceLimit() { + List reservations = new ArrayList<>(); String checkTag = storageTags.get(0); DiskOffering diskOffering = Mockito.mock(DiskOffering.class); Mockito.when(diskOffering.getTags()).thenReturn(checkTag); Mockito.when(diskOffering.getTagsArray()).thenReturn(new String[]{checkTag}); Account account = Mockito.mock(Account.class); - try { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); - resourceLimitManager.checkVolumeResourceLimit(account, true, 100L, diskOffering); - List tags = new ArrayList<>(); - tags.add(null); - tags.add(checkTag); - for (String tag: tags) { - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.volume, tag); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, tag, 100L); - } + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + resourceLimitManager.checkVolumeResourceLimit(account, true, 100L, diskOffering, reservations); + Assert.assertEquals(2, reservations.size()); } catch (ResourceAllocationException e) { Assert.fail("Exception encountered: " + e.getMessage()); } @@ -934,13 +890,6 @@ public class ResourceLimitManagerImplTest extends TestCase { Mockito.anyList(), Mockito.eq(tag)); } - private void mockCheckResourceLimitWithTag() throws ResourceAllocationException { - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag( - Mockito.any(Account.class), Mockito.any(Resource.ResourceType.class), Mockito.anyString()); - Mockito.doNothing().when(resourceLimitManager).checkResourceLimitWithTag( - Mockito.any(Account.class), Mockito.any(Resource.ResourceType.class), Mockito.anyString(), Mockito.anyLong()); - } - private void mockIncrementResourceCountWithTag() { Mockito.doNothing().when(resourceLimitManager).incrementResourceCountWithTag( Mockito.anyLong(), Mockito.any(Resource.ResourceType.class), Mockito.anyString()); @@ -957,6 +906,7 @@ public class ResourceLimitManagerImplTest extends TestCase { @Test public void testCheckVolumeResourceCount() throws ResourceAllocationException { + List reservations = new ArrayList<>(); Account account = Mockito.mock(Account.class); String tag = "tag"; long delta = 10L; @@ -970,12 +920,11 @@ public class ResourceLimitManagerImplTest extends TestCase { Mockito.doReturn(List.of(tag)).when(resourceLimitManager) .getResourceLimitStorageTagsForResourceCountOperation(Mockito.anyBoolean(), Mockito.any(DiskOffering.class)); - mockCheckResourceLimitWithTag(); - resourceLimitManager.checkVolumeResourceLimit(account, false, delta, Mockito.mock(DiskOffering.class)); - Mockito.verify(resourceLimitManager, Mockito.times(1)).checkResourceLimitWithTag( - account, Resource.ResourceType.volume, tag); - Mockito.verify(resourceLimitManager, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, tag, 10L); + + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + resourceLimitManager.checkVolumeResourceLimit(account, false, delta, Mockito.mock(DiskOffering.class), reservations); + Assert.assertEquals(2, reservations.size()); + } } @Test diff --git a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java index 6c25e69876f..5a81d7d8ce3 100644 --- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java @@ -26,7 +26,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -41,6 +40,7 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.user.volume.CheckAndRepairVolumeCmd; @@ -83,6 +83,7 @@ import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.Spy; @@ -91,7 +92,6 @@ import org.springframework.test.util.ReflectionTestUtils; import com.cloud.api.query.dao.ServiceOfferingJoinDao; import com.cloud.configuration.ConfigurationManager; -import com.cloud.configuration.Resource; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenterVO; @@ -545,7 +545,9 @@ public class VolumeApiServiceImplTest { @Test public void attachRootVolumePositive() throws NoSuchFieldException, IllegalAccessException { thrown.expect(NullPointerException.class); - volumeApiServiceImpl.attachVolumeToVM(2L, 6L, 0L, false); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + volumeApiServiceImpl.attachVolumeToVM(2L, 6L, 0L, false); + } } // Negative test - attach data volume, to the vm on non-kvm hypervisor @@ -564,7 +566,9 @@ public class VolumeApiServiceImplTest { DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class); when(diskOffering.getEncrypt()).thenReturn(true); when(_diskOfferingDao.findById(anyLong())).thenReturn(diskOffering); - volumeApiServiceImpl.attachVolumeToVM(4L, 10L, 1L, false); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + volumeApiServiceImpl.attachVolumeToVM(4L, 10L, 1L, false); + } } // volume not Ready @@ -649,9 +653,7 @@ public class VolumeApiServiceImplTest { * The resource limit check for primary storage should not be skipped for Volume in 'Uploaded' state. */ @Test - public void testResourceLimitCheckForUploadedVolume() throws NoSuchFieldException, IllegalAccessException, ResourceAllocationException { - doThrow(new ResourceAllocationException("primary storage resource limit check failed", Resource.ResourceType.primary_storage)).when(resourceLimitServiceMock) - .checkResourceLimit(any(AccountVO.class), any(Resource.ResourceType.class), any(Long.class)); + public void testAttachVolumeToVMPerformsResourceReservation() throws NoSuchFieldException, IllegalAccessException, ResourceAllocationException { UserVmVO vm = Mockito.mock(UserVmVO.class); AccountVO acc = Mockito.mock(AccountVO.class); VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); @@ -672,10 +674,10 @@ public class VolumeApiServiceImplTest { DataCenterVO zoneWithDisabledLocalStorage = Mockito.mock(DataCenterVO.class); when(_dcDao.findById(anyLong())).thenReturn(zoneWithDisabledLocalStorage); when(zoneWithDisabledLocalStorage.isLocalStorageEnabled()).thenReturn(true); - try { + doReturn(volumeVoMock).when(volumeApiServiceImpl).getVolumeAttachJobResult(Mockito.any(), Mockito.any(), Mockito.any()); + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { volumeApiServiceImpl.attachVolumeToVM(2L, 9L, null, false); - } catch (InvalidParameterValueException e) { - Assert.assertEquals(e.getMessage(), ("primary storage resource limit check failed")); + Assert.assertEquals(1, mockCheckedReservation.constructed().size()); } } @@ -2199,4 +2201,34 @@ public class VolumeApiServiceImplTest { Assert.fail(); } } + + @Test + public void getRequiredPrimaryStorageSizeForVolumeAttachTestTagsAreEmptyReturnsZero() { + List tags = new ArrayList<>(); + + Long result = volumeApiServiceImpl.getRequiredPrimaryStorageSizeForVolumeAttach(tags, volumeInfoMock); + + Assert.assertEquals(0L, (long) result); + } + + @Test + public void getRequiredPrimaryStorageSizeForVolumeAttachTestVolumeIsReadyReturnsZero() { + List tags = List.of("tag1", "tag2"); + Mockito.doReturn(Volume.State.Ready).when(volumeInfoMock).getState(); + + Long result = volumeApiServiceImpl.getRequiredPrimaryStorageSizeForVolumeAttach(tags, volumeInfoMock); + + Assert.assertEquals(0L, (long) result); + } + + @Test + public void getRequiredPrimaryStorageSizeForVolumeAttachTestTagsAreNotEmptyAndVolumeIsUploadedReturnsVolumeSize() { + List tags = List.of("tag1", "tag2"); + Mockito.doReturn(Volume.State.Uploaded).when(volumeInfoMock).getState(); + Mockito.doReturn(2L).when(volumeInfoMock).getSize(); + + Long result = volumeApiServiceImpl.getRequiredPrimaryStorageSizeForVolumeAttach(tags, volumeInfoMock); + + Assert.assertEquals(2L, (long) result); + } } diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index 06fb65921c3..3ef304f4ec7 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Map; import com.cloud.network.NetworkService; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.BaseCmd.HTTPMethod; @@ -54,6 +55,7 @@ import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd; import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.template.VnfTemplateManager; @@ -65,6 +67,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; @@ -609,7 +612,6 @@ public class UserVmManagerImplTest { Mockito.doNothing().when(userVmManagerImpl).validateOldAndNewAccounts(Mockito.nullable(Account.class), Mockito.nullable(Account.class), Mockito.anyLong(), Mockito.nullable(String.class), Mockito.nullable(Long.class)); Mockito.doNothing().when(userVmManagerImpl).validateIfVmHasNoRules(Mockito.any(), Mockito.anyLong()); Mockito.doNothing().when(userVmManagerImpl).removeInstanceFromInstanceGroup(Mockito.anyLong()); - Mockito.doNothing().when(userVmManagerImpl).verifyResourceLimitsForAccountAndStorage(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyList(), Mockito.any()); Mockito.doNothing().when(userVmManagerImpl).validateIfNewOwnerHasAccessToTemplate(Mockito.any(), Mockito.any(), Mockito.any()); Mockito.doNothing().when(userVmManagerImpl).updateVmOwner(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); @@ -1615,23 +1617,11 @@ public class UserVmManagerImplTest { Mockito.when(vol5.isDisplay()).thenReturn(true); List volumes = List.of(vol1, undisplayedVolume, vol3, vol4, vol5); - Long size = volumes.stream().filter(VolumeVO::isDisplay).mapToLong(VolumeVO::getSize).sum(); - try { - userVmManagerImpl.checkVolumesLimits(account, volumes); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimit(account, Resource.ResourceType.volume, 4); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimit(account, Resource.ResourceType.primary_storage, size); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.volume, "tag1", 2); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.volume, "tag2", 3); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, "tag1", - vol1.getSize() + vol5.getSize()); - Mockito.verify(resourceLimitMgr, Mockito.times(1)) - .checkResourceLimitWithTag(account, Resource.ResourceType.primary_storage, "tag2", - vol1.getSize() + vol3.getSize() + vol5.getSize()); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { + userVmManagerImpl.checkVolumesLimits(account, volumes, reservations); + Assert.assertEquals(8, reservations.size()); } catch (ResourceAllocationException e) { Assert.fail(e.getMessage()); } @@ -1922,26 +1912,26 @@ public class UserVmManagerImplTest { @Test public void verifyResourceLimitsForAccountAndStorageTestCountOnlyRunningVmsInResourceLimitationIsTrueDoesNotCallVmResourceLimitCheck() throws ResourceAllocationException { + List reservations = new ArrayList<>(); LinkedList volumeVoList = new LinkedList(); Mockito.doReturn(true).when(userVmManagerImpl).countOnlyRunningVmsInResourceLimitation(); - userVmManagerImpl.verifyResourceLimitsForAccountAndStorage(accountMock, userVmVoMock, serviceOfferingVoMock, volumeVoList, virtualMachineTemplateMock); + userVmManagerImpl.verifyResourceLimitsForAccountAndStorage(accountMock, userVmVoMock, serviceOfferingVoMock, volumeVoList, virtualMachineTemplateMock, reservations); - Mockito.verify(resourceLimitMgr, Mockito.never()).checkVmResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any()); - Mockito.verify(resourceLimitMgr).checkResourceLimit(accountMock, Resource.ResourceType.volume, 0l); - Mockito.verify(resourceLimitMgr).checkResourceLimit(accountMock, Resource.ResourceType.primary_storage, 0l); + Mockito.verify(resourceLimitMgr, Mockito.never()).checkVmResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()); + Mockito.verify(resourceLimitMgr, Mockito.never()).checkVolumeResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()); } @Test public void verifyResourceLimitsForAccountAndStorageTestCountOnlyRunningVmsInResourceLimitationIsFalseCallsVmResourceLimitCheck() throws ResourceAllocationException { + List reservations = new ArrayList<>(); LinkedList volumeVoList = new LinkedList(); Mockito.doReturn(false).when(userVmManagerImpl).countOnlyRunningVmsInResourceLimitation(); - userVmManagerImpl.verifyResourceLimitsForAccountAndStorage(accountMock, userVmVoMock, serviceOfferingVoMock, volumeVoList, virtualMachineTemplateMock); + userVmManagerImpl.verifyResourceLimitsForAccountAndStorage(accountMock, userVmVoMock, serviceOfferingVoMock, volumeVoList, virtualMachineTemplateMock, reservations); - Mockito.verify(resourceLimitMgr).checkVmResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any()); - Mockito.verify(resourceLimitMgr).checkResourceLimit(accountMock, Resource.ResourceType.volume, 0l); - Mockito.verify(resourceLimitMgr).checkResourceLimit(accountMock, Resource.ResourceType.primary_storage, 0l); + Mockito.verify(resourceLimitMgr).checkVmResourceLimit(Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()); + Mockito.verify(userVmManagerImpl).checkVolumesLimits(Mockito.any(), Mockito.any(), Mockito.any()); } @Test @@ -2986,7 +2976,7 @@ public class UserVmManagerImplTest { configureDoNothingForMethodsThatWeDoNotWantToTest(); Mockito.doThrow(ResourceAllocationException.class).when(userVmManagerImpl).verifyResourceLimitsForAccountAndStorage(Mockito.any(), Mockito.any(), - Mockito.any(), Mockito.any(), Mockito.any()); + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); Assert.assertThrows(ResourceAllocationException.class, () -> userVmManagerImpl.moveVmToUser(assignVmCmdMock)); } diff --git a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java index 8d48fc4dac5..01512b448b2 100644 --- a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java +++ b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java @@ -357,13 +357,13 @@ public class VMSnapshotManagerTest { _vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO); verify(_vmSnapshotMgr).changeUserVmServiceOffering(userVm, vmSnapshotVO); - verify(_vmSnapshotMgr).getVmMapDetails(userVm); + verify(_vmSnapshotMgr).getVmMapDetails(vmSnapshotVO); verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture()); } @Test public void testGetVmMapDetails() { - Map result = _vmSnapshotMgr.getVmMapDetails(userVm); + Map result = _vmSnapshotMgr.getVmMapDetails(vmSnapshotVO); assert(result.containsKey(userVmDetailCpuNumber.getName())); assert(result.containsKey(userVmDetailMemory.getName())); assertEquals(userVmDetails.size(), result.size()); @@ -375,7 +375,7 @@ public class VMSnapshotManagerTest { public void testChangeUserVmServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException { when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true); _vmSnapshotMgr.changeUserVmServiceOffering(userVm, vmSnapshotVO); - verify(_vmSnapshotMgr).getVmMapDetails(userVm); + verify(_vmSnapshotMgr).getVmMapDetails(vmSnapshotVO); verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture()); } @@ -383,7 +383,7 @@ public class VMSnapshotManagerTest { public void testChangeUserVmServiceOfferingFailOnUpgradeVMServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException { when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(false); _vmSnapshotMgr.changeUserVmServiceOffering(userVm, vmSnapshotVO); - verify(_vmSnapshotMgr).getVmMapDetails(userVm); + verify(_vmSnapshotMgr).getVmMapDetails(vmSnapshotVO); verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture()); } diff --git a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java index 045f2178529..2ac4d6c862c 100644 --- a/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockResourceLimitManagerImpl.java @@ -24,6 +24,7 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.resourcelimit.Reserver; import org.springframework.stereotype.Component; import com.cloud.configuration.Resource.ResourceType; @@ -273,7 +274,7 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } @Override - public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException { + public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering, List reservations) throws ResourceAllocationException { } @@ -284,13 +285,13 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc @Override public void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize, - DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException { + DiskOffering currentOffering, DiskOffering newOffering, List reservations) throws ResourceAllocationException { } @Override public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, - DiskOffering diskOffering) { + DiskOffering diskOffering, List reservations) { } @@ -334,7 +335,7 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } @Override - public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException { + public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, List reservations) throws ResourceAllocationException { } @@ -351,19 +352,14 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc @Override public void checkVmResourceLimitsForServiceOfferingChange(Account owner, Boolean display, Long currentCpu, Long newCpu, Long currentMemory, Long newMemory, ServiceOffering currentOffering, ServiceOffering newOffering, - VirtualMachineTemplate template) throws ResourceAllocationException { + VirtualMachineTemplate template, List reservations) throws ResourceAllocationException { } @Override public void checkVmResourceLimitsForTemplateChange(Account owner, Boolean display, ServiceOffering offering, VirtualMachineTemplate currentTemplate, - VirtualMachineTemplate newTemplate) throws ResourceAllocationException { - - } - - @Override - public void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException { + VirtualMachineTemplate newTemplate, List reservations) throws ResourceAllocationException { } @@ -377,11 +373,6 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc } - @Override - public void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException { - - } - @Override public void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) { diff --git a/server/src/test/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImplTest.java index 419acc0ca0b..f3947a75e6e 100644 --- a/server/src/test/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImplTest.java @@ -268,9 +268,6 @@ public class VolumeImportUnmanageManagerImplTest { doNothing().when(volumeImportUnmanageManager).checkIfVolumeIsEncrypted(volumeOnStorageTO); doNothing().when(volumeImportUnmanageManager).checkIfVolumeHasBackingFile(volumeOnStorageTO); - doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.volume); - doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.primary_storage, virtualSize); - DiskOfferingVO diskOffering = mock(DiskOfferingVO.class); when(diskOffering.isCustomized()).thenReturn(true); doReturn(diskOffering).when(volumeImportUnmanageManager).getOrCreateDiskOffering(account, diskOfferingId, zoneId, isLocal); diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 09f62f7a049..282f3fb3a70 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.UUID; import com.cloud.offering.DiskOffering; +import com.cloud.resourcelimit.CheckedReservation; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.ServerApiException; @@ -67,6 +68,7 @@ import org.junit.runner.RunWith; import org.mockito.BDDMockito; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -89,7 +91,6 @@ import com.cloud.agent.api.GetUnmanagedInstancesCommand; import com.cloud.agent.api.ImportConvertedInstanceAnswer; import com.cloud.agent.api.ImportConvertedInstanceCommand; import com.cloud.agent.api.to.DataStoreTO; -import com.cloud.configuration.Resource; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; @@ -106,7 +107,6 @@ import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.PermissionDeniedException; -import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.UnsupportedServiceException; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -282,7 +282,6 @@ public class UnmanagedVMsManagerImplTest { clusterVO.setHypervisorType(Hypervisor.HypervisorType.VMware.toString()); when(clusterDao.findById(anyLong())).thenReturn(clusterVO); when(configurationDao.getValue(Mockito.anyString())).thenReturn(null); - doNothing().when(resourceLimitService).checkResourceLimit(any(Account.class), any(Resource.ResourceType.class), anyLong()); List hosts = new ArrayList<>(); HostVO hostVO = Mockito.mock(HostVO.class); when(hostVO.isInMaintenanceStates()).thenReturn(false); @@ -422,7 +421,8 @@ public class UnmanagedVMsManagerImplTest { when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance"); when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null); when(volumeApiService.doesStoragePoolSupportDiskOffering(any(StoragePool.class), any())).thenReturn(true); - try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { + try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class); + MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd); } } @@ -520,7 +520,8 @@ public class UnmanagedVMsManagerImplTest { CopyRemoteVolumeAnswer copyAnswer = Mockito.mock(CopyRemoteVolumeAnswer.class); when(copyAnswer.getResult()).thenReturn(true); when(agentManager.easySend(anyLong(), any(CopyRemoteVolumeCommand.class))).thenReturn(copyAnswer); - try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { + try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class); + MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { unmanagedVMsManager.importVm(cmd); } } @@ -707,7 +708,8 @@ public class UnmanagedVMsManagerImplTest { Mockito.lenient().when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(ImportConvertedInstanceCommand.class))).thenReturn(convertImportedInstanceAnswer); } - try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { + try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class); + MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { unmanagedVMsManager.importVm(importVmCmd); verify(vmwareGuru).getHypervisorVMOutOfBandAndCloneIfRequired(Mockito.eq(host), Mockito.eq(vmName), anyMap()); verify(vmwareGuru).createVMTemplateOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class), anyInt()); @@ -760,7 +762,8 @@ public class UnmanagedVMsManagerImplTest { when(volumeApiService.doesStoragePoolSupportDiskOffering(any(StoragePool.class), any())).thenReturn(true); StoragePoolHostVO storagePoolHost = Mockito.mock(StoragePoolHostVO.class); when(storagePoolHostDao.findByPoolHost(anyLong(), anyLong())).thenReturn(storagePoolHost); - try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { + try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class); + MockedConstruction mockCheckedReservation = Mockito.mockConstruction(CheckedReservation.class)) { unmanagedVMsManager.importVm(cmd); } } @@ -1107,40 +1110,4 @@ public class UnmanagedVMsManagerImplTest { unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId); } - - @Test - public void testCheckUnmanagedDiskLimits() { - Account owner = Mockito.mock(Account.class); - UnmanagedInstanceTO.Disk disk = Mockito.mock(UnmanagedInstanceTO.Disk.class); - Mockito.when(disk.getDiskId()).thenReturn("disk1"); - Mockito.when(disk.getCapacity()).thenReturn(100L); - ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class); - Mockito.when(serviceOffering.getDiskOfferingId()).thenReturn(1L); - UnmanagedInstanceTO.Disk dataDisk = Mockito.mock(UnmanagedInstanceTO.Disk.class); - Mockito.when(dataDisk.getDiskId()).thenReturn("disk2"); - Mockito.when(dataDisk.getCapacity()).thenReturn(1000L); - Map dataDiskMap = new HashMap<>(); - dataDiskMap.put("disk2", 2L); - DiskOfferingVO offering1 = Mockito.mock(DiskOfferingVO.class); - Mockito.when(diskOfferingDao.findById(1L)).thenReturn(offering1); - String tag1 = "tag1"; - Mockito.when(resourceLimitService.getResourceLimitStorageTags(offering1)).thenReturn(List.of(tag1)); - DiskOfferingVO offering2 = Mockito.mock(DiskOfferingVO.class); - Mockito.when(diskOfferingDao.findById(2L)).thenReturn(offering2); - String tag2 = "tag2"; - Mockito.when(resourceLimitService.getResourceLimitStorageTags(offering2)).thenReturn(List.of(tag2)); - try { - Mockito.doNothing().when(resourceLimitService).checkResourceLimit(any(), any(), any()); - Mockito.doNothing().when(resourceLimitService).checkResourceLimitWithTag(any(), any(), any(), any()); - unmanagedVMsManager.checkUnmanagedDiskLimits(owner, disk, serviceOffering, List.of(dataDisk), dataDiskMap); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimit(owner, Resource.ResourceType.volume, 2); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimit(owner, Resource.ResourceType.primary_storage, 1100L); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.volume, tag1,1); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.volume, tag2,1); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.primary_storage, tag1,100L); - Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.primary_storage, tag2,1000L); - } catch (ResourceAllocationException e) { - Assert.fail("Exception encountered: " + e.getMessage()); - } - } } From 497266270b9f0a238f9712e9c8cd82a99024ff76 Mon Sep 17 00:00:00 2001 From: Fabricio Duarte Date: Sun, 8 Mar 2026 13:25:27 -0300 Subject: [PATCH 16/43] Cleanup imported VM from disk on failure due to volume allocation + prevent duplicate volume and primary storage increment on import --- .../service/VolumeOrchestrationService.java | 2 +- .../cloud/vm/VirtualMachineManagerImpl.java | 6 +++--- .../orchestration/VolumeOrchestrator.java | 4 ++-- .../vm/UnmanagedVMsManagerImpl.java | 21 +++++++++---------- .../vm/UnmanagedVMsManagerImplTest.java | 4 ++-- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java index 7950dda4d68..84e110a8940 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java @@ -107,7 +107,7 @@ public interface VolumeOrchestrationService { void destroyVolume(Volume volume); DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, - Account owner, Long deviceId); + Account owner, Long deviceId, boolean incrementResourceCount); VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo volume, HypervisorType rootDiskHyperType, StoragePool storagePool) throws NoTransitionException; diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 2dcb8fa2059..74ff3801b6a 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -536,7 +536,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (dataDiskOfferings != null) { for (final DiskOfferingInfo dataDiskOfferingInfo : dataDiskOfferings) { volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId(), dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(), - dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, null); + dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, null, true); } } if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) { @@ -546,7 +546,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac long diskOfferingSize = diskOffering.getDiskSize() / (1024 * 1024 * 1024); VMTemplateVO dataDiskTemplate = _templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey()); volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId() + "-" + String.valueOf(diskNumber), diskOffering, diskOfferingSize, null, null, - persistedVm, dataDiskTemplate, owner, Long.valueOf(diskNumber)); + persistedVm, dataDiskTemplate, owner, Long.valueOf(diskNumber), true); diskNumber++; } } @@ -576,7 +576,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac String rootVolumeName = String.format("ROOT-%s", vm.getId()); if (template.getFormat() == ImageFormat.ISO) { volumeMgr.allocateRawVolume(Type.ROOT, rootVolumeName, rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(), - rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null); + rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null, true); } else if (template.getFormat() == ImageFormat.BAREMETAL) { logger.debug("%s has format [{}]. Skipping ROOT volume [{}] allocation.", template.toString(), ImageFormat.BAREMETAL, rootVolumeName); } else { diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 0fc61d81588..5eef84e354c 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -835,7 +835,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true) @Override public DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Long size, Long minIops, Long maxIops, VirtualMachine vm, VirtualMachineTemplate template, Account owner, - Long deviceId) { + Long deviceId, boolean incrementResourceCount) { if (size == null) { size = offering.getDiskSize(); } else { @@ -874,7 +874,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati saveVolumeDetails(offering.getId(), vol.getId()); // Save usage event and update resource count for user vm volumes - if (vm.getType() == VirtualMachine.Type.User) { + if (vm.getType() == VirtualMachine.Type.User && incrementResourceCount) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offering.getId(), null, size, Volume.class.getName(), vol.getUuid(), vol.isDisplayVolume()); _resourceLimitMgr.incrementVolumeResourceCount(vm.getAccountId(), vol.isDisplayVolume(), vol.getSize(), offering); diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 1917804fb8d..a32459ed059 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -2489,13 +2489,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); } String rootVolumeName = String.format("ROOT-%s", userVm.getId()); - DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null); + DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false); DiskProfile[] dataDiskProfiles = new DiskProfile[dataDisks.size()]; int diskSeq = 0; for (UnmanagedInstanceTO.Disk disk : dataDisks) { DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); - DiskProfile dataDiskProfile = volumeManager.allocateRawVolume(Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), offering, null, null, null, userVm, template, owner, null); + DiskProfile dataDiskProfile = volumeManager.allocateRawVolume(Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), offering, null, null, null, userVm, template, owner, null, false); dataDiskProfiles[diskSeq++] = dataDiskProfile; } @@ -2653,7 +2653,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { reservations.add(volumeReservation); String rootVolumeName = String.format("ROOT-%s", userVm.getId()); - DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null); + DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false); final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null); ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId()); @@ -2696,14 +2696,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { throw new CloudRuntimeException("Disk not found or is invalid"); } diskProfile.setSize(checkVolumeAnswer.getSize()); - try { - CheckedReservation primaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.primary_storage, resourceLimitStorageTags, - CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? diskProfile.getSize() : 0L, reservationDao, resourceLimitService); - reservations.add(primaryStorageReservation); - } catch (ResourceAllocationException e) { - cleanupFailedImportVM(userVm); - throw e; - } + + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.primary_storage, resourceLimitStorageTags, + CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? diskProfile.getSize() : 0L, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); List> diskProfileStoragePoolList = new ArrayList<>(); try { @@ -2724,6 +2720,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); return userVm; + } catch (ResourceAllocationException e) { + cleanupFailedImportVM(userVm); + throw e; } finally { ReservationHelper.closeAll(reservations); } diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 282f3fb3a70..d98eebebd62 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -508,7 +508,7 @@ public class UnmanagedVMsManagerImplTest { DeployDestination mockDest = Mockito.mock(DeployDestination.class); when(deploymentPlanningManager.planDeployment(any(), any(), any(), any())).thenReturn(mockDest); DiskProfile diskProfile = Mockito.mock(DiskProfile.class); - when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) + when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) .thenReturn(diskProfile); Map storage = new HashMap<>(); VolumeVO volume = Mockito.mock(VolumeVO.class); @@ -743,7 +743,7 @@ public class UnmanagedVMsManagerImplTest { DeployDestination mockDest = Mockito.mock(DeployDestination.class); when(deploymentPlanningManager.planDeployment(any(), any(), any(), any())).thenReturn(mockDest); DiskProfile diskProfile = Mockito.mock(DiskProfile.class); - when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) + when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) .thenReturn(diskProfile); Map storage = new HashMap<>(); VolumeVO volume = Mockito.mock(VolumeVO.class); From e8f8aca694f7d1f46045a542c416cea135d1cf56 Mon Sep 17 00:00:00 2001 From: Fabricio Duarte Date: Sun, 8 Mar 2026 14:08:05 -0300 Subject: [PATCH 17/43] Fix failing tests --- .../org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index d98eebebd62..d56299126a5 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -508,7 +508,7 @@ public class UnmanagedVMsManagerImplTest { DeployDestination mockDest = Mockito.mock(DeployDestination.class); when(deploymentPlanningManager.planDeployment(any(), any(), any(), any())).thenReturn(mockDest); DiskProfile diskProfile = Mockito.mock(DiskProfile.class); - when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) + when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean())) .thenReturn(diskProfile); Map storage = new HashMap<>(); VolumeVO volume = Mockito.mock(VolumeVO.class); @@ -743,7 +743,7 @@ public class UnmanagedVMsManagerImplTest { DeployDestination mockDest = Mockito.mock(DeployDestination.class); when(deploymentPlanningManager.planDeployment(any(), any(), any(), any())).thenReturn(mockDest); DiskProfile diskProfile = Mockito.mock(DiskProfile.class); - when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())) + when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean())) .thenReturn(diskProfile); Map storage = new HashMap<>(); VolumeVO volume = Mockito.mock(VolumeVO.class); From dc7068a13517717edb37431966527d3bc1c6374e Mon Sep 17 00:00:00 2001 From: Fabricio Duarte Date: Tue, 10 Mar 2026 18:33:00 -0300 Subject: [PATCH 18/43] Address public IP limit validations --- .../com/cloud/dc/dao/AccountVlanMapDao.java | 2 +- .../cloud/dc/dao/AccountVlanMapDaoImpl.java | 4 +- .../com/cloud/dc/dao/DomainVlanMapDao.java | 2 +- .../cloud/dc/dao/DomainVlanMapDaoImpl.java | 4 +- .../main/java/com/cloud/api/ApiDBUtils.java | 4 + .../ConfigurationManagerImpl.java | 74 +++++++++++++------ .../com/cloud/network/NetworkServiceImpl.java | 24 +++--- .../resourcelimit/CheckedReservation.java | 6 ++ 8 files changed, 81 insertions(+), 39 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 { public List 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 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 { public List listDomainVlanMapsByDomain(long domainId); public List 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 } @Override - public DomainVlanMapVO findDomainVlanMap(long domainId, long vlanDbId) { + public DomainVlanMapVO findDomainVlanMap(Long domainId, long vlanDbId) { SearchCriteria 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 4ef1b28b9c0..4ff25472efe 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -2244,6 +2244,10 @@ public class ApiDBUtils { return s_accountService.isAdmin(account.getId()); } + public static Account getSystemAccount() { + return s_accountService.getSystemAccount(); + } + public static List 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 eb138bb10b0..8ba8234c1ba 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.exception.UnsupportedServiceException; import com.cloud.network.as.AutoScaleManager; +import com.cloud.resourcelimit.CheckedReservation; import com.cloud.user.AccountManagerImpl; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker; @@ -128,6 +129,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; @@ -395,6 +397,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Inject ResourceLimitService _resourceLimitMgr; @Inject + ReservationDao reservationDao; + @Inject ProjectManager _projectMgr; @Inject DataStoreManager _dataStoreMgr; @@ -4833,22 +4837,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.isForNsx()); + 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.isForNsx()); + } } private Network getNetwork(Long networkId) { @@ -5377,7 +5379,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) { @@ -5397,24 +5399,50 @@ 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 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 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) { + 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); @@ -5801,12 +5829,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 ips = _publicIpAddressDao.listByVlanId(vlanDbId); @@ -5827,6 +5849,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } + // 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()); @@ -5850,6 +5876,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } return vlan; + + } } @Override diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index 54c1d133943..c75fe4efbcc 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -40,6 +40,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; @@ -75,6 +76,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; @@ -328,6 +330,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @Inject ResourceLimitService _resourceLimitMgr; @Inject + ReservationDao reservationDao; + @Inject DomainManager _domainMgr; @Inject ProjectManager _projectMgr; @@ -1143,15 +1147,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 maps = _accountVlanMapDao.listAccountVlanMapsByVlan(ipVO.getVlanId()); ipVO.setAllocatedTime(new Date()); ipVO.setAllocatedToAccountId(account.getAccountId()); @@ -1161,10 +1160,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) { From 23b19a9776ded761f5c36b5e8fb367450fb62d2f Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:28:30 +0530 Subject: [PATCH 19/43] review comments --- .../cloud/projects/ProjectManagerImpl.java | 39 +++++++++---------- .../storage/ImageStoreUploadMonitorImpl.java | 2 +- .../storage/download/DownloadListener.java | 7 +++- .../cloud/template/TemplateManagerImpl.java | 14 ++++--- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index 43efccd04f9..6942400c82c 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -604,16 +604,16 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C boolean shouldIncrementResourceCount = projectRole != null && Role.Admin == projectRole; try (CheckedReservation cr = new CheckedReservation(userAccount, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { - if (assignUserToProject(project, user.getId(), user.getAccountId(), projectRole, - Optional.ofNullable(role).map(ProjectRole::getId).orElse(null)) != null) { - if (shouldIncrementResourceCount) { - _resourceLimitMgr.incrementResourceCount(userAccount.getId(), ResourceType.project); + if (assignUserToProject(project, user.getId(), user.getAccountId(), projectRole, + Optional.ofNullable(role).map(ProjectRole::getId).orElse(null)) != null) { + if (shouldIncrementResourceCount) { + _resourceLimitMgr.incrementResourceCount(userAccount.getId(), ResourceType.project); + } + return true; + } else { + logger.warn("Failed to add user to project: {}", project); + return false; } - return true; - } else { - logger.warn("Failed to add user to project: {}", project); - return false; - } } } } @@ -721,19 +721,16 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C } try (CheckedReservation checkedReservation = new CheckedReservation(futureOwnerAccount, ResourceType.project, null, null, 1L, reservationDao, _resourceLimitMgr)) { + //unset the role for the old owner + ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId()); + currentOwner.setAccountRole(Role.Regular); + _projectAccountDao.update(currentOwner.getId(), currentOwner); + _resourceLimitMgr.decrementResourceCount(currentOwnerAccount.getId(), ResourceType.project); - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(futureOwnerAccount.getId()), ResourceType.project); - - //unset the role for the old owner - ProjectAccountVO currentOwner = _projectAccountDao.findByProjectIdAccountId(projectId, currentOwnerAccount.getId()); - currentOwner.setAccountRole(Role.Regular); - _projectAccountDao.update(currentOwner.getId(), currentOwner); - _resourceLimitMgr.decrementResourceCount(currentOwnerAccount.getId(), ResourceType.project); - - //set new owner - futureOwner.setAccountRole(Role.Admin); - _projectAccountDao.update(futureOwner.getId(), futureOwner); - _resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project); + //set new owner + futureOwner.setAccountRole(Role.Admin); + _projectAccountDao.update(futureOwner.getId(), futureOwner); + _resourceLimitMgr.incrementResourceCount(futureOwnerAccount.getId(), ResourceType.project); } } else { logger.trace("Future owner {}is already the owner of the project {}", newOwnerName, project); diff --git a/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java b/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java index b56e5b56213..a63b0a6f5ec 100755 --- a/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java +++ b/server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java @@ -360,7 +360,7 @@ public class ImageStoreUploadMonitorImpl extends ManagerBase implements ImageSto boolean success = true; Long currentSize = answer.getVirtualSize() != 0 ? answer.getVirtualSize() : answer.getPhysicalSize(); Long lastSize = volume.getSize() != null ? volume.getSize() : 0L; - if (!checkAndUpdateSecondaryStorageResourceLimit(volume.getAccountId(), volume.getSize(), currentSize)) { + if (!checkAndUpdateSecondaryStorageResourceLimit(volume.getAccountId(), lastSize, currentSize)) { volumeDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR); volumeDataStore.setState(State.Failed); volumeDataStore.setErrorString("Storage Limit Reached"); diff --git a/server/src/main/java/com/cloud/storage/download/DownloadListener.java b/server/src/main/java/com/cloud/storage/download/DownloadListener.java index 058881fdb54..695b1c060e4 100644 --- a/server/src/main/java/com/cloud/storage/download/DownloadListener.java +++ b/server/src/main/java/com/cloud/storage/download/DownloadListener.java @@ -280,7 +280,7 @@ public class DownloadListener implements Listener { } private Long getSizeFromDB() { - Long lastSize = 0L; + Long lastSize = null; if (DataObjectType.TEMPLATE.equals(object.getType())) { TemplateDataStoreVO t = _templateDataStoreDao.findByStoreTemplate(object.getDataStore().getId(), object.getId()); lastSize = t.getSize(); @@ -288,7 +288,7 @@ public class DownloadListener implements Listener { VolumeVO v = _volumeDao.findById(object.getId()); lastSize = v.getSize(); } - return lastSize; + return lastSize == null ? 0L : lastSize; } private Boolean checkAndUpdateResourceLimits(DownloadAnswer answer) { @@ -297,6 +297,9 @@ public class DownloadListener implements Listener { if (currentSize > lastSize) { Long accountId = getAccountIdForDataObject(); + if (accountId == null) { + return true; + } Account account = _accountMgr.getAccount(accountId); Long usage = currentSize - lastSize; try (CheckedReservation secStorageReservation = new CheckedReservation(account, Resource.ResourceType.secondary_storage, usage, _reservationDao, _resourceLimitMgr)) { diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index 1f3eb567c6f..34996710bdb 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -1035,12 +1035,14 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, logger.debug("There is Template {} in secondary storage {} in zone {} , don't need to copy", template, dstSecStore, dataCenterVOs.get(destZoneId)); continue; } - try (CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, template.getSize(), reservationDao, _resourceLimitMgr)) { - if (!copy(userId, template, srcSecStore, dataCenterVOs.get(destZoneId))) { - failedZones.add(dataCenterVOs.get(destZoneId).getName()); - continue; - } - _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, template.getSize()); + if (template.getSize() != null) { + try (CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, template.getSize(), reservationDao, _resourceLimitMgr)) { + if (!copy(userId, template, srcSecStore, dataCenterVOs.get(destZoneId))) { + failedZones.add(dataCenterVOs.get(destZoneId).getName()); + continue; + } + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, template.getSize()); + } } } } From 61afb4cb782a0fa1042c85ee894370eb71b5cce4 Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Fri, 13 Mar 2026 17:26:43 +0530 Subject: [PATCH 20/43] fix identation --- .../orchestration/NetworkOrchestrator.java | 603 +++++++++--------- .../ConfigurationManagerImpl.java | 58 +- .../com/cloud/network/NetworkServiceImpl.java | 28 +- .../com/cloud/network/vpc/VpcManagerImpl.java | 34 +- .../cloud/projects/ProjectManagerImpl.java | 81 ++- .../cloud/storage/VolumeApiServiceImpl.java | 569 +++++++++-------- .../cloud/template/TemplateManagerImpl.java | 196 +++--- .../java/com/cloud/vm/UserVmManagerImpl.java | 78 +-- .../VolumeImportUnmanageManagerImpl.java | 20 +- .../vm/UnmanagedVMsManagerImpl.java | 288 ++++----- 10 files changed, 973 insertions(+), 982 deletions(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 4fd5cbd1949..2da0c837a84 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2745,358 +2745,357 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra boolean ipv6 = false; try (CheckedReservation networkReservation = new CheckedReservation(owner, domainId, Resource.ResourceType.network, null, null, 1L, reservationDao, _resourceLimitMgr)) { - - if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { - ipv6 = true; - } - // Validate zone - if (zone.getNetworkType() == NetworkType.Basic) { - // In Basic zone the network should have aclType=Domain, domainId=1, subdomainAccess=true - if (aclType == null || aclType != ACLType.Domain) { - throw new InvalidParameterValueException("Only AclType=Domain can be specified for network creation in Basic zone"); + if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { + ipv6 = true; } + // Validate zone + if (zone.getNetworkType() == NetworkType.Basic) { + // In Basic zone the network should have aclType=Domain, domainId=1, subdomainAccess=true + if (aclType == null || aclType != ACLType.Domain) { + throw new InvalidParameterValueException("Only AclType=Domain can be specified for network creation in Basic zone"); + } - // Only one guest network is supported in Basic zone - final List guestNetworks = _networksDao.listByZoneAndTrafficType(zone.getId(), TrafficType.Guest); - if (!guestNetworks.isEmpty()) { - throw new InvalidParameterValueException("Can't have more than one Guest network in zone with network type " + NetworkType.Basic); - } + // Only one guest network is supported in Basic zone + final List guestNetworks = _networksDao.listByZoneAndTrafficType(zone.getId(), TrafficType.Guest); + if (!guestNetworks.isEmpty()) { + throw new InvalidParameterValueException("Can't have more than one Guest network in zone with network type " + NetworkType.Basic); + } - // if zone is basic, only Shared network offerings w/o source nat service are allowed - if (!(ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { - throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + " with disabled " - + Service.SourceNat.getName() + " service are allowed"); - } + // if zone is basic, only Shared network offerings w/o source nat service are allowed + if (!(ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { + throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + " with disabled " + + Service.SourceNat.getName() + " service are allowed"); + } - if (domainId == null || domainId != Domain.ROOT_DOMAIN) { - throw new InvalidParameterValueException("Guest network in Basic zone should be dedicated to ROOT domain"); - } + if (domainId == null || domainId != Domain.ROOT_DOMAIN) { + throw new InvalidParameterValueException("Guest network in Basic zone should be dedicated to ROOT domain"); + } - if (subdomainAccess == null) { - subdomainAccess = true; - } else if (!subdomainAccess) { - throw new InvalidParameterValueException("Subdomain access should be set to true for the" + " guest network in the Basic zone"); - } + if (subdomainAccess == null) { + subdomainAccess = true; + } else if (!subdomainAccess) { + throw new InvalidParameterValueException("Subdomain access should be set to true for the" + " guest network in the Basic zone"); + } - if (vlanId == null) { - vlanId = Vlan.UNTAGGED; - } else { - if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) { - throw new InvalidParameterValueException("Only vlan " + Vlan.UNTAGGED + " can be created in " + "the zone of type " + NetworkType.Basic); + if (vlanId == null) { + vlanId = Vlan.UNTAGGED; + } else { + if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) { + throw new InvalidParameterValueException("Only vlan " + Vlan.UNTAGGED + " can be created in " + "the zone of type " + NetworkType.Basic); + } + } + + } else if (zone.getNetworkType() == NetworkType.Advanced) { + if (zone.isSecurityGroupEnabled()) { + if (isolatedPvlan != null) { + throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!"); + } + // Only Account specific Isolated network with sourceNat service disabled are allowed in security group + // enabled zone + if ((ntwkOff.getGuestType() != GuestType.Shared) && (ntwkOff.getGuestType() != GuestType.L2)) { + throw new InvalidParameterValueException("Only shared or L2 guest network can be created in security group enabled zone"); + } + if (_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)) { + throw new InvalidParameterValueException("Service SourceNat is not allowed in security group enabled zone"); + } + } + + //don't allow eip/elb networks in Advance zone + if (ntwkOff.isElasticIp() || ntwkOff.isElasticLb()) { + throw new InvalidParameterValueException("Elastic IP and Elastic LB services are supported in zone of type " + NetworkType.Basic); } } - } else if (zone.getNetworkType() == NetworkType.Advanced) { - if (zone.isSecurityGroupEnabled()) { - if (isolatedPvlan != null) { - throw new InvalidParameterValueException("Isolated Private VLAN is not supported with security group!"); - } - // Only Account specific Isolated network with sourceNat service disabled are allowed in security group - // enabled zone - if ((ntwkOff.getGuestType() != GuestType.Shared) && (ntwkOff.getGuestType() != GuestType.L2)) { - throw new InvalidParameterValueException("Only shared or L2 guest network can be created in security group enabled zone"); - } - if (_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)) { - throw new InvalidParameterValueException("Service SourceNat is not allowed in security group enabled zone"); + if (ipv6 && !GuestType.Shared.equals(ntwkOff.getGuestType())) { + _networkModel.checkIp6CidrSizeEqualTo64(ip6Cidr); + } + + //TODO(VXLAN): Support VNI specified + // VlanId can be specified only when network offering supports it + final boolean vlanSpecified = vlanId != null; + if (vlanSpecified != ntwkOff.isSpecifyVlan()) { + if (vlanSpecified) { + if (!isSharedNetworkWithoutSpecifyVlan(ntwkOff) && !isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { + throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); + } + } else { + throw new InvalidParameterValueException("Vlan has to be specified; corresponding offering says specifyVlan=true"); } } - //don't allow eip/elb networks in Advance zone - if (ntwkOff.isElasticIp() || ntwkOff.isElasticLb()) { - throw new InvalidParameterValueException("Elastic IP and Elastic LB services are supported in zone of type " + NetworkType.Basic); - } - } - - if (ipv6 && !GuestType.Shared.equals(ntwkOff.getGuestType())) { - _networkModel.checkIp6CidrSizeEqualTo64(ip6Cidr); - } - - //TODO(VXLAN): Support VNI specified - // VlanId can be specified only when network offering supports it - final boolean vlanSpecified = vlanId != null; - if (vlanSpecified != ntwkOff.isSpecifyVlan()) { if (vlanSpecified) { - if (!isSharedNetworkWithoutSpecifyVlan(ntwkOff) && !isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { - throw new InvalidParameterValueException("Can't specify vlan; corresponding offering says specifyVlan=false"); + URI uri = encodeVlanIdIntoBroadcastUri(vlanId, pNtwk); + // Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks + URI secondaryUri = StringUtils.isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null; + if (isSharedNetworkWithoutSpecifyVlan(ntwkOff) || isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { + bypassVlanOverlapCheck = true; } - } else { - throw new InvalidParameterValueException("Vlan has to be specified; corresponding offering says specifyVlan=true"); - } - } - - if (vlanSpecified) { - URI uri = encodeVlanIdIntoBroadcastUri(vlanId, pNtwk); - // Aux: generate secondary URI for secondary VLAN ID (if provided) for performing checks - URI secondaryUri = StringUtils.isNotBlank(isolatedPvlan) ? BroadcastDomainType.fromString(isolatedPvlan) : null; - if (isSharedNetworkWithoutSpecifyVlan(ntwkOff) || isPrivateGatewayWithoutSpecifyVlan(ntwkOff)) { - bypassVlanOverlapCheck = true; - } - //don't allow to specify vlan tag used by physical network for dynamic vlan allocation - if (!(bypassVlanOverlapCheck && (ntwkOff.getGuestType() == GuestType.Shared || isPrivateNetwork)) - && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) { - throw new InvalidParameterValueException("The VLAN tag to use for new guest network, " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " - + zone.getName()); - } - if (secondaryUri != null && !(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && - _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(secondaryUri)).size() > 0) { - throw new InvalidParameterValueException(String.format( - "The VLAN tag for isolated PVLAN %s is already being used for dynamic vlan allocation for the guest network in zone %s", - isolatedPvlan, zone)); - } - if (!UuidUtils.isUuid(vlanId)) { - // For Isolated and L2 networks, don't allow to create network with vlan that already exists in the zone - if (!hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff, isPrivateNetwork)) { - if (_networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), null).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network vlans in zone %s", - vlanId, zone)); - } else if (secondaryUri != null && _networksDao.listByZoneAndUriAndGuestType(zoneId, secondaryUri.toString(), null).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network vlans in zone %s", - isolatedPvlan, zone)); - } else { - final List dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri)); - //for the network that is created as part of private gateway, - //the vnet is not coming from the data center vnet table, so the list can be empty - if (!dcVnets.isEmpty()) { - final DataCenterVnetVO dcVnet = dcVnets.get(0); - // Fail network creation if specified vlan is dedicated to a different account - if (dcVnet.getAccountGuestVlanMapId() != null) { - final Long accountGuestVlanMapId = dcVnet.getAccountGuestVlanMapId(); - final AccountGuestVlanMapVO map = _accountGuestVlanMapDao.findById(accountGuestVlanMapId); - if (map.getAccountId() != owner.getAccountId()) { - throw new InvalidParameterValueException("Vlan " + vlanId + " is dedicated to a different account"); - } - // Fail network creation if owner has a dedicated range of vlans but the specified vlan belongs to the system pool - } else { - final List maps = _accountGuestVlanMapDao.listAccountGuestVlanMapsByAccount(owner.getAccountId()); - if (maps != null && !maps.isEmpty()) { - final int vnetsAllocatedToAccount = _datacenterVnetDao.countVnetsAllocatedToAccount(zoneId, owner.getAccountId()); - final int vnetsDedicatedToAccount = _datacenterVnetDao.countVnetsDedicatedToAccount(zoneId, owner.getAccountId()); - if (vnetsAllocatedToAccount < vnetsDedicatedToAccount) { - throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + " to the vlan range dedicated to the owner " - + owner.getAccountName()); + //don't allow to specify vlan tag used by physical network for dynamic vlan allocation + if (!(bypassVlanOverlapCheck && (ntwkOff.getGuestType() == GuestType.Shared || isPrivateNetwork)) + && _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(uri)).size() > 0) { + throw new InvalidParameterValueException("The VLAN tag to use for new guest network, " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + + zone.getName()); + } + if (secondaryUri != null && !(bypassVlanOverlapCheck && ntwkOff.getGuestType() == GuestType.Shared) && + _dcDao.findVnet(zoneId, pNtwk.getId(), BroadcastDomainType.getValue(secondaryUri)).size() > 0) { + throw new InvalidParameterValueException(String.format( + "The VLAN tag for isolated PVLAN %s is already being used for dynamic vlan allocation for the guest network in zone %s", + isolatedPvlan, zone)); + } + if (!UuidUtils.isUuid(vlanId)) { + // For Isolated and L2 networks, don't allow to create network with vlan that already exists in the zone + if (!hasGuestBypassVlanOverlapCheck(bypassVlanOverlapCheck, ntwkOff, isPrivateNetwork)) { + if (_networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), null).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network vlans in zone %s", + vlanId, zone)); + } else if (secondaryUri != null && _networksDao.listByZoneAndUriAndGuestType(zoneId, secondaryUri.toString(), null).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network vlans in zone %s", + isolatedPvlan, zone)); + } else { + final List dcVnets = _datacenterVnetDao.findVnet(zoneId, BroadcastDomainType.getValue(uri)); + //for the network that is created as part of private gateway, + //the vnet is not coming from the data center vnet table, so the list can be empty + if (!dcVnets.isEmpty()) { + final DataCenterVnetVO dcVnet = dcVnets.get(0); + // Fail network creation if specified vlan is dedicated to a different account + if (dcVnet.getAccountGuestVlanMapId() != null) { + final Long accountGuestVlanMapId = dcVnet.getAccountGuestVlanMapId(); + final AccountGuestVlanMapVO map = _accountGuestVlanMapDao.findById(accountGuestVlanMapId); + if (map.getAccountId() != owner.getAccountId()) { + throw new InvalidParameterValueException("Vlan " + vlanId + " is dedicated to a different account"); + } + // Fail network creation if owner has a dedicated range of vlans but the specified vlan belongs to the system pool + } else { + final List maps = _accountGuestVlanMapDao.listAccountGuestVlanMapsByAccount(owner.getAccountId()); + if (maps != null && !maps.isEmpty()) { + final int vnetsAllocatedToAccount = _datacenterVnetDao.countVnetsAllocatedToAccount(zoneId, owner.getAccountId()); + final int vnetsDedicatedToAccount = _datacenterVnetDao.countVnetsDedicatedToAccount(zoneId, owner.getAccountId()); + if (vnetsAllocatedToAccount < vnetsDedicatedToAccount) { + throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + " to the vlan range dedicated to the owner " + + owner.getAccountName()); + } } } } } + } else { + // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or + // shared network with same Vlan ID in the zone + if (!bypassVlanOverlapCheck && _networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), GuestType.Isolated).size() > 0) { + throw new InvalidParameterValueException(String.format( + "There is an existing isolated/shared network that overlaps with vlan id:%s in zone %s", vlanId, zone)); + } } - } else { - // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or - // shared network with same Vlan ID in the zone - if (!bypassVlanOverlapCheck && _networksDao.listByZoneAndUriAndGuestType(zoneId, uri.toString(), GuestType.Isolated).size() > 0) { + } + + } + + // If networkDomain is not specified, take it from the global configuration + if (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Dns)) { + final Map dnsCapabilities = _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId), + Service.Dns); + final String isUpdateDnsSupported = dnsCapabilities.get(Capability.AllowDnsSuffixModification); + if (isUpdateDnsSupported == null || !Boolean.valueOf(isUpdateDnsSupported)) { + if (networkDomain != null) { + // TBD: NetworkOfferingId and zoneId. Send uuids instead. throw new InvalidParameterValueException(String.format( - "There is an existing isolated/shared network that overlaps with vlan id:%s in zone %s", vlanId, zone)); + "Domain name change is not supported by network offering id=%d in zone %s", + networkOfferingId, zone)); } - } - } - - } - - // If networkDomain is not specified, take it from the global configuration - if (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Dns)) { - final Map dnsCapabilities = _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId), - Service.Dns); - final String isUpdateDnsSupported = dnsCapabilities.get(Capability.AllowDnsSuffixModification); - if (isUpdateDnsSupported == null || !Boolean.valueOf(isUpdateDnsSupported)) { - if (networkDomain != null) { - // TBD: NetworkOfferingId and zoneId. Send uuids instead. - throw new InvalidParameterValueException(String.format( - "Domain name change is not supported by network offering id=%d in zone %s", - networkOfferingId, zone)); - } - } else { - if (networkDomain == null) { - // 1) Get networkDomain from the corresponding account/domain/zone - if (aclType == ACLType.Domain) { - networkDomain = _networkModel.getDomainNetworkDomain(domainId, zoneId); - } else if (aclType == ACLType.Account) { - networkDomain = _networkModel.getAccountNetworkDomain(owner.getId(), zoneId); - } - - // 2) If null, generate networkDomain using domain suffix from the global config variables - if (networkDomain == null) { - networkDomain = "cs" + Long.toHexString(owner.getId()) + GuestDomainSuffix.valueIn(zoneId); - } - } else { - // validate network domain - if (!NetUtils.verifyDomainName(networkDomain)) { - throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " - + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + if (networkDomain == null) { + // 1) Get networkDomain from the corresponding account/domain/zone + if (aclType == ACLType.Domain) { + networkDomain = _networkModel.getDomainNetworkDomain(domainId, zoneId); + } else if (aclType == ACLType.Account) { + networkDomain = _networkModel.getAccountNetworkDomain(owner.getId(), zoneId); + } + + // 2) If null, generate networkDomain using domain suffix from the global config variables + if (networkDomain == null) { + networkDomain = "cs" + Long.toHexString(owner.getId()) + GuestDomainSuffix.valueIn(zoneId); + } + + } else { + // validate network domain + if (!NetUtils.verifyDomainName(networkDomain)) { + throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " + + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); + } } } } - } - // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x - // limitation, remove after we introduce support for multiple ip ranges - // with different Cidrs for the same Shared network - final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced - && ntwkOff.getTrafficType() == TrafficType.Guest - && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated - && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat) - && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.Gateway))); - if (cidr == null && ip6Cidr == null && cidrRequired) { - if (ntwkOff.getGuestType() == GuestType.Shared) { - throw new InvalidParameterValueException(String.format("Gateway/netmask are required when creating %s networks.", Network.GuestType.Shared)); - } else { - throw new InvalidParameterValueException("gateway/netmask are required when create network of" + " type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); + // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x + // limitation, remove after we introduce support for multiple ip ranges + // with different Cidrs for the same Shared network + final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced + && ntwkOff.getTrafficType() == TrafficType.Guest + && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated + && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat) + && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.Gateway))); + if (cidr == null && ip6Cidr == null && cidrRequired) { + if (ntwkOff.getGuestType() == GuestType.Shared) { + throw new InvalidParameterValueException(String.format("Gateway/netmask are required when creating %s networks.", Network.GuestType.Shared)); + } else { + throw new InvalidParameterValueException("gateway/netmask are required when create network of" + " type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); + } } - } - checkL2OfferingServices(ntwkOff); + checkL2OfferingServices(ntwkOff); - // No cidr can be specified in Basic zone - if (zone.getNetworkType() == NetworkType.Basic && cidr != null) { - throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic); - } + // No cidr can be specified in Basic zone + if (zone.getNetworkType() == NetworkType.Basic && cidr != null) { + throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic); + } - // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4 - if (cidr != null && (ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) && - !NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { + // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4 + if (cidr != null && (ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) && + !NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { throw new InvalidParameterValueException("Virtual Guest Cidr " + cidr + " is not RFC 1918 or 6598 compliant"); - } + } - final String networkDomainFinal = networkDomain; - final String vlanIdFinal = vlanId; - final Boolean subdomainAccessFinal = subdomainAccess; - final Network network = Transaction.execute(new TransactionCallback() { - @Override - public Network doInTransaction(final TransactionStatus status) { - Long physicalNetworkId = null; - if (pNtwk != null) { - physicalNetworkId = pNtwk.getId(); - } - final DataCenterDeployment plan = new DataCenterDeployment(zoneId, null, null, null, null, physicalNetworkId); - final NetworkVO userNetwork = new NetworkVO(); - userNetwork.setNetworkDomain(networkDomainFinal); + final String networkDomainFinal = networkDomain; + final String vlanIdFinal = vlanId; + final Boolean subdomainAccessFinal = subdomainAccess; + final Network network = Transaction.execute(new TransactionCallback() { + @Override + public Network doInTransaction(final TransactionStatus status) { + Long physicalNetworkId = null; + if (pNtwk != null) { + physicalNetworkId = pNtwk.getId(); + } + final DataCenterDeployment plan = new DataCenterDeployment(zoneId, null, null, null, null, physicalNetworkId); + final NetworkVO userNetwork = new NetworkVO(); + userNetwork.setNetworkDomain(networkDomainFinal); - if (cidr != null && gateway != null) { - userNetwork.setCidr(cidr); - userNetwork.setGateway(gateway); - } + if (cidr != null && gateway != null) { + userNetwork.setCidr(cidr); + userNetwork.setGateway(gateway); + } - if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { - userNetwork.setIp6Cidr(ip6Cidr); - userNetwork.setIp6Gateway(ip6Gateway); - } + if (StringUtils.isNoneBlank(ip6Gateway, ip6Cidr)) { + userNetwork.setIp6Cidr(ip6Cidr); + userNetwork.setIp6Gateway(ip6Gateway); + } - if (externalId != null) { - userNetwork.setExternalId(externalId); - } + if (externalId != null) { + userNetwork.setExternalId(externalId); + } - if (StringUtils.isNotBlank(routerIp)) { - userNetwork.setRouterIp(routerIp); - } + if (StringUtils.isNotBlank(routerIp)) { + userNetwork.setRouterIp(routerIp); + } - if (StringUtils.isNotBlank(routerIpv6)) { - userNetwork.setRouterIpv6(routerIpv6); - } + if (StringUtils.isNotBlank(routerIpv6)) { + userNetwork.setRouterIpv6(routerIpv6); + } - if (vrIfaceMTUs != null) { - if (vrIfaceMTUs.first() != null && vrIfaceMTUs.first() > 0) { - userNetwork.setPublicMtu(vrIfaceMTUs.first()); + if (vrIfaceMTUs != null) { + if (vrIfaceMTUs.first() != null && vrIfaceMTUs.first() > 0) { + userNetwork.setPublicMtu(vrIfaceMTUs.first()); + } else { + userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); + } + + if (vrIfaceMTUs.second() != null && vrIfaceMTUs.second() > 0) { + userNetwork.setPrivateMtu(vrIfaceMTUs.second()); + } else { + userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); + } } else { userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); - } - - if (vrIfaceMTUs.second() != null && vrIfaceMTUs.second() > 0) { - userNetwork.setPrivateMtu(vrIfaceMTUs.second()); - } else { userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); } - } else { - userNetwork.setPublicMtu(Integer.valueOf(NetworkService.VRPublicInterfaceMtu.defaultValue())); - userNetwork.setPrivateMtu(Integer.valueOf(NetworkService.VRPrivateInterfaceMtu.defaultValue())); - } - if (!GuestType.L2.equals(userNetwork.getGuestType())) { - if (StringUtils.isNotBlank(ip4Dns1)) { - userNetwork.setDns1(ip4Dns1); + if (!GuestType.L2.equals(userNetwork.getGuestType())) { + if (StringUtils.isNotBlank(ip4Dns1)) { + userNetwork.setDns1(ip4Dns1); + } + if (StringUtils.isNotBlank(ip4Dns2)) { + userNetwork.setDns2(ip4Dns2); + } + if (StringUtils.isNotBlank(ip6Dns1)) { + userNetwork.setIp6Dns1(ip6Dns1); + } + if (StringUtils.isNotBlank(ip6Dns2)) { + userNetwork.setIp6Dns2(ip6Dns2); + } } - if (StringUtils.isNotBlank(ip4Dns2)) { - userNetwork.setDns2(ip4Dns2); - } - if (StringUtils.isNotBlank(ip6Dns1)) { - userNetwork.setIp6Dns1(ip6Dns1); - } - if (StringUtils.isNotBlank(ip6Dns2)) { - userNetwork.setIp6Dns2(ip6Dns2); - } - } - if (vlanIdFinal != null) { - if (isolatedPvlan == null) { - URI uri = null; - if (UuidUtils.isUuid(vlanIdFinal)) { - //Logical router's UUID provided as VLAN_ID - userNetwork.setVlanIdAsUUID(vlanIdFinal); //Set transient field - } else { - uri = encodeVlanIdIntoBroadcastUri(vlanIdFinal, pNtwk); - } - - if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString()).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with vlan %s already exists or overlaps with other network pvlans in zone %s", - vlanIdFinal, zone)); - } - - userNetwork.setBroadcastUri(uri); - if (!vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { - userNetwork.setBroadcastDomainType(BroadcastDomainType.Vlan); - } else { - userNetwork.setBroadcastDomainType(BroadcastDomainType.Native); - } - } else { - if (vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { - throw new InvalidParameterValueException("Cannot support pvlan with untagged primary vlan!"); - } - URI uri = NetUtils.generateUriForPvlan(vlanIdFinal, isolatedPvlan, isolatedPvlanType.toString()); - if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString(), isolatedPvlanType).size() > 0) { - throw new InvalidParameterValueException(String.format( - "Network with primary vlan %s and secondary vlan %s type %s already exists or overlaps with other network pvlans in zone %s", - vlanIdFinal, isolatedPvlan, isolatedPvlanType, zone)); - } - userNetwork.setBroadcastUri(uri); - userNetwork.setBroadcastDomainType(BroadcastDomainType.Pvlan); - userNetwork.setPvlanType(isolatedPvlanType); - } - } - userNetwork.setNetworkCidrSize(networkCidrSize); - final List networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId, - isDisplayNetworkEnabled); - Network network = null; - if (networks == null || networks.isEmpty()) { - throw new CloudRuntimeException("Fail to create a network"); - } else { - if (networks.size() > 0 && networks.get(0).getGuestType() == Network.GuestType.Isolated && networks.get(0).getTrafficType() == TrafficType.Guest) { - Network defaultGuestNetwork = networks.get(0); - for (final Network nw : networks) { - if (nw.getCidr() != null && nw.getCidr().equals(zone.getGuestNetworkCidr())) { - defaultGuestNetwork = nw; + if (vlanIdFinal != null) { + if (isolatedPvlan == null) { + URI uri = null; + if (UuidUtils.isUuid(vlanIdFinal)) { + //Logical router's UUID provided as VLAN_ID + userNetwork.setVlanIdAsUUID(vlanIdFinal); //Set transient field + } else { + uri = encodeVlanIdIntoBroadcastUri(vlanIdFinal, pNtwk); } + + if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString()).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with vlan %s already exists or overlaps with other network pvlans in zone %s", + vlanIdFinal, zone)); + } + + userNetwork.setBroadcastUri(uri); + if (!vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { + userNetwork.setBroadcastDomainType(BroadcastDomainType.Vlan); + } else { + userNetwork.setBroadcastDomainType(BroadcastDomainType.Native); + } + } else { + if (vlanIdFinal.equalsIgnoreCase(Vlan.UNTAGGED)) { + throw new InvalidParameterValueException("Cannot support pvlan with untagged primary vlan!"); + } + URI uri = NetUtils.generateUriForPvlan(vlanIdFinal, isolatedPvlan, isolatedPvlanType.toString()); + if (_networksDao.listByPhysicalNetworkPvlan(physicalNetworkId, uri.toString(), isolatedPvlanType).size() > 0) { + throw new InvalidParameterValueException(String.format( + "Network with primary vlan %s and secondary vlan %s type %s already exists or overlaps with other network pvlans in zone %s", + vlanIdFinal, isolatedPvlan, isolatedPvlanType, zone)); + } + userNetwork.setBroadcastUri(uri); + userNetwork.setBroadcastDomainType(BroadcastDomainType.Pvlan); + userNetwork.setPvlanType(isolatedPvlanType); } - network = defaultGuestNetwork; - } else { - // For shared network - network = networks.get(0); } + userNetwork.setNetworkCidrSize(networkCidrSize); + final List networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId, + isDisplayNetworkEnabled); + Network network = null; + if (networks == null || networks.isEmpty()) { + throw new CloudRuntimeException("Fail to create a network"); + } else { + if (networks.size() > 0 && networks.get(0).getGuestType() == Network.GuestType.Isolated && networks.get(0).getTrafficType() == TrafficType.Guest) { + Network defaultGuestNetwork = networks.get(0); + for (final Network nw : networks) { + if (nw.getCidr() != null && nw.getCidr().equals(zone.getGuestNetworkCidr())) { + defaultGuestNetwork = nw; + } + } + network = defaultGuestNetwork; + } else { + // For shared network + network = networks.get(0); + } + } + + if (isResourceCountUpdateNeeded(ntwkOff)) { + changeAccountResourceCountOrRecalculateDomainResourceCount(owner.getAccountId(), domainId, isDisplayNetworkEnabled, true); + } + UsageEventUtils.publishNetworkCreation(network); + + return network; } + }); - if (isResourceCountUpdateNeeded(ntwkOff)) { - changeAccountResourceCountOrRecalculateDomainResourceCount(owner.getAccountId(), domainId, isDisplayNetworkEnabled, true); - } - UsageEventUtils.publishNetworkCreation(network); - - return network; - } - }); - - CallContext.current().setEventDetails("Network Id: " + network.getId()); - CallContext.current().putContextParameter(Network.class, network.getUuid()); - return network; + CallContext.current().setEventDetails("Network Id: " + network.getId()); + CallContext.current().putContextParameter(Network.class, network.getUuid()); + return network; } } diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 8ba8234c1ba..ac8e53caddf 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -5430,19 +5430,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } 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); - 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 (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); @@ -5852,31 +5850,29 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // 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); - 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())); - } - - return vlan; + // increment resource count for dedicated public ip's + if (vlanOwner != null) { + _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size())); + } + return vlan; } } diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index c75fe4efbcc..9744961e037 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -1150,21 +1150,19 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C long reservedIpAddressesAmount = ipDedicatedAccountId == null ? 1L : 0L; try (CheckedReservation publicIpAddressReservation = new CheckedReservation(account, Resource.ResourceType.public_ip, reservedIpAddressesAmount, reservationDao, _resourceLimitMgr)) { - - List maps = _accountVlanMapDao.listAccountVlanMapsByVlan(ipVO.getVlanId()); - ipVO.setAllocatedTime(new Date()); - ipVO.setAllocatedToAccountId(account.getAccountId()); - ipVO.setAllocatedInDomainId(account.getDomainId()); - ipVO.setState(State.Reserved); - if (displayIp != null) { - ipVO.setDisplay(displayIp); - } - ipVO = _ipAddressDao.persist(ipVO); - if (reservedIpAddressesAmount > 0) { - _resourceLimitMgr.incrementResourceCount(account.getId(), Resource.ResourceType.public_ip); - } - return ipVO; - + List maps = _accountVlanMapDao.listAccountVlanMapsByVlan(ipVO.getVlanId()); + ipVO.setAllocatedTime(new Date()); + ipVO.setAllocatedToAccountId(account.getAccountId()); + ipVO.setAllocatedInDomainId(account.getDomainId()); + ipVO.setState(State.Reserved); + if (displayIp != null) { + ipVO.setDisplay(displayIp); + } + ipVO = _ipAddressDao.persist(ipVO); + 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."); diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java index 4a952bb582d..74fb4160848 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -1248,25 +1248,25 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis vpc.setDisplay(Boolean.TRUE.equals(displayVpc)); try (CheckedReservation vpcReservation = new CheckedReservation(owner, ResourceType.vpc, null, null, 1L, reservationDao, _resourceLimitMgr)) { - if (vpc.getCidr() == null && cidrSize != null) { - // Allocate a CIDR for VPC - Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForVpc(vpc, cidrSize); - if (subnet != null) { - vpc.setCidr(subnet.getSubnet()); - } else { - throw new CloudRuntimeException("Failed to allocate a CIDR with requested size for VPC."); + if (vpc.getCidr() == null && cidrSize != null) { + // Allocate a CIDR for VPC + Ipv4GuestSubnetNetworkMap subnet = routedIpv4Manager.getOrCreateIpv4SubnetForVpc(vpc, cidrSize); + if (subnet != null) { + vpc.setCidr(subnet.getSubnet()); + } else { + throw new CloudRuntimeException("Failed to allocate a CIDR with requested size for VPC."); + } } - } - Vpc newVpc = createVpc(displayVpc, vpc); - // assign Ipv4 subnet to Routed VPC - if (routedIpv4Manager.isRoutedVpc(vpc)) { - routedIpv4Manager.assignIpv4SubnetToVpc(newVpc); - } - if (CollectionUtils.isNotEmpty(bgpPeerIds)) { - routedIpv4Manager.persistBgpPeersForVpc(newVpc.getId(), bgpPeerIds); - } - return newVpc; + Vpc newVpc = createVpc(displayVpc, vpc); + // assign Ipv4 subnet to Routed VPC + if (routedIpv4Manager.isRoutedVpc(vpc)) { + routedIpv4Manager.assignIpv4SubnetToVpc(newVpc); + } + if (CollectionUtils.isNotEmpty(bgpPeerIds)) { + routedIpv4Manager.persistBgpPeersForVpc(newVpc.getId(), bgpPeerIds); + } + return newVpc; } } diff --git a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java index 6942400c82c..d165f0cd1b6 100644 --- a/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java +++ b/server/src/main/java/com/cloud/projects/ProjectManagerImpl.java @@ -277,40 +277,39 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C } try (CheckedReservation projectReservation = new CheckedReservation(owner, ResourceType.project, null, null, 1L, reservationDao, _resourceLimitMgr)) { + final Account ownerFinal = owner; + User finalUser = user; + Project project = Transaction.execute(new TransactionCallback() { + @Override + public Project doInTransaction(TransactionStatus status) { - final Account ownerFinal = owner; - User finalUser = user; - Project project = Transaction.execute(new TransactionCallback() { - @Override - public Project doInTransaction(TransactionStatus status) { + //Create an account associated with the project + StringBuilder acctNm = new StringBuilder("PrjAcct-"); + acctNm.append(name).append("-").append(ownerFinal.getDomainId()); - //Create an account associated with the project - StringBuilder acctNm = new StringBuilder("PrjAcct-"); - acctNm.append(name).append("-").append(ownerFinal.getDomainId()); + Account projectAccount = _accountMgr.createAccount(acctNm.toString(), Account.Type.PROJECT, null, domainId, null, null, UUID.randomUUID().toString()); - Account projectAccount = _accountMgr.createAccount(acctNm.toString(), Account.Type.PROJECT, null, domainId, null, null, UUID.randomUUID().toString()); + Project project = _projectDao.persist(new ProjectVO(name, displayText, ownerFinal.getDomainId(), projectAccount.getId())); - Project project = _projectDao.persist(new ProjectVO(name, displayText, ownerFinal.getDomainId(), projectAccount.getId())); + //assign owner to the project + assignAccountToProject(project, ownerFinal.getId(), ProjectAccount.Role.Admin, + Optional.ofNullable(finalUser).map(User::getId).orElse(null), null); - //assign owner to the project - assignAccountToProject(project, ownerFinal.getId(), ProjectAccount.Role.Admin, - Optional.ofNullable(finalUser).map(User::getId).orElse(null), null); + if (project != null) { + CallContext.current().setEventDetails("Project id=" + project.getId()); + CallContext.current().putContextParameter(Project.class, project.getUuid()); + } - if (project != null) { - CallContext.current().setEventDetails("Project id=" + project.getId()); - CallContext.current().putContextParameter(Project.class, project.getUuid()); + //Increment resource count + _resourceLimitMgr.incrementResourceCount(ownerFinal.getId(), ResourceType.project); + + return project; } + }); - //Increment resource count - _resourceLimitMgr.incrementResourceCount(ownerFinal.getId(), ResourceType.project); + messageBus.publish(_name, ProjectManager.MESSAGE_CREATE_TUNGSTEN_PROJECT_EVENT, PublishScope.LOCAL, project); - return project; - } - }); - - messageBus.publish(_name, ProjectManager.MESSAGE_CREATE_TUNGSTEN_PROJECT_EVENT, PublishScope.LOCAL, project); - - return project; + return project; } } @@ -671,13 +670,13 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C boolean shouldIncrementResourceCount = Role.Admin == newAccRole; try (CheckedReservation checkedReservation = new CheckedReservation(account, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { - futureOwner.setAccountRole(newAccRole); - _projectAccountDao.update(futureOwner.getId(), futureOwner); - if (shouldIncrementResourceCount) { - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.project); - } else { - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.project); - } + futureOwner.setAccountRole(newAccRole); + _projectAccountDao.update(futureOwner.getId(), futureOwner); + if (shouldIncrementResourceCount) { + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.project); + } else { + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.project); + } } } @@ -877,16 +876,16 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C boolean shouldIncrementResourceCount = projectRoleType != null && Role.Admin == projectRoleType; try (CheckedReservation cr = new CheckedReservation(account, ResourceType.project, shouldIncrementResourceCount ? 1L : 0L, reservationDao, _resourceLimitMgr)) { - if (assignAccountToProject(project, account.getId(), projectRoleType, null, - Optional.ofNullable(projectRole).map(ProjectRole::getId).orElse(null)) != null) { - if (shouldIncrementResourceCount) { - _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.project); + if (assignAccountToProject(project, account.getId(), projectRoleType, null, + Optional.ofNullable(projectRole).map(ProjectRole::getId).orElse(null)) != null) { + if (shouldIncrementResourceCount) { + _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.project); + } + return true; + } else { + logger.warn("Failed to add account {} to project {}", accountName, project); + return false; } - return true; - } else { - logger.warn("Failed to add account {} to project {}", accountName, project); - return false; - } } } } diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 7186b07334d..026a9ae1dd9 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -431,9 +431,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic List reservations = new ArrayList<>(); try { - - validateVolume(caller, ownerId, zoneId, volumeName, url, format, diskOfferingId, reservations); - volume = persistVolume(owner, zoneId, volumeName, url, format, diskOfferingId, Volume.State.Allocated); + validateVolume(caller, ownerId, zoneId, volumeName, url, format, diskOfferingId, reservations); + volume = persistVolume(owner, zoneId, volumeName, url, format, diskOfferingId, Volume.State.Allocated); } finally { ReservationHelper.closeAll(reservations); @@ -479,74 +478,74 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic List reservations = new ArrayList<>(); try { - validateVolume(caller, ownerId, zoneId, volumeName, null, format, diskOfferingId, reservations); + validateVolume(caller, ownerId, zoneId, volumeName, null, format, diskOfferingId, reservations); - return Transaction.execute(new TransactionCallbackWithException() { - @Override - public GetUploadParamsResponse doInTransaction(TransactionStatus status) throws MalformedURLException { + return Transaction.execute(new TransactionCallbackWithException() { + @Override + public GetUploadParamsResponse doInTransaction(TransactionStatus status) throws MalformedURLException { - VolumeVO volume = persistVolume(owner, zoneId, volumeName, null, format, diskOfferingId, Volume.State.NotUploaded); + VolumeVO volume = persistVolume(owner, zoneId, volumeName, null, format, diskOfferingId, Volume.State.NotUploaded); - final DataStore store = _tmpltMgr.getImageStore(imageStoreUuid, zoneId, volume); + final DataStore store = _tmpltMgr.getImageStore(imageStoreUuid, zoneId, volume); - VolumeInfo vol = volFactory.getVolume(volume.getId()); + VolumeInfo vol = volFactory.getVolume(volume.getId()); - RegisterVolumePayload payload = new RegisterVolumePayload(null, cmd.getChecksum(), format); - vol.addPayload(payload); + RegisterVolumePayload payload = new RegisterVolumePayload(null, cmd.getChecksum(), format); + vol.addPayload(payload); - Pair pair = volService.registerVolumeForPostUpload(vol, store); - EndPoint ep = pair.first(); - DataObject dataObject = pair.second(); + Pair pair = volService.registerVolumeForPostUpload(vol, store); + EndPoint ep = pair.first(); + DataObject dataObject = pair.second(); - GetUploadParamsResponse response = new GetUploadParamsResponse(); + GetUploadParamsResponse response = new GetUploadParamsResponse(); - String ssvmUrlDomain = _configDao.getValue(Config.SecStorageSecureCopyCert.key()); - String protocol = UseHttpsToUpload.value() ? "https" : "http"; + String ssvmUrlDomain = _configDao.getValue(Config.SecStorageSecureCopyCert.key()); + String protocol = UseHttpsToUpload.value() ? "https" : "http"; - String url = ImageStoreUtil.generatePostUploadUrl(ssvmUrlDomain, ep.getPublicAddr(), vol.getUuid(), protocol); - response.setPostURL(new URL(url)); + String url = ImageStoreUtil.generatePostUploadUrl(ssvmUrlDomain, ep.getPublicAddr(), vol.getUuid(), protocol); + response.setPostURL(new URL(url)); - // set the post url, this is used in the monitoring thread to determine the SSVM - VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(vol.getId()); - assert (volumeStore != null) : "sincle volume is registered, volumestore cannot be null at this stage"; - volumeStore.setExtractUrl(url); - _volumeStoreDao.persist(volumeStore); + // set the post url, this is used in the monitoring thread to determine the SSVM + VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(vol.getId()); + assert (volumeStore != null) : "sincle volume is registered, volumestore cannot be null at this stage"; + volumeStore.setExtractUrl(url); + _volumeStoreDao.persist(volumeStore); - response.setId(UUID.fromString(vol.getUuid())); + response.setId(UUID.fromString(vol.getUuid())); - int timeout = ImageStoreUploadMonitorImpl.getUploadOperationTimeout(); - DateTime currentDateTime = new DateTime(DateTimeZone.UTC); - String expires = currentDateTime.plusMinutes(timeout).toString(); - response.setTimeout(expires); + int timeout = ImageStoreUploadMonitorImpl.getUploadOperationTimeout(); + DateTime currentDateTime = new DateTime(DateTimeZone.UTC); + String expires = currentDateTime.plusMinutes(timeout).toString(); + response.setTimeout(expires); - String key = _configDao.getValue(Config.SSVMPSK.key()); - /* - * encoded metadata using the post upload config key - */ - TemplateOrVolumePostUploadCommand command = new TemplateOrVolumePostUploadCommand(vol.getId(), vol.getUuid(), volumeStore.getInstallPath(), cmd.getChecksum(), vol.getType().toString(), - vol.getName(), vol.getFormat().toString(), dataObject.getDataStore().getUri(), dataObject.getDataStore().getRole().toString()); - command.setLocalPath(volumeStore.getLocalDownloadPath()); - //using the existing max upload size configuration - command.setProcessTimeout(NumbersUtil.parseLong(_configDao.getValue("vmware.package.ova.timeout"), 3600)); - command.setMaxUploadSize(_configDao.getValue(Config.MaxUploadVolumeSize.key())); + String key = _configDao.getValue(Config.SSVMPSK.key()); + /* + * encoded metadata using the post upload config key + */ + TemplateOrVolumePostUploadCommand command = new TemplateOrVolumePostUploadCommand(vol.getId(), vol.getUuid(), volumeStore.getInstallPath(), cmd.getChecksum(), vol.getType().toString(), + vol.getName(), vol.getFormat().toString(), dataObject.getDataStore().getUri(), dataObject.getDataStore().getRole().toString()); + command.setLocalPath(volumeStore.getLocalDownloadPath()); + //using the existing max upload size configuration + command.setProcessTimeout(NumbersUtil.parseLong(_configDao.getValue("vmware.package.ova.timeout"), 3600)); + command.setMaxUploadSize(_configDao.getValue(Config.MaxUploadVolumeSize.key())); - long accountId = vol.getAccountId(); - Account account = _accountDao.findById(accountId); - Domain domain = domainDao.findById(account.getDomainId()); + long accountId = vol.getAccountId(); + Account account = _accountDao.findById(accountId); + Domain domain = domainDao.findById(account.getDomainId()); - command.setDefaultMaxSecondaryStorageInBytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null)); - command.setAccountId(accountId); - Gson gson = new GsonBuilder().create(); - String metadata = EncryptionUtil.encodeData(gson.toJson(command), key); - response.setMetadata(metadata); + command.setDefaultMaxSecondaryStorageInBytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null)); + command.setAccountId(accountId); + Gson gson = new GsonBuilder().create(); + String metadata = EncryptionUtil.encodeData(gson.toJson(command), key); + response.setMetadata(metadata); - /* - * signature calculated on the url, expiry, metadata. - */ - response.setSignature(EncryptionUtil.generateSignature(metadata + url + expires, key)); - return response; - } - }); + /* + * signature calculated on the url, expiry, metadata. + */ + response.setSignature(EncryptionUtil.generateSignature(metadata + url + expires, key)); + return response; + } + }); } finally { ReservationHelper.closeAll(reservations); @@ -945,29 +944,29 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic List reservations = new ArrayList<>(); try { - _resourceLimitMgr.checkVolumeResourceLimit(owner, displayVolume, size, diskOffering, reservations); + _resourceLimitMgr.checkVolumeResourceLimit(owner, displayVolume, size, diskOffering, reservations); - // Verify that zone exists - DataCenterVO zone = _dcDao.findById(zoneId); - if (zone == null) { - throw new InvalidParameterValueException("Unable to find zone by id " + zoneId); - } + // Verify that zone exists + DataCenterVO zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Unable to find zone by id " + zoneId); + } - // Check if zone is disabled - if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) { - throw new PermissionDeniedException(String.format("Cannot perform this operation, Zone: %s is currently disabled", zone)); - } + // Check if zone is disabled + if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) { + throw new PermissionDeniedException(String.format("Cannot perform this operation, Zone: %s is currently disabled", zone)); + } - // If local storage is disabled then creation of volume with local disk - // offering not allowed - if (!zone.isLocalStorageEnabled() && diskOffering.isUseLocalStorage()) { - throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it"); - } + // If local storage is disabled then creation of volume with local disk + // offering not allowed + if (!zone.isLocalStorageEnabled() && diskOffering.isUseLocalStorage()) { + throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it"); + } - String userSpecifiedName = getVolumeNameFromCommand(cmd); + String userSpecifiedName = getVolumeNameFromCommand(cmd); - return commitVolume(cmd, caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName, - _uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details); + return commitVolume(cmd, caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName, + _uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details); } finally { ReservationHelper.closeAll(reservations); } @@ -1298,134 +1297,134 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic List reservations = new ArrayList<>(); try { - validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, diskOffering, newDiskOffering, reservations); + validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, diskOffering, newDiskOffering, reservations); - // Note: The storage plug-in in question should perform validation on the IOPS to check if a sufficient number of IOPS is available to perform - // the requested change + // Note: The storage plug-in in question should perform validation on the IOPS to check if a sufficient number of IOPS is available to perform + // the requested change - /* If this volume has never been beyond allocated state, short circuit everything and simply update the database. */ - // We need to publish this event to usage_volume table - if (volume.getState() == Volume.State.Allocated) { - logger.debug("Volume is in the allocated state, but has never been created. Simply updating database with new size and IOPS."); + /* If this volume has never been beyond allocated state, short circuit everything and simply update the database. */ + // We need to publish this event to usage_volume table + if (volume.getState() == Volume.State.Allocated) { + logger.debug("Volume is in the allocated state, but has never been created. Simply updating database with new size and IOPS."); - volume.setSize(newSize); - volume.setMinIops(newMinIops); - volume.setMaxIops(newMaxIops); - volume.setHypervisorSnapshotReserve(newHypervisorSnapshotReserve); + volume.setSize(newSize); + volume.setMinIops(newMinIops); + volume.setMaxIops(newMaxIops); + volume.setHypervisorSnapshotReserve(newHypervisorSnapshotReserve); - if (newDiskOffering != null) { - volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); - } - - _volsDao.update(volume.getId(), volume); - _resourceLimitMgr.updateVolumeResourceCountForDiskOfferingChange(volume.getAccountId(), volume.isDisplayVolume(), currentSize, newSize, - diskOffering, newDiskOffering); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); - return volume; - } - - Long newDiskOfferingId = newDiskOffering != null ? newDiskOffering.getId() : diskOffering.getId(); - - boolean volumeMigrateRequired = false; - List suitableStoragePoolsWithEnoughSpace = null; - StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); - if (!storageMgr.storagePoolHasEnoughSpaceForResize(storagePool, currentSize, newSize)) { - if (!autoMigrateVolume) { - throw new CloudRuntimeException(String.format("Failed to resize volume %s since the storage pool does not have enough space to accommodate new size for the volume %s, try with automigrate set to true in order to check in the other suitable pools for the new size and then migrate & resize volume there.", volume.getUuid(), volume.getName())); - } - Pair, List> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOfferingId, currentSize, newMinIops, newMaxIops, true, false); - List suitableStoragePools = poolsPair.second(); - if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resize failed for volume ID: %s as no suitable pool(s) found for migrating to support new disk offering or new size", volume.getUuid())); - } - final Long newSizeFinal = newSize; - suitableStoragePoolsWithEnoughSpace = suitableStoragePools.stream().filter(pool -> storageMgr.storagePoolHasEnoughSpaceForResize(pool, 0L, newSizeFinal)).collect(Collectors.toList()); - if (CollectionUtils.isEmpty(suitableStoragePoolsWithEnoughSpace)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resize failed for volume ID: %s as no suitable pool(s) with enough space found.", volume.getUuid())); - } - Collections.shuffle(suitableStoragePoolsWithEnoughSpace); - volumeMigrateRequired = true; - } - - boolean volumeResizeRequired = false; - if (currentSize != newSize || !compareEqualsIncludingNullOrZero(newMaxIops, volume.getMaxIops()) || !compareEqualsIncludingNullOrZero(newMinIops, volume.getMinIops())) { - volumeResizeRequired = true; - } - if (!volumeMigrateRequired && !volumeResizeRequired && newDiskOffering != null) { - _volsDao.updateDiskOffering(volume.getId(), newDiskOffering.getId()); - volume = _volsDao.findById(volume.getId()); - updateStorageWithTheNewDiskOffering(volume, newDiskOffering); - - return volume; - } - - if (volumeMigrateRequired) { - MigrateVolumeCmd migrateVolumeCmd = new MigrateVolumeCmd(volume.getId(), suitableStoragePoolsWithEnoughSpace.get(0).getId(), newDiskOfferingId, true); - try { - Volume result = migrateVolume(migrateVolumeCmd); - volume = (result != null) ? _volsDao.findById(result.getId()) : null; - if (volume == null) { - throw new CloudRuntimeException(String.format("Volume resize operation failed for volume ID: %s as migration failed to storage pool %s accommodating new size", volume.getUuid(), suitableStoragePoolsWithEnoughSpace.get(0).getId())); - } - } catch (Exception e) { - throw new CloudRuntimeException(String.format("Volume resize operation failed for volume ID: %s as migration failed to storage pool %s accommodating new size", volume.getUuid(), suitableStoragePoolsWithEnoughSpace.get(0).getId())); - } - } - - UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); - - if (userVm != null) { - // serialize VM operation - AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - - if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { - // avoid re-entrance - - VmWorkJobVO placeHolder = null; - - placeHolder = createPlaceHolderWork(userVm.getId()); - - try { - return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, - newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); - } finally { - _workJobDao.expunge(placeHolder.getId()); - } - } else { - Outcome outcome = resizeVolumeThroughJobQueue(userVm.getId(), volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, - newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); - - try { - outcome.get(); - } catch (InterruptedException e) { - throw new RuntimeException("Operation was interrupted", e); - } catch (ExecutionException e) { - throw new RuntimeException("Execution exception", e); + if (newDiskOffering != null) { + volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); } - Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + _volsDao.update(volume.getId(), volume); + _resourceLimitMgr.updateVolumeResourceCountForDiskOfferingChange(volume.getAccountId(), volume.isDisplayVolume(), currentSize, newSize, + diskOffering, newDiskOffering); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), + volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); + return volume; + } - if (jobResult != null) { - if (jobResult instanceof ConcurrentOperationException) { - throw (ConcurrentOperationException) jobResult; - } else if (jobResult instanceof ResourceAllocationException) { - throw (ResourceAllocationException) jobResult; - } else if (jobResult instanceof RuntimeException) { - throw (RuntimeException) jobResult; - } else if (jobResult instanceof Throwable) { - throw new RuntimeException("Unexpected exception", (Throwable) jobResult); - } else if (jobResult instanceof Long) { - return _volsDao.findById((Long) jobResult); - } + Long newDiskOfferingId = newDiskOffering != null ? newDiskOffering.getId() : diskOffering.getId(); + + boolean volumeMigrateRequired = false; + List suitableStoragePoolsWithEnoughSpace = null; + StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); + if (!storageMgr.storagePoolHasEnoughSpaceForResize(storagePool, currentSize, newSize)) { + if (!autoMigrateVolume) { + throw new CloudRuntimeException(String.format("Failed to resize volume %s since the storage pool does not have enough space to accommodate new size for the volume %s, try with automigrate set to true in order to check in the other suitable pools for the new size and then migrate & resize volume there.", volume.getUuid(), volume.getName())); } + Pair, List> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOfferingId, currentSize, newMinIops, newMaxIops, true, false); + List suitableStoragePools = poolsPair.second(); + if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resize failed for volume ID: %s as no suitable pool(s) found for migrating to support new disk offering or new size", volume.getUuid())); + } + final Long newSizeFinal = newSize; + suitableStoragePoolsWithEnoughSpace = suitableStoragePools.stream().filter(pool -> storageMgr.storagePoolHasEnoughSpaceForResize(pool, 0L, newSizeFinal)).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(suitableStoragePoolsWithEnoughSpace)) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resize failed for volume ID: %s as no suitable pool(s) with enough space found.", volume.getUuid())); + } + Collections.shuffle(suitableStoragePoolsWithEnoughSpace); + volumeMigrateRequired = true; + } + + boolean volumeResizeRequired = false; + if (currentSize != newSize || !compareEqualsIncludingNullOrZero(newMaxIops, volume.getMaxIops()) || !compareEqualsIncludingNullOrZero(newMinIops, volume.getMinIops())) { + volumeResizeRequired = true; + } + if (!volumeMigrateRequired && !volumeResizeRequired && newDiskOffering != null) { + _volsDao.updateDiskOffering(volume.getId(), newDiskOffering.getId()); + volume = _volsDao.findById(volume.getId()); + updateStorageWithTheNewDiskOffering(volume, newDiskOffering); return volume; } - } - return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, - shrinkOk); + if (volumeMigrateRequired) { + MigrateVolumeCmd migrateVolumeCmd = new MigrateVolumeCmd(volume.getId(), suitableStoragePoolsWithEnoughSpace.get(0).getId(), newDiskOfferingId, true); + try { + Volume result = migrateVolume(migrateVolumeCmd); + volume = (result != null) ? _volsDao.findById(result.getId()) : null; + if (volume == null) { + throw new CloudRuntimeException(String.format("Volume resize operation failed for volume ID: %s as migration failed to storage pool %s accommodating new size", volume.getUuid(), suitableStoragePoolsWithEnoughSpace.get(0).getId())); + } + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Volume resize operation failed for volume ID: %s as migration failed to storage pool %s accommodating new size", volume.getUuid(), suitableStoragePoolsWithEnoughSpace.get(0).getId())); + } + } + + UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); + + if (userVm != null) { + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + + if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + + VmWorkJobVO placeHolder = null; + + placeHolder = createPlaceHolderWork(userVm.getId()); + + try { + return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, + newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + } finally { + _workJobDao.expunge(placeHolder.getId()); + } + } else { + Outcome outcome = resizeVolumeThroughJobQueue(userVm.getId(), volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, + newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + + try { + outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation was interrupted", e); + } catch (ExecutionException e) { + throw new RuntimeException("Execution exception", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) { + throw (ConcurrentOperationException) jobResult; + } else if (jobResult instanceof ResourceAllocationException) { + throw (ResourceAllocationException) jobResult; + } else if (jobResult instanceof RuntimeException) { + throw (RuntimeException) jobResult; + } else if (jobResult instanceof Throwable) { + throw new RuntimeException("Unexpected exception", (Throwable) jobResult); + } else if (jobResult instanceof Long) { + return _volsDao.findById((Long) jobResult); + } + } + + return volume; + } + } + + return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops,newHypervisorSnapshotReserve, + newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); } finally { ReservationHelper.closeAll(reservations); @@ -2116,96 +2115,96 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic List reservations = new ArrayList<>(); try { - validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, existingDiskOffering, newDiskOffering, reservations); + validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, existingDiskOffering, newDiskOffering, reservations); - /* If this volume has never been beyond allocated state, short circuit everything and simply update the database. */ - // We need to publish this event to usage_volume table - if (volume.getState() == Volume.State.Allocated) { - logger.debug("Volume {} is in the allocated state, but has never been created. Simply updating database with new size and IOPS.", volume); + /* If this volume has never been beyond allocated state, short circuit everything and simply update the database. */ + // We need to publish this event to usage_volume table + if (volume.getState() == Volume.State.Allocated) { + logger.debug("Volume {} is in the allocated state, but has never been created. Simply updating database with new size and IOPS.", volume); - volume.setSize(newSize); - volume.setMinIops(newMinIops); - volume.setMaxIops(newMaxIops); - volume.setHypervisorSnapshotReserve(newHypervisorSnapshotReserve); + volume.setSize(newSize); + volume.setMinIops(newMinIops); + volume.setMaxIops(newMaxIops); + volume.setHypervisorSnapshotReserve(newHypervisorSnapshotReserve); - if (newDiskOffering != null) { - volume.setDiskOfferingId(newDiskOfferingId); - _volumeMgr.saveVolumeDetails(newDiskOfferingId, volume.getId()); - } - - _volsDao.update(volume.getId(), volume); - _resourceLimitMgr.updateVolumeResourceCountForDiskOfferingChange(volume.getAccountId(), volume.isDisplayVolume(), currentSize, newSize, - existingDiskOffering, newDiskOffering); - - if (currentSize != newSize) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); - } - return volume; - } - - if (currentSize != newSize || !compareEqualsIncludingNullOrZero(newMaxIops, volume.getMaxIops()) || !compareEqualsIncludingNullOrZero(newMinIops, volume.getMinIops())) { - volumeResizeRequired = true; - validateVolumeReadyStateAndHypervisorChecks(volume, currentSize, newSize); - } - - StoragePoolVO existingStoragePool = _storagePoolDao.findById(volume.getPoolId()); - - Pair, List> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOffering.getId(), currentSize, newMinIops, newMaxIops, true, false); - List suitableStoragePools = poolsPair.second(); - - if (!suitableStoragePools.stream().anyMatch(p -> (p.getId() == existingStoragePool.getId()))) { - volumeMigrateRequired = true; - if (!autoMigrateVolume) { - throw new InvalidParameterValueException(String.format("Failed to change offering for volume %s since automigrate is set to false but volume needs to migrated", volume.getUuid())); - } - } - - if (!volumeMigrateRequired && !volumeResizeRequired) { - _volsDao.updateDiskOffering(volume.getId(), newDiskOffering.getId()); - volume = _volsDao.findById(volume.getId()); - updateStorageWithTheNewDiskOffering(volume, newDiskOffering); - - return volume; - } - - if (volumeMigrateRequired) { - if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume change offering operation failed for volume: %s as no suitable pool(s) found for migrating to support new disk offering", volume)); - } - final Long newSizeFinal = newSize; - List suitableStoragePoolsWithEnoughSpace = suitableStoragePools.stream().filter(pool -> storageMgr.storagePoolHasEnoughSpaceForResize(pool, 0L, newSizeFinal)).collect(Collectors.toList()); - if (CollectionUtils.isEmpty(suitableStoragePoolsWithEnoughSpace)) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume change offering operation failed for volume: %s as no suitable pool(s) with enough space found for volume migration.", volume)); - } - Collections.shuffle(suitableStoragePoolsWithEnoughSpace); - MigrateVolumeCmd migrateVolumeCmd = new MigrateVolumeCmd(volume.getId(), suitableStoragePoolsWithEnoughSpace.get(0).getId(), newDiskOffering.getId(), true); - try { - Volume result = migrateVolume(migrateVolumeCmd); - volume = (result != null) ? _volsDao.findById(result.getId()) : null; - if (volume == null) { - throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume: %s migration failed to storage pool %s", volume, suitableStoragePools.get(0))); + if (newDiskOffering != null) { + volume.setDiskOfferingId(newDiskOfferingId); + _volumeMgr.saveVolumeDetails(newDiskOfferingId, volume.getId()); } - } catch (Exception e) { - throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume: %s migration failed to storage pool %s due to %s", volume, suitableStoragePools.get(0), e.getMessage())); - } - } - if (volumeResizeRequired) { - // refresh volume data - volume = _volsDao.findById(volume.getId()); - try { - volume = resizeVolumeInternal(volume, newDiskOffering, currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, shrinkOk); - } catch (Exception e) { - if (volumeMigrateRequired) { - logger.warn(String.format("Volume change offering operation succeeded for volume ID: %s but volume resize operation failed, so please try resize volume operation separately", volume.getUuid())); - } else { - throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume ID: %s due to resize volume operation failed", volume.getUuid())); + _volsDao.update(volume.getId(), volume); + _resourceLimitMgr.updateVolumeResourceCountForDiskOfferingChange(volume.getAccountId(), volume.isDisplayVolume(), currentSize, newSize, + existingDiskOffering, newDiskOffering); + + if (currentSize != newSize) { + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), + volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); + } + return volume; + } + + if (currentSize != newSize || !compareEqualsIncludingNullOrZero(newMaxIops, volume.getMaxIops()) || !compareEqualsIncludingNullOrZero(newMinIops, volume.getMinIops())) { + volumeResizeRequired = true; + validateVolumeReadyStateAndHypervisorChecks(volume, currentSize, newSize); + } + + StoragePoolVO existingStoragePool = _storagePoolDao.findById(volume.getPoolId()); + + Pair, List> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOffering.getId(), currentSize, newMinIops, newMaxIops, true, false); + List suitableStoragePools = poolsPair.second(); + + if (!suitableStoragePools.stream().anyMatch(p -> (p.getId() == existingStoragePool.getId()))) { + volumeMigrateRequired = true; + if (!autoMigrateVolume) { + throw new InvalidParameterValueException(String.format("Failed to change offering for volume %s since automigrate is set to false but volume needs to migrated", volume.getUuid())); } } - } - return volume; + if (!volumeMigrateRequired && !volumeResizeRequired) { + _volsDao.updateDiskOffering(volume.getId(), newDiskOffering.getId()); + volume = _volsDao.findById(volume.getId()); + updateStorageWithTheNewDiskOffering(volume, newDiskOffering); + + return volume; + } + + if (volumeMigrateRequired) { + if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume change offering operation failed for volume: %s as no suitable pool(s) found for migrating to support new disk offering", volume)); + } + final Long newSizeFinal = newSize; + List suitableStoragePoolsWithEnoughSpace = suitableStoragePools.stream().filter(pool -> storageMgr.storagePoolHasEnoughSpaceForResize(pool, 0L, newSizeFinal)).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(suitableStoragePoolsWithEnoughSpace)) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume change offering operation failed for volume: %s as no suitable pool(s) with enough space found for volume migration.", volume)); + } + Collections.shuffle(suitableStoragePoolsWithEnoughSpace); + MigrateVolumeCmd migrateVolumeCmd = new MigrateVolumeCmd(volume.getId(), suitableStoragePoolsWithEnoughSpace.get(0).getId(), newDiskOffering.getId(), true); + try { + Volume result = migrateVolume(migrateVolumeCmd); + volume = (result != null) ? _volsDao.findById(result.getId()) : null; + if (volume == null) { + throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume: %s migration failed to storage pool %s", volume, suitableStoragePools.get(0))); + } + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume: %s migration failed to storage pool %s due to %s", volume, suitableStoragePools.get(0), e.getMessage())); + } + } + + if (volumeResizeRequired) { + // refresh volume data + volume = _volsDao.findById(volume.getId()); + try { + volume = resizeVolumeInternal(volume, newDiskOffering, currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, shrinkOk); + } catch (Exception e) { + if (volumeMigrateRequired) { + logger.warn(String.format("Volume change offering operation succeeded for volume ID: %s but volume resize operation failed, so please try resize volume operation separately", volume.getUuid())); + } else { + throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume ID: %s due to resize volume operation failed", volume.getUuid())); + } + } + } + + return volume; } finally { ReservationHelper.closeAll(reservations); @@ -2690,13 +2689,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic try (CheckedReservation primaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, resourceLimitStorageTags, requiredPrimaryStorageSpace, reservationDao, _resourceLimitMgr)) { - _jobMgr.updateAsyncJobAttachment(job.getId(), "Volume", volumeId); + _jobMgr.updateAsyncJobAttachment(job.getId(), "Volume", volumeId); - if (asyncExecutionContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { - return safelyOrchestrateAttachVolume(vmId, volumeId, deviceId); - } else { - return getVolumeAttachJobResult(vmId, volumeId, deviceId); - } + if (asyncExecutionContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + return safelyOrchestrateAttachVolume(vmId, volumeId, deviceId); + } else { + return getVolumeAttachJobResult(vmId, volumeId, deviceId); + } } catch (ResourceAllocationException e) { logger.error("primary storage resource limit check failed", e); @@ -4247,16 +4246,16 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic List reservations = new ArrayList<>(); try { - _resourceLimitMgr.checkVolumeResourceLimit(newAccount, true, volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId()), reservations); + _resourceLimitMgr.checkVolumeResourceLimit(newAccount, true, volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId()), reservations); - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - updateVolumeAccount(oldAccount, volume, newAccount); - } - }); + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + updateVolumeAccount(oldAccount, volume, newAccount); + } + }); - return volume; + return volume; } finally { ReservationHelper.closeAll(reservations); diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index 34996710bdb..49b056e8813 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -403,23 +403,23 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, try (CheckedReservation templateReservation = new CheckedReservation(owner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr); CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, ResourceType.secondary_storage, null, null, secondaryStorageUsage, reservationDao, _resourceLimitMgr)) { - TemplateProfile profile = adapter.prepare(cmd); - VMTemplateVO template = adapter.create(profile); + TemplateProfile profile = adapter.prepare(cmd); + VMTemplateVO template = adapter.create(profile); - // Secondary storage resource usage will be incremented in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack - // for HypervisorTemplateAdapter - _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); - if (secondaryStorageUsage > 0) { - _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, secondaryStorageUsage); - } - - if (template != null) { - CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); - if (cmd instanceof RegisterVnfTemplateCmd) { - vnfTemplateManager.persistVnfTemplate(template.getId(), (RegisterVnfTemplateCmd) cmd); + // Secondary storage resource usage will be incremented in com.cloud.template.HypervisorTemplateAdapter.createTemplateAsyncCallBack + // for HypervisorTemplateAdapter + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + if (secondaryStorageUsage > 0) { + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, secondaryStorageUsage); + } + + if (template != null) { + CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); + if (cmd instanceof RegisterVnfTemplateCmd) { + vnfTemplateManager.persistVnfTemplate(template.getId(), (RegisterVnfTemplateCmd) cmd); + } + return template; } - return template; - } } throw new CloudRuntimeException("Failed to create a Template"); } @@ -2000,102 +2000,102 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, try (CheckedReservation templateReservation = new CheckedReservation(templateOwner, ResourceType.template, null, null, 1L, reservationDao, _resourceLimitMgr); CheckedReservation secondaryStorageReservation = new CheckedReservation(templateOwner, ResourceType.secondary_storage, null, null, templateSize, reservationDao, _resourceLimitMgr)) { - if (!isAdmin || featured == null) { - featured = Boolean.FALSE; - } - Long guestOSId = cmd.getOsTypeId(); - GuestOSVO guestOS = _guestOSDao.findById(guestOSId); - if (guestOS == null) { - throw new InvalidParameterValueException("GuestOS with ID: " + guestOSId + " does not exist."); - } - - Long nextTemplateId = _tmpltDao.getNextInSequence(Long.class, "id"); - String description = cmd.getDisplayText(); - boolean isExtractable = false; - Long sourceTemplateId = null; - if (volume != null) { - VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId()); - isExtractable = template != null && template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM; - if (template != null) { - arch = template.getArch(); + if (!isAdmin || featured == null) { + featured = Boolean.FALSE; } - if (volume.getIsoId() != null && volume.getIsoId() != 0) { - sourceTemplateId = volume.getIsoId(); - } else if (volume.getTemplateId() != null) { - sourceTemplateId = volume.getTemplateId(); + Long guestOSId = cmd.getOsTypeId(); + GuestOSVO guestOS = _guestOSDao.findById(guestOSId); + if (guestOS == null) { + throw new InvalidParameterValueException("GuestOS with ID: " + guestOSId + " does not exist."); } - } - String templateTag = cmd.getTemplateTag(); - if (templateTag != null) { - if (logger.isDebugEnabled()) { - logger.debug("Adding Template tag: " + templateTag); + + Long nextTemplateId = _tmpltDao.getNextInSequence(Long.class, "id"); + String description = cmd.getDisplayText(); + boolean isExtractable = false; + Long sourceTemplateId = null; + if (volume != null) { + VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId()); + isExtractable = template != null && template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM; + if (template != null) { + arch = template.getArch(); + } + if (volume.getIsoId() != null && volume.getIsoId() != 0) { + sourceTemplateId = volume.getIsoId(); + } else if (volume.getTemplateId() != null) { + sourceTemplateId = volume.getTemplateId(); + } } - } - privateTemplate = new VMTemplateVO(nextTemplateId, name, ImageFormat.RAW, isPublic, featured, isExtractable, - TemplateType.USER, null, requiresHvmValue, bitsValue, templateOwner.getId(), null, description, - passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag, cmd.getDetails(), sshKeyEnabledValue, isDynamicScalingEnabled, false, false, arch); - - if (sourceTemplateId != null) { - if (logger.isDebugEnabled()) { - logger.debug("This Template is getting created from other Template, setting source Template ID to: " + sourceTemplateId); + String templateTag = cmd.getTemplateTag(); + if (templateTag != null) { + if (logger.isDebugEnabled()) { + logger.debug("Adding Template tag: " + templateTag); + } } - } - - - // for region wide storage, set cross zones flag - List stores = _imgStoreDao.findRegionImageStores(); - if (!CollectionUtils.isEmpty(stores)) { - privateTemplate.setCrossZones(true); - } - - privateTemplate.setSourceTemplateId(sourceTemplateId); - - VMTemplateVO template = _tmpltDao.persist(privateTemplate); - // Increment the number of templates - if (template != null) { - Map details = new HashMap(); + privateTemplate = new VMTemplateVO(nextTemplateId, name, ImageFormat.RAW, isPublic, featured, isExtractable, + TemplateType.USER, null, requiresHvmValue, bitsValue, templateOwner.getId(), null, description, + passwordEnabledValue, guestOS.getId(), true, hyperType, templateTag, cmd.getDetails(), sshKeyEnabledValue, isDynamicScalingEnabled, false, false, arch); if (sourceTemplateId != null) { - VMTemplateVO sourceTemplate = _tmpltDao.findById(sourceTemplateId); - if (sourceTemplate != null && sourceTemplate.getDetails() != null) { - details.putAll(sourceTemplate.getDetails()); + if (logger.isDebugEnabled()) { + logger.debug("This Template is getting created from other Template, setting source Template ID to: " + sourceTemplateId); } } - if (volume != null) { - Long vmId = volume.getInstanceId(); - if (vmId != null) { - UserVmVO userVm = _userVmDao.findById(vmId); - if (userVm != null) { - _userVmDao.loadDetails(userVm); - Map vmDetails = userVm.getDetails(); - vmDetails = vmDetails.entrySet() - .stream() - .filter(map -> map.getValue() != null) - .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); - details.putAll(vmDetails); + + // for region wide storage, set cross zones flag + List stores = _imgStoreDao.findRegionImageStores(); + if (!CollectionUtils.isEmpty(stores)) { + privateTemplate.setCrossZones(true); + } + + privateTemplate.setSourceTemplateId(sourceTemplateId); + + VMTemplateVO template = _tmpltDao.persist(privateTemplate); + // Increment the number of templates + if (template != null) { + Map details = new HashMap(); + + if (sourceTemplateId != null) { + VMTemplateVO sourceTemplate = _tmpltDao.findById(sourceTemplateId); + if (sourceTemplate != null && sourceTemplate.getDetails() != null) { + details.putAll(sourceTemplate.getDetails()); } } - } - if (cmd.getDetails() != null) { - details.remove(VmDetailConstants.ENCRYPTED_PASSWORD); // new password will be generated during vm deployment from password enabled template - details.putAll(cmd.getDetails()); - } - if (!details.isEmpty()) { - privateTemplate.setDetails(details); - _tmpltDao.saveDetails(privateTemplate); + + if (volume != null) { + Long vmId = volume.getInstanceId(); + if (vmId != null) { + UserVmVO userVm = _userVmDao.findById(vmId); + if (userVm != null) { + _userVmDao.loadDetails(userVm); + Map vmDetails = userVm.getDetails(); + vmDetails = vmDetails.entrySet() + .stream() + .filter(map -> map.getValue() != null) + .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); + details.putAll(vmDetails); + } + } + } + if (cmd.getDetails() != null) { + details.remove(VmDetailConstants.ENCRYPTED_PASSWORD); // new password will be generated during vm deployment from password enabled template + details.putAll(cmd.getDetails()); + } + if (!details.isEmpty()) { + privateTemplate.setDetails(details); + _tmpltDao.saveDetails(privateTemplate); + } + + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.template); + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, templateSize); } - _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.template); - _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, templateSize); - } - - if (template != null) { - CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); - return template; - } else { - throw new CloudRuntimeException("Failed to create a Template"); - } + if (template != null) { + CallContext.current().putContextParameter(VirtualMachineTemplate.class, template.getUuid()); + return template; + } else { + throw new CloudRuntimeException("Failed to create a Template"); + } } } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 8b77cb506a8..c68a8618037 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -1356,30 +1356,30 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List reservations = new ArrayList<>(); try { - if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, - (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations); - } + if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + _resourceLimitMgr.checkVmResourceLimitsForServiceOfferingChange(owner, vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, + (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template, reservations); + } - // Check that the specified service offering ID is valid - _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering); + // Check that the specified service offering ID is valid + _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering); - // Check if the new service offering can be applied to vm instance - _accountMgr.checkAccess(owner, newServiceOffering, _dcDao.findById(vmInstance.getDataCenterId())); + // Check if the new service offering can be applied to vm instance + _accountMgr.checkAccess(owner, newServiceOffering, _dcDao.findById(vmInstance.getDataCenterId())); - // resize and migrate the root volume if required - DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newServiceOffering.getDiskOfferingId()); - changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters, vmInstance.getDataCenterId()); + // resize and migrate the root volume if required + DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newServiceOffering.getDiskOfferingId()); + changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters, vmInstance.getDataCenterId()); - _itMgr.upgradeVmDb(vmId, newServiceOffering, currentServiceOffering); + _itMgr.upgradeVmDb(vmId, newServiceOffering, currentServiceOffering); - // Increment or decrement CPU and Memory count accordingly. - if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - _resourceLimitMgr.updateVmResourceCountForServiceOfferingChange(owner.getAccountId(), vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, - (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template); - } + // Increment or decrement CPU and Memory count accordingly. + if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + _resourceLimitMgr.updateVmResourceCountForServiceOfferingChange(owner.getAccountId(), vmInstance.isDisplay(), (long) currentCpu, (long) newCpu, + (long) currentMemory, (long) newMemory, currentServiceOffering, newServiceOffering, template); + } - return _vmDao.findById(vmInstance.getId()); + return _vmDao.findById(vmInstance.getId()); } finally { ReservationHelper.closeAll(reservations); @@ -2338,34 +2338,34 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List reservations = new ArrayList<>(); try { - // First check that the maximum number of UserVMs, CPU and Memory limit for the given - // accountId will not be exceeded - if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template, reservations); - } + // First check that the maximum number of UserVMs, CPU and Memory limit for the given + // accountId will not be exceeded + if (!VirtualMachineManager.ResourceCountRunningVMsonly.value()) { + resourceLimitService.checkVmResourceLimit(account, vm.isDisplayVm(), serviceOffering, template, reservations); + } - _haMgr.cancelDestroy(vm, vm.getHostId()); + _haMgr.cancelDestroy(vm, vm.getHostId()); - try { - if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) { - logger.debug("Unable to recover the vm {} because it is not in the correct state. current state: {}", vm, vm.getState()); + try { + if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) { + logger.debug("Unable to recover the vm {} because it is not in the correct state. current state: {}", vm, vm.getState()); + throw new InvalidParameterValueException(String.format("Unable to recover the vm %s because it is not in the correct state. current state: %s", vm, vm.getState())); + } + } catch (NoTransitionException e) { throw new InvalidParameterValueException(String.format("Unable to recover the vm %s because it is not in the correct state. current state: %s", vm, vm.getState())); } - } catch (NoTransitionException e) { - throw new InvalidParameterValueException(String.format("Unable to recover the vm %s because it is not in the correct state. current state: %s", vm, vm.getState())); - } - // Recover the VM's disks - List volumes = _volsDao.findByInstance(vmId); - for (VolumeVO volume : volumes) { - if (volume.getVolumeType().equals(Volume.Type.ROOT)) { - recoverRootVolume(volume, vmId); - break; + // Recover the VM's disks + List volumes = _volsDao.findByInstance(vmId); + for (VolumeVO volume : volumes) { + if (volume.getVolumeType().equals(Volume.Type.ROOT)) { + recoverRootVolume(volume, vmId); + break; + } } - } - //Update Resource Count for the given account - resourceCountIncrement(account.getId(), vm.isDisplayVm(), serviceOffering, template); + //Update Resource Count for the given account + resourceCountIncrement(account.getId(), vm.isDisplayVm(), serviceOffering, template); } finally { ReservationHelper.closeAll(reservations); diff --git a/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java b/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java index 47aa57f728c..0fc1953dfef 100644 --- a/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/storage/volume/VolumeImportUnmanageManagerImpl.java @@ -211,20 +211,20 @@ public class VolumeImportUnmanageManagerImpl implements VolumeImportUnmanageServ List reservations = new ArrayList<>(); try { - // 6. check resource limitation - checkResourceLimitForImportVolume(owner, volume, diskOffering, reservations); + // 6. check resource limitation + checkResourceLimitForImportVolume(owner, volume, diskOffering, reservations); - // 7. create records - String volumeName = StringUtils.isNotBlank(cmd.getName()) ? cmd.getName().trim() : volumePath; - VolumeVO volumeVO = importVolumeInternal(volume, diskOffering, owner, pool, volumeName); + // 7. create records + String volumeName = StringUtils.isNotBlank(cmd.getName()) ? cmd.getName().trim() : volumePath; + VolumeVO volumeVO = importVolumeInternal(volume, diskOffering, owner, pool, volumeName); - // 8. Update resource count - updateResourceLimitForVolumeImport(volumeVO); + // 8. Update resource count + updateResourceLimitForVolumeImport(volumeVO); - // 9. Publish event - publicUsageEventForVolumeImportAndUnmanage(volumeVO, true); + // 9. Publish event + publicUsageEventForVolumeImportAndUnmanage(volumeVO, true); - return responseGenerator.createVolumeResponse(ResponseObject.ResponseView.Full, volumeVO); + return responseGenerator.createVolumeResponse(ResponseObject.ResponseView.Full, volumeVO); } finally { ReservationHelper.closeAll(reservations); diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index a32459ed059..1b588b04210 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -2466,91 +2466,91 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List reservations = new ArrayList<>(); try { - checkVolumeResourceLimitsForExternalKvmVmImport(owner, rootDisk, dataDisks, diskOffering, dataDiskOfferingMap, reservations); + checkVolumeResourceLimitsForExternalKvmVmImport(owner, rootDisk, dataDisks, diskOffering, dataDiskOfferingMap, reservations); - // Check NICs and supplied networks - Map nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap); - Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, Hypervisor.HypervisorType.KVM); - if (!CollectionUtils.isEmpty(unmanagedInstance.getNics())) { - allDetails.put(VmDetailConstants.NIC_ADAPTER, unmanagedInstance.getNics().get(0).getAdapterType()); - } - VirtualMachine.PowerState powerState = VirtualMachine.PowerState.PowerOff; + // Check NICs and supplied networks + Map nicIpAddressMap = getNicIpAddresses(unmanagedInstance.getNics(), callerNicIpAddressMap); + Map allNicNetworkMap = getUnmanagedNicNetworkMap(unmanagedInstance.getName(), unmanagedInstance.getNics(), nicNetworkMap, nicIpAddressMap, zone, hostName, owner, Hypervisor.HypervisorType.KVM); + if (!CollectionUtils.isEmpty(unmanagedInstance.getNics())) { + allDetails.put(VmDetailConstants.NIC_ADAPTER, unmanagedInstance.getNics().get(0).getAdapterType()); + } + VirtualMachine.PowerState powerState = VirtualMachine.PowerState.PowerOff; - try { - userVm = userVmManager.importVM(zone, null, template, null, displayName, owner, - null, caller, true, null, owner.getAccountId(), userId, - serviceOffering, null, hostName, - Hypervisor.HypervisorType.KVM, allDetails, powerState, null); - } catch (InsufficientCapacityException ice) { - logger.error(String.format("Failed to import vm name: %s", instanceName), ice); - throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage()); - } - if (userVm == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); - } - String rootVolumeName = String.format("ROOT-%s", userVm.getId()); - DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false); + try { + userVm = userVmManager.importVM(zone, null, template, null, displayName, owner, + null, caller, true, null, owner.getAccountId(), userId, + serviceOffering, null, hostName, + Hypervisor.HypervisorType.KVM, allDetails, powerState, null); + } catch (InsufficientCapacityException ice) { + logger.error(String.format("Failed to import vm name: %s", instanceName), ice); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage()); + } + if (userVm == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); + } + String rootVolumeName = String.format("ROOT-%s", userVm.getId()); + DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false); - DiskProfile[] dataDiskProfiles = new DiskProfile[dataDisks.size()]; - int diskSeq = 0; - for (UnmanagedInstanceTO.Disk disk : dataDisks) { - DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); - DiskProfile dataDiskProfile = volumeManager.allocateRawVolume(Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), offering, null, null, null, userVm, template, owner, null, false); - dataDiskProfiles[diskSeq++] = dataDiskProfile; - } - - final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null); - ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId()); - profile.setServiceOffering(dummyOffering); - DeploymentPlanner.ExcludeList excludeList = new DeploymentPlanner.ExcludeList(); - final DataCenterDeployment plan = new DataCenterDeployment(zone.getId(), null, null, null, null, null); - DeployDestination dest = null; - try { - dest = deploymentPlanningManager.planDeployment(profile, plan, excludeList, null); - } catch (Exception e) { - logger.warn("Import failed for Vm: {} while finding deployment destination", userVm, e); - cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s while finding deployment destination", userVm.getInstanceName())); - } - if(dest == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName())); - } - - List> diskProfileStoragePoolList = new ArrayList<>(); - try { - diskProfileStoragePoolList.add(importExternalDisk(rootDisk, userVm, dest, diskOffering, Volume.Type.ROOT, - template, null, remoteUrl, username, password, tmpPath, diskProfile)); - - long deviceId = 1L; - diskSeq = 0; + DiskProfile[] dataDiskProfiles = new DiskProfile[dataDisks.size()]; + int diskSeq = 0; for (UnmanagedInstanceTO.Disk disk : dataDisks) { - DiskProfile dataDiskProfile = dataDiskProfiles[diskSeq++]; DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); + DiskProfile dataDiskProfile = volumeManager.allocateRawVolume(Volume.Type.DATADISK, String.format("DATA-%d-%s", userVm.getId(), disk.getDiskId()), offering, null, null, null, userVm, template, owner, null, false); + dataDiskProfiles[diskSeq++] = dataDiskProfile; + } - diskProfileStoragePoolList.add(importExternalDisk(disk, userVm, dest, offering, Volume.Type.DATADISK, - template, deviceId, remoteUrl, username, password, tmpPath, dataDiskProfile)); - deviceId++; + final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null); + ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId()); + profile.setServiceOffering(dummyOffering); + DeploymentPlanner.ExcludeList excludeList = new DeploymentPlanner.ExcludeList(); + final DataCenterDeployment plan = new DataCenterDeployment(zone.getId(), null, null, null, null, null); + DeployDestination dest = null; + try { + dest = deploymentPlanningManager.planDeployment(profile, plan, excludeList, null); + } catch (Exception e) { + logger.warn("Import failed for Vm: {} while finding deployment destination", userVm, e); + cleanupFailedImportVM(userVm); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s while finding deployment destination", userVm.getInstanceName())); } - } catch (Exception e) { - logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e); - cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); - } - try { - int nicIndex = 0; - for (UnmanagedInstanceTO.Nic nic : unmanagedInstance.getNics()) { - Network network = networkDao.findById(allNicNetworkMap.get(nic.getNicId())); - Network.IpAddresses ipAddresses = nicIpAddressMap.get(nic.getNicId()); - importNic(nic, userVm, network, ipAddresses, nicIndex, nicIndex==0, true); - nicIndex++; + if(dest == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName())); } - } catch (Exception e) { - logger.error(String.format("Failed to import NICs while importing vm: %s", instanceName), e); - cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import NICs while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); - } - publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); - return userVm; + + List> diskProfileStoragePoolList = new ArrayList<>(); + try { + diskProfileStoragePoolList.add(importExternalDisk(rootDisk, userVm, dest, diskOffering, Volume.Type.ROOT, + template, null, remoteUrl, username, password, tmpPath, diskProfile)); + + long deviceId = 1L; + diskSeq = 0; + for (UnmanagedInstanceTO.Disk disk : dataDisks) { + DiskProfile dataDiskProfile = dataDiskProfiles[diskSeq++]; + DiskOffering offering = diskOfferingDao.findById(dataDiskOfferingMap.get(disk.getDiskId())); + + diskProfileStoragePoolList.add(importExternalDisk(disk, userVm, dest, offering, Volume.Type.DATADISK, + template, deviceId, remoteUrl, username, password, tmpPath, dataDiskProfile)); + deviceId++; + } + } catch (Exception e) { + logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e); + cleanupFailedImportVM(userVm); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); + } + try { + int nicIndex = 0; + for (UnmanagedInstanceTO.Nic nic : unmanagedInstance.getNics()) { + Network network = networkDao.findById(allNicNetworkMap.get(nic.getNicId())); + Network.IpAddresses ipAddresses = nicIpAddressMap.get(nic.getNicId()); + importNic(nic, userVm, network, ipAddresses, nicIndex, nicIndex==0, true); + nicIndex++; + } + } catch (Exception e) { + logger.error(String.format("Failed to import NICs while importing vm: %s", instanceName), e); + cleanupFailedImportVM(userVm); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import NICs while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); + } + publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); + return userVm; } finally { ReservationHelper.closeAll(reservations); @@ -2648,77 +2648,77 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List reservations = new ArrayList<>(); List resourceLimitStorageTags = resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); try { - CheckedReservation volumeReservation = new CheckedReservation(owner, Resource.ResourceType.volume, resourceLimitStorageTags, + CheckedReservation volumeReservation = new CheckedReservation(owner, Resource.ResourceType.volume, resourceLimitStorageTags, CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? 1L : 0L, reservationDao, resourceLimitService); - reservations.add(volumeReservation); + reservations.add(volumeReservation); - String rootVolumeName = String.format("ROOT-%s", userVm.getId()); - DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false); + String rootVolumeName = String.format("ROOT-%s", userVm.getId()); + DiskProfile diskProfile = volumeManager.allocateRawVolume(Volume.Type.ROOT, rootVolumeName, diskOffering, null, null, null, userVm, template, owner, null, false); - final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null); - ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId()); - profile.setServiceOffering(dummyOffering); - DeploymentPlanner.ExcludeList excludeList = new DeploymentPlanner.ExcludeList(); - final DataCenterDeployment plan = new DataCenterDeployment(zone.getId(), null, null, hostId, poolId, null); - DeployDestination dest = null; - try { - dest = deploymentPlanningManager.planDeployment(profile, plan, excludeList, null); - } catch (Exception e) { - logger.warn("Import failed for Vm: {} while finding deployment destination", userVm, e); - cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s while finding deployment destination", userVm.getInstanceName())); - } - if(dest == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName())); - } - - Map storage = dest.getStorageForDisks(); - Volume volume = volumeDao.findById(diskProfile.getVolumeId()); - StoragePool storagePool = storage.get(volume); - CheckVolumeCommand checkVolumeCommand = new CheckVolumeCommand(); - checkVolumeCommand.setSrcFile(diskPath); - StorageFilerTO storageTO = new StorageFilerTO(storagePool); - checkVolumeCommand.setStorageFilerTO(storageTO); - Answer answer = agentManager.easySend(dest.getHost().getId(), checkVolumeCommand); - if (!(answer instanceof CheckVolumeAnswer)) { - cleanupFailedImportVM(userVm); - throw new CloudRuntimeException("Disk not found or is invalid"); - } - CheckVolumeAnswer checkVolumeAnswer = (CheckVolumeAnswer) answer; - try { - checkVolume(checkVolumeAnswer.getVolumeDetails()); - } catch (CloudRuntimeException e) { - cleanupFailedImportVM(userVm); - throw e; - } - if (!checkVolumeAnswer.getResult()) { - cleanupFailedImportVM(userVm); - throw new CloudRuntimeException("Disk not found or is invalid"); - } - diskProfile.setSize(checkVolumeAnswer.getSize()); - - CheckedReservation primaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.primary_storage, resourceLimitStorageTags, - CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? diskProfile.getSize() : 0L, reservationDao, resourceLimitService); - reservations.add(primaryStorageReservation); - - List> diskProfileStoragePoolList = new ArrayList<>(); - try { - long deviceId = 1L; - if(ImportSource.SHARED == importSource) { - diskProfileStoragePoolList.add(importKVMSharedDisk(userVm, diskOffering, Volume.Type.ROOT, - template, deviceId, poolId, diskPath, diskProfile)); - } else if(ImportSource.LOCAL == importSource) { - diskProfileStoragePoolList.add(importKVMLocalDisk(userVm, diskOffering, Volume.Type.ROOT, - template, deviceId, hostId, diskPath, diskProfile)); + final VirtualMachineProfile profile = new VirtualMachineProfileImpl(userVm, template, serviceOffering, owner, null); + ServiceOfferingVO dummyOffering = serviceOfferingDao.findById(userVm.getId(), serviceOffering.getId()); + profile.setServiceOffering(dummyOffering); + DeploymentPlanner.ExcludeList excludeList = new DeploymentPlanner.ExcludeList(); + final DataCenterDeployment plan = new DataCenterDeployment(zone.getId(), null, null, hostId, poolId, null); + DeployDestination dest = null; + try { + dest = deploymentPlanningManager.planDeployment(profile, plan, excludeList, null); + } catch (Exception e) { + logger.warn("Import failed for Vm: {} while finding deployment destination", userVm, e); + cleanupFailedImportVM(userVm); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s while finding deployment destination", userVm.getInstanceName())); } - } catch (Exception e) { - logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e); - cleanupFailedImportVM(userVm); - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); - } - networkOrchestrationService.importNic(macAddress, 0, network, true, userVm, requestedIpPair, zone, true); - publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); - return userVm; + if(dest == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Import failed for Vm: %s. Suitable deployment destination not found", userVm.getInstanceName())); + } + + Map storage = dest.getStorageForDisks(); + Volume volume = volumeDao.findById(diskProfile.getVolumeId()); + StoragePool storagePool = storage.get(volume); + CheckVolumeCommand checkVolumeCommand = new CheckVolumeCommand(); + checkVolumeCommand.setSrcFile(diskPath); + StorageFilerTO storageTO = new StorageFilerTO(storagePool); + checkVolumeCommand.setStorageFilerTO(storageTO); + Answer answer = agentManager.easySend(dest.getHost().getId(), checkVolumeCommand); + if (!(answer instanceof CheckVolumeAnswer)) { + cleanupFailedImportVM(userVm); + throw new CloudRuntimeException("Disk not found or is invalid"); + } + CheckVolumeAnswer checkVolumeAnswer = (CheckVolumeAnswer) answer; + try { + checkVolume(checkVolumeAnswer.getVolumeDetails()); + } catch (CloudRuntimeException e) { + cleanupFailedImportVM(userVm); + throw e; + } + if (!checkVolumeAnswer.getResult()) { + cleanupFailedImportVM(userVm); + throw new CloudRuntimeException("Disk not found or is invalid"); + } + diskProfile.setSize(checkVolumeAnswer.getSize()); + + CheckedReservation primaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.primary_storage, resourceLimitStorageTags, + CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? diskProfile.getSize() : 0L, reservationDao, resourceLimitService); + reservations.add(primaryStorageReservation); + + List> diskProfileStoragePoolList = new ArrayList<>(); + try { + long deviceId = 1L; + if(ImportSource.SHARED == importSource) { + diskProfileStoragePoolList.add(importKVMSharedDisk(userVm, diskOffering, Volume.Type.ROOT, + template, deviceId, poolId, diskPath, diskProfile)); + } else if(ImportSource.LOCAL == importSource) { + diskProfileStoragePoolList.add(importKVMLocalDisk(userVm, diskOffering, Volume.Type.ROOT, + template, deviceId, hostId, diskPath, diskProfile)); + } + } catch (Exception e) { + logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e); + cleanupFailedImportVM(userVm); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import volumes while importing vm: %s. %s", instanceName, StringUtils.defaultString(e.getMessage()))); + } + networkOrchestrationService.importNic(macAddress, 0, network, true, userVm, requestedIpPair, zone, true); + publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); + return userVm; } catch (ResourceAllocationException e) { cleanupFailedImportVM(userVm); From 7cdcf571fac5e4a7f1e683668293d6251803f101 Mon Sep 17 00:00:00 2001 From: Vishesh <8760112+vishesh92@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:46:01 +0530 Subject: [PATCH 21/43] Fix xcpng test failures (#12812) --- ...st_redundant_router_deployment_planning.py | 786 +++++++++--------- .../integration/smoke/test_public_ip_range.py | 60 +- 2 files changed, 436 insertions(+), 410 deletions(-) diff --git a/test/integration/component/maint/test_redundant_router_deployment_planning.py b/test/integration/component/maint/test_redundant_router_deployment_planning.py index 5012fd1f2e9..d10e3ee5a7f 100644 --- a/test/integration/component/maint/test_redundant_router_deployment_planning.py +++ b/test/integration/component/maint/test_redundant_router_deployment_planning.py @@ -398,141 +398,146 @@ class TestRvRDeploymentPlanning(cloudstackTestCase): self.apiclient.updatePod(cmd) self.debug("Enabled first pod for testing..") - # Creating network using the network offering created - self.debug("Creating network with network offering: %s" % - self.network_offering.id) - network = Network.create( - self.apiclient, - self.services["network"], - accountid=self.account.name, - domainid=self.account.domainid, - networkofferingid=self.network_offering.id, - zoneid=self.zone.id - ) - self.debug("Created network with ID: %s" % network.id) + try: + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % network.id) - networks = Network.list( - self.apiclient, - id=network.id, - listall=True - ) - self.assertEqual( - isinstance(networks, list), - True, - "List networks should return a valid response for created network" - ) - nw_response = networks[0] + networks = Network.list( + self.apiclient, + id=network.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return a valid response for created network" + ) + nw_response = networks[0] - self.debug("Network state: %s" % nw_response.state) - self.assertEqual( - nw_response.state, - "Allocated", - "The network should be in allocated state after creation" - ) - - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - routers, - None, - "Routers should not be spawned when network is in allocated state" - ) - - self.debug("Deploying VM in account: %s" % self.account.name) - - # Spawn an instance in that network - virtual_machine = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - networkids=[str(network.id)] - ) - self.debug("Deployed VM in network: %s" % network.id) - - vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine.id, - listall=True - ) - self.assertEqual( - isinstance(vms, list), - True, - "List Vms should return a valid list" - ) - vm = vms[0] - self.assertEqual( - vm.state, - "Running", - "Vm should be in running state after deployment" - ) - - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - isinstance(routers, list), - True, - "list router should return Primary and backup routers" - ) - self.assertEqual( - len(routers), - 2, - "Length of the list router should be 2 (Backup & Primary)" - ) - - hosts = Host.list( - self.apiclient, - id=routers[0].hostid, - listall=True - ) - self.assertEqual( - isinstance(hosts, list), - True, - "List host should return a valid data" - ) - first_host = hosts[0] - - hosts = Host.list( - self.apiclient, - id=routers[1].hostid, - listall=True - ) - self.assertEqual( - isinstance(hosts, list), - True, - "List host should return a valid data" - ) - second_host = hosts[0] - - # Checking if the cluster IDs of both routers are different? - self.assertNotEqual( - first_host.clusterid, - second_host.clusterid, - "Both the routers should be in different clusters" - ) - self.debug("Enabling remaining pods if any..") - pods = Pod.list( - self.apiclient, - zoneid=self.zone.id, - listall=True, - allocationstate="Disabled" + self.debug("Network state: %s" % nw_response.state) + self.assertEqual( + nw_response.state, + "Allocated", + "The network should be in allocated state after creation" ) - if pods is not None: - for pod in pods: - cmd = updatePod.updatePodCmd() - cmd.id = pod.id - cmd.allocationstate = 'Enabled' - self.apiclient.updatePod(cmd) + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + routers, + None, + "Routers should not be spawned when network is in allocated state" + ) + + self.debug("Deploying VM in account: %s" % self.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)] + ) + self.debug("Deployed VM in network: %s" % network.id) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List Vms should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "Vm should be in running state after deployment" + ) + + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + isinstance(routers, list), + True, + "list router should return Primary and backup routers" + ) + self.assertEqual( + len(routers), + 2, + "Length of the list router should be 2 (Backup & Primary)" + ) + + hosts = Host.list( + self.apiclient, + id=routers[0].hostid, + listall=True + ) + self.assertEqual( + isinstance(hosts, list), + True, + "List host should return a valid data" + ) + first_host = hosts[0] + + hosts = Host.list( + self.apiclient, + id=routers[1].hostid, + listall=True + ) + self.assertEqual( + isinstance(hosts, list), + True, + "List host should return a valid data" + ) + second_host = hosts[0] + + # Checking if the cluster IDs of both routers are different? + self.assertNotEqual( + first_host.clusterid, + second_host.clusterid, + "Both the routers should be in different clusters" + ) + finally: + try: + self.debug("Enabling remaining pods if any..") + pods = Pod.list( + self.apiclient, + zoneid=self.zone.id, + listall=True, + allocationstate="Disabled" + ) + + if pods is not None: + for pod in pods: + cmd = updatePod.updatePodCmd() + cmd.id = pod.id + cmd.allocationstate = 'Enabled' + self.apiclient.updatePod(cmd) + except Exception as e: + self.debug("Warning: Exception during pod re-enablement: %s" % e) return # @attr(tags=["advanced", "advancedns"]) @@ -557,7 +562,7 @@ class TestRvRDeploymentPlanning(cloudstackTestCase): # 3. VM should be deployed and in Running state and on the specified # host # 4. There should be two routers (PRIMARY and BACKUP) for this network - # ensure both routers should be on different storage pools + # ensure both routers should be on different hosts self.debug( "Checking if the current zone has multiple active pods in it..") @@ -636,144 +641,150 @@ class TestRvRDeploymentPlanning(cloudstackTestCase): self.apiclient.updateCluster(cmd) self.debug("Enabled first cluster for testing..") - # Creating network using the network offering created - self.debug("Creating network with network offering: %s" % - self.network_offering.id) - network = Network.create( - self.apiclient, - self.services["network"], - accountid=self.account.name, - domainid=self.account.domainid, - networkofferingid=self.network_offering.id, - zoneid=self.zone.id - ) - self.debug("Created network with ID: %s" % network.id) + try: + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % network.id) - networks = Network.list( - self.apiclient, - id=network.id, - listall=True - ) - self.assertEqual( - isinstance(networks, list), - True, - "List networks should return a valid response for created network" - ) - nw_response = networks[0] + networks = Network.list( + self.apiclient, + id=network.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return a valid response for created network" + ) + nw_response = networks[0] - self.debug("Network state: %s" % nw_response.state) - self.assertEqual( - nw_response.state, - "Allocated", - "The network should be in allocated state after creation" - ) + self.debug("Network state: %s" % nw_response.state) + self.assertEqual( + nw_response.state, + "Allocated", + "The network should be in allocated state after creation" + ) - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - routers, - None, - "Routers should not be spawned when network is in allocated state" - ) - - self.debug("Retrieving the list of hosts in the cluster") - hosts = Host.list( - self.apiclient, - clusterid=enabled_cluster.id, - listall=True - ) - self.assertEqual( - isinstance(hosts, list), - True, - "List hosts should not return an empty response" - ) - host = hosts[0] - - self.debug("Deploying VM in account: %s" % self.account.name) - - # Spawn an instance in that network - virtual_machine = VirtualMachine.create( + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - networkids=[str(network.id)], - hostid=host.id - ) - self.debug("Deployed VM in network: %s" % network.id) - - vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine.id, + networkid=network.id, listall=True ) - self.assertEqual( - isinstance(vms, list), - True, - "List Vms should return a valid list" - ) - vm = vms[0] - self.assertEqual( - vm.state, - "Running", - "Vm should be in running state after deployment" - ) + self.assertEqual( + routers, + None, + "Routers should not be spawned when network is in allocated state" + ) - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( + self.debug("Retrieving the list of hosts in the cluster") + hosts = Host.list( self.apiclient, - networkid=network.id, + clusterid=enabled_cluster.id, listall=True ) - self.assertEqual( - isinstance(routers, list), - True, - "list router should return Primary and backup routers" - ) - self.assertEqual( - len(routers), - 2, - "Length of the list router should be 2 (Backup & Primary)" - ) - self.assertNotEqual( - routers[0].hostid, - routers[1].hostid, - "Both the routers should be in different storage pools" - ) - self.debug("Enabling remaining pods if any..") - pods = Pod.list( - self.apiclient, - zoneid=self.zone.id, - listall=True, - allocationstate="Disabled" + self.assertEqual( + isinstance(hosts, list), + True, + "List hosts should not return an empty response" + ) + host = hosts[0] + + self.debug("Deploying VM in account: %s" % self.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)], + hostid=host.id + ) + self.debug("Deployed VM in network: %s" % network.id) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List Vms should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "Vm should be in running state after deployment" + ) + + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + isinstance(routers, list), + True, + "list router should return Primary and backup routers" ) - if pods is not None: - for pod in pods: - cmd = updatePod.updatePodCmd() - cmd.id = pod.id - cmd.allocationstate = 'Enabled' - self.apiclient.updatePod(cmd) - - clusters = Cluster.list( - self.apiclient, - allocationstate="Disabled", - podid=enabled_pod.id, - listall=True + self.assertEqual( + len(routers), + 2, + "Length of the list router should be 2 (Backup & Primary)" + ) + self.assertNotEqual( + routers[0].hostid, + routers[1].hostid, + "Both the routers should be in different hosts" ) + finally: + try: + self.debug("Enabling remaining pods if any..") + pods = Pod.list( + self.apiclient, + zoneid=self.zone.id, + listall=True, + allocationstate="Disabled" + ) + if pods is not None: + for pod in pods: + cmd = updatePod.updatePodCmd() + cmd.id = pod.id + cmd.allocationstate = 'Enabled' + self.apiclient.updatePod(cmd) - if clusters is not None: - for cluster in clusters: - cmd = updateCluster.updateClusterCmd() - cmd.id = cluster.id - cmd.allocationstate = 'Enabled' - self.apiclient.updateCluster(cmd) + clusters = Cluster.list( + self.apiclient, + allocationstate="Disabled", + podid=enabled_pod.id, + listall=True + ) + + if clusters is not None: + for cluster in clusters: + cmd = updateCluster.updateClusterCmd() + cmd.id = cluster.id + cmd.allocationstate = 'Enabled' + self.apiclient.updateCluster(cmd) + except Exception as e: + self.debug("Warning: Exception during resource re-enablement: %s" % e) return + # @attr(tags=["advanced", "advancedns", "ssh"]) @attr(tags=["TODO"]) def test_RvR_multihosts(self): @@ -874,140 +885,145 @@ class TestRvRDeploymentPlanning(cloudstackTestCase): self.apiclient.updateCluster(cmd) self.debug("Enabled first cluster for testing..") - # Creating network using the network offering created - self.debug("Creating network with network offering: %s" % - self.network_offering.id) - network = Network.create( - self.apiclient, - self.services["network"], - accountid=self.account.name, - domainid=self.account.domainid, - networkofferingid=self.network_offering.id, - zoneid=self.zone.id - ) - self.debug("Created network with ID: %s" % network.id) + try: + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.name, + domainid=self.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % network.id) - networks = Network.list( - self.apiclient, - id=network.id, - listall=True - ) - self.assertEqual( - isinstance(networks, list), - True, - "List networks should return a valid response for created network" - ) - nw_response = networks[0] + networks = Network.list( + self.apiclient, + id=network.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return a valid response for created network" + ) + nw_response = networks[0] - self.debug("Network state: %s" % nw_response.state) - self.assertEqual( - nw_response.state, - "Allocated", - "The network should be in allocated state after creation" - ) - - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - routers, - None, - "Routers should not be spawned when network is in allocated state" - ) - - self.debug("Retrieving the list of hosts in the cluster") - hosts = Host.list( - self.apiclient, - clusterid=enabled_cluster.id, - listall=True - ) - self.assertEqual( - isinstance(hosts, list), - True, - "List hosts should not return an empty response" - ) - host = hosts[0] - - self.debug("Deploying VM in account: %s" % self.account.name) - - # Spawn an instance in that network - virtual_machine = VirtualMachine.create( - self.apiclient, - self.services["virtual_machine"], - accountid=self.account.name, - domainid=self.account.domainid, - serviceofferingid=self.service_offering.id, - networkids=[str(network.id)], - hostid=host.id - ) - self.debug("Deployed VM in network: %s" % network.id) - - vms = VirtualMachine.list( - self.apiclient, - id=virtual_machine.id, - listall=True - ) - self.assertEqual( - isinstance(vms, list), - True, - "List Vms should return a valid list" - ) - vm = vms[0] - self.assertEqual( - vm.state, - "Running", - "Vm should be in running state after deployment" - ) - - self.debug("Listing routers for network: %s" % network.name) - routers = Router.list( - self.apiclient, - networkid=network.id, - listall=True - ) - self.assertEqual( - isinstance(routers, list), - True, - "list router should return Primary and backup routers" - ) - self.assertEqual( - len(routers), - 2, - "Length of the list router should be 2 (Backup & Primary)" - ) - self.assertNotEqual( - routers[0].hostid, - routers[1].hostid, - "Both the routers should be in different hosts" - ) - self.debug("Enabling remaining pods if any..") - pods = Pod.list( - self.apiclient, - zoneid=self.zone.id, - listall=True, - allocationstate="Disabled" + self.debug("Network state: %s" % nw_response.state) + self.assertEqual( + nw_response.state, + "Allocated", + "The network should be in allocated state after creation" ) - if pods is not None: - for pod in pods: - cmd = updatePod.updatePodCmd() - cmd.id = pod.id - cmd.allocationstate = 'Enabled' - self.apiclient.updatePod(cmd) + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + routers, + None, + "Routers should not be spawned when network is in allocated state" + ) - clusters = Cluster.list( - self.apiclient, - allocationstate="Disabled", - podid=enabled_pod.id, - listall=True + self.debug("Retrieving the list of hosts in the cluster") + hosts = Host.list( + self.apiclient, + clusterid=enabled_cluster.id, + listall=True + ) + self.assertEqual( + isinstance(hosts, list), + True, + "List hosts should not return an empty response" + ) + host = hosts[0] + + self.debug("Deploying VM in account: %s" % self.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(network.id)], + hostid=host.id + ) + self.debug("Deployed VM in network: %s" % network.id) + + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List Vms should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "Vm should be in running state after deployment" + ) + + self.debug("Listing routers for network: %s" % network.name) + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertEqual( + isinstance(routers, list), + True, + "list router should return Primary and backup routers" + ) + self.assertEqual( + len(routers), + 2, + "Length of the list router should be 2 (Backup & Primary)" + ) + self.assertNotEqual( + routers[0].hostid, + routers[1].hostid, + "Both the routers should be in different hosts" ) - if clusters is not None: - for cluster in clusters: - cmd = updateCluster.updateClusterCmd() - cmd.id = cluster.id - cmd.allocationstate = 'Enabled' - self.apiclient.updateCluster(cmd) + finally: + try: + self.debug("Enabling remaining pods if any..") + pods = Pod.list( + self.apiclient, + zoneid=self.zone.id, + listall=True, + allocationstate="Disabled" + ) + + if pods is not None: + for pod in pods: + cmd = updatePod.updatePodCmd() + cmd.id = pod.id + cmd.allocationstate = 'Enabled' + self.apiclient.updatePod(cmd) + + clusters = Cluster.list( + self.apiclient, + allocationstate="Disabled", + podid=enabled_pod.id, + listall=True + ) + if clusters is not None: + for cluster in clusters: + cmd = updateCluster.updateClusterCmd() + cmd.id = cluster.id + cmd.allocationstate = 'Enabled' + self.apiclient.updateCluster(cmd) + except Exception as e: + self.debug("Warning: Exception during resource re-enablement: %s" % e) return diff --git a/test/integration/smoke/test_public_ip_range.py b/test/integration/smoke/test_public_ip_range.py index 19edc4c164f..997716caaaf 100644 --- a/test/integration/smoke/test_public_ip_range.py +++ b/test/integration/smoke/test_public_ip_range.py @@ -286,20 +286,25 @@ class TestDedicatePublicIPRange(cloudstackTestCase): cmd.allocationstate = 'Disabled' self.apiclient.updateZone(cmd) - # Delete System VM and IP range, so System VM can get IP from original ranges - self.debug("Destroying System VM: %s" % systemvm_id) - cmd = destroySystemVm.destroySystemVmCmd() - cmd.id = systemvm_id - self.apiclient.destroySystemVm(cmd) + try: + # Delete System VM and IP range, so System VM can get IP from original ranges + self.debug("Destroying System VM: %s" % systemvm_id) + cmd = destroySystemVm.destroySystemVmCmd() + cmd.id = systemvm_id + self.apiclient.destroySystemVm(cmd) - domain_id = self.public_ip_range.vlan.domainid - self.public_ip_range.delete(self.apiclient) + domain_id = self.public_ip_range.vlan.domainid + self.public_ip_range.delete(self.apiclient) - # Enable Zone - cmd = updateZone.updateZoneCmd() - cmd.id = self.zone.id - cmd.allocationstate = 'Enabled' - self.apiclient.updateZone(cmd) + finally: + # Enable Zone + try: + cmd = updateZone.updateZoneCmd() + cmd.id = self.zone.id + cmd.allocationstate = 'Enabled' + self.apiclient.updateZone(cmd) + except Exception as e: + self.debug("Warning: Exception during zone re-enablement in base_system_vm: %s" % e) # Wait for System VM to start and check System VM public IP systemvm_id = self.wait_for_system_vm_start( @@ -399,18 +404,23 @@ class TestDedicatePublicIPRange(cloudstackTestCase): cmd.allocationstate = 'Disabled' self.apiclient.updateZone(cmd) - # Delete System VM and IP range, so System VM can get IP from original ranges - if system_vms: - for v in system_vms: - self.debug("Destroying System VM: %s" % v.id) - cmd = destroySystemVm.destroySystemVmCmd() - cmd.id = v.id - self.apiclient.destroySystemVm(cmd) + try: + # Delete System VM and IP range, so System VM can get IP from original ranges + if system_vms: + for v in system_vms: + self.debug("Destroying System VM: %s" % v.id) + cmd = destroySystemVm.destroySystemVmCmd() + cmd.id = v.id + self.apiclient.destroySystemVm(cmd) - self.public_ip_range.delete(self.apiclient) + self.public_ip_range.delete(self.apiclient) - # Enable Zone - cmd = updateZone.updateZoneCmd() - cmd.id = self.zone.id - cmd.allocationstate = 'Enabled' - self.apiclient.updateZone(cmd) + finally: + # Enable Zone + try: + cmd = updateZone.updateZoneCmd() + cmd.id = self.zone.id + cmd.allocationstate = 'Enabled' + self.apiclient.updateZone(cmd) + except Exception as e: + self.debug("Warning: Exception during zone re-enablement in delete_range: %s" % e) From b497f58022ca86afa9eb2284422d16909e7eb9ca Mon Sep 17 00:00:00 2001 From: Pearl Dsilva Date: Thu, 19 Mar 2026 04:17:22 -0400 Subject: [PATCH 22/43] Fix K8s scaling and deletion issue if firewall rule is for ALL ports (#12806) --- .../KubernetesClusterResourceModifierActionWorker.java | 3 ++- .../actionworkers/KubernetesClusterScaleWorker.java | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index d92d0692ca1..bd59cbbee6b 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -517,7 +518,7 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu FirewallRule rule = null; List firewallRules = firewallRulesDao.listByIpAndPurposeAndNotRevoked(publicIp.getId(), FirewallRule.Purpose.Firewall); for (FirewallRuleVO firewallRule : firewallRules) { - if (firewallRule.getSourcePortStart() == CLUSTER_NODES_DEFAULT_START_SSH_PORT) { + if (Objects.equals(firewallRule.getSourcePortStart(), CLUSTER_NODES_DEFAULT_START_SSH_PORT)) { rule = firewallRule; firewallService.revokeIngressFwRule(firewallRule.getId(), true); logger.debug("The SSH firewall rule [%s] with the id [%s] was revoked",firewallRule.getName(),firewallRule.getId()); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java index f6828e3b203..38e919fc664 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterScaleWorker.java @@ -124,10 +124,14 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif // Remove existing SSH firewall rules FirewallRule firewallRule = removeSshFirewallRule(publicIp); + int existingFirewallRuleSourcePortEnd; if (firewallRule == null) { - throw new ManagementServerException("Firewall rule for node SSH access can't be provisioned"); + logger.warn("SSH firewall rule not found for Kubernetes cluster: {}. It may have been manually deleted or modified.", kubernetesCluster.getName()); + existingFirewallRuleSourcePortEnd = CLUSTER_NODES_DEFAULT_START_SSH_PORT + clusterVMIds.size() - 1; + } else { + existingFirewallRuleSourcePortEnd = firewallRule.getSourcePortEnd(); } - int existingFirewallRuleSourcePortEnd = firewallRule.getSourcePortEnd(); + try { removePortForwardingRules(publicIp, network, owner, CLUSTER_NODES_DEFAULT_START_SSH_PORT, existingFirewallRuleSourcePortEnd); } catch (ResourceUnavailableException e) { From c19630f0a4f34a82a9414b9b6d16b5ec44b86860 Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Tue, 17 Mar 2026 06:56:09 +0530 Subject: [PATCH 23/43] Fix snapshot copy resource limit concurrency --- .../storage/snapshot/SnapshotManagerImpl.java | 117 +++++++++--------- .../snapshot/SnapshotManagerImplTest.java | 4 +- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 4a4a7544ce7..0d6e9de509f 100755 --- a/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -1748,17 +1748,17 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement try (CheckedReservation volumeSnapshotReservation = new CheckedReservation(owner, ResourceType.snapshot, null, null, 1L, reservationDao, _resourceLimitMgr); CheckedReservation storageReservation = new CheckedReservation(owner, storeResourceType, null, null, volume.getSize(), reservationDao, _resourceLimitMgr)) { - SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName, - (short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), volume.getMinIops(), volume.getMaxIops(), hypervisorType, locationType); + SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName, + (short)snapshotType.ordinal(), snapshotType.name(), volume.getSize(), volume.getMinIops(), volume.getMaxIops(), hypervisorType, locationType); - SnapshotVO snapshot = _snapshotDao.persist(snapshotVO); - if (snapshot == null) { - throw new CloudRuntimeException(String.format("Failed to create snapshot for volume: %s", volume)); - } - CallContext.current().putContextParameter(Snapshot.class, snapshot.getUuid()); - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot); - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), storeResourceType, volume.getSize()); - return snapshot; + SnapshotVO snapshot = _snapshotDao.persist(snapshotVO); + if (snapshot == null) { + throw new CloudRuntimeException(String.format("Failed to create snapshot for volume: %s", volume)); + } + CallContext.current().putContextParameter(Snapshot.class, snapshot.getUuid()); + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot); + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), storeResourceType, volume.getSize()); + return snapshot; } catch (ResourceAllocationException e) { if (snapshotType != Type.MANUAL) { String msg = String.format("Snapshot resource limit exceeded for account id : %s. Failed to create recurring snapshots", owner.getId()); @@ -1807,50 +1807,54 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement @DB private boolean copySnapshotToZone(SnapshotDataStoreVO snapshotDataStoreVO, DataStore srcSecStore, - DataCenterVO dstZone, DataStore dstSecStore, Account account) + DataCenterVO dstZone, DataStore dstSecStore, Account account, boolean shouldCheckResourceLimits) throws ResourceAllocationException { final long snapshotId = snapshotDataStoreVO.getSnapshotId(); final long dstZoneId = dstZone.getId(); if (checkAndProcessSnapshotAlreadyExistInStore(snapshotId, dstSecStore)) { return true; } - _resourceLimitMgr.checkResourceLimit(account, ResourceType.secondary_storage, snapshotDataStoreVO.getSize()); - // snapshotId may refer to ID of a removed parent snapshot - SnapshotInfo snapshotOnSecondary = snapshotFactory.getSnapshot(snapshotId, srcSecStore); - String copyUrl = null; - try { - AsyncCallFuture future = snapshotSrv.queryCopySnapshot(snapshotOnSecondary); - CreateCmdResult result = future.get(); - if (!result.isFailed()) { - copyUrl = result.getPath(); + // Resource limit checks are not performed here at the moment, but they were added in case this method is used + // in the future to copy a standalone snapshot + long requiredSecondaryStorageSpace = shouldCheckResourceLimits ? snapshotDataStoreVO.getSize() : 0L; + try (CheckedReservation secondaryStorageReservation = new CheckedReservation(account, ResourceType.secondary_storage, null, null, null, requiredSecondaryStorageSpace, null, reservationDao, _resourceLimitMgr)) { + SnapshotInfo snapshotOnSecondary = snapshotFactory.getSnapshot(snapshotId, srcSecStore); + String copyUrl = null; + try { + AsyncCallFuture future = snapshotSrv.queryCopySnapshot(snapshotOnSecondary); + CreateCmdResult result = future.get(); + if (!result.isFailed()) { + copyUrl = result.getPath(); + } + } catch (InterruptedException | ExecutionException | ResourceUnavailableException ex) { + logger.error("Failed to prepare URL for copy for snapshot ID: {} on store: {}", snapshotId, srcSecStore, ex); } - } catch (InterruptedException | ExecutionException | ResourceUnavailableException ex) { - logger.error("Failed to prepare URL for copy for snapshot ID: {} on store: {}", snapshotId, srcSecStore, ex); - } - if (StringUtils.isEmpty(copyUrl)) { - logger.error("Unable to prepare URL for copy for snapshot ID: {} on store: {}", snapshotId, srcSecStore); - return false; - } - logger.debug(String.format("Copying snapshot ID: %d to destination zones using download URL: %s", snapshotId, copyUrl)); - try { - AsyncCallFuture future = snapshotSrv.copySnapshot(snapshotOnSecondary, copyUrl, dstSecStore); - SnapshotResult result = future.get(); - if (result.isFailed()) { - logger.debug("Copy snapshot ID: {} failed for image store {}: {}", snapshotId, dstSecStore, result.getResult()); + if (StringUtils.isEmpty(copyUrl)) { + logger.error("Unable to prepare URL for copy for snapshot ID: {} on store: {}", snapshotId, srcSecStore); return false; } - snapshotZoneDao.addSnapshotToZone(snapshotId, dstZoneId); - _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.secondary_storage, snapshotDataStoreVO.getSize()); - if (account.getId() != Account.ACCOUNT_ID_SYSTEM) { - SnapshotVO snapshotVO = _snapshotDao.findByIdIncludingRemoved(snapshotId); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_COPY, account.getId(), dstZoneId, snapshotId, null, null, null, snapshotVO.getSize(), - snapshotVO.getSize(), snapshotVO.getClass().getName(), snapshotVO.getUuid()); + logger.debug(String.format("Copying snapshot ID: %d to destination zones using download URL: %s", snapshotId, copyUrl)); + try { + AsyncCallFuture future = snapshotSrv.copySnapshot(snapshotOnSecondary, copyUrl, dstSecStore); + SnapshotResult result = future.get(); + if (result.isFailed()) { + logger.debug("Copy snapshot ID: {} failed for image store {}: {}", snapshotId, dstSecStore, result.getResult()); + return false; + } + snapshotZoneDao.addSnapshotToZone(snapshotId, dstZoneId); + _resourceLimitMgr.incrementResourceCount(account.getId(), ResourceType.secondary_storage, snapshotDataStoreVO.getSize()); + if (account.getId() != Account.ACCOUNT_ID_SYSTEM) { + SnapshotVO snapshotVO = _snapshotDao.findByIdIncludingRemoved(snapshotId); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_COPY, account.getId(), dstZoneId, snapshotId, null, null, null, snapshotVO.getSize(), + snapshotVO.getSize(), snapshotVO.getClass().getName(), snapshotVO.getUuid()); + } + + return true; + } catch (InterruptedException | ExecutionException | ResourceUnavailableException ex) { + logger.debug("Failed to copy snapshot ID: {} to image store: {}", snapshotId, dstSecStore); } - return true; - } catch (InterruptedException | ExecutionException | ResourceUnavailableException ex) { - logger.debug("Failed to copy snapshot ID: {} to image store: {}", snapshotId, dstSecStore); + return false; } - return false; } @DB @@ -1881,13 +1885,6 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement if (CollectionUtils.isEmpty(snapshotChain)) { return true; } - try { - _resourceLimitMgr.checkResourceLimit(account, ResourceType.secondary_storage, size); - } catch (ResourceAllocationException e) { - logger.error(String.format("Unable to allocate secondary storage resources for snapshot chain for %s with size: %d", snapshotVO, size), e); - return false; - } - Collections.reverse(snapshotChain); if (dstSecStore == null) { // find all eligible image stores for the destination zone List dstSecStores = dataStoreMgr.getImageStoresByScopeExcludingReadOnly(new ZoneScope(destZoneId)); @@ -1899,15 +1896,21 @@ public class SnapshotManagerImpl extends MutualExclusiveIdsManagerBase implement throw new StorageUnavailableException("Destination zone is not ready, no image store with free capacity", DataCenter.class, destZoneId); } } - logger.debug("Copying snapshot chain for snapshot ID: {} on secondary store: {} of zone ID: {}", snapshotVO, dstSecStore, destZone); - for (SnapshotDataStoreVO snapshotDataStoreVO : snapshotChain) { - if (!copySnapshotToZone(snapshotDataStoreVO, srcSecStore, destZone, dstSecStore, account)) { - logger.error("Failed to copy snapshot: {} to zone: {} due to failure to copy snapshot ID: {} from snapshot chain", - snapshotVO, destZone, snapshotDataStoreVO.getSnapshotId()); - return false; + try (CheckedReservation secondaryStorageReservation = new CheckedReservation(account, ResourceType.secondary_storage, null, null, null, size, null, reservationDao, _resourceLimitMgr)) { + logger.debug("Copying snapshot chain for snapshot ID: {} on secondary store: {} of zone ID: {}", snapshotVO, dstSecStore, destZone); + Collections.reverse(snapshotChain); + for (SnapshotDataStoreVO snapshotDataStoreVO : snapshotChain) { + if (!copySnapshotToZone(snapshotDataStoreVO, srcSecStore, destZone, dstSecStore, account, false)) { + logger.error("Failed to copy snapshot: {} to zone: {} due to failure to copy snapshot ID: {} from snapshot chain", + snapshotVO, destZone, snapshotDataStoreVO.getSnapshotId()); + return false; + } } + return true; + } catch (ResourceAllocationException e) { + logger.error(String.format("Unable to allocate secondary storage resources for snapshot chain for %s with size: %d", snapshotVO, size), e); + return false; } - return true; } @DB diff --git a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java index 86fdcfecc13..32b103f39d3 100644 --- a/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java +++ b/server/src/test/java/com/cloud/storage/snapshot/SnapshotManagerImplTest.java @@ -46,7 +46,6 @@ import com.cloud.dc.dao.DataCenterDao; import com.cloud.event.ActionEventUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; -import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.org.Grouping; import com.cloud.storage.DataStoreRole; @@ -284,12 +283,11 @@ public class SnapshotManagerImplTest { Mockito.when(result1.isFailed()).thenReturn(false); AsyncCallFuture future1 = Mockito.mock(AsyncCallFuture.class); try { - Mockito.doNothing().when(resourceLimitService).checkResourceLimit(Mockito.any(), Mockito.any(), Mockito.anyLong()); Mockito.when(future.get()).thenReturn(result); Mockito.when(snapshotService.queryCopySnapshot(Mockito.any())).thenReturn(future); Mockito.when(future1.get()).thenReturn(result1); Mockito.when(snapshotService.copySnapshot(Mockito.any(SnapshotInfo.class), Mockito.anyString(), Mockito.any(DataStore.class))).thenReturn(future1); - } catch (ResourceAllocationException | ResourceUnavailableException | ExecutionException | InterruptedException e) { + } catch (ResourceUnavailableException | ExecutionException | InterruptedException e) { Assert.fail(e.getMessage()); } List addedZone = new ArrayList<>(); From 4b7370a6017a6b886576790ed5f284a884abb172 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Thu, 26 Mar 2026 08:38:12 +0100 Subject: [PATCH 24/43] upgrade: skip the upgrade paths which are not needed (#12881) * upgrade: skip the upgrade paths which are not needed --- .../upgrade/DatabaseVersionHierarchy.java | 5 ++++- .../META-INF/db/schema-42000to42010.sql | 2 +- .../upgrade/DatabaseUpgradeCheckerTest.java | 21 +++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java index 445a59310fb..377b2f91375 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseVersionHierarchy.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.upgrade; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -96,7 +97,9 @@ public final class DatabaseVersionHierarchy { // we cannot find the version specified, so get the // most recent one immediately before this version if (!contains(fromVersion)) { - return getPath(getRecentVersion(fromVersion), toVersion); + DbUpgrade[] dbUpgrades = getPath(getRecentVersion(fromVersion), toVersion); + return Arrays.stream(dbUpgrades).filter(up -> CloudStackVersion.compare(up.getUpgradedVersion(), fromVersion.toString()) > 0) + .toArray(DbUpgrade[]::new); } final Predicate predicate; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql index 3dd6c18f57c..4405250f271 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql @@ -31,7 +31,7 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" '); -- Create a new group for Usage Server related configurations -INSERT INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Usage Server', 'Usage Server related configuration', 9); +INSERT IGNORE INTO `cloud`.`configuration_group` (`name`, `description`, `precedence`) VALUES ('Usage Server', 'Usage Server related configuration', 9); UPDATE `cloud`.`configuration_subgroup` set `group_id` = (SELECT `id` FROM `cloud`.`configuration_group` WHERE `name` = 'Usage Server'), `precedence` = 1 WHERE `name`='Usage'; UPDATE `cloud`.`configuration` SET `group_id` = (SELECT `id` FROM `cloud`.`configuration_group` WHERE `name` = 'Usage Server') where `subgroup_id` = (SELECT `id` FROM `cloud`.`configuration_subgroup` WHERE `name` = 'Usage'); diff --git a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java index 27995eb179a..1b84fc3a9e3 100644 --- a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java +++ b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java @@ -44,6 +44,7 @@ import com.cloud.upgrade.dao.Upgrade41120to41130; import com.cloud.upgrade.dao.Upgrade41120to41200; import com.cloud.upgrade.dao.Upgrade41510to41520; import com.cloud.upgrade.dao.Upgrade41610to41700; +import com.cloud.upgrade.dao.Upgrade42020to42030; import com.cloud.upgrade.dao.Upgrade452to453; import com.cloud.upgrade.dao.Upgrade453to460; import com.cloud.upgrade.dao.Upgrade460to461; @@ -380,4 +381,24 @@ public class DatabaseUpgradeCheckerTest { assertFalse("DatabaseUpgradeChecker should not be a standalone component", checker.isStandalone()); } + @Test + public void testCalculateUpgradePath42010to42030() { + + final CloudStackVersion dbVersion = CloudStackVersion.parse("4.20.1.0"); + assertNotNull(dbVersion); + + final CloudStackVersion currentVersion = CloudStackVersion.parse("4.20.3.0"); + assertNotNull(currentVersion); + + final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker(); + final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion); + + assertNotNull(upgrades); + assertEquals(1, upgrades.length); + assertTrue(upgrades[0] instanceof Upgrade42020to42030); + + assertArrayEquals(new String[]{"4.20.2.0", "4.20.3.0"}, upgrades[0].getUpgradableVersionRange()); + assertEquals(currentVersion.toString(), upgrades[0].getUpgradedVersion()); + + } } From b22dbbe2d7ad128f5d4b73f9b24586ce095cc1d6 Mon Sep 17 00:00:00 2001 From: Abhisar Sinha <63767682+abh1sar@users.noreply.github.com> Date: Fri, 27 Mar 2026 10:19:52 +0530 Subject: [PATCH 25/43] Fix Revert Instance to Snapshot with custom service offering (#12885) * Fix revertVM with custom svc offering --- .../vm/snapshot/VMSnapshotManagerImpl.java | 54 +++++++++++++------ .../vm/snapshot/VMSnapshotManagerTest.java | 50 +++++++++++++---- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java index 8e7ce351246..b2ac831a633 100644 --- a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -754,11 +754,17 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme "Instance Snapshot reverting failed because the Instance is not in Running or Stopped state."); } - if (userVm.getState() == VirtualMachine.State.Running && vmSnapshotVo.getType() == VMSnapshot.Type.Disk || userVm.getState() == VirtualMachine.State.Stopped - && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory) { + if (userVm.getState() == VirtualMachine.State.Running && vmSnapshotVo.getType() == VMSnapshot.Type.Disk) { throw new InvalidParameterValueException( - "Reverting to the Instance Snapshot is not allowed for running Instances as this would result in a Instance state change. For running Instances only Snapshots with memory can be reverted. In order to revert to a Snapshot without memory you need to first stop the Instance." - + " Snapshot"); + "Reverting to the Instance Snapshot is not allowed for running Instances as this would result in an Instance state change. " + + "For running Instances only Snapshots with memory can be reverted. " + + "In order to revert to a Snapshot without memory you need to first stop the Instance."); + } + + if (userVm.getState() == VirtualMachine.State.Stopped && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory) { + throw new InvalidParameterValueException( + "Reverting to the Instance Snapshot is not allowed for stopped Instances when the Snapshot contains memory as this would result in an Instance state change. " + + "In order to revert to a Snapshot with memory you need to first start the Instance."); } // if snapshot is not created, error out @@ -811,20 +817,36 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme } /** - * If snapshot was taken with a different service offering than actual used in vm, should change it back to it. - * We also call changeUserVmServiceOffering in case the service offering is dynamic in order to - * perform resource limit validation, as the amount of CPUs or memory may have been changed. - * @param vmSnapshotVo vm snapshot + * Check if service offering change is needed for user vm when reverting to vm snapshot. + * Service offering change is needed when snapshot was taken with a different service offering than actual used in vm. + * Service offering change is also needed when service offering is dynamic and the amount of cpu, memory or cpu speed + * has been changed since snapshot was taken. + * @param userVm + * @param vmSnapshotVo + * @return true if service offering change is needed; false otherwise */ - protected void updateUserVmServiceOffering(UserVm userVm, VMSnapshotVO vmSnapshotVo) { + protected boolean userVmServiceOfferingNeedsChange(UserVm userVm, VMSnapshotVO vmSnapshotVo) { if (vmSnapshotVo.getServiceOfferingId() != userVm.getServiceOfferingId()) { - changeUserVmServiceOffering(userVm, vmSnapshotVo); - return; + return true; } - ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(userVm.getServiceOfferingId()); - if (serviceOffering.isDynamic()) { - changeUserVmServiceOffering(userVm, vmSnapshotVo); + + ServiceOfferingVO currentServiceOffering = _serviceOfferingDao.findByIdIncludingRemoved(userVm.getId(), userVm.getServiceOfferingId()); + if (currentServiceOffering.isDynamic()) { + Map vmDetails = getVmMapDetails(vmSnapshotVo); + ServiceOfferingVO newServiceOffering = _serviceOfferingDao.getComputeOffering(currentServiceOffering, vmDetails); + + int newCpu = newServiceOffering.getCpu(); + int newMemory = newServiceOffering.getRamSize(); + int newSpeed = newServiceOffering.getSpeed(); + int currentCpu = currentServiceOffering.getCpu(); + int currentMemory = currentServiceOffering.getRamSize(); + int currentSpeed = currentServiceOffering.getSpeed(); + + if (newCpu != currentCpu || newMemory != currentMemory || newSpeed != currentSpeed) { + return true; + } } + return false; } /** @@ -944,7 +966,9 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) throws CloudRuntimeException { - updateUserVmServiceOffering(userVm, vmSnapshotVo); + if (userVmServiceOfferingNeedsChange(userVm, vmSnapshotVo)) { + changeUserVmServiceOffering(userVm, vmSnapshotVo); + } revertCustomServiceOfferingDetailsFromVmSnapshot(userVm, vmSnapshotVo); } }); diff --git a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java index 01512b448b2..26f452cb9f4 100644 --- a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java +++ b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java @@ -228,6 +228,7 @@ public class VMSnapshotManagerTest { when(vmSnapshotVO.getId()).thenReturn(VM_SNAPSHOT_ID); when(serviceOffering.isDynamic()).thenReturn(false); when(_serviceOfferingDao.findById(SERVICE_OFFERING_ID)).thenReturn(serviceOffering); + when(_serviceOfferingDao.findByIdIncludingRemoved(TEST_VM_ID, SERVICE_OFFERING_ID)).thenReturn(serviceOffering); for (ResourceDetail detail : Arrays.asList(userVmDetailCpuNumber, vmSnapshotDetailCpuNumber)) { when(detail.getName()).thenReturn(VmDetailConstants.CPU_NUMBER); @@ -345,20 +346,51 @@ public class VMSnapshotManagerTest { } @Test - public void testUpdateUserVmServiceOfferingSameServiceOffering() { - _vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO); - verify(_vmSnapshotMgr, never()).changeUserVmServiceOffering(userVm, vmSnapshotVO); + public void testUserVmServiceOfferingNeedsChangeWhenSnapshotOfferingDiffers() { + when(userVm.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_DIFFERENT_ID); + when(vmSnapshotVO.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_ID); + + assertTrue(_vmSnapshotMgr.userVmServiceOfferingNeedsChange(userVm, vmSnapshotVO)); + + verify(_serviceOfferingDao, never()).findByIdIncludingRemoved(anyLong(), anyLong()); + verify(_serviceOfferingDao, never()).getComputeOffering(any(ServiceOfferingVO.class), any()); } @Test - public void testUpdateUserVmServiceOfferingDifferentServiceOffering() throws ConcurrentOperationException, ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException { - when(userVm.getServiceOfferingId()).thenReturn(SERVICE_OFFERING_DIFFERENT_ID); - when(_userVmManager.upgradeVirtualMachine(eq(TEST_VM_ID), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture())).thenReturn(true); - _vmSnapshotMgr.updateUserVmServiceOffering(userVm, vmSnapshotVO); + public void testUserVmServiceOfferingNeedsChangeWhenSameNonDynamicOffering() { + assertFalse(_vmSnapshotMgr.userVmServiceOfferingNeedsChange(userVm, vmSnapshotVO)); - verify(_vmSnapshotMgr).changeUserVmServiceOffering(userVm, vmSnapshotVO); + verify(_serviceOfferingDao).findByIdIncludingRemoved(TEST_VM_ID, SERVICE_OFFERING_ID); + verify(_serviceOfferingDao, never()).getComputeOffering(any(ServiceOfferingVO.class), any()); + } + + @Test + public void testUserVmServiceOfferingNeedsChangeWhenDynamicOfferingMatchesSnapshot() { + when(serviceOffering.isDynamic()).thenReturn(true); + when(serviceOffering.getCpu()).thenReturn(2); + when(serviceOffering.getRamSize()).thenReturn(2048); + when(serviceOffering.getSpeed()).thenReturn(1000); + when(_serviceOfferingDao.getComputeOffering(eq(serviceOffering), any())).thenReturn(serviceOffering); + + assertFalse(_vmSnapshotMgr.userVmServiceOfferingNeedsChange(userVm, vmSnapshotVO)); + + verify(_serviceOfferingDao).getComputeOffering(eq(serviceOffering), any()); verify(_vmSnapshotMgr).getVmMapDetails(vmSnapshotVO); - verify(_vmSnapshotMgr).upgradeUserVmServiceOffering(eq(userVm), eq(SERVICE_OFFERING_ID), mapDetailsCaptor.capture()); + } + + @Test + public void testUserVmServiceOfferingNeedsChangeWhenDynamicCpuDiffersFromSnapshot() { + when(serviceOffering.isDynamic()).thenReturn(true); + when(serviceOffering.getCpu()).thenReturn(2); + when(serviceOffering.getRamSize()).thenReturn(2048); + when(serviceOffering.getSpeed()).thenReturn(1000); + ServiceOfferingVO fromSnapshot = mock(ServiceOfferingVO.class); + when(fromSnapshot.getCpu()).thenReturn(4); + when(fromSnapshot.getRamSize()).thenReturn(2048); + when(fromSnapshot.getSpeed()).thenReturn(1000); + when(_serviceOfferingDao.getComputeOffering(eq(serviceOffering), any())).thenReturn(fromSnapshot); + + assertTrue(_vmSnapshotMgr.userVmServiceOfferingNeedsChange(userVm, vmSnapshotVO)); } @Test From 2416db2a443932ee9ad924d7f3948c7c1d4c698e Mon Sep 17 00:00:00 2001 From: Fabricio Duarte Date: Fri, 27 Mar 2026 01:55:16 -0300 Subject: [PATCH 26/43] Fix NPE on external/unmanaged instance import using custom offerings (#12884) * Fix NPE on external/unmanaged instance import using custom offerings --- .../vm/UnmanagedVMsManagerImpl.java | 125 ++++++++++++++---- .../vm/UnmanagedVMsManagerImplTest.java | 118 ++++++++++++++++- 2 files changed, 216 insertions(+), 27 deletions(-) diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 1b588b04210..d1265562deb 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1352,10 +1352,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List managedVms = new ArrayList<>(additionalNameFilters); managedVms.addAll(getHostsManagedVms(hosts)); - List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); - try (CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); - CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, Long.valueOf(serviceOffering.getCpu()), reservationDao, resourceLimitService); - CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, Long.valueOf(serviceOffering.getRamSize()), reservationDao, resourceLimitService)) { + try { ActionEventUtils.onStartedActionEvent(userId, owner.getId(), EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, true, 0); @@ -1497,7 +1494,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { String hostName, Account caller, Account owner, long userId, ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, Map nicNetworkMap, Map nicIpAddressMap, - Map details, Boolean migrateAllowed, List managedVms, boolean forced) { + Map details, Boolean migrateAllowed, List managedVms, boolean forced) throws ResourceAllocationException { UserVm userVm = null; for (HostVO host : hosts) { HashMap unmanagedInstances = getUnmanagedInstancesForHost(host, instanceName, managedVms); @@ -1541,11 +1538,18 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { template.setGuestOSId(guestOSHypervisor.getGuestOsId()); } - userVm = importVirtualMachineInternal(unmanagedInstance, instanceName, zone, cluster, host, - template, displayName, hostName, CallContext.current().getCallingAccount(), owner, userId, - serviceOffering, dataDiskOfferingMap, - nicNetworkMap, nicIpAddressMap, - details, migrateAllowed, forced, true); + + List reservations = new ArrayList<>(); + try { + checkVmResourceLimitsForUnmanagedInstanceImport(owner, unmanagedInstance, serviceOffering, template, reservations); + userVm = importVirtualMachineInternal(unmanagedInstance, instanceName, zone, cluster, host, + template, displayName, hostName, CallContext.current().getCallingAccount(), owner, userId, + serviceOffering, dataDiskOfferingMap, + nicNetworkMap, nicIpAddressMap, + details, migrateAllowed, forced, true); + } finally { + ReservationHelper.closeAll(reservations); + } break; } if (userVm != null) { @@ -1555,6 +1559,36 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { return userVm; } + protected void checkVmResourceLimitsForUnmanagedInstanceImport(Account owner, UnmanagedInstanceTO unmanagedInstance, ServiceOfferingVO serviceOffering, VMTemplateVO template, List reservations) throws ResourceAllocationException { + // When importing an unmanaged instance, the amount of CPUs and memory is obtained from the hypervisor unless powered off + // and not using a dynamic offering, unlike the external VM import that always obtains it from the compute offering + Integer cpu = serviceOffering.getCpu(); + Integer memory = serviceOffering.getRamSize(); + + if (serviceOffering.isDynamic() || !UnmanagedInstanceTO.PowerState.PowerOff.equals(unmanagedInstance.getPowerState())) { + cpu = unmanagedInstance.getCpuCores(); + memory = unmanagedInstance.getMemory(); + } + + if (cpu == null || cpu == 0) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("CPU cores [%s] is not valid for importing VM [%s].", cpu, unmanagedInstance.getName())); + } + if (memory == null || memory == 0) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Memory [%s] is not valid for importing VM [%s].", memory, unmanagedInstance.getName())); + } + + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); + + CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); + reservations.add(vmReservation); + + CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, cpu.longValue(), reservationDao, resourceLimitService); + reservations.add(cpuReservation); + + CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, memory.longValue(), reservationDao, resourceLimitService); + reservations.add(memReservation); + } + private Pair getSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM) { @@ -1582,7 +1616,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { Account caller, Account owner, long userId, ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, Map nicNetworkMap, Map nicIpAddressMap, - Map details, ImportVmCmd cmd, boolean forced) { + Map details, ImportVmCmd cmd, boolean forced) throws ResourceAllocationException { Long existingVcenterId = cmd.getExistingVcenterId(); String vcenter = cmd.getVcenter(); String datacenterName = cmd.getDatacenterName(); @@ -1620,6 +1654,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { UnmanagedInstanceTO sourceVMwareInstance = null; DataStoreTO temporaryConvertLocation = null; String ovfTemplateOnConvertLocation = null; + List reservations = new ArrayList<>(); + try { HostVO convertHost = selectKVMHostForConversionInCluster(destinationCluster, convertInstanceHostId); HostVO importHost = selectKVMHostForImportingInCluster(destinationCluster, importInstanceHostId); @@ -1634,6 +1670,10 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { Pair sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName); sourceVMwareInstance = sourceInstanceDetails.first(); isClonedInstance = sourceInstanceDetails.second(); + + // Ensure that the configured resource limits will not be exceeded before beginning the conversion process + checkVmResourceLimitsForUnmanagedInstanceImport(owner, sourceVMwareInstance, serviceOffering, template, reservations); + boolean isWindowsVm = sourceVMwareInstance.getOperatingSystem().toLowerCase().contains("windows"); if (isWindowsVm) { checkConversionSupportOnHost(convertHost, sourceVMName, true); @@ -1681,6 +1721,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { if (temporaryConvertLocation != null && StringUtils.isNotBlank(ovfTemplateOnConvertLocation)) { removeTemplate(temporaryConvertLocation, ovfTemplateOnConvertLocation); } + ReservationHelper.closeAll(reservations); } } @@ -2400,10 +2441,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { UserVm userVm = null; - List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); - try (CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); - CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, Long.valueOf(serviceOffering.getCpu()), reservationDao, resourceLimitService); - CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, Long.valueOf(serviceOffering.getRamSize()), reservationDao, resourceLimitService)) { + try { if (ImportSource.EXTERNAL == importSource) { String username = cmd.getUsername(); @@ -2466,6 +2504,7 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { List reservations = new ArrayList<>(); try { + checkVmResourceLimitsForExternalKvmVmImport(owner, serviceOffering, (VMTemplateVO) template, details, reservations); checkVolumeResourceLimitsForExternalKvmVmImport(owner, rootDisk, dataDisks, diskOffering, dataDiskOfferingMap, reservations); // Check NICs and supplied networks @@ -2630,24 +2669,20 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { profiles.add(nicProfile); networkNicMap.put(network.getUuid(), profiles); + List reservations = new ArrayList<>(); try { + checkVmResourceLimitsForExternalKvmVmImport(owner, serviceOffering, (VMTemplateVO) template, details, reservations); userVm = userVmManager.importVM(zone, null, template, null, displayName, owner, null, caller, true, null, owner.getAccountId(), userId, serviceOffering, null, hostName, Hypervisor.HypervisorType.KVM, allDetails, powerState, networkNicMap); - } catch (InsufficientCapacityException ice) { - logger.error(String.format("Failed to import vm name: %s", instanceName), ice); - throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage()); - } - if (userVm == null) { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); - } - DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + if (userVm == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to import vm name: %s", instanceName)); + } - List reservations = new ArrayList<>(); - List resourceLimitStorageTags = resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); - try { + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + List resourceLimitStorageTags = resourceLimitService.getResourceLimitStorageTagsForResourceCountOperation(true, diskOffering); CheckedReservation volumeReservation = new CheckedReservation(owner, Resource.ResourceType.volume, resourceLimitStorageTags, CollectionUtils.isNotEmpty(resourceLimitStorageTags) ? 1L : 0L, reservationDao, resourceLimitService); reservations.add(volumeReservation); @@ -2720,6 +2755,9 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { publishVMUsageUpdateResourceCount(userVm, dummyOffering, template); return userVm; + } catch (InsufficientCapacityException ice) { // This will be thrown by com.cloud.vm.UserVmService.importVM + logger.error(String.format("Failed to import vm name: %s", instanceName), ice); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ice.getMessage()); } catch (ResourceAllocationException e) { cleanupFailedImportVM(userVm); throw e; @@ -2728,6 +2766,41 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager { } } + protected void checkVmResourceLimitsForExternalKvmVmImport(Account owner, ServiceOfferingVO serviceOffering, VMTemplateVO template, Map details, List reservations) throws ResourceAllocationException { + // When importing an external VM, the amount of CPUs and memory is always obtained from the compute offering, + // unlike the unmanaged instance import that obtains it from the hypervisor unless the VM is powered off and the offering is fixed + Integer cpu = serviceOffering.getCpu(); + Integer memory = serviceOffering.getRamSize(); + + if (serviceOffering.isDynamic()) { + cpu = getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details); + memory = getDetailAsInteger(VmDetailConstants.MEMORY, details); + } + + List resourceLimitHostTags = resourceLimitService.getResourceLimitHostTags(serviceOffering, template); + + CheckedReservation vmReservation = new CheckedReservation(owner, Resource.ResourceType.user_vm, resourceLimitHostTags, 1L, reservationDao, resourceLimitService); + reservations.add(vmReservation); + + CheckedReservation cpuReservation = new CheckedReservation(owner, Resource.ResourceType.cpu, resourceLimitHostTags, cpu.longValue(), reservationDao, resourceLimitService); + reservations.add(cpuReservation); + + CheckedReservation memReservation = new CheckedReservation(owner, Resource.ResourceType.memory, resourceLimitHostTags, memory.longValue(), reservationDao, resourceLimitService); + reservations.add(memReservation); + } + + protected Integer getDetailAsInteger(String key, Map details) { + String detail = details.get(key); + if (detail == null) { + throw new InvalidParameterValueException(String.format("Detail '%s' must be provided.", key)); + } + try { + return Integer.valueOf(detail); + } catch (NumberFormatException e) { + throw new InvalidParameterValueException(String.format("Please provide a valid integer value for detail '%s'.", key)); + } + } + private void checkVolume(Map volumeDetails) { if (MapUtils.isEmpty(volumeDetails)) { return; diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index d56299126a5..be513e8b717 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -37,8 +37,10 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import com.cloud.exception.ResourceAllocationException; import com.cloud.offering.DiskOffering; import com.cloud.resourcelimit.CheckedReservation; +import com.cloud.vm.VmDetailConstants; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.ServerApiException; @@ -55,6 +57,7 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.resourcelimit.Reserver; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -72,6 +75,7 @@ import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import com.cloud.agent.AgentManager; @@ -168,7 +172,8 @@ import com.cloud.vm.dao.VMInstanceDao; public class UnmanagedVMsManagerImplTest { @InjectMocks - private UnmanagedVMsManagerImpl unmanagedVMsManager = new UnmanagedVMsManagerImpl(); + @Spy + private UnmanagedVMsManagerImpl unmanagedVMsManager; @Mock private UserVmManager userVmManager; @@ -237,6 +242,14 @@ public class UnmanagedVMsManagerImplTest { EntityManager entityMgr; @Mock DeploymentPlanningManager deploymentPlanningManager; + @Mock + private Account accountMock; + @Mock + private ServiceOfferingVO serviceOfferingMock; + @Mock + private VMTemplateVO templateMock; + @Mock + private UnmanagedInstanceTO unmanagedInstanceMock; private static final long virtualMachineId = 1L; @@ -363,6 +376,11 @@ public class UnmanagedVMsManagerImplTest { when(vmDao.findById(virtualMachineId)).thenReturn(virtualMachine); when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Running); + + when(unmanagedInstanceMock.getCpuCores()).thenReturn(8); + when(unmanagedInstanceMock.getMemory()).thenReturn(4096); + when(serviceOfferingMock.getCpu()).thenReturn(4); + when(serviceOfferingMock.getRamSize()).thenReturn(2048); } @NotNull @@ -1110,4 +1128,102 @@ public class UnmanagedVMsManagerImplTest { unmanagedVMsManager.selectKVMHostForConversionInCluster(cluster, hostId); } + + @Test + public void checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromHypervisorWhenOfferingIsDynamic() throws Exception { + when(serviceOfferingMock.isDynamic()).thenReturn(true); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock, unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(unmanagedInstanceMock).getCpuCores(); + verify(unmanagedInstanceMock).getMemory(); + } + } + + @Test + public void checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromHypervisorWhenVmIsPoweredOn() throws Exception { + when(unmanagedInstanceMock.getPowerState()).thenReturn(UnmanagedInstanceTO.PowerState.PowerOn); + when(serviceOfferingMock.isDynamic()).thenReturn(false); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock, unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(unmanagedInstanceMock).getCpuCores(); + verify(unmanagedInstanceMock).getMemory(); + } + } + + @Test + public void checkVmResourceLimitsForUnmanagedInstanceImportTestUsesInformationFromOfferingWhenOfferingIsNotDynamicAndVmIsPoweredOff() throws Exception { + when(unmanagedInstanceMock.getPowerState()).thenReturn(UnmanagedInstanceTO.PowerState.PowerOff); + when(serviceOfferingMock.isDynamic()).thenReturn(false); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForUnmanagedInstanceImport(accountMock, unmanagedInstanceMock, serviceOfferingMock, templateMock, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(serviceOfferingMock).getCpu(); + verify(serviceOfferingMock).getRamSize(); + verify(unmanagedInstanceMock, Mockito.never()).getCpuCores(); + verify(unmanagedInstanceMock, Mockito.never()).getMemory(); + } + } + + @Test + public void checkVmResourceLimitsForExternalKvmVmImportTestUsesInformationFromOfferingWhenOfferingIsNotDynamic() throws ResourceAllocationException { + when(serviceOfferingMock.isDynamic()).thenReturn(false); + Map details = new HashMap<>(); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForExternalKvmVmImport(accountMock, serviceOfferingMock, templateMock, details, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(serviceOfferingMock).getCpu(); + verify(serviceOfferingMock).getRamSize(); + verify(unmanagedVMsManager, Mockito.never()).getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details); + verify(unmanagedVMsManager, Mockito.never()).getDetailAsInteger(VmDetailConstants.MEMORY, details); + } + } + + @Test + public void checkVmResourceLimitsForExternalKvmVmImportTestUsesInformationFromDetailsWhenOfferingIsDynamic() throws ResourceAllocationException { + when(serviceOfferingMock.isDynamic()).thenReturn(true); + Map details = new HashMap<>(); + details.put(VmDetailConstants.CPU_NUMBER, "8"); + details.put(VmDetailConstants.MEMORY, "4096"); + List reservations = new ArrayList<>(); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(CheckedReservation.class)) { + unmanagedVMsManager.checkVmResourceLimitsForExternalKvmVmImport(accountMock, serviceOfferingMock, templateMock, details, reservations); + + Assert.assertEquals(3, mockedConstruction.constructed().size()); + Assert.assertEquals(3, reservations.size()); + verify(unmanagedVMsManager).getDetailAsInteger(VmDetailConstants.CPU_NUMBER, details); + verify(unmanagedVMsManager).getDetailAsInteger(VmDetailConstants.MEMORY, details); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void getDetailAsIntegerTestThrowsInvalidParameterValueExceptionWhenDetailIsNull() { + Map details = new HashMap<>(); + unmanagedVMsManager.getDetailAsInteger("non-existent", details); + } + + @Test(expected = InvalidParameterValueException.class) + public void getDetailAsIntegerTestThrowsInvalidParameterValueExceptionWhenValueIsInvalid() { + Map details = new HashMap<>(); + details.put("key", "not-a-number"); + unmanagedVMsManager.getDetailAsInteger("key", details); + } } From d38c1f8d1250172b535f13161028359b02fc8fb0 Mon Sep 17 00:00:00 2001 From: Vishesh <8760112+vishesh92@users.noreply.github.com> Date: Fri, 27 Mar 2026 10:29:13 +0530 Subject: [PATCH 27/43] Fix error message while creating local storage pool (#12767) * Fix error message while creating local storage pool --- .../com/cloud/storage/StorageManagerImpl.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index d1dca0fa901..6751da3dde0 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage; +import static com.cloud.configuration.ConfigurationManagerImpl.SystemVMUseLocalStorage; import static com.cloud.utils.NumbersUtil.toHumanReadableSize; import java.io.UnsupportedEncodingException; @@ -144,6 +145,7 @@ import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.time.DateUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.EnumUtils; import org.springframework.stereotype.Component; @@ -176,7 +178,6 @@ import com.cloud.capacity.dao.CapacityDao; import com.cloud.cluster.ClusterManagerListener; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; -import com.cloud.configuration.ConfigurationManagerImpl; import com.cloud.configuration.Resource.ResourceType; import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; @@ -803,6 +804,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return createLocalStorage(host, pInfo); } + private boolean isLocalStorageEnabledForZone(DataCenterVO zone) { + return zone.isLocalStorageEnabled() || BooleanUtils.toBoolean(SystemVMUseLocalStorage.valueIn(zone.getId())); + } + @DB @Override public DataStore createLocalStorage(Host host, StoragePoolInfo pInfo) throws ConnectionException { @@ -810,12 +815,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C if (dc == null) { return null; } - boolean useLocalStorageForSystemVM = false; - Boolean isLocal = ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dc.getId()); - if (isLocal != null) { - useLocalStorageForSystemVM = isLocal.booleanValue(); - } - if (!(dc.isLocalStorageEnabled() || useLocalStorageForSystemVM)) { + if (!isLocalStorageEnabledForZone(dc)) { return null; } DataStore store = null; @@ -1017,6 +1017,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(account.getId())) { throw new PermissionDeniedException(String.format("Cannot perform this operation, Zone is currently disabled: %s", zone)); } + // Check if it's local storage and if it's enabled on the zone + if (isFileScheme && !isLocalStorageEnabledForZone(zone)) { + throw new InvalidParameterValueException("Local storage is not enabled for zone: " + zone); + } managementService.checkJsInterpretationAllowedIfNeededForParameterValue(ApiConstants.IS_TAG_A_RULE, Boolean.TRUE.equals(cmd.isTagARule())); From e2497cfc4df7daa13167ad2cf12e2eed782b0558 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 1 Apr 2026 15:34:04 +0530 Subject: [PATCH 28/43] backport: default system vm template update implementation (#12935) Fixes #12934 Backported from 889fc62 Signed-off-by: Abhishek Kumar --- .../upgrade/dao/DbUpgradeSystemVmTemplate.java | 16 +++++++++++++++- .../cloud/upgrade/dao/Upgrade42020to42030.java | 4 ---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java index 4211898adc7..a8d2436672e 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java @@ -17,9 +17,23 @@ package com.cloud.upgrade.dao; +import com.cloud.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.sql.Connection; public interface DbUpgradeSystemVmTemplate { - void updateSystemVmTemplates(Connection conn); + default void updateSystemVmTemplates(Connection conn) { + Logger logger = LogManager.getLogger(getClass()); + logger.debug("Updating System Vm template IDs"); + try { + SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); + systemVmTemplateRegistration.updateSystemVmTemplates(conn); + } catch (Exception e) { + throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java index 68100e16401..e0aa38a717b 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42020to42030.java @@ -57,8 +57,4 @@ public class Upgrade42020to42030 extends DbUpgradeAbstractImpl implements DbUpgr public InputStream[] getCleanupScripts() { return null; } - - @Override - public void updateSystemVmTemplates(Connection conn) { - } } From 6f1aa96b4cd5e78513afeedfe2e06833ef736bda Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Mon, 6 Apr 2026 13:49:04 +0200 Subject: [PATCH 29/43] engine/schema: fix new systemvm template is not registered during upgrade if hypervisor is not KVM (#12952) * engine/schema: fix new systemvm template is not registered during upgrade if hypervisor is not KVM --- .../cloud/upgrade/SystemVmTemplateRegistration.java | 10 +++++----- .../upgrade/SystemVmTemplateRegistrationTest.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java index a6aecf477f7..2e45d567357 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java @@ -315,11 +315,11 @@ public class SystemVmTemplateRegistration { public static final List> hypervisorList = Arrays.asList( new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), - new Pair<>(Hypervisor.HypervisorType.VMware, null), - new Pair<>(Hypervisor.HypervisorType.XenServer, null), - new Pair<>(Hypervisor.HypervisorType.Hyperv, null), - new Pair<>(Hypervisor.HypervisorType.LXC, null), - new Pair<>(Hypervisor.HypervisorType.Ovm3, null) + new Pair<>(Hypervisor.HypervisorType.VMware, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.XenServer, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.Hyperv, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.LXC, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.Ovm3, CPU.CPUArch.amd64) ); public static final Map NewTemplateMap = new HashMap<>(); diff --git a/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java index 8c2fe43c9d3..aad8760ed77 100644 --- a/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java +++ b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java @@ -155,7 +155,7 @@ public class SystemVmTemplateRegistrationTest { templateDetails = SystemVmTemplateRegistration.NewTemplateMap.get("vmware"); assertNotNull(templateDetails); - assertNull(templateDetails.getArch()); + assertEquals(CPU.CPUArch.amd64, templateDetails.getArch()); assertEquals(Hypervisor.HypervisorType.VMware, templateDetails.getHypervisorType()); } From c8599040b4756034c7105806a53d2c99eb7ea0fe Mon Sep 17 00:00:00 2001 From: Abhisar Sinha Date: Mon, 6 Apr 2026 17:22:19 +0530 Subject: [PATCH 30/43] Updating pom.xml version numbers for release 4.20.3.0 Signed-off-by: Abhisar Sinha --- agent/pom.xml | 2 +- api/pom.xml | 2 +- client/pom.xml | 2 +- core/pom.xml | 2 +- debian/changelog | 6 ++++++ developer/pom.xml | 2 +- engine/api/pom.xml | 2 +- engine/components-api/pom.xml | 2 +- engine/orchestration/pom.xml | 2 +- engine/pom.xml | 2 +- engine/schema/pom.xml | 2 +- engine/service/pom.xml | 2 +- engine/storage/cache/pom.xml | 2 +- engine/storage/configdrive/pom.xml | 2 +- engine/storage/datamotion/pom.xml | 2 +- engine/storage/image/pom.xml | 2 +- engine/storage/integration-test/pom.xml | 2 +- engine/storage/object/pom.xml | 2 +- engine/storage/pom.xml | 2 +- engine/storage/snapshot/pom.xml | 2 +- engine/storage/volume/pom.xml | 2 +- engine/userdata/cloud-init/pom.xml | 2 +- engine/userdata/pom.xml | 2 +- framework/agent-lb/pom.xml | 2 +- framework/ca/pom.xml | 2 +- framework/cluster/pom.xml | 2 +- framework/config/pom.xml | 2 +- framework/db/pom.xml | 2 +- framework/direct-download/pom.xml | 2 +- framework/events/pom.xml | 2 +- framework/ipc/pom.xml | 2 +- framework/jobs/pom.xml | 2 +- framework/managed-context/pom.xml | 2 +- framework/pom.xml | 2 +- framework/quota/pom.xml | 2 +- framework/rest/pom.xml | 2 +- framework/security/pom.xml | 2 +- framework/spring/lifecycle/pom.xml | 2 +- framework/spring/module/pom.xml | 2 +- plugins/acl/dynamic-role-based/pom.xml | 2 +- plugins/acl/project-role-based/pom.xml | 2 +- plugins/acl/static-role-based/pom.xml | 2 +- .../affinity-group-processors/explicit-dedication/pom.xml | 2 +- plugins/affinity-group-processors/host-affinity/pom.xml | 2 +- .../affinity-group-processors/host-anti-affinity/pom.xml | 2 +- .../non-strict-host-affinity/pom.xml | 2 +- .../non-strict-host-anti-affinity/pom.xml | 2 +- plugins/alert-handlers/snmp-alerts/pom.xml | 2 +- plugins/alert-handlers/syslog-alerts/pom.xml | 2 +- plugins/api/discovery/pom.xml | 2 +- plugins/api/rate-limit/pom.xml | 2 +- plugins/api/solidfire-intg-test/pom.xml | 2 +- plugins/api/vmware-sioc/pom.xml | 2 +- plugins/backup/dummy/pom.xml | 2 +- plugins/backup/nas/pom.xml | 2 +- plugins/backup/networker/pom.xml | 2 +- plugins/backup/veeam/pom.xml | 2 +- plugins/ca/root-ca/pom.xml | 2 +- plugins/database/mysql-ha/pom.xml | 2 +- plugins/database/quota/pom.xml | 2 +- plugins/dedicated-resources/pom.xml | 2 +- plugins/deployment-planners/implicit-dedication/pom.xml | 2 +- plugins/deployment-planners/user-concentrated-pod/pom.xml | 2 +- plugins/deployment-planners/user-dispersing/pom.xml | 2 +- plugins/drs/cluster/balanced/pom.xml | 2 +- plugins/drs/cluster/condensed/pom.xml | 2 +- plugins/event-bus/inmemory/pom.xml | 2 +- plugins/event-bus/kafka/pom.xml | 2 +- plugins/event-bus/rabbitmq/pom.xml | 2 +- plugins/event-bus/webhook/pom.xml | 2 +- plugins/ha-planners/skip-heurestics/pom.xml | 2 +- plugins/host-allocators/random/pom.xml | 2 +- plugins/hypervisors/baremetal/pom.xml | 2 +- plugins/hypervisors/hyperv/pom.xml | 2 +- plugins/hypervisors/kvm/pom.xml | 2 +- plugins/hypervisors/ovm/pom.xml | 2 +- plugins/hypervisors/ovm3/pom.xml | 2 +- plugins/hypervisors/simulator/pom.xml | 2 +- plugins/hypervisors/ucs/pom.xml | 2 +- plugins/hypervisors/vmware/pom.xml | 2 +- plugins/hypervisors/xenserver/pom.xml | 2 +- plugins/integrations/cloudian/pom.xml | 2 +- plugins/integrations/kubernetes-service/pom.xml | 2 +- plugins/integrations/prometheus/pom.xml | 2 +- plugins/metrics/pom.xml | 2 +- plugins/network-elements/bigswitch/pom.xml | 2 +- plugins/network-elements/brocade-vcs/pom.xml | 2 +- plugins/network-elements/cisco-vnmc/pom.xml | 2 +- plugins/network-elements/dns-notifier/pom.xml | 2 +- plugins/network-elements/elastic-loadbalancer/pom.xml | 2 +- plugins/network-elements/globodns/pom.xml | 2 +- plugins/network-elements/internal-loadbalancer/pom.xml | 2 +- plugins/network-elements/juniper-contrail/pom.xml | 2 +- plugins/network-elements/netscaler/pom.xml | 2 +- plugins/network-elements/nicira-nvp/pom.xml | 2 +- plugins/network-elements/nsx/pom.xml | 2 +- plugins/network-elements/opendaylight/pom.xml | 2 +- plugins/network-elements/ovs/pom.xml | 2 +- plugins/network-elements/palo-alto/pom.xml | 2 +- plugins/network-elements/stratosphere-ssp/pom.xml | 2 +- plugins/network-elements/tungsten/pom.xml | 2 +- plugins/network-elements/vxlan/pom.xml | 2 +- plugins/outofbandmanagement-drivers/ipmitool/pom.xml | 2 +- .../outofbandmanagement-drivers/nested-cloudstack/pom.xml | 2 +- plugins/outofbandmanagement-drivers/redfish/pom.xml | 2 +- plugins/pom.xml | 2 +- plugins/shutdown/pom.xml | 2 +- plugins/storage-allocators/random/pom.xml | 2 +- plugins/storage/image/default/pom.xml | 2 +- plugins/storage/image/s3/pom.xml | 2 +- plugins/storage/image/sample/pom.xml | 2 +- plugins/storage/image/swift/pom.xml | 2 +- plugins/storage/object/ceph/pom.xml | 2 +- plugins/storage/object/minio/pom.xml | 2 +- plugins/storage/object/simulator/pom.xml | 2 +- plugins/storage/sharedfs/storagevm/pom.xml | 2 +- plugins/storage/volume/adaptive/pom.xml | 2 +- plugins/storage/volume/cloudbyte/pom.xml | 2 +- plugins/storage/volume/datera/pom.xml | 2 +- plugins/storage/volume/default/pom.xml | 2 +- plugins/storage/volume/flasharray/pom.xml | 2 +- plugins/storage/volume/linstor/pom.xml | 2 +- plugins/storage/volume/nexenta/pom.xml | 2 +- plugins/storage/volume/primera/pom.xml | 2 +- plugins/storage/volume/sample/pom.xml | 2 +- plugins/storage/volume/scaleio/pom.xml | 2 +- plugins/storage/volume/solidfire/pom.xml | 2 +- plugins/storage/volume/storpool/pom.xml | 2 +- plugins/user-authenticators/ldap/pom.xml | 2 +- plugins/user-authenticators/md5/pom.xml | 2 +- plugins/user-authenticators/oauth2/pom.xml | 2 +- plugins/user-authenticators/pbkdf2/pom.xml | 2 +- plugins/user-authenticators/plain-text/pom.xml | 2 +- plugins/user-authenticators/saml2/pom.xml | 2 +- plugins/user-authenticators/sha256salted/pom.xml | 2 +- plugins/user-two-factor-authenticators/static-pin/pom.xml | 2 +- plugins/user-two-factor-authenticators/totp/pom.xml | 2 +- pom.xml | 2 +- quickcloud/pom.xml | 2 +- server/pom.xml | 2 +- services/console-proxy/pom.xml | 2 +- services/console-proxy/rdpconsole/pom.xml | 2 +- services/console-proxy/server/pom.xml | 2 +- services/pom.xml | 2 +- services/secondary-storage/controller/pom.xml | 2 +- services/secondary-storage/pom.xml | 2 +- services/secondary-storage/server/pom.xml | 2 +- systemvm/pom.xml | 2 +- test/pom.xml | 2 +- tools/apidoc/pom.xml | 2 +- tools/checkstyle/pom.xml | 2 +- tools/devcloud-kvm/pom.xml | 2 +- tools/devcloud4/pom.xml | 2 +- tools/docker/Dockerfile | 2 +- tools/docker/Dockerfile.marvin | 4 ++-- tools/marvin/pom.xml | 2 +- tools/marvin/setup.py | 2 +- tools/pom.xml | 2 +- usage/pom.xml | 2 +- utils/pom.xml | 2 +- vmware-base/pom.xml | 2 +- 161 files changed, 167 insertions(+), 161 deletions(-) diff --git a/agent/pom.xml b/agent/pom.xml index 92c836f10ef..122dfd1005d 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/api/pom.xml b/api/pom.xml index 377b6e6e341..f431d8e69f4 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/client/pom.xml b/client/pom.xml index 5174dfe493a..bd91063425d 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/core/pom.xml b/core/pom.xml index 0b3cbfb7619..80ceac98c7c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/debian/changelog b/debian/changelog index 49d971dfc5e..d06479e19a7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +cloudstack (4.20.3.0) unstable; urgency=low + + * Update the version to 4.20.3.0 + + -- the Apache CloudStack project Mon, 06 Apr 2026 17:22:19 +0530 + cloudstack (4.20.3.0-SNAPSHOT) unstable; urgency=low * Update the version to 4.20.3.0-SNAPSHOT diff --git a/developer/pom.xml b/developer/pom.xml index 17572a2f65e..e4af35300c7 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/engine/api/pom.xml b/engine/api/pom.xml index 2ca2e5ecf0f..5825f0609af 100644 --- a/engine/api/pom.xml +++ b/engine/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/engine/components-api/pom.xml b/engine/components-api/pom.xml index 93f31ef7a9c..f7b3c13c165 100644 --- a/engine/components-api/pom.xml +++ b/engine/components-api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/engine/orchestration/pom.xml b/engine/orchestration/pom.xml index 10208afeba2..ac8fddf0874 100755 --- a/engine/orchestration/pom.xml +++ b/engine/orchestration/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/engine/pom.xml b/engine/pom.xml index fc35b9bb2eb..828203d4bfc 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/engine/schema/pom.xml b/engine/schema/pom.xml index 4d901f10bb4..2293aa51f93 100644 --- a/engine/schema/pom.xml +++ b/engine/schema/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/engine/service/pom.xml b/engine/service/pom.xml index 51d61b95d6c..5ecc54f6275 100644 --- a/engine/service/pom.xml +++ b/engine/service/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 cloud-engine-service war diff --git a/engine/storage/cache/pom.xml b/engine/storage/cache/pom.xml index 2a804e01042..497f49fc9e6 100644 --- a/engine/storage/cache/pom.xml +++ b/engine/storage/cache/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/storage/configdrive/pom.xml b/engine/storage/configdrive/pom.xml index aad1edeb038..ff101fab43a 100644 --- a/engine/storage/configdrive/pom.xml +++ b/engine/storage/configdrive/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/storage/datamotion/pom.xml b/engine/storage/datamotion/pom.xml index 89bc81851c7..38a25db4309 100644 --- a/engine/storage/datamotion/pom.xml +++ b/engine/storage/datamotion/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/storage/image/pom.xml b/engine/storage/image/pom.xml index f1dd7bedd8c..c6fb56967fc 100644 --- a/engine/storage/image/pom.xml +++ b/engine/storage/image/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/storage/integration-test/pom.xml b/engine/storage/integration-test/pom.xml index 6f51682dc02..14c3c9e9380 100644 --- a/engine/storage/integration-test/pom.xml +++ b/engine/storage/integration-test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/storage/object/pom.xml b/engine/storage/object/pom.xml index b9a6c422d2a..514e5b7e3bf 100644 --- a/engine/storage/object/pom.xml +++ b/engine/storage/object/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/storage/pom.xml b/engine/storage/pom.xml index 390c9365876..8a7b1f01d0e 100644 --- a/engine/storage/pom.xml +++ b/engine/storage/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/engine/storage/snapshot/pom.xml b/engine/storage/snapshot/pom.xml index 72c127c7b51..c683a0973a0 100644 --- a/engine/storage/snapshot/pom.xml +++ b/engine/storage/snapshot/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/storage/volume/pom.xml b/engine/storage/volume/pom.xml index 3e650153239..7ff2ca42cf6 100644 --- a/engine/storage/volume/pom.xml +++ b/engine/storage/volume/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/userdata/cloud-init/pom.xml b/engine/userdata/cloud-init/pom.xml index 35ae6cf0daa..cd3d9d0380c 100644 --- a/engine/userdata/cloud-init/pom.xml +++ b/engine/userdata/cloud-init/pom.xml @@ -23,7 +23,7 @@ cloud-engine org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/engine/userdata/pom.xml b/engine/userdata/pom.xml index 3723ab2a0a7..750e724c4cd 100644 --- a/engine/userdata/pom.xml +++ b/engine/userdata/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/agent-lb/pom.xml b/framework/agent-lb/pom.xml index 9affba6b1fa..1dc9806d924 100644 --- a/framework/agent-lb/pom.xml +++ b/framework/agent-lb/pom.xml @@ -24,7 +24,7 @@ cloudstack-framework org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/ca/pom.xml b/framework/ca/pom.xml index fbe984e2ac4..3482f9787e8 100644 --- a/framework/ca/pom.xml +++ b/framework/ca/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/cluster/pom.xml b/framework/cluster/pom.xml index 74db4683ecc..e46256af8ed 100644 --- a/framework/cluster/pom.xml +++ b/framework/cluster/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/config/pom.xml b/framework/config/pom.xml index 512410ac6fe..86251b08165 100644 --- a/framework/config/pom.xml +++ b/framework/config/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/db/pom.xml b/framework/db/pom.xml index 49d2d6fb536..8f7caaeb7f7 100644 --- a/framework/db/pom.xml +++ b/framework/db/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/direct-download/pom.xml b/framework/direct-download/pom.xml index 6e54a117ca6..9e8614c04fa 100644 --- a/framework/direct-download/pom.xml +++ b/framework/direct-download/pom.xml @@ -32,7 +32,7 @@ cloudstack-framework org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/events/pom.xml b/framework/events/pom.xml index d3066a4763e..ea9b80e6202 100644 --- a/framework/events/pom.xml +++ b/framework/events/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/ipc/pom.xml b/framework/ipc/pom.xml index 9580ca2ab65..d1e5531ed85 100644 --- a/framework/ipc/pom.xml +++ b/framework/ipc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/jobs/pom.xml b/framework/jobs/pom.xml index 46784b81b2f..26aafca7907 100644 --- a/framework/jobs/pom.xml +++ b/framework/jobs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/managed-context/pom.xml b/framework/managed-context/pom.xml index dc00b5c236c..2bb77534369 100644 --- a/framework/managed-context/pom.xml +++ b/framework/managed-context/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/framework/pom.xml b/framework/pom.xml index ca4c9d71b83..01962c68f0f 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/framework/quota/pom.xml b/framework/quota/pom.xml index 538d96ed506..0c73586f011 100644 --- a/framework/quota/pom.xml +++ b/framework/quota/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/rest/pom.xml b/framework/rest/pom.xml index b7fe9b5a0b2..d65317fe491 100644 --- a/framework/rest/pom.xml +++ b/framework/rest/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml cloud-framework-rest diff --git a/framework/security/pom.xml b/framework/security/pom.xml index 130487c983b..28d5c67d811 100644 --- a/framework/security/pom.xml +++ b/framework/security/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/framework/spring/lifecycle/pom.xml b/framework/spring/lifecycle/pom.xml index bdc3f30ebaa..dabeb4b8e64 100644 --- a/framework/spring/lifecycle/pom.xml +++ b/framework/spring/lifecycle/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/framework/spring/module/pom.xml b/framework/spring/module/pom.xml index eb49ce3f1a7..7bede4f9e2f 100644 --- a/framework/spring/module/pom.xml +++ b/framework/spring/module/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/acl/dynamic-role-based/pom.xml b/plugins/acl/dynamic-role-based/pom.xml index a2154fd74c3..9830d856033 100644 --- a/plugins/acl/dynamic-role-based/pom.xml +++ b/plugins/acl/dynamic-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/acl/project-role-based/pom.xml b/plugins/acl/project-role-based/pom.xml index f27f24f9703..60d5eb58621 100644 --- a/plugins/acl/project-role-based/pom.xml +++ b/plugins/acl/project-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/acl/static-role-based/pom.xml b/plugins/acl/static-role-based/pom.xml index 51ad2ef0c52..1dc50c712a0 100644 --- a/plugins/acl/static-role-based/pom.xml +++ b/plugins/acl/static-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/affinity-group-processors/explicit-dedication/pom.xml b/plugins/affinity-group-processors/explicit-dedication/pom.xml index 0aef5b34c31..dbeb9665f26 100644 --- a/plugins/affinity-group-processors/explicit-dedication/pom.xml +++ b/plugins/affinity-group-processors/explicit-dedication/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/affinity-group-processors/host-affinity/pom.xml b/plugins/affinity-group-processors/host-affinity/pom.xml index bbdda0209eb..c8643f79274 100644 --- a/plugins/affinity-group-processors/host-affinity/pom.xml +++ b/plugins/affinity-group-processors/host-affinity/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/affinity-group-processors/host-anti-affinity/pom.xml b/plugins/affinity-group-processors/host-anti-affinity/pom.xml index 415d1b02ba8..3af661a62ee 100644 --- a/plugins/affinity-group-processors/host-anti-affinity/pom.xml +++ b/plugins/affinity-group-processors/host-anti-affinity/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml b/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml index 970c7c97e7f..8f722b01ead 100644 --- a/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml +++ b/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml b/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml index d0c8f0b0477..1593c847fad 100644 --- a/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml +++ b/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml @@ -32,7 +32,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/alert-handlers/snmp-alerts/pom.xml b/plugins/alert-handlers/snmp-alerts/pom.xml index 231ae59036d..dc3d383d59a 100644 --- a/plugins/alert-handlers/snmp-alerts/pom.xml +++ b/plugins/alert-handlers/snmp-alerts/pom.xml @@ -24,7 +24,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/alert-handlers/syslog-alerts/pom.xml b/plugins/alert-handlers/syslog-alerts/pom.xml index 54643a77dc4..8468a603fcd 100644 --- a/plugins/alert-handlers/syslog-alerts/pom.xml +++ b/plugins/alert-handlers/syslog-alerts/pom.xml @@ -24,7 +24,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/api/discovery/pom.xml b/plugins/api/discovery/pom.xml index e809e5c8a38..74f6e1e8f95 100644 --- a/plugins/api/discovery/pom.xml +++ b/plugins/api/discovery/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/api/rate-limit/pom.xml b/plugins/api/rate-limit/pom.xml index c9263903dfd..9160d704ffe 100644 --- a/plugins/api/rate-limit/pom.xml +++ b/plugins/api/rate-limit/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/api/solidfire-intg-test/pom.xml b/plugins/api/solidfire-intg-test/pom.xml index 6c48cc2fdba..060cc911f8c 100644 --- a/plugins/api/solidfire-intg-test/pom.xml +++ b/plugins/api/solidfire-intg-test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/api/vmware-sioc/pom.xml b/plugins/api/vmware-sioc/pom.xml index 33e51a3e7b0..5147a0d5cd4 100644 --- a/plugins/api/vmware-sioc/pom.xml +++ b/plugins/api/vmware-sioc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/backup/dummy/pom.xml b/plugins/backup/dummy/pom.xml index df18ad8d85a..45ab1814b4d 100644 --- a/plugins/backup/dummy/pom.xml +++ b/plugins/backup/dummy/pom.xml @@ -23,7 +23,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/backup/nas/pom.xml b/plugins/backup/nas/pom.xml index 93c8d8d8ca0..5d1ca823a41 100644 --- a/plugins/backup/nas/pom.xml +++ b/plugins/backup/nas/pom.xml @@ -25,7 +25,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/backup/networker/pom.xml b/plugins/backup/networker/pom.xml index fc3fee6f5ba..9fbf6e0a5a4 100644 --- a/plugins/backup/networker/pom.xml +++ b/plugins/backup/networker/pom.xml @@ -25,7 +25,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/backup/veeam/pom.xml b/plugins/backup/veeam/pom.xml index 772773ea4a0..e69a922d512 100644 --- a/plugins/backup/veeam/pom.xml +++ b/plugins/backup/veeam/pom.xml @@ -23,7 +23,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/ca/root-ca/pom.xml b/plugins/ca/root-ca/pom.xml index 91c680df7ba..ff37a8784d9 100644 --- a/plugins/ca/root-ca/pom.xml +++ b/plugins/ca/root-ca/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/database/mysql-ha/pom.xml b/plugins/database/mysql-ha/pom.xml index 33849e84eec..399d8811d6d 100644 --- a/plugins/database/mysql-ha/pom.xml +++ b/plugins/database/mysql-ha/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/database/quota/pom.xml b/plugins/database/quota/pom.xml index ce3706db308..d6fcf9febe5 100644 --- a/plugins/database/quota/pom.xml +++ b/plugins/database/quota/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/dedicated-resources/pom.xml b/plugins/dedicated-resources/pom.xml index 42bd5c6406d..d92e4989d21 100644 --- a/plugins/dedicated-resources/pom.xml +++ b/plugins/dedicated-resources/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/plugins/deployment-planners/implicit-dedication/pom.xml b/plugins/deployment-planners/implicit-dedication/pom.xml index 68214c91b4b..da570d0da3f 100644 --- a/plugins/deployment-planners/implicit-dedication/pom.xml +++ b/plugins/deployment-planners/implicit-dedication/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/deployment-planners/user-concentrated-pod/pom.xml b/plugins/deployment-planners/user-concentrated-pod/pom.xml index 19b93a60eae..2bce273ef76 100644 --- a/plugins/deployment-planners/user-concentrated-pod/pom.xml +++ b/plugins/deployment-planners/user-concentrated-pod/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/deployment-planners/user-dispersing/pom.xml b/plugins/deployment-planners/user-dispersing/pom.xml index 797a12d41eb..93b9e4e4567 100644 --- a/plugins/deployment-planners/user-dispersing/pom.xml +++ b/plugins/deployment-planners/user-dispersing/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/drs/cluster/balanced/pom.xml b/plugins/drs/cluster/balanced/pom.xml index b7c52924824..b8e292e1f57 100644 --- a/plugins/drs/cluster/balanced/pom.xml +++ b/plugins/drs/cluster/balanced/pom.xml @@ -27,7 +27,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/drs/cluster/condensed/pom.xml b/plugins/drs/cluster/condensed/pom.xml index b39765a9ee1..46c0ad9670e 100644 --- a/plugins/drs/cluster/condensed/pom.xml +++ b/plugins/drs/cluster/condensed/pom.xml @@ -27,7 +27,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/event-bus/inmemory/pom.xml b/plugins/event-bus/inmemory/pom.xml index c7d92b90b61..d4bb54bd8d0 100644 --- a/plugins/event-bus/inmemory/pom.xml +++ b/plugins/event-bus/inmemory/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/event-bus/kafka/pom.xml b/plugins/event-bus/kafka/pom.xml index 054e38c49f9..742ff584dce 100644 --- a/plugins/event-bus/kafka/pom.xml +++ b/plugins/event-bus/kafka/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/event-bus/rabbitmq/pom.xml b/plugins/event-bus/rabbitmq/pom.xml index f6d1b8d623b..63df0fe015d 100644 --- a/plugins/event-bus/rabbitmq/pom.xml +++ b/plugins/event-bus/rabbitmq/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/event-bus/webhook/pom.xml b/plugins/event-bus/webhook/pom.xml index 1c1b89e88b9..6ed70b05b20 100644 --- a/plugins/event-bus/webhook/pom.xml +++ b/plugins/event-bus/webhook/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/ha-planners/skip-heurestics/pom.xml b/plugins/ha-planners/skip-heurestics/pom.xml index eff74a69d07..0a8d39149ac 100644 --- a/plugins/ha-planners/skip-heurestics/pom.xml +++ b/plugins/ha-planners/skip-heurestics/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/host-allocators/random/pom.xml b/plugins/host-allocators/random/pom.xml index fd8a6d20e27..c4e0fa3f439 100644 --- a/plugins/host-allocators/random/pom.xml +++ b/plugins/host-allocators/random/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/hypervisors/baremetal/pom.xml b/plugins/hypervisors/baremetal/pom.xml index 01eb615f1af..81c90b8ec8b 100755 --- a/plugins/hypervisors/baremetal/pom.xml +++ b/plugins/hypervisors/baremetal/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml cloud-plugin-hypervisor-baremetal diff --git a/plugins/hypervisors/hyperv/pom.xml b/plugins/hypervisors/hyperv/pom.xml index f2c9f5b99da..3c00d85ddea 100644 --- a/plugins/hypervisors/hyperv/pom.xml +++ b/plugins/hypervisors/hyperv/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/hypervisors/kvm/pom.xml b/plugins/hypervisors/kvm/pom.xml index e2e1721b3a7..f12f55e74e0 100644 --- a/plugins/hypervisors/kvm/pom.xml +++ b/plugins/hypervisors/kvm/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/hypervisors/ovm/pom.xml b/plugins/hypervisors/ovm/pom.xml index 74f2bc20567..9c64173619c 100644 --- a/plugins/hypervisors/ovm/pom.xml +++ b/plugins/hypervisors/ovm/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/hypervisors/ovm3/pom.xml b/plugins/hypervisors/ovm3/pom.xml index 45ceebbc43f..3afd0e11a36 100644 --- a/plugins/hypervisors/ovm3/pom.xml +++ b/plugins/hypervisors/ovm3/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/hypervisors/simulator/pom.xml b/plugins/hypervisors/simulator/pom.xml index a10b3dacad2..fb39e9147ca 100644 --- a/plugins/hypervisors/simulator/pom.xml +++ b/plugins/hypervisors/simulator/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml cloud-plugin-hypervisor-simulator diff --git a/plugins/hypervisors/ucs/pom.xml b/plugins/hypervisors/ucs/pom.xml index bcca93d3242..67bbba96d06 100644 --- a/plugins/hypervisors/ucs/pom.xml +++ b/plugins/hypervisors/ucs/pom.xml @@ -23,7 +23,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml cloud-plugin-hypervisor-ucs diff --git a/plugins/hypervisors/vmware/pom.xml b/plugins/hypervisors/vmware/pom.xml index 7d60eff768a..6fcdc7f519a 100644 --- a/plugins/hypervisors/vmware/pom.xml +++ b/plugins/hypervisors/vmware/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/hypervisors/xenserver/pom.xml b/plugins/hypervisors/xenserver/pom.xml index eefad234a05..a74ddc5ce97 100644 --- a/plugins/hypervisors/xenserver/pom.xml +++ b/plugins/hypervisors/xenserver/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/integrations/cloudian/pom.xml b/plugins/integrations/cloudian/pom.xml index af2a1a80576..efbbe518367 100644 --- a/plugins/integrations/cloudian/pom.xml +++ b/plugins/integrations/cloudian/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/integrations/kubernetes-service/pom.xml b/plugins/integrations/kubernetes-service/pom.xml index 195e0d6383e..cb16a29e37e 100644 --- a/plugins/integrations/kubernetes-service/pom.xml +++ b/plugins/integrations/kubernetes-service/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/integrations/prometheus/pom.xml b/plugins/integrations/prometheus/pom.xml index 7102cd70719..9ab6e67e683 100644 --- a/plugins/integrations/prometheus/pom.xml +++ b/plugins/integrations/prometheus/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/metrics/pom.xml b/plugins/metrics/pom.xml index 7fc982c8597..ed43634c8fc 100644 --- a/plugins/metrics/pom.xml +++ b/plugins/metrics/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/plugins/network-elements/bigswitch/pom.xml b/plugins/network-elements/bigswitch/pom.xml index 9e6d750a22c..07804433865 100644 --- a/plugins/network-elements/bigswitch/pom.xml +++ b/plugins/network-elements/bigswitch/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/brocade-vcs/pom.xml b/plugins/network-elements/brocade-vcs/pom.xml index ae4a8d3ad86..c989d04c540 100644 --- a/plugins/network-elements/brocade-vcs/pom.xml +++ b/plugins/network-elements/brocade-vcs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/cisco-vnmc/pom.xml b/plugins/network-elements/cisco-vnmc/pom.xml index 09cb83727be..5f5557bfa96 100644 --- a/plugins/network-elements/cisco-vnmc/pom.xml +++ b/plugins/network-elements/cisco-vnmc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/dns-notifier/pom.xml b/plugins/network-elements/dns-notifier/pom.xml index 086737e4ce1..c246bb2dee8 100644 --- a/plugins/network-elements/dns-notifier/pom.xml +++ b/plugins/network-elements/dns-notifier/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml cloud-plugin-example-dns-notifier diff --git a/plugins/network-elements/elastic-loadbalancer/pom.xml b/plugins/network-elements/elastic-loadbalancer/pom.xml index 1913b20888d..977978fdb7d 100644 --- a/plugins/network-elements/elastic-loadbalancer/pom.xml +++ b/plugins/network-elements/elastic-loadbalancer/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/globodns/pom.xml b/plugins/network-elements/globodns/pom.xml index 5ad9c6e9ae7..cb209263c98 100644 --- a/plugins/network-elements/globodns/pom.xml +++ b/plugins/network-elements/globodns/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/internal-loadbalancer/pom.xml b/plugins/network-elements/internal-loadbalancer/pom.xml index 10e9db66400..860b36ba960 100644 --- a/plugins/network-elements/internal-loadbalancer/pom.xml +++ b/plugins/network-elements/internal-loadbalancer/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/juniper-contrail/pom.xml b/plugins/network-elements/juniper-contrail/pom.xml index f333db535f8..7e98b74aa79 100644 --- a/plugins/network-elements/juniper-contrail/pom.xml +++ b/plugins/network-elements/juniper-contrail/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/netscaler/pom.xml b/plugins/network-elements/netscaler/pom.xml index 51ac5b8554a..161347945b7 100644 --- a/plugins/network-elements/netscaler/pom.xml +++ b/plugins/network-elements/netscaler/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/nicira-nvp/pom.xml b/plugins/network-elements/nicira-nvp/pom.xml index 1487199c74e..3fdd1f8c0c1 100644 --- a/plugins/network-elements/nicira-nvp/pom.xml +++ b/plugins/network-elements/nicira-nvp/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/nsx/pom.xml b/plugins/network-elements/nsx/pom.xml index 6a3153ac265..91ef087fab2 100644 --- a/plugins/network-elements/nsx/pom.xml +++ b/plugins/network-elements/nsx/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/opendaylight/pom.xml b/plugins/network-elements/opendaylight/pom.xml index b79627b9735..dd6d4a2eedb 100644 --- a/plugins/network-elements/opendaylight/pom.xml +++ b/plugins/network-elements/opendaylight/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/ovs/pom.xml b/plugins/network-elements/ovs/pom.xml index 05b1e433c40..04f72dbfcbe 100644 --- a/plugins/network-elements/ovs/pom.xml +++ b/plugins/network-elements/ovs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/palo-alto/pom.xml b/plugins/network-elements/palo-alto/pom.xml index bb7425ef033..74c114fb012 100644 --- a/plugins/network-elements/palo-alto/pom.xml +++ b/plugins/network-elements/palo-alto/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/stratosphere-ssp/pom.xml b/plugins/network-elements/stratosphere-ssp/pom.xml index ea0989e80de..30e5fbdb0d7 100644 --- a/plugins/network-elements/stratosphere-ssp/pom.xml +++ b/plugins/network-elements/stratosphere-ssp/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/tungsten/pom.xml b/plugins/network-elements/tungsten/pom.xml index b0c213cc260..42357c95d9e 100644 --- a/plugins/network-elements/tungsten/pom.xml +++ b/plugins/network-elements/tungsten/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/network-elements/vxlan/pom.xml b/plugins/network-elements/vxlan/pom.xml index 36922527b21..da7d4746fb3 100644 --- a/plugins/network-elements/vxlan/pom.xml +++ b/plugins/network-elements/vxlan/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/ipmitool/pom.xml b/plugins/outofbandmanagement-drivers/ipmitool/pom.xml index 75eaee7bf02..a3de4fe97c1 100644 --- a/plugins/outofbandmanagement-drivers/ipmitool/pom.xml +++ b/plugins/outofbandmanagement-drivers/ipmitool/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml b/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml index 6f47d204bac..9ccc80d388c 100644 --- a/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml +++ b/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/redfish/pom.xml b/plugins/outofbandmanagement-drivers/redfish/pom.xml index d3a711c1689..21c9c026d72 100644 --- a/plugins/outofbandmanagement-drivers/redfish/pom.xml +++ b/plugins/outofbandmanagement-drivers/redfish/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/pom.xml b/plugins/pom.xml index 73b5d17fc35..b157e64e5a2 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/plugins/shutdown/pom.xml b/plugins/shutdown/pom.xml index 2ef6b401b60..196e9af147a 100644 --- a/plugins/shutdown/pom.xml +++ b/plugins/shutdown/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/plugins/storage-allocators/random/pom.xml b/plugins/storage-allocators/random/pom.xml index a678a9f6ad8..b5b95c35b74 100644 --- a/plugins/storage-allocators/random/pom.xml +++ b/plugins/storage-allocators/random/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/storage/image/default/pom.xml b/plugins/storage/image/default/pom.xml index d51f0d5b513..9b77b037a0c 100644 --- a/plugins/storage/image/default/pom.xml +++ b/plugins/storage/image/default/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/image/s3/pom.xml b/plugins/storage/image/s3/pom.xml index 4d319555b6e..7f3885d3236 100644 --- a/plugins/storage/image/s3/pom.xml +++ b/plugins/storage/image/s3/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/image/sample/pom.xml b/plugins/storage/image/sample/pom.xml index be86dbb1e5a..8e1ae807fd9 100644 --- a/plugins/storage/image/sample/pom.xml +++ b/plugins/storage/image/sample/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/image/swift/pom.xml b/plugins/storage/image/swift/pom.xml index 7715a02a1dc..e7966a66d54 100644 --- a/plugins/storage/image/swift/pom.xml +++ b/plugins/storage/image/swift/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/object/ceph/pom.xml b/plugins/storage/object/ceph/pom.xml index d7b36e2d901..e0bed0aafab 100644 --- a/plugins/storage/object/ceph/pom.xml +++ b/plugins/storage/object/ceph/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/object/minio/pom.xml b/plugins/storage/object/minio/pom.xml index eca6b6476b5..ffda7f018d4 100644 --- a/plugins/storage/object/minio/pom.xml +++ b/plugins/storage/object/minio/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/object/simulator/pom.xml b/plugins/storage/object/simulator/pom.xml index 635bf0b757f..d1f08d8dcf3 100644 --- a/plugins/storage/object/simulator/pom.xml +++ b/plugins/storage/object/simulator/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/sharedfs/storagevm/pom.xml b/plugins/storage/sharedfs/storagevm/pom.xml index 176c8073334..868cc210178 100644 --- a/plugins/storage/sharedfs/storagevm/pom.xml +++ b/plugins/storage/sharedfs/storagevm/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/adaptive/pom.xml b/plugins/storage/volume/adaptive/pom.xml index fd646e32c11..43c8b51b97a 100644 --- a/plugins/storage/volume/adaptive/pom.xml +++ b/plugins/storage/volume/adaptive/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/cloudbyte/pom.xml b/plugins/storage/volume/cloudbyte/pom.xml index da4d3a8955f..e4908ecc340 100644 --- a/plugins/storage/volume/cloudbyte/pom.xml +++ b/plugins/storage/volume/cloudbyte/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/datera/pom.xml b/plugins/storage/volume/datera/pom.xml index 13b061da288..79ed8ddd4d8 100644 --- a/plugins/storage/volume/datera/pom.xml +++ b/plugins/storage/volume/datera/pom.xml @@ -16,7 +16,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/default/pom.xml b/plugins/storage/volume/default/pom.xml index 48d5169ed93..58c36948510 100644 --- a/plugins/storage/volume/default/pom.xml +++ b/plugins/storage/volume/default/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/flasharray/pom.xml b/plugins/storage/volume/flasharray/pom.xml index afacaa941ae..c5bd880569d 100644 --- a/plugins/storage/volume/flasharray/pom.xml +++ b/plugins/storage/volume/flasharray/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/linstor/pom.xml b/plugins/storage/volume/linstor/pom.xml index abc421547fa..35bea52cc73 100644 --- a/plugins/storage/volume/linstor/pom.xml +++ b/plugins/storage/volume/linstor/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/nexenta/pom.xml b/plugins/storage/volume/nexenta/pom.xml index 1ef81e92e39..3cddcb6a730 100644 --- a/plugins/storage/volume/nexenta/pom.xml +++ b/plugins/storage/volume/nexenta/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/primera/pom.xml b/plugins/storage/volume/primera/pom.xml index e829ab0d2b7..114a33827d4 100644 --- a/plugins/storage/volume/primera/pom.xml +++ b/plugins/storage/volume/primera/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/sample/pom.xml b/plugins/storage/volume/sample/pom.xml index fc9072ae56b..b6c547918da 100644 --- a/plugins/storage/volume/sample/pom.xml +++ b/plugins/storage/volume/sample/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/scaleio/pom.xml b/plugins/storage/volume/scaleio/pom.xml index 3602dbb3586..52ad326dba2 100644 --- a/plugins/storage/volume/scaleio/pom.xml +++ b/plugins/storage/volume/scaleio/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/solidfire/pom.xml b/plugins/storage/volume/solidfire/pom.xml index e5bfc8900f7..f3d69319a13 100644 --- a/plugins/storage/volume/solidfire/pom.xml +++ b/plugins/storage/volume/solidfire/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/storage/volume/storpool/pom.xml b/plugins/storage/volume/storpool/pom.xml index 4341347082c..56dda548633 100644 --- a/plugins/storage/volume/storpool/pom.xml +++ b/plugins/storage/volume/storpool/pom.xml @@ -17,7 +17,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../../pom.xml diff --git a/plugins/user-authenticators/ldap/pom.xml b/plugins/user-authenticators/ldap/pom.xml index 32f699aabd6..1b5f8c0d198 100644 --- a/plugins/user-authenticators/ldap/pom.xml +++ b/plugins/user-authenticators/ldap/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/user-authenticators/md5/pom.xml b/plugins/user-authenticators/md5/pom.xml index c73e9f68830..5b9327b958b 100644 --- a/plugins/user-authenticators/md5/pom.xml +++ b/plugins/user-authenticators/md5/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/user-authenticators/oauth2/pom.xml b/plugins/user-authenticators/oauth2/pom.xml index 65e7d1ff65a..bed07446186 100644 --- a/plugins/user-authenticators/oauth2/pom.xml +++ b/plugins/user-authenticators/oauth2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/user-authenticators/pbkdf2/pom.xml b/plugins/user-authenticators/pbkdf2/pom.xml index 90d9401f88f..2be04401516 100644 --- a/plugins/user-authenticators/pbkdf2/pom.xml +++ b/plugins/user-authenticators/pbkdf2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/user-authenticators/plain-text/pom.xml b/plugins/user-authenticators/plain-text/pom.xml index 997b1bf8367..99e1f0e3f39 100644 --- a/plugins/user-authenticators/plain-text/pom.xml +++ b/plugins/user-authenticators/plain-text/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/user-authenticators/saml2/pom.xml b/plugins/user-authenticators/saml2/pom.xml index 5f5d6b14253..5d0b17ca74d 100644 --- a/plugins/user-authenticators/saml2/pom.xml +++ b/plugins/user-authenticators/saml2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/user-authenticators/sha256salted/pom.xml b/plugins/user-authenticators/sha256salted/pom.xml index 3717cd1482a..87392c8ab20 100644 --- a/plugins/user-authenticators/sha256salted/pom.xml +++ b/plugins/user-authenticators/sha256salted/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/user-two-factor-authenticators/static-pin/pom.xml b/plugins/user-two-factor-authenticators/static-pin/pom.xml index 422eee6cda4..6b71e40d10e 100644 --- a/plugins/user-two-factor-authenticators/static-pin/pom.xml +++ b/plugins/user-two-factor-authenticators/static-pin/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/plugins/user-two-factor-authenticators/totp/pom.xml b/plugins/user-two-factor-authenticators/totp/pom.xml index b4d5fd2c761..f316d794bd5 100644 --- a/plugins/user-two-factor-authenticators/totp/pom.xml +++ b/plugins/user-two-factor-authenticators/totp/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../../pom.xml diff --git a/pom.xml b/pom.xml index e57d6dfc46a..b00797d5975 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 pom Apache CloudStack Apache CloudStack is an IaaS ("Infrastructure as a Service") cloud orchestration platform. diff --git a/quickcloud/pom.xml b/quickcloud/pom.xml index 218621ebb86..c6b72516116 100644 --- a/quickcloud/pom.xml +++ b/quickcloud/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/server/pom.xml b/server/pom.xml index b9d031a005e..f3b4e998144 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/services/console-proxy/pom.xml b/services/console-proxy/pom.xml index fa54fc9248f..bdcb89cea9e 100644 --- a/services/console-proxy/pom.xml +++ b/services/console-proxy/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-services - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/services/console-proxy/rdpconsole/pom.xml b/services/console-proxy/rdpconsole/pom.xml index e6e98f1d981..09ce7d5b9c0 100644 --- a/services/console-proxy/rdpconsole/pom.xml +++ b/services/console-proxy/rdpconsole/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-service-console-proxy - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml index 9b70cc49093..c3b94b6abda 100644 --- a/services/console-proxy/server/pom.xml +++ b/services/console-proxy/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-console-proxy - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/services/pom.xml b/services/pom.xml index d247d3fed88..140fd6f0a05 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/services/secondary-storage/controller/pom.xml b/services/secondary-storage/controller/pom.xml index 89823297621..7869dc9762e 100644 --- a/services/secondary-storage/controller/pom.xml +++ b/services/secondary-storage/controller/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-secondary-storage - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/services/secondary-storage/pom.xml b/services/secondary-storage/pom.xml index 3015ada1c08..24616b19d3c 100644 --- a/services/secondary-storage/pom.xml +++ b/services/secondary-storage/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-services - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/services/secondary-storage/server/pom.xml b/services/secondary-storage/server/pom.xml index 6c744e795ea..be5a508f5d0 100644 --- a/services/secondary-storage/server/pom.xml +++ b/services/secondary-storage/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-secondary-storage - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/systemvm/pom.xml b/systemvm/pom.xml index d1be06d42d7..835aa68d988 100644 --- a/systemvm/pom.xml +++ b/systemvm/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 94321fd1715..abc50dddc82 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/tools/apidoc/pom.xml b/tools/apidoc/pom.xml index 37721668b0f..aba4f356064 100644 --- a/tools/apidoc/pom.xml +++ b/tools/apidoc/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/tools/checkstyle/pom.xml b/tools/checkstyle/pom.xml index e4e1a4d939a..575060ee066 100644 --- a/tools/checkstyle/pom.xml +++ b/tools/checkstyle/pom.xml @@ -22,7 +22,7 @@ Apache CloudStack Developer Tools - Checkstyle Configuration org.apache.cloudstack checkstyle - 4.20.3.0-SNAPSHOT + 4.20.3.0 UTF-8 diff --git a/tools/devcloud-kvm/pom.xml b/tools/devcloud-kvm/pom.xml index d45d306d66d..ec771426b0d 100644 --- a/tools/devcloud-kvm/pom.xml +++ b/tools/devcloud-kvm/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/tools/devcloud4/pom.xml b/tools/devcloud4/pom.xml index 7bb47b87f7b..d1ec12fa579 100644 --- a/tools/devcloud4/pom.xml +++ b/tools/devcloud4/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index aacfefaacd9..9ba37a72232 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -19,7 +19,7 @@ FROM ubuntu:22.04 -LABEL Vendor="Apache.org" License="ApacheV2" Version="4.20.3.0-SNAPSHOT" Author="Apache CloudStack " +LABEL Vendor="Apache.org" License="ApacheV2" Version="4.20.3.0" Author="Apache CloudStack " ARG DEBIAN_FRONTEND=noninteractive diff --git a/tools/docker/Dockerfile.marvin b/tools/docker/Dockerfile.marvin index bc781a55e9f..d1eab6bbcda 100644 --- a/tools/docker/Dockerfile.marvin +++ b/tools/docker/Dockerfile.marvin @@ -19,11 +19,11 @@ # build for cloudstack_home_dir not this folder FROM python:2 -LABEL Vendor="Apache.org" License="ApacheV2" Version="4.20.3.0-SNAPSHOT" Author="Apache CloudStack " +LABEL Vendor="Apache.org" License="ApacheV2" Version="4.20.3.0" Author="Apache CloudStack " ENV WORK_DIR=/marvin -ENV PKG_URL=https://builds.cloudstack.org/job/build-master-marvin/lastSuccessfulBuild/artifact/tools/marvin/dist/Marvin-4.20.3.0-SNAPSHOT.tar.gz +ENV PKG_URL=https://builds.cloudstack.org/job/build-master-marvin/lastSuccessfulBuild/artifact/tools/marvin/dist/Marvin-4.20.3.0.tar.gz RUN apt-get update && apt-get install -y vim RUN pip install --upgrade paramiko nose requests diff --git a/tools/marvin/pom.xml b/tools/marvin/pom.xml index 167bcbaa6f3..674f8b4ddfb 100644 --- a/tools/marvin/pom.xml +++ b/tools/marvin/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/tools/marvin/setup.py b/tools/marvin/setup.py index f01863d6989..1ceff3dc204 100644 --- a/tools/marvin/setup.py +++ b/tools/marvin/setup.py @@ -27,7 +27,7 @@ except ImportError: raise RuntimeError("python setuptools is required to build Marvin") -VERSION = "4.20.3.0-SNAPSHOT" +VERSION = "4.20.3.0" setup(name="Marvin", version=VERSION, diff --git a/tools/pom.xml b/tools/pom.xml index 4f16cb1c7ad..c5ad12fab6a 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/usage/pom.xml b/usage/pom.xml index c4820883222..5afe81f37aa 100644 --- a/usage/pom.xml +++ b/usage/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 diff --git a/utils/pom.xml b/utils/pom.xml index 6b8b1249423..b18d5fea1d1 100755 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 ../pom.xml diff --git a/vmware-base/pom.xml b/vmware-base/pom.xml index f3caced3614..72a7a6ee5da 100644 --- a/vmware-base/pom.xml +++ b/vmware-base/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0-SNAPSHOT + 4.20.3.0 From 8eb162cb996d2020447c1cdce69a43e04c0a5e69 Mon Sep 17 00:00:00 2001 From: Abhisar Sinha Date: Mon, 13 Apr 2026 15:48:18 +0530 Subject: [PATCH 31/43] Updating pom.xml version numbers for release 4.20.4.0-SNAPSHOT --- agent/pom.xml | 2 +- api/pom.xml | 2 +- client/pom.xml | 2 +- core/pom.xml | 2 +- debian/changelog | 6 ++++++ developer/pom.xml | 2 +- engine/api/pom.xml | 2 +- engine/components-api/pom.xml | 2 +- engine/orchestration/pom.xml | 2 +- engine/pom.xml | 2 +- engine/schema/pom.xml | 2 +- engine/service/pom.xml | 2 +- engine/storage/cache/pom.xml | 2 +- engine/storage/configdrive/pom.xml | 2 +- engine/storage/datamotion/pom.xml | 2 +- engine/storage/image/pom.xml | 2 +- engine/storage/integration-test/pom.xml | 2 +- engine/storage/object/pom.xml | 2 +- engine/storage/pom.xml | 2 +- engine/storage/snapshot/pom.xml | 2 +- engine/storage/volume/pom.xml | 2 +- engine/userdata/cloud-init/pom.xml | 2 +- engine/userdata/pom.xml | 2 +- framework/agent-lb/pom.xml | 2 +- framework/ca/pom.xml | 2 +- framework/cluster/pom.xml | 2 +- framework/config/pom.xml | 2 +- framework/db/pom.xml | 2 +- framework/direct-download/pom.xml | 2 +- framework/events/pom.xml | 2 +- framework/ipc/pom.xml | 2 +- framework/jobs/pom.xml | 2 +- framework/managed-context/pom.xml | 2 +- framework/pom.xml | 2 +- framework/quota/pom.xml | 2 +- framework/rest/pom.xml | 2 +- framework/security/pom.xml | 2 +- framework/spring/lifecycle/pom.xml | 2 +- framework/spring/module/pom.xml | 2 +- plugins/acl/dynamic-role-based/pom.xml | 2 +- plugins/acl/project-role-based/pom.xml | 2 +- plugins/acl/static-role-based/pom.xml | 2 +- .../affinity-group-processors/explicit-dedication/pom.xml | 2 +- plugins/affinity-group-processors/host-affinity/pom.xml | 2 +- .../affinity-group-processors/host-anti-affinity/pom.xml | 2 +- .../non-strict-host-affinity/pom.xml | 2 +- .../non-strict-host-anti-affinity/pom.xml | 2 +- plugins/alert-handlers/snmp-alerts/pom.xml | 2 +- plugins/alert-handlers/syslog-alerts/pom.xml | 2 +- plugins/api/discovery/pom.xml | 2 +- plugins/api/rate-limit/pom.xml | 2 +- plugins/api/solidfire-intg-test/pom.xml | 2 +- plugins/api/vmware-sioc/pom.xml | 2 +- plugins/backup/dummy/pom.xml | 2 +- plugins/backup/nas/pom.xml | 2 +- plugins/backup/networker/pom.xml | 2 +- plugins/backup/veeam/pom.xml | 2 +- plugins/ca/root-ca/pom.xml | 2 +- plugins/database/mysql-ha/pom.xml | 2 +- plugins/database/quota/pom.xml | 2 +- plugins/dedicated-resources/pom.xml | 2 +- plugins/deployment-planners/implicit-dedication/pom.xml | 2 +- plugins/deployment-planners/user-concentrated-pod/pom.xml | 2 +- plugins/deployment-planners/user-dispersing/pom.xml | 2 +- plugins/drs/cluster/balanced/pom.xml | 2 +- plugins/drs/cluster/condensed/pom.xml | 2 +- plugins/event-bus/inmemory/pom.xml | 2 +- plugins/event-bus/kafka/pom.xml | 2 +- plugins/event-bus/rabbitmq/pom.xml | 2 +- plugins/event-bus/webhook/pom.xml | 2 +- plugins/ha-planners/skip-heurestics/pom.xml | 2 +- plugins/host-allocators/random/pom.xml | 2 +- plugins/hypervisors/baremetal/pom.xml | 2 +- plugins/hypervisors/hyperv/pom.xml | 2 +- plugins/hypervisors/kvm/pom.xml | 2 +- plugins/hypervisors/ovm/pom.xml | 2 +- plugins/hypervisors/ovm3/pom.xml | 2 +- plugins/hypervisors/simulator/pom.xml | 2 +- plugins/hypervisors/ucs/pom.xml | 2 +- plugins/hypervisors/vmware/pom.xml | 2 +- plugins/hypervisors/xenserver/pom.xml | 2 +- plugins/integrations/cloudian/pom.xml | 2 +- plugins/integrations/kubernetes-service/pom.xml | 2 +- plugins/integrations/prometheus/pom.xml | 2 +- plugins/metrics/pom.xml | 2 +- plugins/network-elements/bigswitch/pom.xml | 2 +- plugins/network-elements/brocade-vcs/pom.xml | 2 +- plugins/network-elements/cisco-vnmc/pom.xml | 2 +- plugins/network-elements/dns-notifier/pom.xml | 2 +- plugins/network-elements/elastic-loadbalancer/pom.xml | 2 +- plugins/network-elements/globodns/pom.xml | 2 +- plugins/network-elements/internal-loadbalancer/pom.xml | 2 +- plugins/network-elements/juniper-contrail/pom.xml | 2 +- plugins/network-elements/netscaler/pom.xml | 2 +- plugins/network-elements/nicira-nvp/pom.xml | 2 +- plugins/network-elements/nsx/pom.xml | 2 +- plugins/network-elements/opendaylight/pom.xml | 2 +- plugins/network-elements/ovs/pom.xml | 2 +- plugins/network-elements/palo-alto/pom.xml | 2 +- plugins/network-elements/stratosphere-ssp/pom.xml | 2 +- plugins/network-elements/tungsten/pom.xml | 2 +- plugins/network-elements/vxlan/pom.xml | 2 +- plugins/outofbandmanagement-drivers/ipmitool/pom.xml | 2 +- .../outofbandmanagement-drivers/nested-cloudstack/pom.xml | 2 +- plugins/outofbandmanagement-drivers/redfish/pom.xml | 2 +- plugins/pom.xml | 2 +- plugins/shutdown/pom.xml | 2 +- plugins/storage-allocators/random/pom.xml | 2 +- plugins/storage/image/default/pom.xml | 2 +- plugins/storage/image/s3/pom.xml | 2 +- plugins/storage/image/sample/pom.xml | 2 +- plugins/storage/image/swift/pom.xml | 2 +- plugins/storage/object/ceph/pom.xml | 2 +- plugins/storage/object/minio/pom.xml | 2 +- plugins/storage/object/simulator/pom.xml | 2 +- plugins/storage/sharedfs/storagevm/pom.xml | 2 +- plugins/storage/volume/adaptive/pom.xml | 2 +- plugins/storage/volume/cloudbyte/pom.xml | 2 +- plugins/storage/volume/datera/pom.xml | 2 +- plugins/storage/volume/default/pom.xml | 2 +- plugins/storage/volume/flasharray/pom.xml | 2 +- plugins/storage/volume/linstor/pom.xml | 2 +- plugins/storage/volume/nexenta/pom.xml | 2 +- plugins/storage/volume/primera/pom.xml | 2 +- plugins/storage/volume/sample/pom.xml | 2 +- plugins/storage/volume/scaleio/pom.xml | 2 +- plugins/storage/volume/solidfire/pom.xml | 2 +- plugins/storage/volume/storpool/pom.xml | 2 +- plugins/user-authenticators/ldap/pom.xml | 2 +- plugins/user-authenticators/md5/pom.xml | 2 +- plugins/user-authenticators/oauth2/pom.xml | 2 +- plugins/user-authenticators/pbkdf2/pom.xml | 2 +- plugins/user-authenticators/plain-text/pom.xml | 2 +- plugins/user-authenticators/saml2/pom.xml | 2 +- plugins/user-authenticators/sha256salted/pom.xml | 2 +- plugins/user-two-factor-authenticators/static-pin/pom.xml | 2 +- plugins/user-two-factor-authenticators/totp/pom.xml | 2 +- pom.xml | 2 +- quickcloud/pom.xml | 2 +- server/pom.xml | 2 +- services/console-proxy/pom.xml | 2 +- services/console-proxy/rdpconsole/pom.xml | 2 +- services/console-proxy/server/pom.xml | 2 +- services/pom.xml | 2 +- services/secondary-storage/controller/pom.xml | 2 +- services/secondary-storage/pom.xml | 2 +- services/secondary-storage/server/pom.xml | 2 +- systemvm/pom.xml | 2 +- test/pom.xml | 2 +- tools/apidoc/pom.xml | 2 +- tools/checkstyle/pom.xml | 2 +- tools/devcloud-kvm/pom.xml | 2 +- tools/devcloud4/pom.xml | 2 +- tools/docker/Dockerfile | 2 +- tools/docker/Dockerfile.marvin | 4 ++-- tools/marvin/pom.xml | 2 +- tools/marvin/setup.py | 2 +- tools/pom.xml | 2 +- usage/pom.xml | 2 +- utils/pom.xml | 2 +- vmware-base/pom.xml | 2 +- 161 files changed, 167 insertions(+), 161 deletions(-) diff --git a/agent/pom.xml b/agent/pom.xml index 122dfd1005d..79e4dbab54b 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/api/pom.xml b/api/pom.xml index f431d8e69f4..caa33b66218 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/client/pom.xml b/client/pom.xml index bd91063425d..2672ed81127 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/core/pom.xml b/core/pom.xml index 80ceac98c7c..4689a1c8683 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/debian/changelog b/debian/changelog index d06479e19a7..6bd9e5e9511 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +cloudstack (4.20.4.0-SNAPSHOT) unstable; urgency=low + + * Update the version to 4.20.4.0-SNAPSHOT + + -- the Apache CloudStack project Mon, 13 Apr 2026 12:36:00 +0530 + cloudstack (4.20.3.0) unstable; urgency=low * Update the version to 4.20.3.0 diff --git a/developer/pom.xml b/developer/pom.xml index e4af35300c7..9608a2ba0f5 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/engine/api/pom.xml b/engine/api/pom.xml index 5825f0609af..17090998d6b 100644 --- a/engine/api/pom.xml +++ b/engine/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/engine/components-api/pom.xml b/engine/components-api/pom.xml index f7b3c13c165..395f7174834 100644 --- a/engine/components-api/pom.xml +++ b/engine/components-api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/engine/orchestration/pom.xml b/engine/orchestration/pom.xml index ac8fddf0874..9f20012b56e 100755 --- a/engine/orchestration/pom.xml +++ b/engine/orchestration/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/engine/pom.xml b/engine/pom.xml index 828203d4bfc..2da9515dbf1 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/engine/schema/pom.xml b/engine/schema/pom.xml index 2293aa51f93..0cd4a401193 100644 --- a/engine/schema/pom.xml +++ b/engine/schema/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/engine/service/pom.xml b/engine/service/pom.xml index 5ecc54f6275..5f6d75a30e7 100644 --- a/engine/service/pom.xml +++ b/engine/service/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT cloud-engine-service war diff --git a/engine/storage/cache/pom.xml b/engine/storage/cache/pom.xml index 497f49fc9e6..9cce847b7ca 100644 --- a/engine/storage/cache/pom.xml +++ b/engine/storage/cache/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/storage/configdrive/pom.xml b/engine/storage/configdrive/pom.xml index ff101fab43a..e4abfdcb474 100644 --- a/engine/storage/configdrive/pom.xml +++ b/engine/storage/configdrive/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/storage/datamotion/pom.xml b/engine/storage/datamotion/pom.xml index 38a25db4309..ce15176efdc 100644 --- a/engine/storage/datamotion/pom.xml +++ b/engine/storage/datamotion/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/storage/image/pom.xml b/engine/storage/image/pom.xml index c6fb56967fc..98df0ff620d 100644 --- a/engine/storage/image/pom.xml +++ b/engine/storage/image/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/storage/integration-test/pom.xml b/engine/storage/integration-test/pom.xml index 14c3c9e9380..c250133fc5f 100644 --- a/engine/storage/integration-test/pom.xml +++ b/engine/storage/integration-test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/storage/object/pom.xml b/engine/storage/object/pom.xml index 514e5b7e3bf..4641f89cf5c 100644 --- a/engine/storage/object/pom.xml +++ b/engine/storage/object/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/storage/pom.xml b/engine/storage/pom.xml index 8a7b1f01d0e..6e2eb1b12f3 100644 --- a/engine/storage/pom.xml +++ b/engine/storage/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/engine/storage/snapshot/pom.xml b/engine/storage/snapshot/pom.xml index c683a0973a0..a6db00995c2 100644 --- a/engine/storage/snapshot/pom.xml +++ b/engine/storage/snapshot/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/storage/volume/pom.xml b/engine/storage/volume/pom.xml index 7ff2ca42cf6..2132bfdea53 100644 --- a/engine/storage/volume/pom.xml +++ b/engine/storage/volume/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/userdata/cloud-init/pom.xml b/engine/userdata/cloud-init/pom.xml index cd3d9d0380c..e50cacebf15 100644 --- a/engine/userdata/cloud-init/pom.xml +++ b/engine/userdata/cloud-init/pom.xml @@ -23,7 +23,7 @@ cloud-engine org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/engine/userdata/pom.xml b/engine/userdata/pom.xml index 750e724c4cd..f8e0958cb22 100644 --- a/engine/userdata/pom.xml +++ b/engine/userdata/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/agent-lb/pom.xml b/framework/agent-lb/pom.xml index 1dc9806d924..8b4d0b597ba 100644 --- a/framework/agent-lb/pom.xml +++ b/framework/agent-lb/pom.xml @@ -24,7 +24,7 @@ cloudstack-framework org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/ca/pom.xml b/framework/ca/pom.xml index 3482f9787e8..c1a57290b97 100644 --- a/framework/ca/pom.xml +++ b/framework/ca/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/cluster/pom.xml b/framework/cluster/pom.xml index e46256af8ed..fb450c4f581 100644 --- a/framework/cluster/pom.xml +++ b/framework/cluster/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/config/pom.xml b/framework/config/pom.xml index 86251b08165..db0edbfc667 100644 --- a/framework/config/pom.xml +++ b/framework/config/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/db/pom.xml b/framework/db/pom.xml index 8f7caaeb7f7..a71e467a23b 100644 --- a/framework/db/pom.xml +++ b/framework/db/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/direct-download/pom.xml b/framework/direct-download/pom.xml index 9e8614c04fa..c8a224bafaa 100644 --- a/framework/direct-download/pom.xml +++ b/framework/direct-download/pom.xml @@ -32,7 +32,7 @@ cloudstack-framework org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/events/pom.xml b/framework/events/pom.xml index ea9b80e6202..305b5dd0cf0 100644 --- a/framework/events/pom.xml +++ b/framework/events/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/ipc/pom.xml b/framework/ipc/pom.xml index d1e5531ed85..ba424f0e343 100644 --- a/framework/ipc/pom.xml +++ b/framework/ipc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/jobs/pom.xml b/framework/jobs/pom.xml index 26aafca7907..c5207f144a7 100644 --- a/framework/jobs/pom.xml +++ b/framework/jobs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/managed-context/pom.xml b/framework/managed-context/pom.xml index 2bb77534369..dec390f9d2e 100644 --- a/framework/managed-context/pom.xml +++ b/framework/managed-context/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/framework/pom.xml b/framework/pom.xml index 01962c68f0f..233fe33725e 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/framework/quota/pom.xml b/framework/quota/pom.xml index 0c73586f011..ed4252a39f7 100644 --- a/framework/quota/pom.xml +++ b/framework/quota/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/rest/pom.xml b/framework/rest/pom.xml index d65317fe491..59ca5e6b763 100644 --- a/framework/rest/pom.xml +++ b/framework/rest/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml cloud-framework-rest diff --git a/framework/security/pom.xml b/framework/security/pom.xml index 28d5c67d811..96f79991447 100644 --- a/framework/security/pom.xml +++ b/framework/security/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-framework - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/framework/spring/lifecycle/pom.xml b/framework/spring/lifecycle/pom.xml index dabeb4b8e64..d5ce5bd45c3 100644 --- a/framework/spring/lifecycle/pom.xml +++ b/framework/spring/lifecycle/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/framework/spring/module/pom.xml b/framework/spring/module/pom.xml index 7bede4f9e2f..6f105cda976 100644 --- a/framework/spring/module/pom.xml +++ b/framework/spring/module/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/acl/dynamic-role-based/pom.xml b/plugins/acl/dynamic-role-based/pom.xml index 9830d856033..9497cf22657 100644 --- a/plugins/acl/dynamic-role-based/pom.xml +++ b/plugins/acl/dynamic-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/acl/project-role-based/pom.xml b/plugins/acl/project-role-based/pom.xml index 60d5eb58621..5d2597bacc4 100644 --- a/plugins/acl/project-role-based/pom.xml +++ b/plugins/acl/project-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/acl/static-role-based/pom.xml b/plugins/acl/static-role-based/pom.xml index 1dc50c712a0..3fae273629d 100644 --- a/plugins/acl/static-role-based/pom.xml +++ b/plugins/acl/static-role-based/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/affinity-group-processors/explicit-dedication/pom.xml b/plugins/affinity-group-processors/explicit-dedication/pom.xml index dbeb9665f26..179a4724250 100644 --- a/plugins/affinity-group-processors/explicit-dedication/pom.xml +++ b/plugins/affinity-group-processors/explicit-dedication/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/affinity-group-processors/host-affinity/pom.xml b/plugins/affinity-group-processors/host-affinity/pom.xml index c8643f79274..e773affb51d 100644 --- a/plugins/affinity-group-processors/host-affinity/pom.xml +++ b/plugins/affinity-group-processors/host-affinity/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/affinity-group-processors/host-anti-affinity/pom.xml b/plugins/affinity-group-processors/host-anti-affinity/pom.xml index 3af661a62ee..c6ab05c17eb 100644 --- a/plugins/affinity-group-processors/host-anti-affinity/pom.xml +++ b/plugins/affinity-group-processors/host-anti-affinity/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml b/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml index 8f722b01ead..dee603e03d1 100644 --- a/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml +++ b/plugins/affinity-group-processors/non-strict-host-affinity/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml b/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml index 1593c847fad..645dff862e9 100644 --- a/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml +++ b/plugins/affinity-group-processors/non-strict-host-anti-affinity/pom.xml @@ -32,7 +32,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/alert-handlers/snmp-alerts/pom.xml b/plugins/alert-handlers/snmp-alerts/pom.xml index dc3d383d59a..e2b16f65d32 100644 --- a/plugins/alert-handlers/snmp-alerts/pom.xml +++ b/plugins/alert-handlers/snmp-alerts/pom.xml @@ -24,7 +24,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/alert-handlers/syslog-alerts/pom.xml b/plugins/alert-handlers/syslog-alerts/pom.xml index 8468a603fcd..670631af6c0 100644 --- a/plugins/alert-handlers/syslog-alerts/pom.xml +++ b/plugins/alert-handlers/syslog-alerts/pom.xml @@ -24,7 +24,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/api/discovery/pom.xml b/plugins/api/discovery/pom.xml index 74f6e1e8f95..d92d76ef0a1 100644 --- a/plugins/api/discovery/pom.xml +++ b/plugins/api/discovery/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/api/rate-limit/pom.xml b/plugins/api/rate-limit/pom.xml index 9160d704ffe..c00468f3a2f 100644 --- a/plugins/api/rate-limit/pom.xml +++ b/plugins/api/rate-limit/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/api/solidfire-intg-test/pom.xml b/plugins/api/solidfire-intg-test/pom.xml index 060cc911f8c..afc731d8969 100644 --- a/plugins/api/solidfire-intg-test/pom.xml +++ b/plugins/api/solidfire-intg-test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/api/vmware-sioc/pom.xml b/plugins/api/vmware-sioc/pom.xml index 5147a0d5cd4..1444f920734 100644 --- a/plugins/api/vmware-sioc/pom.xml +++ b/plugins/api/vmware-sioc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/backup/dummy/pom.xml b/plugins/backup/dummy/pom.xml index 45ab1814b4d..66bd9ce389a 100644 --- a/plugins/backup/dummy/pom.xml +++ b/plugins/backup/dummy/pom.xml @@ -23,7 +23,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/backup/nas/pom.xml b/plugins/backup/nas/pom.xml index 5d1ca823a41..9307d6e147a 100644 --- a/plugins/backup/nas/pom.xml +++ b/plugins/backup/nas/pom.xml @@ -25,7 +25,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/backup/networker/pom.xml b/plugins/backup/networker/pom.xml index 9fbf6e0a5a4..014365cd3c5 100644 --- a/plugins/backup/networker/pom.xml +++ b/plugins/backup/networker/pom.xml @@ -25,7 +25,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/backup/veeam/pom.xml b/plugins/backup/veeam/pom.xml index e69a922d512..09cc16abcc7 100644 --- a/plugins/backup/veeam/pom.xml +++ b/plugins/backup/veeam/pom.xml @@ -23,7 +23,7 @@ cloudstack-plugins org.apache.cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/ca/root-ca/pom.xml b/plugins/ca/root-ca/pom.xml index ff37a8784d9..7a2b2b9d9a8 100644 --- a/plugins/ca/root-ca/pom.xml +++ b/plugins/ca/root-ca/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/database/mysql-ha/pom.xml b/plugins/database/mysql-ha/pom.xml index 399d8811d6d..f65be3fdae9 100644 --- a/plugins/database/mysql-ha/pom.xml +++ b/plugins/database/mysql-ha/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/database/quota/pom.xml b/plugins/database/quota/pom.xml index d6fcf9febe5..e16e6e1d059 100644 --- a/plugins/database/quota/pom.xml +++ b/plugins/database/quota/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/dedicated-resources/pom.xml b/plugins/dedicated-resources/pom.xml index d92e4989d21..a993cbe1952 100644 --- a/plugins/dedicated-resources/pom.xml +++ b/plugins/dedicated-resources/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/plugins/deployment-planners/implicit-dedication/pom.xml b/plugins/deployment-planners/implicit-dedication/pom.xml index da570d0da3f..2884cb6b894 100644 --- a/plugins/deployment-planners/implicit-dedication/pom.xml +++ b/plugins/deployment-planners/implicit-dedication/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/deployment-planners/user-concentrated-pod/pom.xml b/plugins/deployment-planners/user-concentrated-pod/pom.xml index 2bce273ef76..bea71c06d85 100644 --- a/plugins/deployment-planners/user-concentrated-pod/pom.xml +++ b/plugins/deployment-planners/user-concentrated-pod/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/deployment-planners/user-dispersing/pom.xml b/plugins/deployment-planners/user-dispersing/pom.xml index 93b9e4e4567..09c0a921465 100644 --- a/plugins/deployment-planners/user-dispersing/pom.xml +++ b/plugins/deployment-planners/user-dispersing/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/drs/cluster/balanced/pom.xml b/plugins/drs/cluster/balanced/pom.xml index b8e292e1f57..21f5f410765 100644 --- a/plugins/drs/cluster/balanced/pom.xml +++ b/plugins/drs/cluster/balanced/pom.xml @@ -27,7 +27,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/drs/cluster/condensed/pom.xml b/plugins/drs/cluster/condensed/pom.xml index 46c0ad9670e..620b31e2a27 100644 --- a/plugins/drs/cluster/condensed/pom.xml +++ b/plugins/drs/cluster/condensed/pom.xml @@ -27,7 +27,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/event-bus/inmemory/pom.xml b/plugins/event-bus/inmemory/pom.xml index d4bb54bd8d0..5ab79b73c58 100644 --- a/plugins/event-bus/inmemory/pom.xml +++ b/plugins/event-bus/inmemory/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/event-bus/kafka/pom.xml b/plugins/event-bus/kafka/pom.xml index 742ff584dce..ffa6f3aea6b 100644 --- a/plugins/event-bus/kafka/pom.xml +++ b/plugins/event-bus/kafka/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/event-bus/rabbitmq/pom.xml b/plugins/event-bus/rabbitmq/pom.xml index 63df0fe015d..4c27b992d75 100644 --- a/plugins/event-bus/rabbitmq/pom.xml +++ b/plugins/event-bus/rabbitmq/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/event-bus/webhook/pom.xml b/plugins/event-bus/webhook/pom.xml index 6ed70b05b20..c8f274ee1d9 100644 --- a/plugins/event-bus/webhook/pom.xml +++ b/plugins/event-bus/webhook/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/ha-planners/skip-heurestics/pom.xml b/plugins/ha-planners/skip-heurestics/pom.xml index 0a8d39149ac..aae6881f987 100644 --- a/plugins/ha-planners/skip-heurestics/pom.xml +++ b/plugins/ha-planners/skip-heurestics/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/host-allocators/random/pom.xml b/plugins/host-allocators/random/pom.xml index c4e0fa3f439..8dee670cb4b 100644 --- a/plugins/host-allocators/random/pom.xml +++ b/plugins/host-allocators/random/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/hypervisors/baremetal/pom.xml b/plugins/hypervisors/baremetal/pom.xml index 81c90b8ec8b..bab9a029ca9 100755 --- a/plugins/hypervisors/baremetal/pom.xml +++ b/plugins/hypervisors/baremetal/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml cloud-plugin-hypervisor-baremetal diff --git a/plugins/hypervisors/hyperv/pom.xml b/plugins/hypervisors/hyperv/pom.xml index 3c00d85ddea..a730d6677ff 100644 --- a/plugins/hypervisors/hyperv/pom.xml +++ b/plugins/hypervisors/hyperv/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/hypervisors/kvm/pom.xml b/plugins/hypervisors/kvm/pom.xml index f12f55e74e0..0e150398dd8 100644 --- a/plugins/hypervisors/kvm/pom.xml +++ b/plugins/hypervisors/kvm/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/hypervisors/ovm/pom.xml b/plugins/hypervisors/ovm/pom.xml index 9c64173619c..c786bcb2ab4 100644 --- a/plugins/hypervisors/ovm/pom.xml +++ b/plugins/hypervisors/ovm/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/hypervisors/ovm3/pom.xml b/plugins/hypervisors/ovm3/pom.xml index 3afd0e11a36..7fe1cf89e5f 100644 --- a/plugins/hypervisors/ovm3/pom.xml +++ b/plugins/hypervisors/ovm3/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/hypervisors/simulator/pom.xml b/plugins/hypervisors/simulator/pom.xml index fb39e9147ca..d21c697c352 100644 --- a/plugins/hypervisors/simulator/pom.xml +++ b/plugins/hypervisors/simulator/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml cloud-plugin-hypervisor-simulator diff --git a/plugins/hypervisors/ucs/pom.xml b/plugins/hypervisors/ucs/pom.xml index 67bbba96d06..3e431814eba 100644 --- a/plugins/hypervisors/ucs/pom.xml +++ b/plugins/hypervisors/ucs/pom.xml @@ -23,7 +23,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml cloud-plugin-hypervisor-ucs diff --git a/plugins/hypervisors/vmware/pom.xml b/plugins/hypervisors/vmware/pom.xml index 6fcdc7f519a..7b650a1de2f 100644 --- a/plugins/hypervisors/vmware/pom.xml +++ b/plugins/hypervisors/vmware/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/hypervisors/xenserver/pom.xml b/plugins/hypervisors/xenserver/pom.xml index a74ddc5ce97..fa6d827eaf8 100644 --- a/plugins/hypervisors/xenserver/pom.xml +++ b/plugins/hypervisors/xenserver/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/integrations/cloudian/pom.xml b/plugins/integrations/cloudian/pom.xml index efbbe518367..93305272eab 100644 --- a/plugins/integrations/cloudian/pom.xml +++ b/plugins/integrations/cloudian/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/integrations/kubernetes-service/pom.xml b/plugins/integrations/kubernetes-service/pom.xml index cb16a29e37e..0f802f5f64f 100644 --- a/plugins/integrations/kubernetes-service/pom.xml +++ b/plugins/integrations/kubernetes-service/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/integrations/prometheus/pom.xml b/plugins/integrations/prometheus/pom.xml index 9ab6e67e683..5329b13bb46 100644 --- a/plugins/integrations/prometheus/pom.xml +++ b/plugins/integrations/prometheus/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/metrics/pom.xml b/plugins/metrics/pom.xml index ed43634c8fc..5d3d488afec 100644 --- a/plugins/metrics/pom.xml +++ b/plugins/metrics/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/plugins/network-elements/bigswitch/pom.xml b/plugins/network-elements/bigswitch/pom.xml index 07804433865..614cd8bd6fe 100644 --- a/plugins/network-elements/bigswitch/pom.xml +++ b/plugins/network-elements/bigswitch/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/brocade-vcs/pom.xml b/plugins/network-elements/brocade-vcs/pom.xml index c989d04c540..deabada9bb1 100644 --- a/plugins/network-elements/brocade-vcs/pom.xml +++ b/plugins/network-elements/brocade-vcs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/cisco-vnmc/pom.xml b/plugins/network-elements/cisco-vnmc/pom.xml index 5f5557bfa96..50f316520b8 100644 --- a/plugins/network-elements/cisco-vnmc/pom.xml +++ b/plugins/network-elements/cisco-vnmc/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/dns-notifier/pom.xml b/plugins/network-elements/dns-notifier/pom.xml index c246bb2dee8..bf7f5dc8c87 100644 --- a/plugins/network-elements/dns-notifier/pom.xml +++ b/plugins/network-elements/dns-notifier/pom.xml @@ -22,7 +22,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml cloud-plugin-example-dns-notifier diff --git a/plugins/network-elements/elastic-loadbalancer/pom.xml b/plugins/network-elements/elastic-loadbalancer/pom.xml index 977978fdb7d..0a1bef1f8fb 100644 --- a/plugins/network-elements/elastic-loadbalancer/pom.xml +++ b/plugins/network-elements/elastic-loadbalancer/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/globodns/pom.xml b/plugins/network-elements/globodns/pom.xml index cb209263c98..8aeb86bf7ee 100644 --- a/plugins/network-elements/globodns/pom.xml +++ b/plugins/network-elements/globodns/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/internal-loadbalancer/pom.xml b/plugins/network-elements/internal-loadbalancer/pom.xml index 860b36ba960..07662ac6295 100644 --- a/plugins/network-elements/internal-loadbalancer/pom.xml +++ b/plugins/network-elements/internal-loadbalancer/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/juniper-contrail/pom.xml b/plugins/network-elements/juniper-contrail/pom.xml index 7e98b74aa79..5f1f67ef865 100644 --- a/plugins/network-elements/juniper-contrail/pom.xml +++ b/plugins/network-elements/juniper-contrail/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/netscaler/pom.xml b/plugins/network-elements/netscaler/pom.xml index 161347945b7..61742831ed6 100644 --- a/plugins/network-elements/netscaler/pom.xml +++ b/plugins/network-elements/netscaler/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/nicira-nvp/pom.xml b/plugins/network-elements/nicira-nvp/pom.xml index 3fdd1f8c0c1..13d22858881 100644 --- a/plugins/network-elements/nicira-nvp/pom.xml +++ b/plugins/network-elements/nicira-nvp/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/nsx/pom.xml b/plugins/network-elements/nsx/pom.xml index 91ef087fab2..ac019091109 100644 --- a/plugins/network-elements/nsx/pom.xml +++ b/plugins/network-elements/nsx/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/opendaylight/pom.xml b/plugins/network-elements/opendaylight/pom.xml index dd6d4a2eedb..0330128ff23 100644 --- a/plugins/network-elements/opendaylight/pom.xml +++ b/plugins/network-elements/opendaylight/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/ovs/pom.xml b/plugins/network-elements/ovs/pom.xml index 04f72dbfcbe..02dbcddc986 100644 --- a/plugins/network-elements/ovs/pom.xml +++ b/plugins/network-elements/ovs/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/palo-alto/pom.xml b/plugins/network-elements/palo-alto/pom.xml index 74c114fb012..fc822a5a7a7 100644 --- a/plugins/network-elements/palo-alto/pom.xml +++ b/plugins/network-elements/palo-alto/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/stratosphere-ssp/pom.xml b/plugins/network-elements/stratosphere-ssp/pom.xml index 30e5fbdb0d7..f997e2cc2fb 100644 --- a/plugins/network-elements/stratosphere-ssp/pom.xml +++ b/plugins/network-elements/stratosphere-ssp/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/tungsten/pom.xml b/plugins/network-elements/tungsten/pom.xml index 42357c95d9e..b71609e7a25 100644 --- a/plugins/network-elements/tungsten/pom.xml +++ b/plugins/network-elements/tungsten/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/network-elements/vxlan/pom.xml b/plugins/network-elements/vxlan/pom.xml index da7d4746fb3..9e695cf874d 100644 --- a/plugins/network-elements/vxlan/pom.xml +++ b/plugins/network-elements/vxlan/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/ipmitool/pom.xml b/plugins/outofbandmanagement-drivers/ipmitool/pom.xml index a3de4fe97c1..bc327eede1c 100644 --- a/plugins/outofbandmanagement-drivers/ipmitool/pom.xml +++ b/plugins/outofbandmanagement-drivers/ipmitool/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml b/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml index 9ccc80d388c..7ddf4eaa114 100644 --- a/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml +++ b/plugins/outofbandmanagement-drivers/nested-cloudstack/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/outofbandmanagement-drivers/redfish/pom.xml b/plugins/outofbandmanagement-drivers/redfish/pom.xml index 21c9c026d72..6ab7b096e24 100644 --- a/plugins/outofbandmanagement-drivers/redfish/pom.xml +++ b/plugins/outofbandmanagement-drivers/redfish/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/pom.xml b/plugins/pom.xml index b157e64e5a2..6a57140c248 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/plugins/shutdown/pom.xml b/plugins/shutdown/pom.xml index 196e9af147a..9af9a56667c 100644 --- a/plugins/shutdown/pom.xml +++ b/plugins/shutdown/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/plugins/storage-allocators/random/pom.xml b/plugins/storage-allocators/random/pom.xml index b5b95c35b74..9888fd72b48 100644 --- a/plugins/storage-allocators/random/pom.xml +++ b/plugins/storage-allocators/random/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/storage/image/default/pom.xml b/plugins/storage/image/default/pom.xml index 9b77b037a0c..f79d7f40ba7 100644 --- a/plugins/storage/image/default/pom.xml +++ b/plugins/storage/image/default/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/image/s3/pom.xml b/plugins/storage/image/s3/pom.xml index 7f3885d3236..7c44607542e 100644 --- a/plugins/storage/image/s3/pom.xml +++ b/plugins/storage/image/s3/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/image/sample/pom.xml b/plugins/storage/image/sample/pom.xml index 8e1ae807fd9..5449634de9a 100644 --- a/plugins/storage/image/sample/pom.xml +++ b/plugins/storage/image/sample/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/image/swift/pom.xml b/plugins/storage/image/swift/pom.xml index e7966a66d54..924fe325482 100644 --- a/plugins/storage/image/swift/pom.xml +++ b/plugins/storage/image/swift/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/object/ceph/pom.xml b/plugins/storage/object/ceph/pom.xml index e0bed0aafab..7975e39e799 100644 --- a/plugins/storage/object/ceph/pom.xml +++ b/plugins/storage/object/ceph/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/object/minio/pom.xml b/plugins/storage/object/minio/pom.xml index ffda7f018d4..735965e4229 100644 --- a/plugins/storage/object/minio/pom.xml +++ b/plugins/storage/object/minio/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/object/simulator/pom.xml b/plugins/storage/object/simulator/pom.xml index d1f08d8dcf3..7a1efaea607 100644 --- a/plugins/storage/object/simulator/pom.xml +++ b/plugins/storage/object/simulator/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/sharedfs/storagevm/pom.xml b/plugins/storage/sharedfs/storagevm/pom.xml index 868cc210178..dfbd71c1c0d 100644 --- a/plugins/storage/sharedfs/storagevm/pom.xml +++ b/plugins/storage/sharedfs/storagevm/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/adaptive/pom.xml b/plugins/storage/volume/adaptive/pom.xml index 43c8b51b97a..8fb1f883189 100644 --- a/plugins/storage/volume/adaptive/pom.xml +++ b/plugins/storage/volume/adaptive/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/cloudbyte/pom.xml b/plugins/storage/volume/cloudbyte/pom.xml index e4908ecc340..b16224c4d10 100644 --- a/plugins/storage/volume/cloudbyte/pom.xml +++ b/plugins/storage/volume/cloudbyte/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/datera/pom.xml b/plugins/storage/volume/datera/pom.xml index 79ed8ddd4d8..8de2877f02b 100644 --- a/plugins/storage/volume/datera/pom.xml +++ b/plugins/storage/volume/datera/pom.xml @@ -16,7 +16,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/default/pom.xml b/plugins/storage/volume/default/pom.xml index 58c36948510..7f5cb0203be 100644 --- a/plugins/storage/volume/default/pom.xml +++ b/plugins/storage/volume/default/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/flasharray/pom.xml b/plugins/storage/volume/flasharray/pom.xml index c5bd880569d..483e918b709 100644 --- a/plugins/storage/volume/flasharray/pom.xml +++ b/plugins/storage/volume/flasharray/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/linstor/pom.xml b/plugins/storage/volume/linstor/pom.xml index 35bea52cc73..eaee08b8695 100644 --- a/plugins/storage/volume/linstor/pom.xml +++ b/plugins/storage/volume/linstor/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/nexenta/pom.xml b/plugins/storage/volume/nexenta/pom.xml index 3cddcb6a730..f1b3a0a5d0c 100644 --- a/plugins/storage/volume/nexenta/pom.xml +++ b/plugins/storage/volume/nexenta/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/primera/pom.xml b/plugins/storage/volume/primera/pom.xml index 114a33827d4..7e6e1214f42 100644 --- a/plugins/storage/volume/primera/pom.xml +++ b/plugins/storage/volume/primera/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/sample/pom.xml b/plugins/storage/volume/sample/pom.xml index b6c547918da..05f507c9f85 100644 --- a/plugins/storage/volume/sample/pom.xml +++ b/plugins/storage/volume/sample/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/scaleio/pom.xml b/plugins/storage/volume/scaleio/pom.xml index 52ad326dba2..ddb28cc229a 100644 --- a/plugins/storage/volume/scaleio/pom.xml +++ b/plugins/storage/volume/scaleio/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/solidfire/pom.xml b/plugins/storage/volume/solidfire/pom.xml index f3d69319a13..0463166d8c2 100644 --- a/plugins/storage/volume/solidfire/pom.xml +++ b/plugins/storage/volume/solidfire/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/storpool/pom.xml b/plugins/storage/volume/storpool/pom.xml index 56dda548633..0ae23a74ca3 100644 --- a/plugins/storage/volume/storpool/pom.xml +++ b/plugins/storage/volume/storpool/pom.xml @@ -17,7 +17,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/user-authenticators/ldap/pom.xml b/plugins/user-authenticators/ldap/pom.xml index 1b5f8c0d198..69616909a65 100644 --- a/plugins/user-authenticators/ldap/pom.xml +++ b/plugins/user-authenticators/ldap/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/md5/pom.xml b/plugins/user-authenticators/md5/pom.xml index 5b9327b958b..8e4a09eba3f 100644 --- a/plugins/user-authenticators/md5/pom.xml +++ b/plugins/user-authenticators/md5/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/oauth2/pom.xml b/plugins/user-authenticators/oauth2/pom.xml index bed07446186..753c9823ecc 100644 --- a/plugins/user-authenticators/oauth2/pom.xml +++ b/plugins/user-authenticators/oauth2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/pbkdf2/pom.xml b/plugins/user-authenticators/pbkdf2/pom.xml index 2be04401516..bc5e514a67d 100644 --- a/plugins/user-authenticators/pbkdf2/pom.xml +++ b/plugins/user-authenticators/pbkdf2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/plain-text/pom.xml b/plugins/user-authenticators/plain-text/pom.xml index 99e1f0e3f39..e65a339927c 100644 --- a/plugins/user-authenticators/plain-text/pom.xml +++ b/plugins/user-authenticators/plain-text/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/saml2/pom.xml b/plugins/user-authenticators/saml2/pom.xml index 5d0b17ca74d..8d23b91d59f 100644 --- a/plugins/user-authenticators/saml2/pom.xml +++ b/plugins/user-authenticators/saml2/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-authenticators/sha256salted/pom.xml b/plugins/user-authenticators/sha256salted/pom.xml index 87392c8ab20..feae20579db 100644 --- a/plugins/user-authenticators/sha256salted/pom.xml +++ b/plugins/user-authenticators/sha256salted/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-two-factor-authenticators/static-pin/pom.xml b/plugins/user-two-factor-authenticators/static-pin/pom.xml index 6b71e40d10e..1d6c17c9208 100644 --- a/plugins/user-two-factor-authenticators/static-pin/pom.xml +++ b/plugins/user-two-factor-authenticators/static-pin/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/plugins/user-two-factor-authenticators/totp/pom.xml b/plugins/user-two-factor-authenticators/totp/pom.xml index f316d794bd5..ef1093ac97a 100644 --- a/plugins/user-two-factor-authenticators/totp/pom.xml +++ b/plugins/user-two-factor-authenticators/totp/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../../pom.xml diff --git a/pom.xml b/pom.xml index b00797d5975..3843dc6817c 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT pom Apache CloudStack Apache CloudStack is an IaaS ("Infrastructure as a Service") cloud orchestration platform. diff --git a/quickcloud/pom.xml b/quickcloud/pom.xml index c6b72516116..30dfc58f6a8 100644 --- a/quickcloud/pom.xml +++ b/quickcloud/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/server/pom.xml b/server/pom.xml index f3b4e998144..a090a67ffb5 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/services/console-proxy/pom.xml b/services/console-proxy/pom.xml index bdcb89cea9e..d5c42c7c87f 100644 --- a/services/console-proxy/pom.xml +++ b/services/console-proxy/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-services - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/services/console-proxy/rdpconsole/pom.xml b/services/console-proxy/rdpconsole/pom.xml index 09ce7d5b9c0..0b2ba396c02 100644 --- a/services/console-proxy/rdpconsole/pom.xml +++ b/services/console-proxy/rdpconsole/pom.xml @@ -26,7 +26,7 @@ org.apache.cloudstack cloudstack-service-console-proxy - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/services/console-proxy/server/pom.xml b/services/console-proxy/server/pom.xml index c3b94b6abda..4bc8761d01e 100644 --- a/services/console-proxy/server/pom.xml +++ b/services/console-proxy/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-console-proxy - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/services/pom.xml b/services/pom.xml index 140fd6f0a05..392f04fd08a 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/services/secondary-storage/controller/pom.xml b/services/secondary-storage/controller/pom.xml index 7869dc9762e..aa780ab6cbe 100644 --- a/services/secondary-storage/controller/pom.xml +++ b/services/secondary-storage/controller/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-secondary-storage - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/services/secondary-storage/pom.xml b/services/secondary-storage/pom.xml index 24616b19d3c..32a574f508b 100644 --- a/services/secondary-storage/pom.xml +++ b/services/secondary-storage/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack-services - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/services/secondary-storage/server/pom.xml b/services/secondary-storage/server/pom.xml index be5a508f5d0..c86ee4e8662 100644 --- a/services/secondary-storage/server/pom.xml +++ b/services/secondary-storage/server/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-service-secondary-storage - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/systemvm/pom.xml b/systemvm/pom.xml index 835aa68d988..c7273c28da6 100644 --- a/systemvm/pom.xml +++ b/systemvm/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index abc50dddc82..fe9e72170b3 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/tools/apidoc/pom.xml b/tools/apidoc/pom.xml index aba4f356064..a687f705695 100644 --- a/tools/apidoc/pom.xml +++ b/tools/apidoc/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/tools/checkstyle/pom.xml b/tools/checkstyle/pom.xml index 575060ee066..25f1f83d308 100644 --- a/tools/checkstyle/pom.xml +++ b/tools/checkstyle/pom.xml @@ -22,7 +22,7 @@ Apache CloudStack Developer Tools - Checkstyle Configuration org.apache.cloudstack checkstyle - 4.20.3.0 + 4.20.4.0-SNAPSHOT UTF-8 diff --git a/tools/devcloud-kvm/pom.xml b/tools/devcloud-kvm/pom.xml index ec771426b0d..7dc6c21070a 100644 --- a/tools/devcloud-kvm/pom.xml +++ b/tools/devcloud-kvm/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/tools/devcloud4/pom.xml b/tools/devcloud4/pom.xml index d1ec12fa579..b0eb6223010 100644 --- a/tools/devcloud4/pom.xml +++ b/tools/devcloud4/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index 9ba37a72232..7b617249baa 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -19,7 +19,7 @@ FROM ubuntu:22.04 -LABEL Vendor="Apache.org" License="ApacheV2" Version="4.20.3.0" Author="Apache CloudStack " +LABEL Vendor="Apache.org" License="ApacheV2" Version="4.20.4.0-SNAPSHOT" Author="Apache CloudStack " ARG DEBIAN_FRONTEND=noninteractive diff --git a/tools/docker/Dockerfile.marvin b/tools/docker/Dockerfile.marvin index d1eab6bbcda..fbf704008fc 100644 --- a/tools/docker/Dockerfile.marvin +++ b/tools/docker/Dockerfile.marvin @@ -19,11 +19,11 @@ # build for cloudstack_home_dir not this folder FROM python:2 -LABEL Vendor="Apache.org" License="ApacheV2" Version="4.20.3.0" Author="Apache CloudStack " +LABEL Vendor="Apache.org" License="ApacheV2" Version="4.20.4.0-SNAPSHOT" Author="Apache CloudStack " ENV WORK_DIR=/marvin -ENV PKG_URL=https://builds.cloudstack.org/job/build-master-marvin/lastSuccessfulBuild/artifact/tools/marvin/dist/Marvin-4.20.3.0.tar.gz +ENV PKG_URL=https://builds.cloudstack.org/job/build-master-marvin/lastSuccessfulBuild/artifact/tools/marvin/dist/Marvin-4.20.4.0-SNAPSHOT.tar.gz RUN apt-get update && apt-get install -y vim RUN pip install --upgrade paramiko nose requests diff --git a/tools/marvin/pom.xml b/tools/marvin/pom.xml index 674f8b4ddfb..690de5a2848 100644 --- a/tools/marvin/pom.xml +++ b/tools/marvin/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloud-tools - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/tools/marvin/setup.py b/tools/marvin/setup.py index 1ceff3dc204..c7f58131f16 100644 --- a/tools/marvin/setup.py +++ b/tools/marvin/setup.py @@ -27,7 +27,7 @@ except ImportError: raise RuntimeError("python setuptools is required to build Marvin") -VERSION = "4.20.3.0" +VERSION = "4.20.4.0" setup(name="Marvin", version=VERSION, diff --git a/tools/pom.xml b/tools/pom.xml index c5ad12fab6a..5cc57ba1288 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/usage/pom.xml b/usage/pom.xml index 5afe81f37aa..4bc9122c030 100644 --- a/usage/pom.xml +++ b/usage/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT diff --git a/utils/pom.xml b/utils/pom.xml index b18d5fea1d1..2f3221cea4f 100755 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT ../pom.xml diff --git a/vmware-base/pom.xml b/vmware-base/pom.xml index 72a7a6ee5da..2cb88c9e34f 100644 --- a/vmware-base/pom.xml +++ b/vmware-base/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.20.3.0 + 4.20.4.0-SNAPSHOT From 0c86899cc198ee5dd75ded9cffa7eb4adc48332e Mon Sep 17 00:00:00 2001 From: Harikrishna Date: Tue, 14 Apr 2026 22:33:01 +0530 Subject: [PATCH 32/43] Added VDDK support in VMware to KVM migrations (#12970) --- agent/conf/agent.properties | 13 + .../agent/properties/AgentProperties.java | 24 ++ .../cloud/agent/api/to/RemoteInstanceTO.java | 20 +- api/src/main/java/com/cloud/host/Host.java | 3 + .../apache/cloudstack/api/ApiConstants.java | 1 + .../api/command/admin/vm/ImportVmCmd.java | 12 + .../api/CheckConvertInstanceCommand.java | 23 ++ .../agent/api/ConvertInstanceCommand.java | 36 ++ .../cloud/agent/manager/AgentManagerImpl.java | 25 +- .../resource/LibvirtComputingResource.java | 162 +++++++++ ...irtCheckConvertInstanceCommandWrapper.java | 10 +- .../LibvirtConvertInstanceCommandWrapper.java | 309 +++++++++++++++--- .../wrapper/LibvirtReadyCommandWrapper.java | 4 + ...heckConvertInstanceCommandWrapperTest.java | 25 ++ ...virtConvertInstanceCommandWrapperTest.java | 124 +++++++ .../vm/UnmanagedVMsManagerImpl.java | 143 ++++++-- .../vm/UnmanagedVMsManagerImplTest.java | 160 ++++++++- ui/public/locales/en.json | 3 + ui/src/views/infra/HostInfo.vue | 16 + .../views/tools/ImportUnmanagedInstance.vue | 100 +++++- 20 files changed, 1117 insertions(+), 96 deletions(-) diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties index 777bd4bf5cd..013c2e5bf2a 100644 --- a/agent/conf/agent.properties +++ b/agent/conf/agent.properties @@ -460,3 +460,16 @@ iscsi.session.cleanup.enabled=false # Time, in seconds, to wait before retrying to rebase during the incremental snapshot process. # incremental.snapshot.retry.rebase.wait=60 + +# Path to the VDDK library directory for VMware to KVM conversion via VDDK, +# passed to virt-v2v as -io vddk-libdir= +#vddk.lib.dir= + +# Ordered VDDK transport preference for VMware to KVM conversion via VDDK, passed as +# -io vddk-transports= to virt-v2v. Example: nbd:nbdssl +#vddk.transports= + +# Optional vCenter SHA1 thumbprint for VMware to KVM conversion via VDDK, passed as +# -io vddk-thumbprint=. If unset, CloudStack computes it on the KVM host via openssl. +#vddk.thumbprint= + diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java index 9d3f9f7df55..1cb9232eec1 100644 --- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java +++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java @@ -808,6 +808,30 @@ public class AgentProperties{ */ public static final Property CONVERT_ENV_VIRTV2V_TMPDIR = new Property<>("convert.instance.env.virtv2v.tmpdir", null, String.class); + /** + * Path to the VDDK library directory on the KVM conversion host, used when converting VMs from VMware to KVM via VDDK. + * This directory is passed to virt-v2v as -io vddk-libdir=<path>. + * Data type: String.
+ * Default value: null + */ + public static final Property VDDK_LIB_DIR = new Property<>("vddk.lib.dir", null, String.class); + + /** + * Ordered list of VDDK transports for virt-v2v, passed as -io vddk-transports=<value>. + * Example: nbd:nbdssl. + * Data type: String.
+ * Default value: null + */ + public static final Property VDDK_TRANSPORTS = new Property<>("vddk.transports", null, String.class); + + /** + * vCenter TLS certificate thumbprint used by virt-v2v VDDK mode, passed as -io vddk-thumbprint=<value>. + * If unset, the KVM host computes it at runtime from the vCenter endpoint. + * Data type: String.
+ * Default value: null + */ + public static final Property VDDK_THUMBPRINT = new Property<>("vddk.thumbprint", null, String.class); + /** * BGP controll CIDR * Data type: String.
diff --git a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java index 18737c584b3..7daeb964917 100644 --- a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java @@ -36,13 +36,17 @@ public class RemoteInstanceTO implements Serializable { private String vcenterPassword; private String vcenterHost; private String datacenterName; + private String clusterName; + private String hostName; public RemoteInstanceTO() { } - public RemoteInstanceTO(String instanceName) { + public RemoteInstanceTO(String instanceName, String clusterName, String hostName) { this.hypervisorType = Hypervisor.HypervisorType.VMware; this.instanceName = instanceName; + this.clusterName = clusterName; + this.hostName = hostName; } public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) { @@ -55,6 +59,12 @@ public class RemoteInstanceTO implements Serializable { this.datacenterName = datacenterName; } + public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName, String clusterName, String hostName) { + this(instanceName, instancePath, vcenterHost, vcenterUsername, vcenterPassword, datacenterName); + this.clusterName = clusterName; + this.hostName = hostName; + } + public Hypervisor.HypervisorType getHypervisorType() { return this.hypervisorType; } @@ -82,4 +92,12 @@ public class RemoteInstanceTO implements Serializable { public String getDatacenterName() { return datacenterName; } + + public String getClusterName() { + return clusterName; + } + + public String getHostName() { + return hostName; + } } diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index 9c011bac319..b5234820151 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -57,6 +57,9 @@ public interface Host extends StateObject, Identity, Partition, HAResour String HOST_UEFI_ENABLE = "host.uefi.enable"; String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; String HOST_INSTANCE_CONVERSION = "host.instance.conversion"; + String HOST_VDDK_SUPPORT = "host.vddk.support"; + String HOST_VDDK_LIB_DIR = "vddk.lib.dir"; + String HOST_VDDK_VERSION = "host.vddk.version"; String HOST_OVFTOOL_VERSION = "host.ovftool.version"; String HOST_VIRTV2V_VERSION = "host.virtv2v.version"; String HOST_SSH_PORT = "host.ssh.port"; diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 43af4b94015..03b73834a94 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -622,6 +622,7 @@ public class ApiConstants { public static final String USER_CONFIGURABLE = "userconfigurable"; public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist"; public static final String USER_SECRET_KEY = "usersecretkey"; + public static final String USE_VDDK = "usevddk"; public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork"; public static final String USE_VIRTUAL_ROUTER_IP_RESOLVER = "userouteripresolver"; public static final String UPDATE_IN_SEQUENCE = "updateinsequence"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java index 50ccfbd69c5..db7dcc3fb44 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java @@ -179,6 +179,14 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd { description = "(only for importing VMs from VMware to KVM) optional - the ID of the guest OS for the imported VM.") private Long guestOsId; + @Parameter(name = ApiConstants.USE_VDDK, + type = CommandType.BOOLEAN, + since = "4.22.1", + description = "(only for importing VMs from VMware to KVM) optional - if true, uses VDDK on the KVM conversion host for converting the VM. " + + "This parameter is mutually exclusive with " + ApiConstants.FORCE_MS_TO_IMPORT_VM_FILES + ".") + private Boolean useVddk; + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -255,6 +263,10 @@ public class ImportVmCmd extends ImportUnmanagedInstanceCmd { return storagePoolId; } + public boolean getUseVddk() { + return BooleanUtils.toBooleanDefaultIfNull(useVddk, true); + } + public String getTmpPath() { return tmpPath; } diff --git a/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java index fc066e5c589..c46fb697a3c 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java @@ -18,6 +18,8 @@ package com.cloud.agent.api; public class CheckConvertInstanceCommand extends Command { boolean checkWindowsGuestConversionSupport = false; + boolean useVddk = false; + String vddkLibDir; public CheckConvertInstanceCommand() { } @@ -26,6 +28,11 @@ public class CheckConvertInstanceCommand extends Command { this.checkWindowsGuestConversionSupport = checkWindowsGuestConversionSupport; } + public CheckConvertInstanceCommand(boolean checkWindowsGuestConversionSupport, boolean useVddk) { + this.checkWindowsGuestConversionSupport = checkWindowsGuestConversionSupport; + this.useVddk = useVddk; + } + @Override public boolean executeInSequence() { return false; @@ -34,4 +41,20 @@ public class CheckConvertInstanceCommand extends Command { public boolean getCheckWindowsGuestConversionSupport() { return checkWindowsGuestConversionSupport; } + + public boolean isUseVddk() { + return useVddk; + } + + public void setUseVddk(boolean useVddk) { + this.useVddk = useVddk; + } + + public String getVddkLibDir() { + return vddkLibDir; + } + + public void setVddkLibDir(String vddkLibDir) { + this.vddkLibDir = vddkLibDir; + } } diff --git a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java index 24336747ccf..38e0dca7736 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java @@ -31,6 +31,10 @@ public class ConvertInstanceCommand extends Command { private boolean exportOvfToConversionLocation; private int threadsCountToExportOvf = 0; private String extraParams; + private boolean useVddk; + private String vddkLibDir; + private String vddkTransports; + private String vddkThumbprint; public ConvertInstanceCommand() { } @@ -90,6 +94,38 @@ public class ConvertInstanceCommand extends Command { this.extraParams = extraParams; } + public boolean isUseVddk() { + return useVddk; + } + + public void setUseVddk(boolean useVddk) { + this.useVddk = useVddk; + } + + public String getVddkLibDir() { + return vddkLibDir; + } + + public void setVddkLibDir(String vddkLibDir) { + this.vddkLibDir = vddkLibDir; + } + + public String getVddkTransports() { + return vddkTransports; + } + + public void setVddkTransports(String vddkTransports) { + this.vddkTransports = vddkTransports; + } + + public String getVddkThumbprint() { + return vddkThumbprint; + } + + public void setVddkThumbprint(String vddkThumbprint) { + this.vddkThumbprint = vddkThumbprint; + } + @Override public boolean executeInSequence() { return false; diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java index 9e85be87db3..8c69dcdc482 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java @@ -805,8 +805,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl String uefiEnabled = detailsMap.get(Host.HOST_UEFI_ENABLE); String virtv2vVersion = detailsMap.get(Host.HOST_VIRTV2V_VERSION); String ovftoolVersion = detailsMap.get(Host.HOST_OVFTOOL_VERSION); + String vddkSupport = detailsMap.get(Host.HOST_VDDK_SUPPORT); + String vddkLibDir = detailsMap.get(Host.HOST_VDDK_LIB_DIR); + String vddkVersion = detailsMap.get(Host.HOST_VDDK_VERSION); logger.debug("Got HOST_UEFI_ENABLE [{}] for host [{}]:", uefiEnabled, host); - if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion)) { + if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion, vddkSupport, vddkLibDir, vddkVersion)) { _hostDao.loadDetails(host); boolean updateNeeded = false; if (StringUtils.isNotBlank(uefiEnabled) && !uefiEnabled.equals(host.getDetails().get(Host.HOST_UEFI_ENABLE))) { @@ -821,6 +824,26 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl host.getDetails().put(Host.HOST_OVFTOOL_VERSION, ovftoolVersion); updateNeeded = true; } + if (StringUtils.isNotBlank(vddkSupport) && !vddkSupport.equals(host.getDetails().get(Host.HOST_VDDK_SUPPORT))) { + host.getDetails().put(Host.HOST_VDDK_SUPPORT, vddkSupport); + updateNeeded = true; + } + if (!StringUtils.defaultString(vddkLibDir).equals(StringUtils.defaultString(host.getDetails().get(Host.HOST_VDDK_LIB_DIR)))) { + if (StringUtils.isBlank(vddkLibDir)) { + host.getDetails().remove(Host.HOST_VDDK_LIB_DIR); + } else { + host.getDetails().put(Host.HOST_VDDK_LIB_DIR, vddkLibDir); + } + updateNeeded = true; + } + if (!StringUtils.defaultString(vddkVersion).equals(StringUtils.defaultString(host.getDetails().get(Host.HOST_VDDK_VERSION)))) { + if (StringUtils.isBlank(vddkVersion)) { + host.getDetails().remove(Host.HOST_VDDK_VERSION); + } else { + host.getDetails().put(Host.HOST_VDDK_VERSION, vddkVersion); + } + updateNeeded = true; + } if (updateNeeded) { _hostDao.saveDetails(host); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 64df98f413a..25162ca9b92 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -18,6 +18,9 @@ package com.cloud.hypervisor.kvm.resource; import static com.cloud.host.Host.HOST_INSTANCE_CONVERSION; import static com.cloud.host.Host.HOST_OVFTOOL_VERSION; +import static com.cloud.host.Host.HOST_VDDK_LIB_DIR; +import static com.cloud.host.Host.HOST_VDDK_SUPPORT; +import static com.cloud.host.Host.HOST_VDDK_VERSION; import static com.cloud.host.Host.HOST_VIRTV2V_VERSION; import static com.cloud.host.Host.HOST_VOLUME_ENCRYPTION; import static org.apache.cloudstack.utils.linux.KVMHostInfo.isHostS390x; @@ -363,6 +366,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv public static final String WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD = "rpm -qa | grep -i virtio-win"; public static final String UBUNTU_WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD = "dpkg -l virtio-win"; public static final String UBUNTU_NBDKIT_PKG_CHECK_CMD = "dpkg -l nbdkit"; + public static final String VDDK_AUTODETECT_PATH_CMD = "find / -type d -name 'vmware-vix-disklib-distrib' 2>/dev/null | head -n 1"; public static final int LIBVIRT_CGROUP_CPU_SHARES_MIN = 2; public static final int LIBVIRT_CGROUP_CPU_SHARES_MAX = 262144; @@ -883,10 +887,16 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv private boolean convertInstanceVerboseMode = false; private Map convertInstanceEnv = null; + private String vddkLibDir = null; + private static final String libguestfsBackend = "direct"; protected boolean dpdkSupport = false; protected String dpdkOvsPath; protected String directDownloadTemporaryDownloadPath; protected String cachePath; + private String vddkTransports = null; + private String vddkThumbprint = null; + private String vddkVersion = null; + private String detectedPasswordFileOption = null; protected String javaTempDir = System.getProperty("java.io.tmpdir"); private String getEndIpFromStartIp(final String startIp, final int numIps) { @@ -951,6 +961,26 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return convertInstanceEnv; } + public String getVddkLibDir() { + return vddkLibDir; + } + + public String getLibguestfsBackend() { + return libguestfsBackend; + } + + public String getVddkTransports() { + return vddkTransports; + } + + public String getVddkThumbprint() { + return vddkThumbprint; + } + + public String getVddkVersion() { + return vddkVersion; + } + /** * Defines resource's public and private network interface according to what is configured in agent.properties. */ @@ -1151,6 +1181,37 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv setConvertInstanceEnv(convertEnvTmpDir, convertEnvVirtv2vTmpDir); + vddkLibDir = StringUtils.trimToNull(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VDDK_LIB_DIR)); + if (StringUtils.isNotBlank(vddkLibDir) && !isVddkLibDirValid(vddkLibDir)) { + LOGGER.warn("Configured VDDK library dir [{}] is invalid (missing lib64/libvixDiskLib.so), attempting auto-detection", vddkLibDir); + vddkLibDir = null; + } + if (StringUtils.isBlank(vddkLibDir)) { + vddkLibDir = detectVddkLibDir(); + } + if (StringUtils.isNotBlank(vddkLibDir)) { + LOGGER.info("Detected VDDK library dir: {}", vddkLibDir); + } else { + LOGGER.warn("Could not detect a valid VDDK library dir; VDDK conversion will be unavailable"); + } + + vddkVersion = detectVddkVersion(); + if (StringUtils.isNotBlank(vddkVersion)) { + LOGGER.info("Detected nbdkit VDDK plugin version: {}", vddkVersion); + } + + vddkTransports = StringUtils.trimToNull( + AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VDDK_TRANSPORTS)); + vddkThumbprint = StringUtils.trimToNull( + AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VDDK_THUMBPRINT)); + + detectedPasswordFileOption = detectPasswordFileOption(); + if (StringUtils.isNotBlank(detectedPasswordFileOption)) { + LOGGER.info("Detected virt-v2v password option: {}", detectedPasswordFileOption); + } else { + LOGGER.warn("Could not detect virt-v2v password option, VDDK conversions may fail"); + } + pool = (String)params.get("pool"); if (pool == null) { pool = "/root"; @@ -4212,6 +4273,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv cmd.setHostTags(getHostTags()); boolean instanceConversionSupported = hostSupportsInstanceConversion(); cmd.getHostDetails().put(HOST_INSTANCE_CONVERSION, String.valueOf(instanceConversionSupported)); + cmd.getHostDetails().put(HOST_VDDK_SUPPORT, String.valueOf(hostSupportsVddk())); + if (StringUtils.isNotBlank(vddkLibDir)) { + cmd.getHostDetails().put(HOST_VDDK_LIB_DIR, vddkLibDir); + } + if (StringUtils.isNotBlank(vddkVersion)) { + cmd.getHostDetails().put(HOST_VDDK_VERSION, vddkVersion); + } if (instanceConversionSupported) { cmd.getHostDetails().put(HOST_VIRTV2V_VERSION, getHostVirtV2vVersion()); } @@ -5927,6 +5995,66 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return exitValue == 0; } + public boolean hostSupportsVddk() { + return hostSupportsVddk(null); + } + + public boolean hostSupportsVddk(String overriddenVddkLibDir) { + String effectiveVddkLibDir = StringUtils.trimToNull(overriddenVddkLibDir); + if (StringUtils.isBlank(effectiveVddkLibDir)) { + effectiveVddkLibDir = StringUtils.trimToNull(vddkLibDir); + } + if (StringUtils.isBlank(effectiveVddkLibDir) || !isVddkLibDirValid(effectiveVddkLibDir)) { + effectiveVddkLibDir = detectVddkLibDir(); + } + return hostSupportsInstanceConversion() && isVddkLibDirValid(effectiveVddkLibDir) && StringUtils.isNotBlank(detectVddkVersion()); + } + + protected boolean isVddkLibDirValid(String path) { + if (StringUtils.isBlank(path)) { + return false; + } + File libDir = new File(path, "lib64"); + if (!libDir.isDirectory()) { + return false; + } + File[] libs = libDir.listFiles((dir, name) -> name.startsWith("libvixDiskLib.so")); + return libs != null && libs.length > 0; + } + + protected String detectVddkLibDir() { + String detectedPath = StringUtils.trimToNull(Script.runSimpleBashScript(VDDK_AUTODETECT_PATH_CMD)); + if (StringUtils.isNotBlank(detectedPath) && isVddkLibDirValid(detectedPath)) { + return detectedPath; + } + return null; + } + + protected String detectVddkVersion() { + try { + ProcessBuilder pb = new ProcessBuilder("nbdkit", "vddk", "--version"); + Process process = pb.start(); + + String output = new String(process.getInputStream().readAllBytes()); + process.waitFor(); + + if (StringUtils.isBlank(output)) { + return null; + } + + for (String line : output.split("\\R")) { + String trimmed = StringUtils.trimToEmpty(line); + if (trimmed.startsWith("vddk ")) { + return StringUtils.trimToNull(trimmed.substring("vddk ".length())); + } + } + return null; + } catch (Exception e) { + LOGGER.error("Failed to detect vddk version: {}", e.getMessage()); + return null; + } + } + public boolean hostSupportsWindowsGuestConversion() { if (isUbuntuOrDebianHost()) { int exitValue = Script.runSimpleBashScriptForExitValue(UBUNTU_WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD); @@ -5941,6 +6069,40 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return exitValue == 0; } + /** + * Detect which password option virt-v2v supports by examining its --help output + * @return "-ip" if supported (virt-v2v >= 2.8.1), "--password-file" if older version, or null if detection fails + */ + protected String detectPasswordFileOption() { + try { + ProcessBuilder pb = new ProcessBuilder("virt-v2v", "--help"); + Process process = pb.start(); + + String output = new String(process.getInputStream().readAllBytes()); + process.waitFor(); + + if (output.contains("-ip ")) { + return "-ip"; + } else if (output.contains("--password-file")) { + return "--password-file"; + } else { + LOGGER.error("virt-v2v does not support -ip or --password-file"); + return null; + } + } catch (Exception e) { + LOGGER.error("Failed to detect virt-v2v password option: {}", e.getMessage()); + return null; + } + } + + /** + * Get the detected password file option for virt-v2v + * @return the password option ("-ip" or "--password-file") or null if not detected + */ + public String getDetectedPasswordFileOption() { + return detectedPasswordFileOption; + } + public String getHostVirtV2vVersion() { if (!hostSupportsInstanceConversion()) { return ""; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java index b94b4830003..de9341715f0 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java @@ -30,7 +30,15 @@ public class LibvirtCheckConvertInstanceCommandWrapper extends CommandWrapper supportedInstanceConvertSourceHypervisors = List.of(Hypervisor.HypervisorType.VMware); + private static final Pattern SHA1_FINGERPRINT_PATTERN = Pattern.compile("(?i)(?:SHA1\\s+)?Fingerprint\\s*=\\s*([0-9A-F:]+)"); @Override public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serverResource) { @@ -61,7 +69,8 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper 1 && !serverResource.ovfExportToolSupportsParallelThreads()) { - noOfThreads = 0; - } - ovfTemplateDirOnConversionLocation = UUID.randomUUID().toString(); - temporaryStoragePool.createFolder(ovfTemplateDirOnConversionLocation); - sourceOVFDirPath = String.format("%s/%s/", temporaryConvertPath, ovfTemplateDirOnConversionLocation); - ovfExported = exportOVAFromVMOnVcenter(exportInstanceOVAUrl, sourceOVFDirPath, noOfThreads, originalVMName, timeout); - if (!ovfExported) { - String err = String.format("Export OVA for the VM %s failed", sourceInstanceName); - logger.error(String.format("(%s) %s", originalVMName, err)); - return new Answer(cmd, false, err); - } - sourceOVFDirPath = String.format("%s%s/", sourceOVFDirPath, sourceInstanceName); - } else { - ovfTemplateDirOnConversionLocation = cmd.getTemplateDirOnConversionLocation(); - sourceOVFDirPath = String.format("%s/%s/", temporaryConvertPath, ovfTemplateDirOnConversionLocation); - } - - logger.info(String.format("(%s) Attempting to convert the OVF %s of the instance %s from %s to KVM", - originalVMName, ovfTemplateDirOnConversionLocation, sourceInstanceName, sourceHypervisorType)); - final String temporaryConvertUuid = UUID.randomUUID().toString(); boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled(); boolean cleanupSecondaryStorage = false; + boolean ovfExported = false; + String ovfTemplateDirOnConversionLocation = null; + try { - boolean result = performInstanceConversion(originalVMName, sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid, - timeout, verboseModeEnabled, extraParams, serverResource); + boolean result; + if (useVddk) { + logger.info("({}) Using VDDK-based conversion (direct from VMware)", originalVMName); + String vddkLibDir = resolveVddkSetting(cmd.getVddkLibDir(), serverResource.getVddkLibDir()); + if (StringUtils.isBlank(vddkLibDir)) { + String err = String.format("VDDK lib dir is not configured on the host. " + + "Set '%s' in agent.properties or in details parameter of the import api calll to use VDDK-based conversion.", "vddk.lib.dir"); + logger.error("({}) {}", originalVMName, err); + return new Answer(cmd, false, err); + } + String vddkTransports = resolveVddkSetting(cmd.getVddkTransports(), serverResource.getVddkTransports()); + String configuredVddkThumbprint = resolveVddkSetting(cmd.getVddkThumbprint(), serverResource.getVddkThumbprint()); + String passwordOption = serverResource.getDetectedPasswordFileOption(); + result = performInstanceConversionUsingVddk(sourceInstance, originalVMName, temporaryConvertPath, + vddkLibDir, serverResource.getLibguestfsBackend(), vddkTransports, configuredVddkThumbprint, + timeout, verboseModeEnabled, extraParams, temporaryConvertUuid, passwordOption); + } else { + logger.info("({}) Using OVF-based conversion (export + local convert)", originalVMName); + String sourceOVFDirPath; + if (cmd.getExportOvfToConversionLocation()) { + String exportInstanceOVAUrl = getExportInstanceOVAUrl(sourceInstance, originalVMName); + + if (StringUtils.isBlank(exportInstanceOVAUrl)) { + String err = String.format("Couldn't export OVA for the VM %s, due to empty url", sourceInstanceName); + logger.error("({}) {}", originalVMName, err); + return new Answer(cmd, false, err); + } + + int noOfThreads = cmd.getThreadsCountToExportOvf(); + if (noOfThreads > 1 && !serverResource.ovfExportToolSupportsParallelThreads()) { + noOfThreads = 0; + } + ovfTemplateDirOnConversionLocation = UUID.randomUUID().toString(); + temporaryStoragePool.createFolder(ovfTemplateDirOnConversionLocation); + sourceOVFDirPath = String.format("%s/%s/", temporaryConvertPath, ovfTemplateDirOnConversionLocation); + ovfExported = exportOVAFromVMOnVcenter(exportInstanceOVAUrl, sourceOVFDirPath, noOfThreads, originalVMName, timeout); + + if (!ovfExported) { + String err = String.format("Export OVA for the VM %s failed", sourceInstanceName); + logger.error("({}) {}", originalVMName, err); + return new Answer(cmd, false, err); + } + sourceOVFDirPath = String.format("%s%s/", sourceOVFDirPath, sourceInstanceName); + } else { + ovfTemplateDirOnConversionLocation = cmd.getTemplateDirOnConversionLocation(); + sourceOVFDirPath = String.format("%s/%s/", temporaryConvertPath, ovfTemplateDirOnConversionLocation); + } + + result = performInstanceConversion(originalVMName, sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid, + timeout, verboseModeEnabled, extraParams, serverResource); + } + if (!result) { - String err = String.format( - "The virt-v2v conversion for the OVF %s failed. Please check the agent logs " + - "for the virt-v2v output. Please try on a different kvm host which " + - "has a different virt-v2v version.", - ovfTemplateDirOnConversionLocation); - logger.error(String.format("(%s) %s", originalVMName, err)); + String err = String.format("Instance conversion failed for VM %s. Please check virt-v2v logs.", sourceInstanceName); + logger.error("({}) {}", originalVMName, err); return new Answer(cmd, false, err); } return new ConvertInstanceAnswer(cmd, temporaryConvertUuid); } catch (Exception e) { - String error = String.format("Error converting instance %s from %s, due to: %s", - sourceInstanceName, sourceHypervisorType, e.getMessage()); - logger.error(String.format("(%s) %s", originalVMName, error), e); + String error = String.format("Error converting instance %s from %s, due to: %s", sourceInstanceName, sourceHypervisorType, e.getMessage()); + logger.error("({}) {}", originalVMName, error, e); cleanupSecondaryStorage = true; return new Answer(cmd, false, error); } finally { @@ -275,4 +298,198 @@ public class LibvirtConvertInstanceCommandWrapper extends CommandWrapper/dev/null | " + + "openssl x509 -fingerprint -sha1 -noout", endpoint); + + Script script = new Script("/bin/bash", timeout, logger); + script.add("-c"); + script.add(command); + + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + script.execute(parser); + + String output = parser.getLines(); + if (script.getExitValue() != 0) { + logger.error("({}) Failed to fetch vCenter thumbprint for {}", originalVMName, vcenterHost); + return null; + } + + String thumbprint = extractSha1Fingerprint(output); + if (StringUtils.isBlank(thumbprint)) { + logger.error("({}) Failed to parse vCenter thumbprint from output for {}", originalVMName, vcenterHost); + return null; + } + return thumbprint; + } + + private String extractSha1Fingerprint(String output) { + String parsedOutput = StringUtils.trimToEmpty(output); + if (StringUtils.isBlank(parsedOutput)) { + return null; + } + + for (String line : parsedOutput.split("\\R")) { + String trimmedLine = StringUtils.trimToEmpty(line); + if (StringUtils.isBlank(trimmedLine)) { + continue; + } + + Matcher matcher = SHA1_FINGERPRINT_PATTERN.matcher(trimmedLine); + if (matcher.find()) { + return matcher.group(1).toUpperCase(Locale.ROOT); + } + + // Fallback for raw fingerprint-only output. + if (trimmedLine.matches("(?i)[0-9a-f]{2}(:[0-9a-f]{2})+")) { + return trimmedLine.toUpperCase(Locale.ROOT); + } + } + return null; + } + + /** + * Build vpx:// URL for virt-v2v + * + * Format: + * vpx://user@vcenter/DC/cluster/host?no_verify=1 + */ + private String buildVpxUrl(RemoteInstanceTO vmwareInstance) { + + String vmName = vmwareInstance.getInstanceName(); + String vcenter = vmwareInstance.getVcenterHost(); + String username = vmwareInstance.getVcenterUsername(); + String datacenter = vmwareInstance.getDatacenterName(); + String cluster = vmwareInstance.getClusterName(); + String host = vmwareInstance.getHostName(); + + String encodedUsername = encodeUsername(username); + + StringBuilder url = new StringBuilder(); + url.append("vpx://") + .append(encodedUsername) + .append("@") + .append(vcenter) + .append("/") + .append(datacenter); + + if (StringUtils.isNotBlank(cluster)) { + url.append("/").append(cluster); + } + + if (StringUtils.isNotBlank(host)) { + url.append("/").append(host); + } + + url.append("?no_verify=1"); + + logger.info("({}) Using VPX URL: {}", vmName, url); + return url.toString(); + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java index e74923b281f..5a7d6d2c203 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java @@ -34,6 +34,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.utils.script.Script; +import org.apache.commons.lang3.StringUtils; @ResourceWrapper(handles = ReadyCommand.class) public final class LibvirtReadyCommandWrapper extends CommandWrapper { @@ -50,6 +51,9 @@ public final class LibvirtReadyCommandWrapper extends CommandWrapper filesMock = Mockito.mockStatic(Files.class); + MockedConstruction