package org.ovirt.engine.core.bll.exportimport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.ConstraintViolation;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.ovirt.engine.core.bll.BaseCommandTest;
import org.ovirt.engine.core.bll.ValidateTestUtils;
import org.ovirt.engine.core.bll.ValidationResult;
import org.ovirt.engine.core.bll.network.macpool.MacPool;
import org.ovirt.engine.core.bll.network.macpool.MacPoolPerCluster;
import org.ovirt.engine.core.bll.validator.ImportValidator;
import org.ovirt.engine.core.bll.validator.VmNicMacsUtils;
import org.ovirt.engine.core.common.action.ImportVmParameters;
import org.ovirt.engine.core.common.businessentities.ArchitectureType;
import org.ovirt.engine.core.common.businessentities.BusinessEntitiesDefinitions;
import org.ovirt.engine.core.common.businessentities.Cluster;
import org.ovirt.engine.core.common.businessentities.DisplayType;
import org.ovirt.engine.core.common.businessentities.GraphicsType;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.StorageDomain;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatus;
import org.ovirt.engine.core.common.businessentities.StorageDomainType;
import org.ovirt.engine.core.common.businessentities.StoragePool;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmDevice;
import org.ovirt.engine.core.common.businessentities.VmDeviceGeneralType;
import org.ovirt.engine.core.common.businessentities.VmDeviceId;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.osinfo.OsRepository;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.utils.SimpleDependencyInjector;
import org.ovirt.engine.core.common.utils.ValidationUtils;
import org.ovirt.engine.core.common.utils.VmDeviceType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.Version;
import org.ovirt.engine.core.utils.MockConfigRule;
import org.ovirt.engine.core.utils.RandomUtils;
import org.ovirt.engine.core.utils.RandomUtilsSeedingRule;
import org.ovirt.engine.core.vdsbroker.vdsbroker.VdsProperties;
public class ImportVmCommandTest extends BaseCommandTest {
@Rule
public RandomUtilsSeedingRule rusr = new RandomUtilsSeedingRule();
static OsRepository osRepository;
@Mock
private MacPool macPool;
@Mock
private MacPoolPerCluster poolPerCluster;
@Mock
private VmNicMacsUtils vmNicMacsUtils;
@Spy
@InjectMocks
private ImportVmCommand<ImportVmParameters> cmd = new ImportVmCommand<>(createParameters(), null);
@ClassRule
public static MockConfigRule mcr = new MockConfigRule();
@BeforeClass
public static void setUpOsRepository() {
// init the injector with the osRepository instance
osRepository = mock(OsRepository.class);
SimpleDependencyInjector.getInstance().bind(OsRepository.class, osRepository);
final int osId = 0;
Map<Integer, Map<Version, List<Pair<GraphicsType, DisplayType>>>> displayTypeMap = new HashMap<>();
displayTypeMap.put(osId, new HashMap<>());
displayTypeMap.get(osId).put(null, Collections.singletonList(new Pair<>(GraphicsType.SPICE, DisplayType.qxl)));
when(osRepository.getGraphicsAndDisplays()).thenReturn(displayTypeMap);
}
@Before
public void setUp() {
doReturn(null).when(cmd).getCluster();
}
@Test
public void insufficientDiskSpaceWithCollapse() {
setupDiskSpaceTest();
doReturn(true).when(cmd).validateImages(anyMap());
when(cmd.getImportValidator().validateSpaceRequirements(anyCollection())).thenReturn(
new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_STORAGE_DOMAIN));
ValidateTestUtils.runAndAssertValidateFailure(cmd,
EngineMessage.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_STORAGE_DOMAIN);
}
@Test
public void insufficientDiskSpaceWithSnapshots() {
setupDiskSpaceTest();
doReturn(true).when(cmd).validateImages(anyMap());
when(cmd.getImportValidator().validateSpaceRequirements(anyCollection())).thenReturn(
new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_STORAGE_DOMAIN));
ValidateTestUtils.runAndAssertValidateFailure(cmd,
EngineMessage.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_STORAGE_DOMAIN);
}
void addBalloonToVm(VM vm) {
Guid deviceId = Guid.newGuid();
Map<String, Object> specParams = new HashMap<>();
specParams.put(VdsProperties.Model, VdsProperties.Virtio);
VmDevice balloon = new VmDevice(new VmDeviceId(deviceId, vm.getId()),
VmDeviceGeneralType.BALLOON, VmDeviceType.MEMBALLOON.toString(), null, specParams,
true, true, true, null, null, null, null);
vm.getManagedVmDeviceMap().put(deviceId, balloon);
}
private void addSoundDeviceToVm(VM vm) {
Guid deviceId = Guid.newGuid();
Map<String, Object> specParams = new HashMap<>();
VmDevice sound = new VmDevice(new VmDeviceId(deviceId, vm.getId()),
VmDeviceGeneralType.SOUND, "", null, specParams,
true, true, true, null, null, null, null);
vm.getManagedVmDeviceMap().put(deviceId, sound);
}
private void setupCanImportPpcTest() {
setupDiskSpaceTest();
cmd.getParameters().getVm().setClusterArch(ArchitectureType.ppc64);
Cluster cluster = new Cluster();
cluster.setArchitecture(ArchitectureType.ppc64);
cluster.setCompatibilityVersion(Version.getLast());
doReturn(cluster).when(cmd).getCluster();
doReturn(true).when(cmd).validateImages(anyMap());
}
@Test
public void refuseBalloonOnPPC() {
setupCanImportPpcTest();
addBalloonToVm(cmd.getVmFromExportDomain(null));
when(osRepository.isBalloonEnabled(cmd.getParameters().getVm().getVmOsId(), cmd.getCluster().getCompatibilityVersion())).thenReturn(false);
assertFalse(cmd.validate());
assertTrue(cmd.getReturnValue()
.getValidationMessages()
.contains(EngineMessage.BALLOON_REQUESTED_ON_NOT_SUPPORTED_ARCH.toString()));
}
@Test
public void refuseSoundDeviceOnPPC() {
setupCanImportPpcTest();
addSoundDeviceToVm(cmd.getVmFromExportDomain(null));
when(osRepository.isSoundDeviceEnabled(cmd.getParameters().getVm().getVmOsId(), cmd.getCluster().getCompatibilityVersion())).thenReturn(false);
assertFalse(cmd.validate());
assertTrue(cmd.getReturnValue()
.getValidationMessages()
.contains(EngineMessage.SOUND_DEVICE_REQUESTED_ON_NOT_SUPPORTED_ARCH.toString()));
}
@Test
public void acceptBalloon() {
setupDiskSpaceTest();
addBalloonToVm(cmd.getParameters().getVm());
cmd.getParameters().getVm().setClusterArch(ArchitectureType.x86_64);
Cluster cluster = new Cluster();
cluster.setId(Guid.newGuid());
cluster.setArchitecture(ArchitectureType.x86_64);
cluster.setCompatibilityVersion(Version.getLast());
doReturn(cluster).when(cmd).getCluster();
cmd.setClusterId(cluster.getId());
cmd.getParameters().setClusterId(cluster.getId());
osRepository.getGraphicsAndDisplays().get(0).put(Version.getLast(),
Collections.singletonList(new Pair<>(GraphicsType.SPICE, DisplayType.qxl)));
when(osRepository.isBalloonEnabled(cmd.getParameters().getVm().getVmOsId(), cluster.getCompatibilityVersion())).thenReturn(true);
cmd.initEffectiveCompatibilityVersion();
assertTrue(cmd.validateBallonDevice());
}
@Test
public void lowThresholdStorageSpace() {
setupDiskSpaceTest();
doReturn(true).when(cmd).validateImages(anyMap());
when(cmd.getImportValidator().validateSpaceRequirements(anyCollection()))
.thenReturn(new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_STORAGE_DOMAIN));
ValidateTestUtils.runAndAssertValidateFailure(cmd,
EngineMessage.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_STORAGE_DOMAIN);
}
private void setupDiskSpaceTest() {
final ImportValidator validator = spy(new ImportValidator(cmd.getParameters()));
doReturn(validator).when(cmd).getImportValidator();
cmd.init();
cmd.getParameters().setCopyCollapse(true);
doReturn(true).when(cmd).validateNoDuplicateVm();
doReturn(true).when(cmd).validateVdsCluster();
doReturn(true).when(cmd).validateUniqueVmName();
doReturn(true).when(cmd).checkTemplateInStorageDomain();
doReturn(true).when(cmd).checkImagesGUIDsLegal();
doReturn(true).when(cmd).setAndValidateDiskProfiles();
doReturn(true).when(cmd).setAndValidateCpuProfile();
doReturn(true).when(cmd).validateNoDuplicateDiskImages(anyCollection());
doReturn(createSourceDomain()).when(cmd).getSourceDomain();
doReturn(createStorageDomain()).when(cmd).getStorageDomain(any());
doReturn(cmd.getParameters().getVm()).when(cmd).getVmFromExportDomain(any());
doReturn(new VmTemplate()).when(cmd).getVmTemplate();
doReturn(new StoragePool()).when(cmd).getStoragePool();
Cluster cluster = new Cluster();
cluster.setClusterId(cmd.getParameters().getClusterId());
doReturn(cluster).when(cmd).getCluster();
doReturn(macPool).when(cmd).getMacPool();
when(poolPerCluster.getMacPoolForCluster(any())).thenReturn(macPool);
ArrayList<Guid> sdIds = new ArrayList<>(Collections.singletonList(Guid.newGuid()));
for (DiskImage image : cmd.getParameters().getVm().getImages()) {
image.setStorageIds(sdIds);
}
doReturn(Collections.emptyList()).when(cmd).createDiskDummiesForSpaceValidations(anyList());
}
protected ImportVmParameters createParameters() {
final VM vm = createVmWithSnapshots();
vm.setName("testVm");
Guid clusterId = Guid.newGuid();
vm.setClusterId(clusterId);
return new ImportVmParameters(vm, Guid.newGuid(), Guid.newGuid(), Guid.newGuid(), clusterId);
}
protected VM createVmWithSnapshots() {
final VM v = new VM();
v.setId(Guid.newGuid());
Snapshot baseSnapshot = new Snapshot();
baseSnapshot.setVmId(v.getId());
Snapshot activeSnapshot = new Snapshot();
activeSnapshot.setVmId(v.getId());
DiskImage baseImage = createDiskImage(Guid.newGuid(), Guid.newGuid(), baseSnapshot.getId(), false);
DiskImage activeImage =
createDiskImage(baseImage.getId(), baseImage.getImageId(), activeSnapshot.getId(), true);
baseSnapshot.setDiskImages(Collections.singletonList(baseImage));
activeSnapshot.setDiskImages(Collections.singletonList(activeImage));
v.setDiskMap(Collections.singletonMap(activeImage.getId(), activeImage));
v.setImages(new ArrayList<>(Arrays.asList(baseImage, activeImage)));
v.setSnapshots(new ArrayList<>(Arrays.asList(baseSnapshot, activeSnapshot)));
v.setClusterId(Guid.Empty);
return v;
}
protected VM createVmWithNoSnapshots() {
final VM v = new VM();
v.setId(Guid.newGuid());
Snapshot activeSnapshot = new Snapshot();
activeSnapshot.setVmId(v.getId());
DiskImage activeImage = createDiskImage(Guid.newGuid(), Guid.newGuid(), activeSnapshot.getId(), true);
activeSnapshot.setDiskImages(Collections.singletonList(activeImage));
v.setImages(new ArrayList<>(Collections.singletonList(activeImage)));
v.setSnapshots(new ArrayList<>(Collections.singletonList(activeSnapshot)));
v.setDiskMap(Collections.singletonMap(activeImage.getId(), activeImage));
v.setClusterId(Guid.Empty);
return v;
}
private DiskImage createDiskImage(Guid imageGroupId, Guid parentImageId, Guid vmSnapshoId, boolean active) {
DiskImage disk = new DiskImage();
disk.setId(imageGroupId);
disk.setImageId(Guid.newGuid());
disk.setSizeInGigabytes(1);
disk.setVmSnapshotId(vmSnapshoId);
disk.setActive(active);
disk.setParentId(parentImageId);
return disk;
}
protected StorageDomain createSourceDomain() {
StorageDomain sd = new StorageDomain();
sd.setStorageDomainType(StorageDomainType.ImportExport);
sd.setStatus(StorageDomainStatus.Active);
return sd;
}
protected StorageDomain createStorageDomain() {
StorageDomain sd = new StorageDomain();
sd.setStorageDomainType(StorageDomainType.Data);
sd.setStatus(StorageDomainStatus.Active);
sd.setAvailableDiskSize(2);
return sd;
}
@Test
public void testValidateNameSizeImportAsCloned() {
checkVmName(true, RandomUtils.instance().nextPropertyString(300));
}
@Test
public void testDoNotValidateNameSizeImport() {
checkVmName(false, RandomUtils.instance().nextPropertyString(300));
}
@Test
public void testValidateNameSpecialCharsImportAsCloned() {
checkVmName(true, "vm_#$@%$#@@");
}
@Test
public void testDoNotValidateNameSpecialCharsImport() {
checkVmName(false, "vm_#$@%$#@@");
}
private void checkVmName(boolean isImportAsNewEntity, String name) {
cmd.getParameters().getVm().setName(name);
cmd.getParameters().setImportAsNewEntity(isImportAsNewEntity);
cmd.init();
Set<ConstraintViolation<ImportVmParameters>> validate =
ValidationUtils.getValidator().validate(cmd.getParameters(),
cmd.getValidationGroups().toArray(new Class<?>[0]));
assertEquals(validate.isEmpty(), !isImportAsNewEntity);
}
/**
* Checking that other fields in {@link org.ovirt.engine.core.common.businessentities.VmStatic#VmStatic} don't get
* validated when importing a VM.
*/
@Test
public void testOtherFieldsNotValidatedInImport() {
String tooLongString =
RandomUtils.instance().nextPropertyString(BusinessEntitiesDefinitions.GENERAL_MAX_SIZE + 1);
cmd.getParameters().getVm().setUserDefinedProperties(tooLongString);
cmd.getParameters().setImportAsNewEntity(true);
cmd.init();
Set<ConstraintViolation<ImportVmParameters>> validate =
ValidationUtils.getValidator().validate(cmd.getParameters(),
cmd.getValidationGroups().toArray(new Class<?>[0]));
assertTrue(validate.isEmpty());
cmd.getParameters().getVm().setUserDefinedProperties(tooLongString);
cmd.getParameters().setImportAsNewEntity(false);
cmd.init();
validate = ValidationUtils.getValidator()
.validate(cmd.getParameters(), cmd.getValidationGroups().toArray(new Class<?>[0]));
assertTrue(validate.isEmpty());
}
/**
* Checking that managed device are sync with the new Guids of disk
*/
@Test
public void testManagedDeviceSyncWithNewDiskId() {
cmd.init();
List<DiskImage> diskList = new ArrayList<>();
DiskImage diskImage = new DiskImage();
diskImage.setStorageIds(new ArrayList<>());
DiskImage diskImage2 = new DiskImage();
diskImage2.setStorageIds(new ArrayList<>());
diskList.add(diskImage);
diskList.add(diskImage2);
DiskImage disk = cmd.getActiveVolumeDisk(diskList);
Map<Guid, VmDevice> managedDevices = new HashMap<>();
managedDevices.put(disk.getId(), new VmDevice());
Guid beforeOldDiskId = disk.getId();
cmd.generateNewDiskId(diskList, disk);
cmd.updateManagedDeviceMap(disk, managedDevices);
Guid oldDiskId = cmd.newDiskIdForDisk.get(disk.getId()).getId();
assertEquals("The old disk id should be similar to the value at the newDiskIdForDisk.",
beforeOldDiskId,
oldDiskId);
assertNotNull("The manged device should return the disk device by the new key",
managedDevices.get(disk.getId()));
assertNull("The manged device should not return the disk device by the old key",
managedDevices.get(beforeOldDiskId));
}
/* Tests for alias generation in addVmImagesAndSnapshots() */
@Test
public void testAliasGenerationByAddVmImagesAndSnapshotsWithCollapse() {
cmd.getParameters().setCopyCollapse(true);
cmd.init();
DiskImage collapsedDisk = cmd.getParameters().getVm().getImages().get(1);
doNothing().when(cmd).saveImage(collapsedDisk);
doNothing().when(cmd).saveBaseDisk(collapsedDisk);
doNothing().when(cmd).saveDiskImageDynamic(collapsedDisk);
doNothing().when(cmd).saveDiskVmElement(any(), any(), any());
doReturn(new Snapshot()).when(cmd).addActiveSnapshot(any());
cmd.addVmImagesAndSnapshots();
assertEquals("Disk alias not generated", "testVm_Disk1", collapsedDisk.getDiskAlias());
}
/* Test import images with empty Guid is failing */
@Test
public void testEmptyGuidFails() {
cmd.getParameters().setCopyCollapse(Boolean.TRUE);
DiskImage diskImage = cmd.getParameters().getVm().getImages().get(0);
diskImage.setVmSnapshotId(Guid.Empty);
doReturn(macPool).when(cmd).getMacPool();
cmd.init();
doReturn(true).when(cmd).validateNoDuplicateVm();
doReturn(true).when(cmd).validateVdsCluster();
doReturn(true).when(cmd).validateUniqueVmName();
doReturn(true).when(cmd).checkTemplateInStorageDomain();
doReturn(true).when(cmd).checkImagesGUIDsLegal();
doReturn(true).when(cmd).setAndValidateDiskProfiles();
doReturn(true).when(cmd).validateNoDuplicateDiskImages(anyCollection());
doReturn(createSourceDomain()).when(cmd).getSourceDomain();
doReturn(createStorageDomain()).when(cmd).getStorageDomain(any());
doReturn(cmd.getParameters().getVm()).when(cmd).getVmFromExportDomain(any());
doReturn(new VmTemplate()).when(cmd).getVmTemplate();
doReturn(new StoragePool()).when(cmd).getStoragePool();
Cluster cluster = new Cluster();
cluster.setId(cmd.getParameters().getClusterId());
doReturn(cluster).when(cmd).getCluster();
assertFalse(cmd.validate());
assertTrue(cmd.getReturnValue()
.getValidationMessages()
.contains(EngineMessage.ACTION_TYPE_FAILED_CORRUPTED_VM_SNAPSHOT_ID.toString()));
}
@Test
public void testAliasGenerationByAddVmImagesAndSnapshotsWithoutCollapse() {
cmd.getParameters().setCopyCollapse(false);
cmd.init();
for (DiskImage image : cmd.getParameters().getVm().getImages()) {
doNothing().when(cmd).saveImage(image);
doNothing().when(cmd).saveSnapshotIfNotExists(any(), eq(image));
doNothing().when(cmd).saveDiskImageDynamic(image);
}
DiskImage activeDisk = cmd.getParameters().getVm().getImages().get(1);
doNothing().when(cmd).updateImage(activeDisk);
doNothing().when(cmd).saveBaseDisk(activeDisk);
doNothing().when(cmd).updateActiveSnapshot(any());
doNothing().when(cmd).saveDiskVmElement(any(), any(), any());
cmd.addVmImagesAndSnapshots();
assertEquals("Disk alias not generated", "testVm_Disk1", activeDisk.getDiskAlias());
}
@Test
public void testCDANoCollapseNoSnapshots() {
final VM v = createVmWithNoSnapshots();
v.setName("testVm");
cmd.getParameters().setVm(v);
cmd.getParameters().setCopyCollapse(false);
cmd.init();
DiskImage activeDisk = cmd.getParameters().getVm().getImages().get(0);
doNothing().when(cmd).saveImage(activeDisk);
doNothing().when(cmd).saveDiskImageDynamic(activeDisk);
doNothing().when(cmd).saveBaseDisk(activeDisk);
doNothing().when(cmd).saveDiskVmElement(any(), any(), any());
doReturn(new Snapshot()).when(cmd).addActiveSnapshot(any());
cmd.addVmImagesAndSnapshots();
assertEquals("Disk alias not generated", "testVm_Disk1", activeDisk.getDiskAlias());
}
}