package org.ovirt.engine.core.bll.validator.storage;
import static org.hamcrest.CoreMatchers.both;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.ovirt.engine.core.bll.validator.ValidationResultMatchers.failsWith;
import static org.ovirt.engine.core.bll.validator.ValidationResultMatchers.isValid;
import static org.ovirt.engine.core.bll.validator.ValidationResultMatchers.replacements;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
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.VM;
import org.ovirt.engine.core.common.businessentities.VmDevice;
import org.ovirt.engine.core.common.businessentities.VmDeviceId;
import org.ovirt.engine.core.common.businessentities.VmEntityType;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.ImageStatus;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.DiskImageDao;
import org.ovirt.engine.core.dao.SnapshotDao;
import org.ovirt.engine.core.dao.VmDao;
import org.ovirt.engine.core.dao.VmDeviceDao;
import org.ovirt.engine.core.utils.RandomUtils;
import org.ovirt.engine.core.utils.RandomUtilsSeedingRule;
@RunWith(MockitoJUnitRunner.class)
public class DiskImagesValidatorTest {
@ClassRule
public static RandomUtilsSeedingRule rusr = new RandomUtilsSeedingRule();
private DiskImage disk1;
private DiskImage disk2;
private DiskImagesValidator validator;
@Mock
private VmDeviceDao vmDeviceDao;
@Mock
private VmDao vmDao;
@Mock
private SnapshotDao snapshotDao;
@Mock
private DiskImageDao diskImageDao;
@Before
public void setUp() {
disk1 = createDisk();
disk1.setDiskAlias("disk1");
disk2 = createDisk();
disk2.setDiskAlias("disk2");
validator = spy(new DiskImagesValidator(Arrays.asList(disk1, disk2)));
doReturn(vmDao).when(validator).getVmDao();
doReturn(vmDeviceDao).when(validator).getVmDeviceDao();
doReturn(snapshotDao).when(validator).getSnapshotDao();
doReturn(diskImageDao).when(validator).getDiskImageDao();
}
private static DiskImage createDisk() {
DiskImage disk = new DiskImage();
disk.setId(Guid.newGuid());
disk.setDiskAlias(RandomUtils.instance().nextString(10));
disk.setActive(true);
disk.setImageStatus(ImageStatus.OK);
ArrayList<Guid> storageDomainIds = new ArrayList<>();
storageDomainIds.add(Guid.newGuid());
disk.setStorageIds(storageDomainIds);
return disk;
}
private static String createAliasReplacements(DiskImage... disks) {
return Arrays.stream(disks).map(DiskImage::getDiskAlias).collect(Collectors.joining(", ", "$diskAliases ", ""));
}
@Test
public void diskImagesNotIllegalBothOK() {
assertThat("Neither disk is illegal", validator.diskImagesNotIllegal(), isValid());
}
@Test
public void diskImagesNotIllegalFirstIllegal() {
disk1.setImageStatus(ImageStatus.ILLEGAL);
assertThat(validator.diskImagesNotIllegal(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_DISKS_ILLEGAL)).and(replacements(hasItem(createAliasReplacements(disk1)))));
}
@Test
public void diskImagesNotIllegalSecondtIllegal() {
disk2.setImageStatus(ImageStatus.ILLEGAL);
assertThat(validator.diskImagesNotIllegal(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_DISKS_ILLEGAL)).and(replacements(hasItem(createAliasReplacements(disk2)))));
}
@Test
public void diskImagesNotIllegalBothIllegal() {
disk1.setImageStatus(ImageStatus.ILLEGAL);
disk2.setImageStatus(ImageStatus.ILLEGAL);
assertThat(validator.diskImagesNotIllegal(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_DISKS_ILLEGAL)).and(replacements
(hasItem(createAliasReplacements(disk1, disk2)))));
}
@Test
public void diskImagesAlreadyExistBothExist() {
doReturn(new DiskImage()).when(validator).getExistingDisk(any(Guid.class));
assertThat(validator.diskImagesAlreadyExist(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_IMPORT_DISKS_ALREADY_EXIST)).and(replacements
(hasItem(createAliasReplacements(disk1, disk2)))));
}
/**
* Test a case when the two validated disks exists and have a null disk alias, in that case the disk aliases in
* the CDA message should be taken from the disks existing on the setup
*/
@Test
public void diskImagesAlreadyDiskInImportWithNullAlias() {
disk1.setDiskAlias(null);
disk2.setDiskAlias(null);
DiskImage existingImage1 = new DiskImage();
existingImage1.setDiskAlias("existingDiskAlias1");
DiskImage existingImage2 = new DiskImage();
existingImage2.setDiskAlias("existingDiskAlias2");
doReturn(existingImage1).when(validator).getExistingDisk(disk1.getId());
doReturn(existingImage2).when(validator).getExistingDisk(disk2.getId());
assertThat(validator.diskImagesAlreadyExist(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_IMPORT_DISKS_ALREADY_EXIST)).and(replacements
(hasItem(createAliasReplacements(existingImage1, existingImage2)))));
}
@Test
public void diskImagesAlreadyExistOneExist() {
doReturn(new DiskImage()).when(validator).getExistingDisk(disk1.getId());
doReturn(null).when(validator).getExistingDisk(disk2.getId());
assertThat(validator.diskImagesAlreadyExist(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_IMPORT_DISKS_ALREADY_EXIST)).and(replacements
(hasItem(createAliasReplacements(disk1)))));
}
@Test
public void diskImagesAlreadyExistBothDoesntExist() {
doReturn(null).when(validator).getExistingDisk(any(Guid.class));
assertThat(validator.diskImagesAlreadyExist(), isValid());
}
@Test
public void diskImagesDontExist() {
doReturn(false).when(validator).isDiskExists(disk1.getId());
doReturn(false).when(validator).isDiskExists(disk2.getId());
assertThat(validator.diskImagesNotExist(), failsWith(EngineMessage.ACTION_TYPE_FAILED_DISKS_NOT_EXIST));
}
@Test
public void diskImagesExist() {
doReturn(true).when(validator).isDiskExists(disk1.getId());
doReturn(true).when(validator).isDiskExists(disk2.getId());
assertThat(validator.diskImagesNotExist(), isValid());
}
@Test
public void diskImagesNotLockedBothOK() {
assertThat("Neither disk is Locked", validator.diskImagesNotLocked(), isValid());
}
@Test
public void diskImagesNotLockedFirstLocked() {
disk1.setImageStatus(ImageStatus.LOCKED);
assertThat(validator.diskImagesNotLocked(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_DISKS_LOCKED)).and(replacements(hasItem(createAliasReplacements(disk1)))));
}
@Test
public void diskImagesNotLockedSecondtLocked() {
disk2.setImageStatus(ImageStatus.LOCKED);
assertThat(validator.diskImagesNotLocked(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_DISKS_LOCKED)).and(replacements(hasItem(createAliasReplacements(disk2)))));
}
@Test
public void diskImagesNotLockedBothLocked() {
disk1.setImageStatus(ImageStatus.LOCKED);
disk2.setImageStatus(ImageStatus.LOCKED);
assertThat(validator.diskImagesNotLocked(),
both(failsWith(EngineMessage.ACTION_TYPE_FAILED_DISKS_LOCKED)).and(replacements
(hasItem(createAliasReplacements(disk1, disk2)))));
}
private List<VmDevice> prepareForCheckingIfDisksSnapshotsAttachedToOtherVms() {
VmDevice device1 = createVmDeviceForDisk(disk1);
VmDevice device2 = createVmDeviceForDisk(disk2);
when(vmDeviceDao.getVmDevicesByDeviceId(disk1.getId(), null)).thenReturn(Collections.singletonList(device1));
when(vmDeviceDao.getVmDevicesByDeviceId(disk2.getId(), null)).thenReturn(Collections.singletonList(device2));
when(vmDao.get(any(Guid.class))).thenReturn(new VM());
when(snapshotDao.get(any(Guid.class))).thenReturn(new Snapshot());
return Arrays.asList(device1, device2);
}
@Test
public void diskImagesHasDerivedDisksNoStorageDomainSpecifiedSuccess() {
disk1.setVmEntityType(VmEntityType.TEMPLATE);
assertThat(validator.diskImagesHaveNoDerivedDisks(null),
isValid());
}
@Test
public void diskImagesHasDerivedDisksNoStorageDomainSpecifiedFailure() {
disk1.setVmEntityType(VmEntityType.TEMPLATE);
when(diskImageDao.getAllSnapshotsForParent(disk1.getImageId())).thenReturn(Collections.singletonList(disk2));
assertThat(validator.diskImagesHaveNoDerivedDisks(null),
failsWith(EngineMessage.ACTION_TYPE_FAILED_DETECTED_DERIVED_DISKS));
}
@Test
public void diskImagesHasDerivedDisksOnStorageDomain() {
Guid storageDomainId = Guid.Empty;
disk1.setVmEntityType(VmEntityType.TEMPLATE);
ArrayList<Guid> storageDomainIds = new ArrayList<>();
storageDomainIds.add(storageDomainId);
disk2.setStorageIds(storageDomainIds);
when(diskImageDao.getAllSnapshotsForParent(disk1.getImageId())).thenReturn(Collections.singletonList(disk2));
assertThat(validator.diskImagesHaveNoDerivedDisks(storageDomainId),
failsWith(EngineMessage.ACTION_TYPE_FAILED_DETECTED_DERIVED_DISKS));
}
@Test
public void diskImagesNoDerivedDisksOnStorageDomain() {
disk1.setVmEntityType(VmEntityType.TEMPLATE);
ArrayList<Guid> storageDomainIds = new ArrayList<>();
storageDomainIds.add(Guid.newGuid());
disk2.setStorageIds(storageDomainIds);
when(diskImageDao.getAllSnapshotsForParent(disk1.getImageId())).thenReturn(Collections.singletonList(disk2));
assertThat(validator.diskImagesHaveNoDerivedDisks(Guid.Empty), isValid());
}
@Test
public void isVolumeChainLimitExceededInvalid() {
List<DiskImage> diskImages = new ArrayList<>();
diskImages.add(new DiskImage());
diskImages.add(new DiskImage());
diskImages.add(new DiskImage());
when(diskImageDao.getAllSnapshotsForImageGroup(disk1.getId())).thenReturn(diskImages);
doReturn(3).when(validator).getMaxVolumeChain();
assertThat(validator.diskImagesHaveNotExceededMaxNumberOfVolumesInImageChain(),
failsWith(EngineMessage.ACTION_TYPE_FAILED_MAXIMUM_LIMIT_OF_VOLUMES_IN_CHAIN));
}
@Test
public void isVolumeChainLimitExceeded() {
List<DiskImage> diskImages = new ArrayList<>();
diskImages.add(new DiskImage());
diskImages.add(new DiskImage());
diskImages.add(new DiskImage());
when(diskImageDao.getAllSnapshotsForImageGroup(disk1.getId())).thenReturn(diskImages);
doReturn(4).when(validator).getMaxVolumeChain();
assertThat(validator.diskImagesHaveNotExceededMaxNumberOfVolumesInImageChain(), isValid());
}
@Test
public void diskImagesSnapshotsNotAttachedToOtherVmsOneDiskSnapshotAttached() {
List<VmDevice> createdDevices = prepareForCheckingIfDisksSnapshotsAttachedToOtherVms();
createdDevices.get(1).setSnapshotId(Guid.newGuid());
assertThat(validator.diskImagesSnapshotsNotAttachedToOtherVms(false),
failsWith(EngineMessage.ACTION_TYPE_FAILED_VM_DISK_SNAPSHOT_IS_ATTACHED_TO_ANOTHER_VM));
verify(snapshotDao, times(1)).get(createdDevices.get(1).getSnapshotId());
verify(snapshotDao, never()).get(createdDevices.get(0).getSnapshotId());
}
@Test
public void diskImagesSnapshotsNotAttachedToOtherVmsNoDiskSnapshotsAttached() {
List<VmDevice> createdDevices = prepareForCheckingIfDisksSnapshotsAttachedToOtherVms();
assertThat(validator.diskImagesSnapshotsNotAttachedToOtherVms(false), isValid());
verify(snapshotDao, never()).get(createdDevices.get(1).getSnapshotId());
verify(snapshotDao, never()).get(createdDevices.get(0).getSnapshotId());
}
private Map<Guid, Set<Guid>> createDiskValidDomainsMap(DiskImage... diskImages) {
Map<Guid, Set<Guid>> toReturn = new HashMap<>();
for (DiskImage diskImage : diskImages) {
toReturn.put(diskImage.getId(), Collections.singleton(diskImage.getStorageIds().get(0)));
}
return toReturn;
}
private Map<Guid, StorageDomain> createStorageDomainsMap(DiskImage... diskImages) {
Map<Guid, StorageDomain> toReturn = new HashMap<>();
for (DiskImage diskImage : diskImages) {
Guid id = diskImage.getStorageIds().get(0);
StorageDomain domain = new StorageDomain();
domain.setId(id);
domain.setStatus(StorageDomainStatus.Active);
toReturn.put(id, domain);
}
return toReturn;
}
@Test
public void diskImagesOnAnyApplicableDomainsValidDomains() {
Map<Guid, Set<Guid>> validDomainsForDisk = createDiskValidDomainsMap(disk1, disk2);
Map<Guid, StorageDomain> storageDomainMap = createStorageDomainsMap(disk1, disk2);
assertThat(validator.diskImagesOnAnyApplicableDomains(validDomainsForDisk,
storageDomainMap,
EngineMessage.ACTION_TYPE_FAILED_NO_VALID_DOMAINS_STATUS_FOR_TEMPLATE_DISKS,
EnumSet.of(StorageDomainStatus.Active)), isValid());
}
@Test
public void diskImagesOnAnyApplicableDomainsNoValidDomainsForAllDisks() {
Map<Guid, Set<Guid>> validDomainsForDisk = new HashMap<>();
validDomainsForDisk.put(disk1.getId(), Collections.emptySet());
validDomainsForDisk.put(disk2.getId(), Collections.emptySet());
Map<Guid, StorageDomain> storageDomainMap = createStorageDomainsMap(disk1, disk2);
assertThat(validator.diskImagesOnAnyApplicableDomains(validDomainsForDisk,
storageDomainMap,
EngineMessage.ACTION_TYPE_FAILED_NO_VALID_DOMAINS_STATUS_FOR_TEMPLATE_DISKS,
EnumSet.of(StorageDomainStatus.Active)), failsWith(EngineMessage.ACTION_TYPE_FAILED_NO_VALID_DOMAINS_STATUS_FOR_TEMPLATE_DISKS));
}
@Test
public void diskImagesOnAnyApplicableDomainsNoValidDomainsForOneDisk() {
Map<Guid, Set<Guid>> validDomainsForDisk = createDiskValidDomainsMap(disk1);
validDomainsForDisk.put(disk2.getId(), Collections.emptySet());
Map<Guid, StorageDomain> storageDomainMap = createStorageDomainsMap(disk1, disk2);
assertThat(validator.diskImagesOnAnyApplicableDomains(validDomainsForDisk,
storageDomainMap,
EngineMessage.ACTION_TYPE_FAILED_NO_VALID_DOMAINS_STATUS_FOR_TEMPLATE_DISKS,
EnumSet.of(StorageDomainStatus.Active)), failsWith(EngineMessage.ACTION_TYPE_FAILED_NO_VALID_DOMAINS_STATUS_FOR_TEMPLATE_DISKS));
}
private VmDevice createVmDeviceForDisk(DiskImage disk) {
VmDevice device = new VmDevice();
device.setId(new VmDeviceId(null, disk.getId()));
return device;
}
}