package org.ovirt.engine.core.bll.exportimport;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.DisableInPrepareMode;
import org.ovirt.engine.core.bll.LockMessage;
import org.ovirt.engine.core.bll.LockMessagesMatchUtil;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler;
import org.ovirt.engine.core.bll.storage.ovfstore.OvfUpdateProcessHelper;
import org.ovirt.engine.core.bll.validator.storage.DiskImagesValidator;
import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.MoveOrCopyImageGroupParameters;
import org.ovirt.engine.core.common.action.MoveOrCopyParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.StorageDomain;
import org.ovirt.engine.core.common.businessentities.StorageDomainType;
import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMapId;
import org.ovirt.engine.core.common.businessentities.storage.CopyVolumeType;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.ImageDbOperationScope;
import org.ovirt.engine.core.common.businessentities.storage.ImageOperation;
import org.ovirt.engine.core.common.errors.EngineException;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.locks.LockingGroup;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.KeyValuePairCompat;
import org.ovirt.engine.core.dao.StoragePoolIsoMapDao;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
@DisableInPrepareMode
public class ExportVmTemplateCommand<T extends MoveOrCopyParameters> extends MoveOrCopyTemplateCommand<T> {
@Inject
private OvfUpdateProcessHelper ovfUpdateProcessHelper;
@Inject
private StoragePoolIsoMapDao storagePoolIsoMapDao;
private String cachedTemplateIsBeingExportedMessage;
public ExportVmTemplateCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
public ExportVmTemplateCommand(Guid commandId) {
super(commandId);
}
@Override
public void init() {
super.init();
if (getVmTemplate() != null) {
setDescription(getVmTemplateName());
setStoragePoolId(getVmTemplate().getStoragePoolId());
}
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Command);
}
@Override
protected void moveOrCopyAllImageGroups(final Guid containerID, final Iterable<DiskImage> disks) {
TransactionSupport.executeInNewTransaction(() -> {
for (DiskImage disk : disks) {
// we force export template image to COW+Sparse but we don't update
// the ovf so the import
// will set the original format
MoveOrCopyImageGroupParameters p = new MoveOrCopyImageGroupParameters(containerID, disk
.getId(), disk.getImageId(), getParameters().getStorageDomainId(),
ImageOperation.Copy);
p.setParentCommand(getActionType());
p.setParentParameters(getParameters());
p.setEntityInfo(getParameters().getEntityInfo());
p.setUseCopyCollapse(true);
p.setCopyVolumeType(CopyVolumeType.SharedVol);
p.setVolumeFormat(disk.getVolumeFormat());
p.setVolumeType(disk.getVolumeType());
p.setForceOverride(getParameters().getForceOverride());
p.setRevertDbOperationScope(ImageDbOperationScope.NONE);
p.setShouldLockImageOnRevert(false);
p.setSourceDomainId(imageFromSourceDomainMap.get(disk.getId()).getStorageIds().get(0));
VdcReturnValueBase vdcRetValue =
runInternalActionWithTasksContext(VdcActionType.CopyImageGroup, p);
if (!vdcRetValue.getSucceeded()) {
throw new EngineException(vdcRetValue.getFault().getError(), vdcRetValue.getFault()
.getMessage());
}
getReturnValue().getVdsmTaskIdList().addAll(vdcRetValue.getInternalVdsmTaskIdList());
}
return null;
});
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return Collections.singletonMap(getVmTemplateId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.REMOTE_TEMPLATE, getTemplateIsBeingExportedMessage()));
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
return Collections.singletonMap(getVmTemplateId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.TEMPLATE, getTemplateIsBeingExportedMessage()));
}
@Override
protected void executeCommand() {
vmHandler.updateVmInitFromDB(getVmTemplate(), true);
if (!getTemplateDisks().isEmpty()) {
moveOrCopyAllImageGroups();
} else {
endVmTemplateRelatedOps();
}
setSucceeded(true);
}
private String getTemplateIsBeingExportedMessage() {
if (cachedTemplateIsBeingExportedMessage == null) {
cachedTemplateIsBeingExportedMessage = new LockMessage(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_IS_BEING_EXPORTED)
.withOptional("TemplateName", getVmTemplate() != null ? getVmTemplate().getName() : null)
.toString();
}
return cachedTemplateIsBeingExportedMessage;
}
@Override
protected boolean validate() {
if (getVmTemplate() == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_DOES_NOT_EXIST);
}
StorageDomainValidator storageDomainValidator = new StorageDomainValidator(getStorageDomain());
if (!validate(storageDomainValidator.isDomainExistAndActive())) {
return false;
}
// export must be to export domain
if (getStorageDomain().getStorageDomainType() != StorageDomainType.ImportExport) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_SPECIFY_DOMAIN_IS_NOT_EXPORT_DOMAIN);
}
if (getTemplateDisks() != null && !getTemplateDisks().isEmpty()) {
ensureDomainMap(getTemplateDisks(), getParameters().getStorageDomainId());
// check that images are ok
ImagesHandler.fillImagesMapBasedOnTemplate(getVmTemplate(),
imageFromSourceDomainMap,
null);
if (getVmTemplate().getDiskTemplateMap().values().size() != imageFromSourceDomainMap.size()) {
log.error("Can not found any default active domain for one of the disks of template with id '{}'",
getVmTemplate().getId());
return failValidation(EngineMessage.ACTION_TYPE_FAILED_MISSED_STORAGES_FOR_SOME_DISKS);
}
if (validate(vmTemplateHandler.isVmTemplateImagesReady(getVmTemplate(), null,
true, true, true, false, getTemplateDisks()))) {
setStoragePoolId(getVmTemplate().getStoragePoolId());
StorageDomainValidator sdValidator = createStorageDomainValidator(getStorageDomain());
if (!validate(sdValidator.isDomainExistAndActive())
|| !validate(sdValidator.isDomainWithinThresholds())
|| !(getParameters().getForceOverride()
|| (!getParameters().isImagesExistOnTargetStorageDomain() && checkIfDisksExist(getTemplateDisks())))
|| !validateFreeSpaceOnDestinationDomain(sdValidator, getTemplateDisks())) {
return false;
}
}
if (storagePoolIsoMapDao.get(new StoragePoolIsoMapId(getStorageDomain().getId(),
getVmTemplate().getStoragePoolId())) == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_MATCH);
}
}
// check if template (with no override option)
if (!getParameters().getForceOverride()) {
if (ExportVmCommand.checkTemplateInStorageDomain(getVmTemplate().getStoragePoolId(),
getParameters().getStorageDomainId(), getVmTemplateId(), getContext().getEngineContext())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NAME_ALREADY_USED);
}
}
return true;
}
private StorageDomainValidator createStorageDomainValidator(StorageDomain storageDomain) {
return new StorageDomainValidator(storageDomain);
}
private boolean validateFreeSpaceOnDestinationDomain(StorageDomainValidator storageDomainValidator, List<DiskImage> disksList) {
return validate(storageDomainValidator.hasSpaceForClonedDisks(disksList));
}
private boolean checkIfDisksExist(Iterable<DiskImage> disksList) {
return validate(new DiskImagesValidator(disksList).diskImagesOnStorage(imageToDestinationDomainMap, getStoragePoolId()));
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__EXPORT);
addValidationMessage(EngineMessage.VAR__TYPE__VM_TEMPLATE);
}
@Override
public AuditLogType getAuditLogTypeValue() {
switch (getActionState()) {
case EXECUTE:
return getSucceeded() ? AuditLogType.IMPORTEXPORT_STARTING_EXPORT_TEMPLATE
: AuditLogType.IMPORTEXPORT_EXPORT_TEMPLATE_FAILED;
case END_SUCCESS:
return getSucceeded() ? AuditLogType.IMPORTEXPORT_EXPORT_TEMPLATE
: AuditLogType.IMPORTEXPORT_EXPORT_TEMPLATE_FAILED;
default:
return AuditLogType.IMPORTEXPORT_EXPORT_TEMPLATE_FAILED;
}
}
@Override
protected void incrementDbGeneration() {
// we want to export the Template's ovf only in case that all tasks has succeeded, otherwise we will attempt to
// revert
// and there's no need for exporting the template's ovf.
if (getParameters().getTaskGroupSuccess()) {
Map<Guid, KeyValuePairCompat<String, List<Guid>>> metaDictionary = new HashMap<>();
ovfUpdateProcessHelper.loadTemplateData(getVmTemplate());
vmTemplateHandler.updateDisksFromDb(getVmTemplate());
// update the target (export) domain
ovfUpdateProcessHelper.buildMetadataDictionaryForTemplate(getVmTemplate(), metaDictionary);
ovfUpdateProcessHelper.executeUpdateVmInSpmCommand(getVmTemplate().getStoragePoolId(),
metaDictionary,
getParameters().getStorageDomainId());
}
}
@Override
protected void endActionOnAllImageGroups() {
for (VdcActionParametersBase p : getParameters().getImagesParameters()) {
p.setTaskGroupSuccess(getParameters().getTaskGroupSuccess());
getBackend().endAction(VdcActionType.CopyImageGroup,
p,
getContext().clone().withoutCompensationContext().withoutExecutionContext().withoutLock());
}
}
@Override
public Map<String, String> getJobMessageProperties() {
if (jobProperties == null) {
jobProperties = super.getJobMessageProperties();
jobProperties.put(VdcObjectType.VmTemplate.name().toLowerCase(),
(getVmTemplateName() == null) ? "" : getVmTemplateName());
}
return jobProperties;
}
}