Implement/fix limit validation for secondary storage

This commit is contained in:
Daniel Augusto Veronezi Salvador 2026-02-22 10:44:24 -03:00 committed by Daan Hoogland
parent 86c9f7bd94
commit e8d57d1b0d
7 changed files with 706 additions and 639 deletions

View File

@ -106,7 +106,6 @@ public class BareMetalTemplateAdapter extends TemplateAdapterBase implements Tem
}
}
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
return template;
}

View File

@ -63,6 +63,7 @@ import com.cloud.configuration.Resource;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.ConnectionException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.host.Host;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
@ -543,6 +544,22 @@ 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;

View File

@ -35,7 +35,6 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import com.cloud.resourcelimit.ReservationHelper;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.InternalIdentity;
@ -111,6 +110,7 @@ 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.storage.to.VolumeObjectTO;
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;
@ -169,6 +169,7 @@ import com.cloud.projects.ProjectManager;
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.server.ResourceTag;
@ -356,10 +357,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
private ManagementService managementService;
@Inject
protected SnapshotHelper snapshotHelper;
@Inject
protected DomainDao domainDao;
@Inject
protected ProjectManager projectManager;
@Inject
@ -374,7 +373,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
EndPointSelector _epSelector;
@Inject
private ReservationDao reservationDao;
@Inject
private VMSnapshotDetailsDao vmSnapshotDetailsDao;
@ -499,76 +497,77 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
List<Reserver> 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<GetUploadParamsResponse, MalformedURLException>() {
@Override
public GetUploadParamsResponse doInTransaction(TransactionStatus status) throws MalformedURLException {
return Transaction.execute(new TransactionCallbackWithException<GetUploadParamsResponse, MalformedURLException>() {
@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<EndPoint, DataObject> pair = volService.registerVolumeForPostUpload(vol, store);
EndPoint ep = pair.first();
DataObject dataObject = pair.second();
Pair<EndPoint, DataObject> 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());
// one of the two following might have to be removed
command.setDefaultMaxSecondaryStorageInGB(_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);
response.setMetadata(metadata);
// one of the two following might have to be removed
command.setDefaultMaxSecondaryStorageInGB(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null));
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);
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);
@ -967,29 +966,29 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
List<Reserver> 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.getSnapshotId(), caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName,
_uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details);
return commitVolume(cmd.getSnapshotId(), caller, owner, displayVolume, zoneId, diskOfferingId, provisioningType, size, minIops, maxIops, parentVolume, userSpecifiedName,
_uuidMgr.generateUuid(Volume.class, cmd.getCustomId()), details);
} finally {
ReservationHelper.closeAll(reservations);
}
@ -1320,134 +1319,134 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
List<Reserver> 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<? extends StoragePool> 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<? extends StoragePool>, List<? extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOfferingId, currentSize, newMinIops, newMaxIops, true, false);
List<? extends StoragePool> 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<Volume> 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<? extends StoragePool> 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<? extends StoragePool>, List<? extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOfferingId, currentSize, newMinIops, newMaxIops, true, false);
List<? extends StoragePool> 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<Volume> 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);
@ -1912,17 +1911,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
List<Reserver> reservations = new ArrayList<>();
try {
_resourceLimitMgr.checkVolumeResourceLimit(_accountMgr.getAccount(volume.getAccountId()), volume.isDisplayVolume(), volume.getSize(), diskOffering, reservations);
_resourceLimitMgr.checkVolumeResourceLimit(_accountMgr.getAccount(volume.getAccountId()), volume.isDisplayVolume(), volume.getSize(), diskOffering, reservations);
try {
_volsDao.detachVolume(volume.getId());
stateTransitTo(volume, Volume.Event.RecoverRequested);
} catch (NoTransitionException e) {
logger.debug("Failed to recover volume {}", volume, e);
throw new CloudRuntimeException(String.format("Failed to recover volume %s", volume), e);
}
_resourceLimitMgr.incrementVolumeResourceCount(volume.getAccountId(), volume.isDisplay(),
volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId()));
try {
_volsDao.detachVolume(volume.getId());
stateTransitTo(volume, Volume.Event.RecoverRequested);
} catch (NoTransitionException e) {
logger.debug("Failed to recover volume {}", volume, e);
throw new CloudRuntimeException(String.format("Failed to recover volume %s", volume), e);
}
_resourceLimitMgr.incrementVolumeResourceCount(volume.getAccountId(), volume.isDisplay(),
volume.getSize(), _diskOfferingDao.findById(volume.getDiskOfferingId()));
} catch (ResourceAllocationException e) {
logger.error("primary storage resource limit check failed", e);
@ -2161,96 +2160,96 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
List<Reserver> 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<? extends StoragePool>, List<? extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOffering.getId(), currentSize, newMinIops, newMaxIops, true, false);
List<? extends StoragePool> 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<? extends StoragePool> 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<? extends StoragePool>, List<? extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOffering.getId(), currentSize, newMinIops, newMaxIops, true, false);
List<? extends StoragePool> 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<? extends StoragePool> 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);
@ -2807,7 +2806,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
// if target VM has associated VM snapshots
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
if (vmSnapshots.size() > 0) {
throw new InvalidParameterValueException(String.format("Unable to attach volume to VM %s/%s, please specify a VM that does not have VM snapshots", vm.getName(), vm.getUuid()));
throw new InvalidParameterValueException(String.format("Unable to attach volume to Instance %s/%s, please specify an Instance that does not have Instance Snapshots", vm.getName(), vm.getUuid()));
}
}
@ -3130,7 +3129,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
// Don't allow detach if target VM has associated VM snapshots
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
if (CollectionUtils.isNotEmpty(vmSnapshots)) {
throw new InvalidParameterValueException("Unable to detach volume, please specify a VM that does not have VM snapshots");
throw new InvalidParameterValueException("Unable to detach volume, please specify an Instance that does not have Instance Snapshots");
}
checkForBackups(vm, false);
@ -3426,7 +3425,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
// Check that Vm to which this volume is attached does not have VM Snapshots
// OfflineVmwareMigration: consider if this is needed and desirable
if (vm != null && _vmSnapshotDao.findByVm(vm.getId()).size() > 0) {
throw new InvalidParameterValueException("Volume cannot be migrated, please remove all VM snapshots for VM to which this volume is attached");
throw new InvalidParameterValueException("Volume cannot be migrated, please remove all Instance Snapshots for Instance to which this volume is attached");
}
StoragePoolVO srcStoragePoolVO = _storagePoolDao.findById(vol.getPoolId());
@ -3435,7 +3434,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
if (vm != null && State.Running.equals(vm.getState())) {
// Check if the VM is GPU enabled.
if (_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) {
throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported");
throw new InvalidParameterValueException("Live Migration of GPU enabled Instance is not supported");
}
// Check if the underlying hypervisor supports storage motion.

View File

@ -34,14 +34,14 @@ 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;
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.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService;
@ -57,15 +57,18 @@ import org.apache.cloudstack.storage.command.TemplateOrVolumePostUploadCommand;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
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;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.alert.AlertManager;
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.Domain;
import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
@ -73,6 +76,7 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.host.HostVO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.org.Grouping;
import com.cloud.resource.ResourceManager;
import com.cloud.storage.ScopeType;
import com.cloud.storage.Storage.ImageFormat;
@ -197,19 +201,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;
}
@ -228,19 +219,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
profile.setForCks(cmd.isForCks());
}
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;
}
@ -268,7 +247,6 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
persistDirectDownloadTemplate(template.getId(), profile.getSize());
}
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
return template;
}
@ -322,6 +300,44 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
}
}
protected boolean isZoneAndImageStoreAvailable(DataStore imageStore, Long zoneId, Set<Long> zoneSet, boolean isTemplatePrivate) {
if (zoneId == null) {
logger.warn(String.format("Zone ID is null, cannot allocate ISO/template in image store [%s].", imageStore));
return false;
}
DataCenterVO zone = _dcDao.findById(zoneId);
if (zone == null) {
logger.warn("Unable to find zone by id [{}], so skip downloading template to its image store [{}].", zoneId, imageStore);
return false;
}
if (Grouping.AllocationState.Disabled == zone.getAllocationState()) {
logger.info("Zone [{}] is disabled. Skip downloading template to its image store [{}].", zone, imageStore);
return false;
}
if (!_statsCollector.imageStoreHasEnoughCapacity(imageStore)) {
logger.info("Image store doesn't have enough capacity. Skip downloading template to this image store [{}].", imageStore);
return false;
}
if (zoneSet == null) {
logger.info(String.format("Zone set is null; therefore, the ISO/template should be allocated in every secondary storage of zone [%s].", zone));
return true;
}
if (isTemplatePrivate && zoneSet.contains(zoneId)) {
logger.info(String.format("The template is private and it is already allocated in a secondary storage in zone [%s]; therefore, image store [%s] will be skipped.",
zone, imageStore));
return false;
}
logger.info(String.format("Private template will be allocated in image store [%s] in zone [%s].", imageStore, zone));
zoneSet.add(zoneId);
return true;
}
@Override
public List<TemplateOrVolumePostUploadCommand> createTemplateForPostUpload(final TemplateProfile profile) {
// persist entry in vm_template, vm_template_details and template_zone_ref tables, not that entry at template_store_ref is not created here, and created in createTemplateAsync.
@ -369,12 +385,67 @@ 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;
}
});
}
/**
* If the template/ISO is marked as private, then it is allocated to a random secondary storage; otherwise, allocates to every storage pool in every zone given by the
* {@link TemplateProfile#getZoneIdList()}.
*/
protected void postUploadAllocation(List<DataStore> imageStores, VMTemplateVO template, List<TemplateOrVolumePostUploadCommand> payloads) {
Set<Long> zoneSet = new HashSet<>();
Collections.shuffle(imageStores);
for (DataStore imageStore : imageStores) {
Long zoneId_is = imageStore.getScope().getScopeId();
if (!isZoneAndImageStoreAvailable(imageStore, zoneId_is, zoneSet, isPrivateTemplate(template))) {
continue;
}
TemplateInfo tmpl = imageFactory.getTemplate(template.getId(), imageStore);
// persist template_store_ref entry
DataObject templateOnStore = imageStore.create(tmpl);
// update template_store_ref and template state
EndPoint ep = _epSelector.select(templateOnStore);
if (ep == null) {
String errMsg = String.format("There is no secondary storage VM for downloading template to image store %s", imageStore);
logger.warn(errMsg);
throw new CloudRuntimeException(errMsg);
}
TemplateOrVolumePostUploadCommand payload = new TemplateOrVolumePostUploadCommand(template.getId(), template.getUuid(), tmpl.getInstallPath(), tmpl
.getChecksum(), tmpl.getType().toString(), template.getUniqueName(), template.getFormat().toString(), templateOnStore.getDataStore().getUri(),
templateOnStore.getDataStore().getRole().toString());
//using the existing max template size configuration
payload.setMaxUploadSize(_configDao.getValue(Config.MaxTemplateAndIsoSize.key()));
Long accountId = template.getAccountId();
Account account = _accountDao.findById(accountId);
Domain domain = _domainDao.findById(account.getDomainId());
payload.setDefaultMaxSecondaryStorageInGB(ByteScaleUtils.bytesToGibibytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null)));
payload.setAccountId(accountId);
payload.setRemoteEndPoint(ep.getPublicAddr());
payload.setRequiresHvm(template.requiresHvm());
payload.setDescription(template.getDisplayText());
payloads.add(payload);
}
}
protected boolean isPrivateTemplate(VMTemplateVO template){
// if public OR featured OR system template
if(template.isPublicTemplate() || template.isFeatured() || template.getTemplateType() == TemplateType.SYSTEM)
return false;
else
return true;
}
private class CreateTemplateContext<T> extends AsyncRpcContext<T> {
final TemplateInfo template;

View File

@ -293,7 +293,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
}
GuestOS noneGuestOs = ApiDBUtils.findGuestOSByDisplayName(ApiConstants.ISO_GUEST_OS_NONE);
if ((guestOSId == null || guestOSId == noneGuestOs.getId()) && bootable == true) {
throw new InvalidParameterValueException("Please pass a valid GuestOS Id");
throw new InvalidParameterValueException("Please pass a valid GuestOS ID");
}
if (bootable == false) {
guestOSId = noneGuestOs.getId(); //Guest os id of None.
@ -309,7 +309,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
requiresHVM = true;
}
if (deployAsIs) {
logger.info("Setting default guest OS for deploy-as-is template while the template registration is not completed");
logger.info("Setting default guest OS for deploy-as-is Template while the Template registration is not completed");
guestOSId = getDefaultDeployAsIsGuestOsId();
}
}
@ -342,7 +342,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
// check whether owner can create public templates
boolean allowPublicUserTemplates = TemplateManager.AllowPublicUserTemplates.valueIn(templateOwner.getId());
if (!isAdmin && !allowPublicUserTemplates && isPublic) {
throw new InvalidParameterValueException("Only private templates/ISO can be created.");
throw new InvalidParameterValueException("Only private Templates/ISO can be created.");
}
if (!isAdmin || featured == null) {
@ -363,8 +363,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) {
@ -386,7 +384,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
Objects.equals(template.getArch(), arch)) {
logger.error("{} for same arch {} is having same name or description", template,
template.getArch());
throw new IllegalArgumentException("Cannot use reserved names for templates");
throw new IllegalArgumentException("Cannot use reserved names for Templates");
}
}
@ -442,11 +440,11 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
if (cmd.isDeployAsIs()) {
if (MapUtils.isNotEmpty(details)) {
if (details.containsKey(VmDetailConstants.ROOT_DISK_CONTROLLER)) {
logger.info("Ignoring the rootDiskController detail provided, as we honour what is defined in the template");
logger.info("Ignoring the rootDiskController detail provided, as we honour what is defined in the Template");
details.remove(VmDetailConstants.ROOT_DISK_CONTROLLER);
}
if (details.containsKey(VmDetailConstants.NIC_ADAPTER)) {
logger.info("Ignoring the nicAdapter detail provided, as we honour what is defined in the template");
logger.info("Ignoring the nicAdapter detail provided, as we honour what is defined in the Template");
details.remove(VmDetailConstants.NIC_ADAPTER);
}
}
@ -512,7 +510,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
public TemplateProfile prepare(GetUploadParamsForTemplateCmd cmd) throws ResourceAllocationException {
Long osTypeId = cmd.getOsTypeId();
if (osTypeId == null) {
logger.info("Setting the default guest OS for deploy-as-is templates while the template upload is not completed");
logger.info("Setting the default guest OS for deploy-as-is Templates while the Template upload is not completed");
osTypeId = getDefaultDeployAsIsGuestOsId();
}
UploadParams params = new TemplateUploadParams(CallContext.current().getCallingUserId(), cmd.getName(),
@ -574,7 +572,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
List<DataCenterVO> dcs = _dcDao.listAll();
if (dcs.isEmpty()) {
throw new CloudRuntimeException("No zones are present in the system, can't add template");
throw new CloudRuntimeException("No zones are present in the system, can't add Template");
}
template.setCrossZones(true);
@ -604,7 +602,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
Account owner = _accountMgr.getAccount(template.getAccountId());
if (owner.getType() == Account.Type.PROJECT) {
if (!_projectMgr.canAccessProjectAccount(account, owner.getId())) {
throw new PermissionDeniedException(msg + ". Permission denied. The caller can't access project's template");
throw new PermissionDeniedException(msg + ". Permission denied. The caller can't access project's Template");
}
} else {
throw new PermissionDeniedException(msg + ". Permission denied.");
@ -638,10 +636,10 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
VMTemplateVO template = _tmpltDao.findById(templateId);
if (template == null) {
throw new InvalidParameterValueException("unable to find template with id " + templateId);
throw new InvalidParameterValueException("Unable to find Template with ID " + templateId);
}
userId = accountAndUserValidation(account, userId, null, template, "Unable to delete template ");
userId = accountAndUserValidation(account, userId, null, template, "Unable to delete Template ");
UserVO user = _userDao.findById(userId);
if (user == null) {
@ -649,11 +647,11 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
}
if (template.getFormat() == ImageFormat.ISO) {
throw new InvalidParameterValueException("Please specify a valid template.");
throw new InvalidParameterValueException("Please specify a valid Template.");
}
if (template.getState() == VirtualMachineTemplate.State.NotUploaded || template.getState() == VirtualMachineTemplate.State.UploadInProgress) {
throw new InvalidParameterValueException("The template is either getting uploaded or it may be initiated shortly, please wait for it to be completed");
throw new InvalidParameterValueException("The Template is either getting uploaded or it may be initiated shortly, please wait for it to be completed");
}
return new TemplateProfile(userId, template, zoneId);
@ -667,7 +665,7 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
VMTemplateVO template = _tmpltDao.findById(templateId);
if (template == null) {
throw new InvalidParameterValueException("unable to find template with id " + templateId);
throw new InvalidParameterValueException("Unable to find Template with ID " + templateId);
}
return new TemplateProfile(userId, template, zoneId);
}
@ -681,10 +679,10 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
VMTemplateVO template = _tmpltDao.findById(templateId);
if (template == null) {
throw new InvalidParameterValueException("unable to find iso with id " + templateId);
throw new InvalidParameterValueException("Unable to find ISO with ID " + templateId);
}
userId = accountAndUserValidation(account, userId, null, template, "Unable to delete iso ");
userId = accountAndUserValidation(account, userId, null, template, "Unable to delete ISO ");
UserVO user = _userDao.findById(userId);
if (user == null) {
@ -692,11 +690,11 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat
}
if (template.getFormat() != ImageFormat.ISO) {
throw new InvalidParameterValueException("Please specify a valid iso.");
throw new InvalidParameterValueException("Please specify a valid ISO.");
}
if (template.getState() == VirtualMachineTemplate.State.NotUploaded || template.getState() == VirtualMachineTemplate.State.UploadInProgress) {
throw new InvalidParameterValueException("The iso is either getting uploaded or it may be initiated shortly, please wait for it to be completed");
throw new InvalidParameterValueException("The ISO is either getting uploaded or it may be initiated shortly, please wait for it to be completed");
}
return new TemplateProfile(userId, template, zoneId);

View File

@ -22,8 +22,8 @@ package com.cloud.template;
import com.cloud.agent.AgentManager;
import com.cloud.api.query.dao.SnapshotJoinDao;
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;
@ -33,6 +33,7 @@ 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;
@ -63,12 +64,14 @@ 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;
@ -104,14 +107,21 @@ 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;
@ -120,11 +130,7 @@ 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;
@ -137,81 +143,81 @@ 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;
@Inject
HypervisorGuruManager _hvGuruMgr;
@Inject
AccountManager _accountMgr;
@Inject
VnfTemplateManager vnfTemplateManager;
@Inject
SnapshotJoinDao snapshotJoinDao;
@Inject
@Mock
ReservationDao reservationDao;
@Inject
@Mock
HypervisorGuruManager _hvGuruMgr;
@Mock
AccountManager _accountMgr;
@Mock
VnfTemplateManager vnfTemplateManager;
@Mock
SnapshotJoinDao snapshotJoinDao;
@Mock
TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao;
@Mock
HeuristicRuleHelper heuristicRuleHelperMock;
public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
@ -241,7 +247,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);
@ -275,7 +280,7 @@ public class TemplateManagerImplTest {
List<TemplateAdapter> adapters = new ArrayList<TemplateAdapter>();
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);
@ -296,7 +301,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 {
@ -311,7 +315,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);
@ -319,7 +322,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);
@ -335,13 +337,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);
@ -356,7 +356,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);
@ -364,7 +363,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);
@ -415,20 +413,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;
@ -446,15 +434,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);
@ -467,9 +450,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());
@ -497,7 +477,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);
@ -510,20 +489,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<ImageStoreVO> 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<VMTemplateVO>() {
when(vmTemplateDao.persist(any(VMTemplateVO.class))).thenAnswer(new Answer<VMTemplateVO>() {
@Override
public VMTemplateVO answer(InvocationOnMock invocationOnMock) throws Throwable {
Object[] args = invocationOnMock.getArguments();
@ -531,8 +507,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<CheckedReservation> 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
@ -544,7 +522,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);
@ -560,7 +538,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);
}
@ -574,7 +551,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);
}
@ -587,7 +563,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);
}
@ -602,7 +578,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);
@ -633,7 +609,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);
@ -646,7 +621,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);