package org.ovirt.engine.core.bll.exportimport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute;
import org.ovirt.engine.core.bll.VmHandler;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.storage.ovfstore.OvfHelper;
import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.action.ImportVmTemplateParameters;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.businessentities.OvfEntityData;
import org.ovirt.engine.core.common.businessentities.StorageDomain;
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.ImageStorageDomainMap;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.utils.CompatibilityVersionUtils;
import org.ovirt.engine.core.common.vdscommands.GetImageInfoVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.GetImagesListVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.ImageStorageDomainMapDao;
import org.ovirt.engine.core.dao.StorageDomainDao;
import org.ovirt.engine.core.dao.UnregisteredDisksDao;
import org.ovirt.engine.core.dao.UnregisteredOVFDataDao;
import org.ovirt.engine.core.utils.ovf.OvfReaderException;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@NonTransactiveCommandAttribute
public class ImportVmTemplateFromConfigurationCommand<T extends ImportVmTemplateParameters> extends ImportVmTemplateCommand {
private static final Logger log = LoggerFactory.getLogger(ImportVmFromConfigurationCommand.class);
private Map<Guid, String> failedDisksToImportForAuditLog = new HashMap<>();
private OvfEntityData ovfEntityData;
VmTemplate vmTemplateFromConfiguration;
private ArrayList<DiskImage> imagesList;
@Inject
private OvfHelper ovfHelper;
@Inject
private ImageStorageDomainMapDao imageStorageDomainMapDao;
@Inject
private StorageDomainDao storageDomainDao;
@Inject
private UnregisteredOVFDataDao unregisteredOVFDataDao;
@Inject
private UnregisteredDisksDao unregisteredDisksDao;
public ImportVmTemplateFromConfigurationCommand(Guid commandId) {
super(commandId);
}
public ImportVmTemplateFromConfigurationCommand(ImportVmTemplateParameters parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Execution);
}
@Override
public Guid getVmTemplateId() {
if (getParameters().isImagesExistOnTargetStorageDomain()) {
return getParameters().getContainerId();
}
return super.getVmTemplateId();
}
@Override
protected boolean validate() {
initVmTemplate();
ArrayList<DiskImage> disks = new ArrayList(getVmTemplate().getDiskTemplateMap().values());
setImagesWithStoragePoolId(getStorageDomain().getStoragePoolId(), disks);
getVmTemplate().setImages(disks);
if (getParameters().isImagesExistOnTargetStorageDomain() &&
!validateUnregisteredEntity(vmTemplateFromConfiguration, ovfEntityData)) {
return false;
}
return super.validate();
}
private boolean validateUnregisteredEntity(VmTemplate entityFromConfiguration, OvfEntityData ovfEntityData) {
if (ovfEntityData == null && !getParameters().isImportAsNewEntity()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_UNSUPPORTED_OVF);
}
if (entityFromConfiguration == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_OVF_CONFIGURATION_NOT_SUPPORTED);
}
for (DiskImage image : new ArrayList<>(getImages())) {
DiskImage fromIrs = null;
Guid storageDomainId = image.getStorageIds().get(0);
Guid imageGroupId = image.getId() != null ? image.getId() : Guid.Empty;
try {
fromIrs = (DiskImage) runVdsCommand(VDSCommandType.GetImageInfo,
new GetImageInfoVDSCommandParameters(getStoragePool().getId(),
storageDomainId,
imageGroupId,
image.getImageId())).getReturnValue();
} catch (Exception e) {
log.debug("Unable to get image info from storage", e);
}
if (fromIrs == null) {
if (!getParameters().isAllowPartialImport()) {
return failValidation(EngineMessage.TEMPLATE_IMAGE_NOT_EXIST);
}
log.warn("Disk image '{}/{}' doesn't exist on storage domain '{}'. Ignoring since force flag in on",
imageGroupId,
image.getImageId(),
storageDomainId);
getImages().remove(image);
failedDisksToImportForAuditLog.putIfAbsent(image.getId(), image.getDiskAlias());
}
}
for (DiskImage image : getImages()) {
StorageDomain sd = storageDomainDao.getForStoragePool(
image.getStorageIds().get(0), getStoragePool().getId());
if (!validate(new StorageDomainValidator(sd).isDomainExistAndActive())) {
return false;
}
}
if (!getStorageDomain().getStorageDomainType().isDataDomain()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_STORAGE_DOMAIN_TYPE_UNSUPPORTED,
String.format("$domainId %1$s", getParameters().getStorageDomainId()),
String.format("$domainType %1$s", getStorageDomain().getStorageDomainType()));
}
return true;
}
private void setImagesWithStoragePoolId(Guid storagePoolId, List<DiskImage> diskImages) {
for (DiskImage diskImage : diskImages) {
diskImage.setStoragePoolId(storagePoolId);
}
}
private void initVmTemplate() {
List<OvfEntityData> ovfEntityList =
unregisteredOVFDataDao.getByEntityIdAndStorageDomain(getParameters().getContainerId(),
getParameters().getStorageDomainId());
if (!ovfEntityList.isEmpty()) {
try {
// We should get only one entity, since we fetched the entity with a specific Storage Domain
ovfEntityData = ovfEntityList.get(0);
vmTemplateFromConfiguration = ovfHelper.readVmTemplateFromOvf(ovfEntityData.getOvfData());
vmTemplateFromConfiguration.setClusterId(getParameters().getClusterId());
setVmTemplate(vmTemplateFromConfiguration);
setEffectiveCompatibilityVersion(CompatibilityVersionUtils.getEffective(getVmTemplate(), this::getCluster));
VmHandler.updateMaxMemorySize(getVmTemplate(), getEffectiveCompatibilityVersion());
getParameters().setVmTemplate(vmTemplateFromConfiguration);
getParameters().setDestDomainId(ovfEntityData.getStorageDomainId());
getParameters().setSourceDomainId(ovfEntityData.getStorageDomainId());
// For quota, update disks when required
if (getParameters().getDiskTemplateMap() != null) {
ArrayList imageList = new ArrayList<>(getParameters().getDiskTemplateMap().values());
vmTemplateFromConfiguration.setDiskList(imageList);
ensureDomainMap(imageList, getParameters().getDestDomainId());
}
} catch (OvfReaderException e) {
log.error("Failed to parse a given ovf configuration: {}:\n{}",
e.getMessage(),
ovfEntityData.getOvfData());
log.debug("Exception", e);
}
}
setClusterId(getParameters().getClusterId());
setStoragePoolId(getCluster().getStoragePoolId());
}
@Override
protected ArrayList<DiskImage> getImages() {
if (imagesList == null) {
imagesList = new ArrayList<>(getParameters().getDiskTemplateMap() != null ?
getParameters().getDiskTemplateMap().values() : getVmTemplate().getDiskTemplateMap().values());
}
return imagesList;
}
@Override
public void executeCommand() {
addAuditLogForPartialVMs();
super.executeCommand();
if (getParameters().isImagesExistOnTargetStorageDomain()) {
if (!getImages().isEmpty()) {
findAndSaveDiskCopies();
getImages().stream().forEach(diskImage -> {
initQcowVersionForDisks(diskImage.getId());
});
}
unregisteredOVFDataDao.removeEntity(ovfEntityData.getEntityId(), null);
unregisteredDisksDao.removeUnregisteredDiskRelatedToVM(ovfEntityData.getEntityId(), null);
}
setActionReturnValue(getVmTemplate().getId());
setSucceeded(true);
}
private void addAuditLogForPartialVMs() {
if (getParameters().isAllowPartialImport() && !failedDisksToImportForAuditLog.isEmpty()) {
addCustomValue("DiskAliases", StringUtils.join(failedDisksToImportForAuditLog.values(), ", "));
auditLogDirector.log(this, AuditLogType.IMPORTEXPORT_PARTIAL_TEMPLATE_DISKS_NOT_EXISTS);
}
}
private void findAndSaveDiskCopies() {
List<OvfEntityData> ovfEntityDataList =
unregisteredOVFDataDao.getByEntityIdAndStorageDomain(ovfEntityData.getEntityId(), null);
List<ImageStorageDomainMap> copiedTemplateDisks = new LinkedList<>();
for (OvfEntityData ovfEntityDataFetched : ovfEntityDataList) {
populateDisksCopies(copiedTemplateDisks,
getImages(),
ovfEntityDataFetched.getStorageDomainId());
}
saveImageStorageDomainMapList(copiedTemplateDisks);
}
private void populateDisksCopies(List<ImageStorageDomainMap> copiedTemplateDisks,
List<DiskImage> originalTemplateImages,
Guid storageDomainId) {
List<Guid> imagesContainedInStorageDomain = getImagesGuidFromStorage(storageDomainId, getStoragePoolId());
for (DiskImage templateDiskImage : originalTemplateImages) {
if (storageDomainId.equals(templateDiskImage.getStorageIds().get(0))) {
// The original Storage Domain was already saved. skipping it.
continue;
}
if (imagesContainedInStorageDomain.contains(templateDiskImage.getId())) {
log.info("Found a copied image of '{}' on Storage Domain id '{}'",
templateDiskImage.getId(),
storageDomainId);
ImageStorageDomainMap imageStorageDomainMap =
new ImageStorageDomainMap(templateDiskImage.getImageId(),
storageDomainId,
templateDiskImage.getQuotaId(),
templateDiskImage.getDiskProfileId());
copiedTemplateDisks.add(imageStorageDomainMap);
}
}
}
private List<Guid> getImagesGuidFromStorage(Guid storageDomainId, Guid storagePoolId) {
List<Guid> imagesList = Collections.emptyList();
try {
VDSReturnValue imagesListResult = runVdsCommand(VDSCommandType.GetImagesList,
new GetImagesListVDSCommandParameters(storageDomainId, storagePoolId));
if (imagesListResult.getSucceeded()) {
imagesList = (List<Guid>) imagesListResult.getReturnValue();
} else {
log.error("Unable to get images list for storage domain, can not update copied template disks related to Storage Domain id '{}'",
storageDomainId);
}
} catch (Exception e) {
log.error("Unable to get images list for storage domain, can not update copied template disks related to Storage Domain id '{}'. error is: {}",
storageDomainId,
e);
}
return imagesList;
}
private void saveImageStorageDomainMapList(final List<ImageStorageDomainMap> copiedTemplateDisks) {
if (!copiedTemplateDisks.isEmpty()) {
TransactionSupport.executeInNewTransaction(() -> {
for (ImageStorageDomainMap imageStorageDomainMap : copiedTemplateDisks) {
imageStorageDomainMapDao.save(imageStorageDomainMap);
}
return null;
});
}
}
@Override
public AuditLogType getAuditLogTypeValue() {
return getSucceeded() ? AuditLogType.TEMPLATE_IMPORT_FROM_CONFIGURATION_SUCCESS :
AuditLogType.TEMPLATE_IMPORT_FROM_CONFIGURATION_FAILED;
}
}