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; } } }