package org.ovirt.engine.core.bll.exportimport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
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.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
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.ClassRule;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.ovirt.engine.core.bll.Backend;
import org.ovirt.engine.core.bll.BaseCommandTest;
import org.ovirt.engine.core.bll.ValidateTestUtils;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.context.EngineContext;
import org.ovirt.engine.core.bll.validator.VmNicMacsUtils;
import org.ovirt.engine.core.common.action.ImportVmTemplateParameters;
import org.ovirt.engine.core.common.businessentities.BusinessEntitiesDefinitions;
import org.ovirt.engine.core.common.businessentities.StorageDomain;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatic;
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.VmDevice;
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.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.EngineMessage;
import org.ovirt.engine.core.common.osinfo.OsRepository;
import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.utils.SimpleDependencyInjector;
import org.ovirt.engine.core.common.utils.ValidationUtils;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.StorageDomainDao;
import org.ovirt.engine.core.dao.StorageDomainStaticDao;
import org.ovirt.engine.core.dao.StoragePoolDao;
import org.ovirt.engine.core.dao.VmTemplateDao;
import org.ovirt.engine.core.utils.MockConfigRule;
public class ImportVmTemplateCommandTest extends BaseCommandTest {
@Mock
private VmNicMacsUtils vmNicMacsUtils;
@Mock
private Backend backend;
@Mock
private StorageDomainDao storageDomainDao;
@Mock
private StorageDomainStaticDao storageDomainStaticDao;
@Mock
private StoragePoolDao storagePoolDao;
@Mock
private VmTemplateDao vmTemplateDao;
@Mock
private OsRepository osRepository;
@ClassRule
public static MockConfigRule mcr = new MockConfigRule();
@Before
public void injectOsRepository() {
SimpleDependencyInjector.getInstance().bind(OsRepository.class, osRepository);
}
@Spy
@InjectMocks
private ImportVmTemplateCommand command =
new ImportVmTemplateCommand(createParameters(), CommandContext.createContext(""));
@Test
public void insufficientDiskSpace() {
// The following is enough since the validation is mocked out anyway. Just want to make sure the flow in CDA is correct.
// Full test for the scenarios is done in the inherited class.
setupVolumeFormatAndTypeTest(VolumeFormat.RAW, VolumeType.Preallocated, StorageType.NFS);
doReturn(false).when(command).validateSpaceRequirements(anyList());
assertFalse(command.validate());
}
@Test
public void validVolumeFormatAndTypeCombinations() throws Exception {
assertValidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Preallocated, StorageType.NFS);
assertValidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Sparse, StorageType.NFS);
assertValidVolumeInfoCombination(VolumeFormat.COW, VolumeType.Sparse, StorageType.NFS);
assertValidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Preallocated, StorageType.ISCSI);
assertValidVolumeInfoCombination(VolumeFormat.COW, VolumeType.Sparse, StorageType.ISCSI);
assertValidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Sparse, StorageType.ISCSI);
assertValidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Preallocated, StorageType.FCP);
assertValidVolumeInfoCombination(VolumeFormat.COW, VolumeType.Sparse, StorageType.FCP);
assertValidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Sparse, StorageType.FCP);
assertValidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Preallocated, StorageType.LOCALFS);
assertValidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Sparse, StorageType.LOCALFS);
assertValidVolumeInfoCombination(VolumeFormat.COW, VolumeType.Sparse, StorageType.LOCALFS);
}
@Test
public void invalidVolumeFormatAndTypeCombinations() throws Exception {
assertInvalidVolumeInfoCombination(VolumeFormat.COW, VolumeType.Preallocated, StorageType.NFS);
assertInvalidVolumeInfoCombination(VolumeFormat.COW, VolumeType.Preallocated, StorageType.ISCSI);
assertInvalidVolumeInfoCombination(VolumeFormat.COW, VolumeType.Preallocated, StorageType.FCP);
assertInvalidVolumeInfoCombination(VolumeFormat.COW, VolumeType.Preallocated, StorageType.LOCALFS);
assertInvalidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Unassigned, StorageType.NFS);
assertInvalidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Unassigned, StorageType.ISCSI);
assertInvalidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Unassigned, StorageType.FCP);
assertInvalidVolumeInfoCombination(VolumeFormat.RAW, VolumeType.Unassigned, StorageType.LOCALFS);
assertInvalidVolumeInfoCombination(VolumeFormat.Unassigned, VolumeType.Preallocated, StorageType.NFS);
assertInvalidVolumeInfoCombination(VolumeFormat.Unassigned, VolumeType.Preallocated, StorageType.ISCSI);
assertInvalidVolumeInfoCombination(VolumeFormat.Unassigned, VolumeType.Preallocated, StorageType.FCP);
assertInvalidVolumeInfoCombination(VolumeFormat.Unassigned, VolumeType.Preallocated, StorageType.LOCALFS);
}
@Test
public void testValidateUniqueTemplateNameInDC() {
setupVolumeFormatAndTypeTest(VolumeFormat.RAW, VolumeType.Preallocated, StorageType.NFS);
doReturn(true).when(command).isVmTemplateWithSameNameExist();
ValidateTestUtils.runAndAssertValidateFailure(command,
EngineMessage.VM_CANNOT_IMPORT_TEMPLATE_NAME_EXISTS);
}
private void assertValidVolumeInfoCombination(VolumeFormat volumeFormat,
VolumeType volumeType,
StorageType storageType) {
setupVolumeFormatAndTypeTest(volumeFormat, volumeType, storageType);
ValidateTestUtils.runAndAssertValidateSuccess(command);
}
private void assertInvalidVolumeInfoCombination(VolumeFormat volumeFormat,
VolumeType volumeType,
StorageType storageType) {
setupVolumeFormatAndTypeTest(volumeFormat, volumeType, storageType);
ValidateTestUtils.runAndAssertValidateFailure(
command, EngineMessage.ACTION_TYPE_FAILED_DISK_CONFIGURATION_NOT_SUPPORTED);
}
/**
* Prepare a command for testing the given volume format and type combination.
*
* @param volumeFormat
* The volume format of the "imported" image.
* @param volumeType
* The volume type of the "imported" image.
* @param storageType
* The target domain's storage type.
*/
private void setupVolumeFormatAndTypeTest(
VolumeFormat volumeFormat, VolumeType volumeType, StorageType storageType) {
doReturn(false).when(command).isVmTemplateWithSameNameExist();
doReturn(true).when(command).isClusterCompatible();
doReturn(true).when(command).validateNoDuplicateDiskImages(any(Iterable.class));
mockGetTemplatesFromExportDomainQuery(volumeFormat, volumeType);
mockStorageDomainStatic(storageType);
mockStoragePool();
mockStorageDomains();
doReturn(true).when(command).setAndValidateDiskProfiles();
doReturn(true).when(command).setAndValidateCpuProfile();
doReturn(true).when(command).validateSpaceRequirements(anyList());
}
private void mockStorageDomains() {
final ImportVmTemplateParameters parameters = command.getParameters();
final StorageDomain srcDomain = new StorageDomain();
srcDomain.setStorageDomainType(StorageDomainType.ImportExport);
srcDomain.setStatus(StorageDomainStatus.Active);
when(storageDomainDao.getForStoragePool(parameters.getSourceDomainId(), parameters.getStoragePoolId()))
.thenReturn(srcDomain);
final StorageDomain destDomain = new StorageDomain();
destDomain.setStorageDomainType(StorageDomainType.Data);
destDomain.setUsedDiskSize(0);
destDomain.setAvailableDiskSize(1000);
destDomain.setStatus(StorageDomainStatus.Active);
when(storageDomainDao.getForStoragePool(parameters.getDestDomainId(), parameters.getStoragePoolId()))
.thenReturn(destDomain);
}
private void mockStoragePool() {
final StoragePool pool = new StoragePool();
pool.setId(command.getParameters().getStoragePoolId());
when(storagePoolDao.get(any(Guid.class))).thenReturn(pool);
}
private void mockGetTemplatesFromExportDomainQuery(VolumeFormat volumeFormat, VolumeType volumeType) {
final VdcQueryReturnValue result = new VdcQueryReturnValue();
Map<VmTemplate, List<DiskImage>> resultMap = new HashMap<>();
DiskImage image = new DiskImage();
image.setActualSizeInBytes(2);
image.setVolumeFormat(volumeFormat);
image.setVolumeType(volumeType);
resultMap.put(new VmTemplate(), Collections.singletonList(image));
result.setReturnValue(resultMap);
result.setSucceeded(true);
when(command.getBackend().runInternalQuery(eq(VdcQueryType.GetTemplatesFromExportDomain),
any(VdcQueryParametersBase.class), any(EngineContext.class))).thenReturn(result);
}
private void mockStorageDomainStatic(StorageType storageType) {
final StorageDomainStatic domain = new StorageDomainStatic();
domain.setStorageType(storageType);
when(storageDomainStaticDao.get(any(Guid.class))).thenReturn(domain);
}
protected ImportVmTemplateParameters createParameters() {
VmTemplate t = new VmTemplate();
t.setName("testTemplate");
return new ImportVmTemplateParameters(Guid.newGuid(), Guid.newGuid(), Guid.newGuid(), Guid.newGuid(), t);
}
private static String createDataSize(int size) {
StringBuilder sb = new StringBuilder(size);
for (int i = 0; i < size; i++) {
sb.append('a');
}
return sb.toString();
}
private final String string100 = createDataSize(100);
private final String string500 = createDataSize(500);
@Test
public void testValidateNameSizeImportAsCloned() {
checkTemplateName(true, string500);
}
@Test
public void testDoNotValidateNameSizeImport() {
checkTemplateName(false, string500);
}
@Test
public void testValidateNameSpecialCharImportAsCloned() {
checkTemplateName(true, "vm_$%$#%#$");
}
@Test
public void testDoNotValidateNameSpecialCharImport() {
checkTemplateName(false, "vm_$%$#%#$");
}
private void checkTemplateName(boolean isImportAsNewEntity, String name) {
command.getParameters().getVmTemplate().setName(name);
command.getParameters().setImportAsNewEntity(isImportAsNewEntity);
Set<ConstraintViolation<ImportVmTemplateParameters>> validate =
ValidationUtils.getValidator().validate(command.getParameters(),
command.getValidationGroups().toArray(new Class<?>[0]));
assertNotEquals(validate.isEmpty(), isImportAsNewEntity);
}
/**
* Checking that other fields in VmTemplate aren't get validated in Import or
* import as cloned.
* testing that 100 char String is set in domain field
*/
@Test
public void testOtherFieldsNotValidatedInImport() {
assertFalse(BusinessEntitiesDefinitions.GENERAL_DOMAIN_SIZE > string100.length());
command.getParameters().setImportAsNewEntity(true);
Set<ConstraintViolation<ImportVmTemplateParameters>> validate =
ValidationUtils.getValidator().validate(command.getParameters(),
command.getValidationGroups().toArray(new Class<?>[0]));
assertTrue(validate.isEmpty());
}
@Test
public void testOtherFieldsNotValidatedInImportNotNewEntity() {
command.getParameters().setImportAsNewEntity(false);
Set<ConstraintViolation<ImportVmTemplateParameters>> validate =
ValidationUtils.getValidator().validate(command.getParameters(),
command.getValidationGroups().toArray(new Class<?>[0]));
assertTrue(validate.isEmpty());
}
/**
* Checking that managed device are sync with the new Guids of disk
*/
@Test
public void testManagedDeviceSyncWithNewDiskId() {
DiskImage disk = new DiskImage();
disk.setStorageIds(new ArrayList<>());
Map<Guid, VmDevice> managedDevices = new HashMap<>();
managedDevices.put(disk.getId(), new VmDevice());
Guid beforeOldDiskId = disk.getId();
command.generateNewDiskId(disk);
command.updateManagedDeviceMap(disk, managedDevices);
Guid oldDiskId = command.getNewDiskIdForDisk(disk.getId()).getId();
assertEquals("The old disk id should be similar to the value at the newDiskIdForDisk.", beforeOldDiskId, oldDiskId);
assertNotNull("The manged deivce should return the disk device by the new key", managedDevices.get(disk.getId()));
assertNull("The manged deivce should not return the disk device by the old key", managedDevices.get(beforeOldDiskId));
}
}