Skip to content

Commit 21af134

Browse files
Fix exceeding of resource limits with powerflex (#9008)
* Fix exceeding of resource limits with powerflex * Add e2e tests * Update server/src/main/java/com/cloud/vm/UserVmManagerImpl.java Co-authored-by: Suresh Kumar Anaparti <[email protected]> * fixup --------- Co-authored-by: Suresh Kumar Anaparti <[email protected]>
1 parent e9ff270 commit 21af134

File tree

15 files changed

+390
-60
lines changed

15 files changed

+390
-60
lines changed

api/src/main/java/com/cloud/user/ResourceLimitService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ public interface ResourceLimitService {
243243
void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize,
244244
DiskOffering currentOffering, DiskOffering newOffering) throws ResourceAllocationException;
245245

246+
void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException;
247+
246248
void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
247249
void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
248250

engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323
import java.util.Set;
2424

25+
import com.cloud.exception.ResourceAllocationException;
2526
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
2627
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
2728
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
@@ -126,7 +127,7 @@ DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Lon
126127

127128
void prepareForMigration(VirtualMachineProfile vm, DeployDestination dest);
128129

129-
void prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException, ConcurrentOperationException, StorageAccessException;
130+
void prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException, ConcurrentOperationException, StorageAccessException, ResourceAllocationException;
130131

131132
boolean canVmRestartOnAnotherServer(long vmId);
132133

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,20 @@ default boolean volumesRequireGrantAccessWhenUsed() {
157157
default boolean zoneWideVolumesAvailableWithoutClusterMotion() {
158158
return false;
159159
}
160+
161+
/**
162+
* This method returns the actual size required on the pool for a volume.
163+
*
164+
* @param volumeSize
165+
* Size of volume to be created on the store
166+
* @param templateSize
167+
* Size of template, if any, which will be used to create the volume
168+
* @param isEncryptionRequired
169+
* true if volume is encrypted
170+
*
171+
* @return the size required on the pool for the volume
172+
*/
173+
default long getVolumeSizeRequiredOnPool(long volumeSize, Long templateSize, boolean isEncryptionRequired) {
174+
return volumeSize;
175+
}
160176
}

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import com.cloud.configuration.Resource;
5353
import com.cloud.domain.Domain;
5454
import com.cloud.domain.dao.DomainDao;
55+
import com.cloud.exception.ResourceAllocationException;
5556
import com.cloud.network.vpc.VpcVO;
5657
import com.cloud.network.vpc.dao.VpcDao;
5758
import com.cloud.user.dao.AccountDao;
@@ -1403,7 +1404,7 @@ public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfil
14031404
logger.warn("unexpected InsufficientCapacityException : {}", e.getScope().getName(), e);
14041405
}
14051406
}
1406-
} catch (ExecutionException | NoTransitionException e) {
1407+
} catch (ExecutionException | NoTransitionException | ResourceAllocationException e) {
14071408
logger.error("Failed to start instance {}", vm, e);
14081409
throw new AgentUnavailableException("Unable to start instance due to " + e.getMessage(), destHostId, e);
14091410
} catch (final StorageAccessException e) {

engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
import javax.inject.Inject;
3939
import javax.naming.ConfigurationException;
4040

41+
import com.cloud.exception.ResourceAllocationException;
42+
import com.cloud.storage.DiskOfferingVO;
43+
import com.cloud.storage.VMTemplateVO;
44+
import com.cloud.storage.dao.VMTemplateDao;
45+
import com.cloud.user.AccountManager;
4146
import org.apache.cloudstack.api.ApiCommandResourceType;
4247
import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy;
4348
import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
@@ -180,6 +185,8 @@ public enum UserVmCloneType {
180185
}
181186

182187

188+
@Inject
189+
private AccountManager _accountMgr;
183190
@Inject
184191
EntityManager _entityMgr;
185192
@Inject
@@ -195,6 +202,8 @@ public enum UserVmCloneType {
195202
@Inject
196203
protected VolumeDao _volumeDao;
197204
@Inject
205+
protected VMTemplateDao _templateDao;
206+
@Inject
198207
protected SnapshotDao _snapshotDao;
199208
@Inject
200209
protected SnapshotDataStoreDao _snapshotDataStoreDao;
@@ -1677,7 +1686,7 @@ protected void checkAndUpdateVolumeAccountResourceCount(VolumeVO originalEntry,
16771686
}
16781687
}
16791688

1680-
private Pair<VolumeVO, DataStore> recreateVolume(VolumeVO vol, VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, StorageAccessException {
1689+
private Pair<VolumeVO, DataStore> recreateVolume(VolumeVO vol, VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, StorageAccessException, ResourceAllocationException {
16811690
String volToString = getReflectOnlySelectedFields(vol);
16821691

16831692
VolumeVO newVol;
@@ -1710,6 +1719,7 @@ private Pair<VolumeVO, DataStore> recreateVolume(VolumeVO vol, VirtualMachinePro
17101719
}
17111720
logger.debug("Created new volume [{}] from old volume [{}].", newVolToString, volToString);
17121721
}
1722+
updateVolumeSize(destPool, newVol);
17131723
VolumeInfo volume = volFactory.getVolume(newVol.getId(), destPool);
17141724
Long templateId = newVol.getTemplateId();
17151725
for (int i = 0; i < 2; i++) {
@@ -1841,8 +1851,39 @@ protected void grantVolumeAccessToHostIfNeeded(PrimaryDataStore volumeStore, lon
18411851
}
18421852
}
18431853

1854+
/**
1855+
* This method checks if size of volume on the data store would be different.
1856+
* If it's different it verifies the resource limits and updates the volume's size
1857+
*/
1858+
protected void updateVolumeSize(DataStore store, VolumeVO vol) throws ResourceAllocationException {
1859+
if (store == null || !(store.getDriver() instanceof PrimaryDataStoreDriver)) {
1860+
return;
1861+
}
1862+
1863+
VMTemplateVO template = vol.getTemplateId() != null ? _templateDao.findById(vol.getTemplateId()) : null;
1864+
PrimaryDataStoreDriver driver = (PrimaryDataStoreDriver) store.getDriver();
1865+
long newSize = driver.getVolumeSizeRequiredOnPool(vol.getSize(),
1866+
template == null ? null : template.getSize(),
1867+
vol.getPassphraseId() != null);
1868+
1869+
if (newSize != vol.getSize()) {
1870+
DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId());
1871+
if (newSize > vol.getSize()) {
1872+
_resourceLimitMgr.checkPrimaryStorageResourceLimit(_accountMgr.getActiveAccountById(vol.getAccountId()),
1873+
vol.isDisplay(), newSize - vol.getSize(), diskOffering);
1874+
_resourceLimitMgr.incrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(),
1875+
newSize - vol.getSize(), diskOffering);
1876+
} else {
1877+
_resourceLimitMgr.decrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(),
1878+
vol.getSize() - newSize, diskOffering);
1879+
}
1880+
vol.setSize(newSize);
1881+
_volsDao.persist(vol);
1882+
}
1883+
}
1884+
18441885
@Override
1845-
public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException, ConcurrentOperationException, StorageAccessException {
1886+
public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException, ConcurrentOperationException, StorageAccessException, ResourceAllocationException {
18461887
if (dest == null) {
18471888
String msg = String.format("Unable to prepare volumes for the VM [%s] because DeployDestination is null.", vm.getVirtualMachine());
18481889
logger.error(msg);
@@ -1865,7 +1906,7 @@ public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws Sto
18651906

18661907
String volToString = getReflectOnlySelectedFields(vol);
18671908

1868-
store = (PrimaryDataStore)dataStoreMgr.getDataStore(task.pool.getId(), DataStoreRole.Primary);
1909+
store = (PrimaryDataStore) dataStoreMgr.getDataStore(task.pool.getId(), DataStoreRole.Primary);
18691910

18701911
// For zone-wide managed storage, it is possible that the VM can be started in another
18711912
// cluster. In that case, make sure that the volume is in the right access group.
@@ -1876,6 +1917,8 @@ public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws Sto
18761917
long lastClusterId = lastHost == null || lastHost.getClusterId() == null ? -1 : lastHost.getClusterId();
18771918
long clusterId = host == null || host.getClusterId() == null ? -1 : host.getClusterId();
18781919

1920+
updateVolumeSize(store, (VolumeVO) vol);
1921+
18791922
if (lastClusterId != clusterId) {
18801923
if (lastHost != null) {
18811924
storageMgr.removeStoragePoolFromCluster(lastHost.getId(), vol.get_iScsiName(), store);
@@ -1895,6 +1938,7 @@ public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws Sto
18951938
}
18961939
} else if (task.type == VolumeTaskType.MIGRATE) {
18971940
store = (PrimaryDataStore) dataStoreMgr.getDataStore(task.pool.getId(), DataStoreRole.Primary);
1941+
updateVolumeSize(store, task.volume);
18981942
vol = migrateVolume(task.volume, store);
18991943
} else if (task.type == VolumeTaskType.RECREATE) {
19001944
Pair<VolumeVO, DataStore> result = recreateVolume(task.volume, vm, dest);

plugins/storage/volume/scaleio/src/main/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriver.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,16 @@ public void resize(DataObject dataObject, AsyncCompletionCallback<CreateCmdResul
13401340
}
13411341
}
13421342

1343+
@Override
1344+
public long getVolumeSizeRequiredOnPool(long volumeSize, Long templateSize, boolean isEncryptionRequired) {
1345+
long newSizeInGB = volumeSize / (1024 * 1024 * 1024);
1346+
if (templateSize != null && isEncryptionRequired && needsExpansionForEncryptionHeader(templateSize, volumeSize)) {
1347+
newSizeInGB = (volumeSize + (1<<30)) / (1024 * 1024 * 1024);
1348+
}
1349+
long newSizeIn8gbBoundary = (long) (Math.ceil(newSizeInGB / 8.0) * 8.0);
1350+
return newSizeIn8gbBoundary * (1024 * 1024 * 1024);
1351+
}
1352+
13431353
@Override
13441354
public void handleQualityOfServiceForVolumeMigration(VolumeInfo volumeInfo, QualityOfServiceState qualityOfServiceState) {
13451355
}

plugins/storage/volume/scaleio/src/test/java/org/apache/cloudstack/storage/datastore/driver/ScaleIOPrimaryDataStoreDriverTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,4 +542,37 @@ public void testCopyOfflineVolumeFailureWhenNoEndpointFound() {
542542

543543
Assert.assertEquals(false, answer.getResult());
544544
}
545+
546+
@Test
547+
public void testGetVolumeSizeRequiredOnPool() {
548+
Assert.assertEquals(16L * (1024 * 1024 * 1024),
549+
scaleIOPrimaryDataStoreDriver.getVolumeSizeRequiredOnPool(
550+
10L * (1024 * 1024 * 1024),
551+
null,
552+
true));
553+
554+
Assert.assertEquals(16L * (1024 * 1024 * 1024),
555+
scaleIOPrimaryDataStoreDriver.getVolumeSizeRequiredOnPool(
556+
10L * (1024 * 1024 * 1024),
557+
null,
558+
false));
559+
560+
Assert.assertEquals(16L * (1024 * 1024 * 1024),
561+
scaleIOPrimaryDataStoreDriver.getVolumeSizeRequiredOnPool(
562+
16L * (1024 * 1024 * 1024),
563+
null,
564+
false));
565+
566+
Assert.assertEquals(16L * (1024 * 1024 * 1024),
567+
scaleIOPrimaryDataStoreDriver.getVolumeSizeRequiredOnPool(
568+
16L * (1024 * 1024 * 1024),
569+
16L * (1024 * 1024 * 1024),
570+
false));
571+
572+
Assert.assertEquals(24L * (1024 * 1024 * 1024),
573+
scaleIOPrimaryDataStoreDriver.getVolumeSizeRequiredOnPool(
574+
16L * (1024 * 1024 * 1024),
575+
16L * (1024 * 1024 * 1024),
576+
true));
577+
}
545578
}

server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,19 @@ public void checkVolumeResourceLimit(Account owner, Boolean display, Long size,
16411641
}
16421642
}
16431643

1644+
@Override
1645+
public void checkPrimaryStorageResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException {
1646+
List<String> tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering);
1647+
if (CollectionUtils.isEmpty(tags)) {
1648+
return;
1649+
}
1650+
if (size != null) {
1651+
for (String tag : tags) {
1652+
checkResourceLimitWithTag(owner, ResourceType.primary_storage, tag, size);
1653+
}
1654+
}
1655+
}
1656+
16441657
@Override
16451658
public void checkVolumeResourceLimitForDiskOfferingChange(Account owner, Boolean display, Long currentSize, Long newSize,
16461659
DiskOffering currentOffering, DiskOffering newOffering

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,16 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep
12401240
}
12411241

12421242
long currentSize = volume.getSize();
1243+
VolumeInfo volInfo = volFactory.getVolume(volume.getId());
1244+
boolean isEncryptionRequired = volume.getPassphraseId() != null;
1245+
if (newDiskOffering != null) {
1246+
isEncryptionRequired = newDiskOffering.getEncrypt();
1247+
}
1248+
1249+
DataStore dataStore = volInfo.getDataStore();
1250+
if (dataStore != null && dataStore.getDriver() instanceof PrimaryDataStoreDriver) {
1251+
newSize = ((PrimaryDataStoreDriver) dataStore.getDriver()).getVolumeSizeRequiredOnPool(newSize, null, isEncryptionRequired);
1252+
}
12431253
validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, diskOffering, newDiskOffering);
12441254

12451255
// 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
@@ -1982,6 +1992,14 @@ private Volume changeDiskOfferingForVolumeInternal(VolumeVO volume, Long newDisk
19821992
newMaxIops = updateNewMaxIops[0];
19831993
newHypervisorSnapshotReserve = updateNewHypervisorSnapshotReserve[0];
19841994
long currentSize = volume.getSize();
1995+
1996+
VolumeInfo volInfo = volFactory.getVolume(volume.getId());
1997+
1998+
DataStore dataStore = volInfo.getDataStore();
1999+
if (dataStore != null && dataStore.getDriver() instanceof PrimaryDataStoreDriver) {
2000+
newSize = ((PrimaryDataStoreDriver) dataStore.getDriver()).getVolumeSizeRequiredOnPool(newSize, null, newDiskOffering.getEncrypt());
2001+
}
2002+
19852003
validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk, existingDiskOffering, newDiskOffering);
19862004

19872005
/* If this volume has never been beyond allocated state, short circuit everything and simply update the database. */

0 commit comments

Comments
 (0)