package org.ovirt.engine.core.bll.exportimport;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.CommandBase;
import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.ImportVmFromExternalProviderParameters;
import org.ovirt.engine.core.common.action.ImportVmFromExternalUrlParameters;
import org.ovirt.engine.core.common.action.ImportVmFromOvaParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.OriginType;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.storage.Disk;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.StorageType;
import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat;
import org.ovirt.engine.core.common.businessentities.storage.VolumeType;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.common.errors.EngineException;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.queries.GetVmFromOvaQueryParameters;
import org.ovirt.engine.core.common.queries.GetVmsFromExternalProviderQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.VmStaticDao;
import org.ovirt.engine.core.di.Injector;
@NonTransactiveCommandAttribute
public class ImportVmFromExternalUrlCommand<P extends ImportVmFromExternalUrlParameters> extends CommandBase<P> {
@Inject
private VmStaticDao vmStaticDao;
private ExternalVmImporter vmImporter;
public ImportVmFromExternalUrlCommand(P parameters, CommandContext cmdContext) {
super(parameters, cmdContext);
}
@Override
protected void init() {
super.init();
setClusterId(getParameters().getClusterId());
if (getCluster() != null) {
setStoragePoolId(getCluster().getStoragePoolId());
}
setStorageDomainId(getParameters().getStorageDomainId());
vmImporter = getVmImporter();
}
@Override
protected boolean validate() {
return super.validate() && vmImporter.validate();
}
@Override
protected void executeCommand() {
setReturnValue(vmImporter.performImport());
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
Set<PermissionSubject> permissionSet = new HashSet<>();
// Destination domain
permissionSet.add(new PermissionSubject(getStorageDomainId(),
VdcObjectType.Storage,
getActionType().getActionGroup()));
return new ArrayList<>(permissionSet);
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__IMPORT);
addValidationMessage(EngineMessage.VAR__TYPE__VM);
}
@Override
public String getVmName() {
return getParameters().getNewVmName() != null
? getParameters().getNewVmName()
: getParameters().getExternalName();
}
private ExternalVmImporter getVmImporter() {
boolean isOvaImport = getParameters().getUrl().startsWith("ova://");
return Injector.injectMembers(
isOvaImport ? new ExternalOvaVmImporter() : new ExternalVmProviderImporter());
}
private abstract class ExternalVmImporter {
public boolean validate() {
return true;
}
public VdcReturnValueBase performImport() {
VM vm = loadExternalVm();
vm.setName(getVmName());
return runInternalAction(getImportActionType(), buildImportVmParameters(vm));
}
private ImportVmFromExternalProviderParameters buildImportVmParameters(VM vm) {
ImportVmFromExternalProviderParameters prm = createImportVmParameters(vm);
prm.setProxyHostId(getParameters().getProxyHostId());
prm.setVirtioIsoName(getParameters().getVirtioIsoName());
prm.setExternalName(getParameters().getExternalName());
if (getParameters().getQuotaId() != null) {
prm.setQuotaId(getParameters().getQuotaId());
}
if (getParameters().getCpuProfileId() != null) {
prm.setCpuProfileId(getParameters().getCpuProfileId());
}
prm.setForceOverride(true);
prm.setCopyCollapse(true);
boolean existsInTheSystem = vmStaticDao.get(vm.getId()) != null;
prm.setImportAsNewEntity(existsInTheSystem);
for (Map.Entry<Guid, Disk> entry : vm.getDiskMap().entrySet()) {
DiskImage disk = (DiskImage) entry.getValue();
disk.setVolumeType(getParameters().getVolumeType());
// in kvm we just copy the image, in other modes such as vmware or xen we use
// virt-v2v which converts the image format as well
if (vm.getOrigin() != OriginType.KVM) {
disk.setVolumeFormat(getDiskVolumeFormat(
disk.getVolumeType(),
getStorageDomain().getStorageType()));
}
if (getParameters().getQuotaId() != null) {
disk.setQuotaId(getParameters().getQuotaId());
}
}
return prm;
}
protected abstract VM loadExternalVm();
protected abstract VdcActionType getImportActionType();
protected abstract ImportVmFromExternalProviderParameters createImportVmParameters(VM vm);
}
private class ExternalVmProviderImporter extends ExternalVmImporter {
@Override
public boolean validate() {
if (!super.validate()) {
return false;
}
if (StringUtils.isBlank(getParameters().getUsername())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_USERNAME_MUST_BE_SPECIFIED);
}
if (StringUtils.isBlank(getParameters().getPassword())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_PASSWORD_MUST_BE_SPECIFIED);
}
return true;
}
@Override
protected VdcActionType getImportActionType() {
return VdcActionType.ImportVmFromExternalProvider;
}
@Override
protected ImportVmFromExternalProviderParameters createImportVmParameters(VM vm) {
ImportVmFromExternalProviderParameters parameters = new ImportVmFromExternalProviderParameters(
vm,
getParameters().getStorageDomainId(),
getStoragePoolId(),
getClusterId());
parameters.setUrl(getParameters().getUrl());
parameters.setUsername(getParameters().getUsername());
parameters.setPassword(getParameters().getPassword());
return parameters;
}
@Override
protected VM loadExternalVm() {
List<VM> externalVms = runInternalQuery(VdcQueryType.GetVmsFromExternalProvider, buildGetVmsParameters())
.getReturnValue();
return (externalVms != null ? externalVms : Collections.<VM>emptyList())
.stream()
.filter(vm -> vm.getName().equals(getParameters().getExternalName()))
.findFirst()
.orElseThrow(() -> new EngineException(EngineError.noVM));
}
public GetVmsFromExternalProviderQueryParameters buildGetVmsParameters() {
return new GetVmsFromExternalProviderQueryParameters(
getParameters().getUrl(),
getParameters().getUsername(),
getParameters().getPassword(),
getParameters().getOriginType(),
getParameters().getProxyHostId(),
getStoragePoolId(),
Collections.singletonList(getParameters().getExternalName()));
}
}
private class ExternalOvaVmImporter extends ExternalVmImporter {
private final String ovaPath;
public ExternalOvaVmImporter() {
ovaPath = getParameters().getUrl().replace("ova://", "");
}
@Override
public boolean validate() {
if (!super.validate()) {
return false;
}
if (getParameters().getProxyHostId() == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_PROXY_HOST_MUST_BE_SPECIFIED);
}
return true;
}
@Override
protected VdcActionType getImportActionType() {
return VdcActionType.ImportVmFromOva;
}
@Override
protected ImportVmFromExternalProviderParameters createImportVmParameters(VM vm) {
ImportVmFromOvaParameters parameters = new ImportVmFromOvaParameters(
vm,
getParameters().getStorageDomainId(),
getStoragePoolId(),
getClusterId());
parameters.setOvaPath(ovaPath);
return parameters;
}
@Override
protected VM loadExternalVm() {
return runInternalQuery(VdcQueryType.GetVmFromOva,
new GetVmFromOvaQueryParameters(getParameters().getProxyHostId(), ovaPath)).getReturnValue();
}
}
// TODO: remove code duplication with frontend AsyncDataProvider
private static VolumeFormat getDiskVolumeFormat(VolumeType volumeType, StorageType storageType) {
if (storageType.isFileDomain()) {
return VolumeFormat.RAW;
} else if (storageType.isBlockDomain()) {
switch (volumeType) {
case Sparse:
return VolumeFormat.COW;
case Preallocated:
return VolumeFormat.RAW;
default:
return VolumeFormat.Unassigned;
}
} else {
return VolumeFormat.Unassigned;
}
}
}