package org.ovirt.engine.core.bll.storage.repoimage;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.Backend;
import org.ovirt.engine.core.bll.CommandBase;
import org.ovirt.engine.core.bll.SerialChildCommandsExecutionCallback;
import org.ovirt.engine.core.bll.SerialChildExecutingCommand;
import org.ovirt.engine.core.bll.VmTemplateHandler;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.job.ExecutionHandler;
import org.ovirt.engine.core.bll.provider.storage.OpenStackImageException;
import org.ovirt.engine.core.bll.provider.storage.OpenStackImageProviderProxy;
import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaStorageConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler;
import org.ovirt.engine.core.bll.tasks.interfaces.CommandCallback;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.bll.utils.VmDeviceUtils;
import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator;
import org.ovirt.engine.core.bll.validator.storage.StoragePoolValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.AddDiskParameters;
import org.ovirt.engine.core.common.action.AddVmTemplateParameters;
import org.ovirt.engine.core.common.action.DownloadImageCommandParameters;
import org.ovirt.engine.core.common.action.ImportRepoImageParameters;
import org.ovirt.engine.core.common.action.RemoveDiskParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase.EndProcedure;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.asynctasks.EntityInfo;
import org.ovirt.engine.core.common.businessentities.ActionGroup;
import org.ovirt.engine.core.common.businessentities.ArchitectureType;
import org.ovirt.engine.core.common.businessentities.Cluster;
import org.ovirt.engine.core.common.businessentities.DisplayType;
import org.ovirt.engine.core.common.businessentities.HttpLocationInfo;
import org.ovirt.engine.core.common.businessentities.VmStatic;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.DiskInterface;
import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement;
import org.ovirt.engine.core.common.businessentities.storage.ImageStatus;
import org.ovirt.engine.core.common.businessentities.storage.RepoImage;
import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat;
import org.ovirt.engine.core.common.businessentities.storage.VolumeType;
import org.ovirt.engine.core.common.constants.StorageConstants;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.osinfo.OsRepository;
import org.ovirt.engine.core.common.utils.SimpleDependencyInjector;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.ClusterDao;
import org.ovirt.engine.core.dao.DiskVmElementDao;
import org.ovirt.engine.core.dao.VmTemplateDao;
public class ImportRepoImageCommand<T extends ImportRepoImageParameters> extends CommandBase<T> implements SerialChildExecutingCommand, QuotaStorageDependent {
@Inject
private VmDeviceUtils vmDeviceUtils;
@Inject
private VmTemplateDao vmTemplateDao;
@Inject
private ClusterDao clusterDao;
@Inject
private DiskVmElementDao diskVmElementDao;
private OpenStackImageProviderProxy providerProxy;
public ImportRepoImageCommand(T parameters, CommandContext cmdContext) {
super(parameters, cmdContext);
getParameters().setCommandType(getActionType());
setDefaultTemplateName();
addAuditLogCustomValues();
}
private void setDefaultTemplateName() {
if (getParameters().getImportAsTemplate() && getParameters().getTemplateName() == null) {
// Following the same convention as the glance disk name,
// using a GlanceTemplate prefix, followed by a short identifier.
getParameters().setTemplateName("GlanceTemplate-" + Guid.newGuid().toString().substring(0, 7));
}
}
@Override
protected void executeCommand() {
setupParameters();
persistCommand(getParameters().getParentCommand(), true);
Backend.getInstance().runInternalAction(VdcActionType.AddDisk,
createAddDiskParameters(),
ExecutionHandler.createDefaultContextForTasks(getContext()));
getParameters().setNextPhase(ImportRepoImageParameters.Phase.DOWNLOAD);
persistCommand(getParameters().getParentCommand(), true);
setActionReturnValue(getParameters().getDiskImage().getId());
setSucceeded(true);
}
private void setupParameters() {
getParameters().setImageGroupID(Guid.newGuid());
getParameters().setDestinationImageId(Guid.newGuid());
if (getParameters().getImportAsTemplate()) {
getParameters().setVmSnapshotId(Guid.newGuid());
}
getParameters().setEntityInfo(
new EntityInfo(VdcObjectType.Disk, getParameters().getImageGroupID()));
}
@Override
public CommandCallback getCallback() {
return new SerialChildCommandsExecutionCallback();
}
protected AddDiskParameters createAddDiskParameters() {
DiskImage diskImage = getParameters().getDiskImage();
ArrayList<Guid> storageIds = new ArrayList<>();
storageIds.add(getParameters().getStorageDomainId());
diskImage.setDiskAlias(getParameters().getDiskAlias());
diskImage.setStorageIds(storageIds);
diskImage.setStoragePoolId(getParameters().getStoragePoolId());
diskImage.setId(getParameters().getImageGroupID());
diskImage.setDiskProfileId(getParameters().getDiskProfileId());
diskImage.setImageId(getParameters().getDestinationImageId());
diskImage.setQuotaId(getParameters().getQuotaId());
AddDiskParameters parameters = new AddDiskParameters(diskImage);
parameters.setStorageDomainId(getParameters().getStorageDomainId());
parameters.setParentCommand(getActionType());
parameters.setParentParameters(getParameters());
parameters.setShouldRemainIllegalOnFailedExecution(true);
parameters.setShouldRemainLockedOnSuccesfulExecution(true);
parameters.setEndProcedure(EndProcedure.COMMAND_MANAGED);
parameters.setUsePassedDiskId(true);
parameters.setUsePassedImageId(true);
parameters.setVmSnapshotId(getParameters().getVmSnapshotId());
return parameters;
}
private HttpLocationInfo prepareRepoImageLocation() {
return new HttpLocationInfo(
getProviderProxy().getImageUrl(getParameters().getSourceRepoImageId()),
getProviderProxy().getDownloadHeaders());
}
protected DownloadImageCommandParameters createDownloadImageParameters() {
DownloadImageCommandParameters parameters = new DownloadImageCommandParameters();
parameters.setDestinationImageId(getParameters().getDestinationImageId());
parameters.setStoragePoolId(getParameters().getStoragePoolId());
parameters.setStorageDomainId(getParameters().getStorageDomainId());
parameters.setImageGroupID(getParameters().getImageGroupID());
parameters.setHttpLocationInfo(prepareRepoImageLocation());
parameters.setParentCommand(getActionType());
parameters.setParentParameters(getParameters());
return parameters;
}
@Override
public boolean performNextOperation(int completedChildCount) {
if (getParameters().getNextPhase() == ImportRepoImageParameters.Phase.DOWNLOAD) {
getParameters().setNextPhase(ImportRepoImageParameters.Phase.END);
persistCommand(getParameters().getParentCommand(), true);
Backend.getInstance().runInternalAction(VdcActionType.DownloadImage,
createDownloadImageParameters(),
ExecutionHandler.createDefaultContextForTasks(getContext()));
return true;
}
return false;
}
@Override
public void handleFailure() {
updateDiskStatus(ImageStatus.ILLEGAL);
removeDisk();
}
public void removeDisk() {
Backend.getInstance().runInternalAction(VdcActionType.RemoveDisk,
new RemoveDiskParameters(getParameters().getImageGroupID()));
}
protected OpenStackImageProviderProxy getProviderProxy() {
if (providerProxy == null) {
providerProxy = OpenStackImageProviderProxy
.getFromStorageDomainId(getParameters().getSourceStorageDomainId());
}
return providerProxy;
}
@Override
public void endSuccessfully() {
super.endSuccessfully();
if (getParameters().getImportAsTemplate()) {
Guid newTemplateId = createTemplate();
// No reason for this to happen, but checking just to make sure
if (newTemplateId != null) {
attachDiskToTemplate(newTemplateId);
}
}
updateDiskStatus(ImageStatus.OK);
setSucceeded(true);
}
@Override
public void endWithFailure() {
updateDiskStatus(ImageStatus.ILLEGAL);
setSucceeded(true);
}
private void updateDiskStatus(ImageStatus status) {
getParameters().getDiskImage().setImageStatus(status);
ImagesHandler.updateImageStatus(getParameters().getDestinationImageId(), status);
}
private Guid createTemplate() {
VmTemplate blankTemplate = vmTemplateDao.get(VmTemplateHandler.BLANK_VM_TEMPLATE_ID);
VmStatic masterVm = new VmStatic(blankTemplate);
OsRepository osRepository = SimpleDependencyInjector.getInstance().get(OsRepository.class);
DiskImage templateDiskImage = getParameters().getDiskImage();
String vmTemplateName = getParameters().getTemplateName();
AddVmTemplateParameters parameters = new AddVmTemplateParameters(masterVm, vmTemplateName, templateDiskImage.getDiskDescription());
// Setting the user from the parent command, as the session might already be invalid
parameters.setParametersCurrentUser(getParameters().getParametersCurrentUser());
// Setting the cluster ID, and other related properties derived from it
if (getParameters().getClusterId() != null) {
masterVm.setClusterId(getParameters().getClusterId());
Cluster vdsGroup = getCluster(masterVm.getClusterId());
masterVm.setOsId(osRepository.getDefaultOSes().get(vdsGroup.getArchitecture()));
DisplayType defaultDisplayType =
osRepository.getGraphicsAndDisplays(masterVm.getOsId(), vdsGroup.getCompatibilityVersion()).get(0).getSecond();
masterVm.setDefaultDisplayType(defaultDisplayType);
}
parameters.setBalloonEnabled(true);
VdcReturnValueBase addVmTemplateReturnValue =
Backend.getInstance().runInternalAction(VdcActionType.AddVmTemplate,
parameters,
ExecutionHandler.createDefaultContextForTasks(getContext()));
// No reason for this to return null, but checking just to make sure, and returning the created template, or null if failed
return addVmTemplateReturnValue.getActionReturnValue() != null ? (Guid) addVmTemplateReturnValue.getActionReturnValue() : null;
}
public Cluster getCluster(Guid clusterId) {
return clusterDao.get(clusterId);
}
private void attachDiskToTemplate(Guid templateId) {
DiskImage templateDiskImage = getParameters().getDiskImage();
DiskVmElement dve = new DiskVmElement(templateDiskImage.getId(), templateId);
dve.setDiskInterface(DiskInterface.VirtIO);
diskVmElementDao.save(dve);
vmDeviceUtils.addDiskDevice(templateId, templateDiskImage.getId());
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
List<PermissionSubject> permissionSubjects = new ArrayList<>();
// NOTE: there's no read-permission from a storage domain
permissionSubjects.add(new PermissionSubject(getParameters().getStorageDomainId(),
VdcObjectType.Storage, ActionGroup.CREATE_DISK));
permissionSubjects.add(new PermissionSubject(getParameters().getSourceStorageDomainId(),
VdcObjectType.Storage, ActionGroup.ACCESS_IMAGE_STORAGE));
return permissionSubjects;
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__IMPORT);
addValidationMessage(EngineMessage.VAR__TYPE__DISK);
}
@Override
public Guid getStorageDomainId() {
return getParameters().getStorageDomainId();
}
@Override
public List<QuotaConsumptionParameter> getQuotaStorageConsumptionParameters() {
List<QuotaConsumptionParameter> list = new ArrayList<>();
list.add(new QuotaStorageConsumptionParameter(
getParameters().getQuotaId(), null, QuotaConsumptionParameter.QuotaAction.CONSUME,
getParameters().getStorageDomainId(), (double) getDiskImage().getSizeInGigabytes()));
return list;
}
protected DiskImage getDiskImage() {
if (getParameters().getDiskImage() == null) {
DiskImage diskImage = getProviderProxy().getImageAsDiskImage(getParameters().getSourceRepoImageId());
if (diskImage != null) {
if (diskImage.getVolumeFormat() == VolumeFormat.RAW &&
getStorageDomain().getStorageType().isBlockDomain()) {
diskImage.setVolumeType(VolumeType.Preallocated);
} else {
diskImage.setVolumeType(VolumeType.Sparse);
}
if (getParameters().getDiskAlias() == null) {
diskImage.setDiskAlias(RepoImage.getRepoImageAlias(
StorageConstants.GLANCE_DISK_ALIAS_PREFIX, getParameters().getSourceRepoImageId()));
} else {
diskImage.setDiskAlias(getParameters().getDiskAlias());
}
}
getParameters().setDiskImage(diskImage);
}
return getParameters().getDiskImage();
}
public String getRepoImageName() {
return getDiskImage() != null ? getDiskImage().getDiskAlias() : "";
}
@Override
public Map<String, String> getJobMessageProperties() {
if (jobProperties == null) {
jobProperties = super.getJobMessageProperties();
jobProperties.put("repoimagename", getRepoImageName());
jobProperties.put("storage", getStorageDomainName());
}
return jobProperties;
}
@Override
public AuditLogType getAuditLogTypeValue() {
switch (getActionState()) {
case EXECUTE:
if (!getParameters().getTaskGroupSuccess()) {
return getParameters().getImportAsTemplate() ?
AuditLogType.USER_IMPORT_IMAGE_AS_TEMPLATE_FINISHED_FAILURE :
AuditLogType.USER_IMPORT_IMAGE_FINISHED_FAILURE;
}
if (getSucceeded()) {
return getParameters().getImportAsTemplate() ? AuditLogType.USER_IMPORT_IMAGE_AS_TEMPLATE :
AuditLogType.USER_IMPORT_IMAGE;
}
break;
case END_SUCCESS:
return getParameters().getImportAsTemplate() ?
AuditLogType.USER_IMPORT_IMAGE_AS_TEMPLATE_FINISHED_SUCCESS :
AuditLogType.USER_IMPORT_IMAGE_FINISHED_SUCCESS;
case END_FAILURE:
return getParameters().getImportAsTemplate() ?
AuditLogType.USER_IMPORT_IMAGE_AS_TEMPLATE_FINISHED_FAILURE :
AuditLogType.USER_IMPORT_IMAGE_FINISHED_FAILURE;
}
return AuditLogType.UNASSIGNED;
}
@Override
protected boolean validate() {
if (!validate(new StoragePoolValidator(getStoragePool()).isUp())) {
return false;
}
if (getParameters().getImportAsTemplate()) {
if (getParameters().getClusterId() == null) {
addValidationMessage(EngineMessage.VDS_CLUSTER_IS_NOT_VALID);
return false;
}
setClusterId(getParameters().getClusterId());
if (getCluster() == null) {
addValidationMessage(EngineMessage.VDS_CLUSTER_IS_NOT_VALID);
return false;
}
// A Template cannot be added in a cluster without a defined architecture
if (getCluster().getArchitecture() == ArchitectureType.undefined) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_CLUSTER_UNDEFINED_ARCHITECTURE);
}
setStoragePoolId(getParameters().getStoragePoolId());
}
DiskImage diskImage = null;
try {
diskImage = getDiskImage();
} catch (OpenStackImageException e) {
log.error("Unable to get the disk image from the provider proxy: ({}) {}",
e.getErrorType(),
e.getMessage());
switch (e.getErrorType()) {
case UNSUPPORTED_CONTAINER_FORMAT:
case UNSUPPORTED_DISK_FORMAT:
return failValidation(EngineMessage.ACTION_TYPE_FAILED_IMAGE_NOT_SUPPORTED);
case UNABLE_TO_DOWNLOAD_IMAGE:
return failValidation(EngineMessage.ACTION_TYPE_FAILED_IMAGE_DOWNLOAD_ERROR);
case UNRECOGNIZED_IMAGE_FORMAT:
return failValidation(EngineMessage.ACTION_TYPE_FAILED_IMAGE_UNRECOGNIZED);
}
}
if (diskImage == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_DISK_NOT_EXIST);
}
return validateSpaceRequirements(diskImage);
}
protected boolean validateSpaceRequirements(DiskImage diskImage) {
diskImage.getSnapshots().add(diskImage); // Added for validation purposes.
StorageDomainValidator sdValidator = createStorageDomainValidator();
boolean result = validate(sdValidator.isDomainExistAndActive())
&& validate(sdValidator.isDomainWithinThresholds())
&& validate(sdValidator.hasSpaceForClonedDisk(diskImage));
diskImage.getSnapshots().remove(diskImage);
return result;
}
protected StorageDomainValidator createStorageDomainValidator() {
return new StorageDomainValidator(getStorageDomain());
}
private void addAuditLogCustomValues() {
if (getParameters().getImportAsTemplate()) {
addCustomValue("TemplateName", getParameters().getTemplateName());
}
}
}