package org.ovirt.engine.core.bll.validator; import static java.util.stream.Collectors.toList; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; 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.utils.MockConfigRule.mockConfig; import java.util.Collections; import java.util.List; import java.util.function.Supplier; import java.util.stream.Stream; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.ovirt.engine.core.bll.DbDependentTestBase; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface; import org.ovirt.engine.core.common.businessentities.network.VnicProfile; 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.DiskInterface; import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.Version; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.dao.DiskVmElementDao; import org.ovirt.engine.core.dao.network.VmNetworkInterfaceDao; import org.ovirt.engine.core.dao.network.VnicProfileDao; import org.ovirt.engine.core.utils.MockConfigRule; import org.ovirt.engine.core.utils.RandomUtils; @RunWith(MockitoJUnitRunner.class) public class VmValidatorTest extends DbDependentTestBase { private VmValidator validator; private VM vm; private static final Version COMPAT_VERSION_FOR_CPU_SOCKET_TEST = Version.v4_0; private static final int MAX_NUM_CPUS = 16; private static final int MAX_NUM_SOCKETS = 4; private static final int MAX_NUM_CPUS_PER_SOCKET = 3; private static final int MAX_NUM_THREADS_PER_CPU = 2; @Rule public MockConfigRule mcr = new MockConfigRule( mockConfig(ConfigValues.MaxNumOfVmCpus, COMPAT_VERSION_FOR_CPU_SOCKET_TEST, MAX_NUM_CPUS), mockConfig(ConfigValues.MaxNumOfVmSockets, COMPAT_VERSION_FOR_CPU_SOCKET_TEST, MAX_NUM_SOCKETS), mockConfig(ConfigValues.MaxNumOfCpuPerSocket, COMPAT_VERSION_FOR_CPU_SOCKET_TEST, MAX_NUM_CPUS_PER_SOCKET), mockConfig(ConfigValues.MaxNumOfThreadsPerCpu, COMPAT_VERSION_FOR_CPU_SOCKET_TEST, MAX_NUM_THREADS_PER_CPU), mockConfig(ConfigValues.SriovHotPlugSupported, Version.v3_6, false), mockConfig(ConfigValues.SriovHotPlugSupported, Version.v4_0, true) ); @Mock VmNetworkInterfaceDao vmNetworkInterfaceDao; @Mock DiskVmElementDao diskVmElementDao; @Mock VnicProfileDao vnicProfileDao; @Before public void setUp() { vm = createVm(); validator = new VmValidator(vm); when(DbFacade.getInstance().getVmNetworkInterfaceDao()).thenReturn(vmNetworkInterfaceDao); when(DbFacade.getInstance().getVnicProfileDao()).thenReturn(vnicProfileDao); } private VM createVm() { VM vm = new VM(); vm.setId(Guid.newGuid()); vm.setClusterCompatibilityVersion(Version.v4_0); return vm; } private void setVmCpuValues(int numOfSockets, int cpuPerSocket, int threadsPerCpu) { vm.setNumOfSockets(numOfSockets); vm.setCpuPerSocket(cpuPerSocket); vm.setThreadsPerCpu(threadsPerCpu); } @Test public void canDisableVirtioScsiSuccess() { Disk disk = new DiskImage(); DiskVmElement dve = new DiskVmElement(disk.getId(), vm.getId()); dve.setDiskInterface(DiskInterface.VirtIO); disk.setDiskVmElements(Collections.singletonList(dve)); assertThat(validator.canDisableVirtioScsi(Collections.singletonList(disk)), isValid()); } @Test public void canDisableVirtioScsiFail() { Disk disk = new DiskImage(); DiskVmElement dve = new DiskVmElement(disk.getId(), vm.getId()); dve.setDiskInterface(DiskInterface.VirtIO_SCSI); disk.setDiskVmElements(Collections.singletonList(dve)); assertThat(validator.canDisableVirtioScsi(Collections.singletonList(disk)), failsWith(EngineMessage.CANNOT_DISABLE_VIRTIO_SCSI_PLUGGED_DISKS)); } @Test public void allPassthroughVnicsMigratableForRegularVnics() { mockVnics(createRegularNics(2)); assertThatAllPassthroughVnicsMigratable(true); } @Test public void allPassthroughVnicsMigratableForEmptyVnicList() { mockVnics(createRegularNics(0)); assertThatAllPassthroughVnicsMigratable(true); } @Test public void allPassthroughVnicsMigratable() { mockVnics(Stream.concat(createMigratablePassthroughNics(2, false), createRegularNics(3))); assertThatAllPassthroughVnicsMigratable(true); } @Test public void testVmHasPluggedDisksUsingScsiReservation() { validateVMPluggedDisksWithReservationStatus(true); } @Test public void testVmHasNoPluggedDisksUsingScsiReservation() { validateVMPluggedDisksWithReservationStatus(false); } @Test public void testVmPassesCpuSocketValidation() { setVmCpuValues(1, 1, 1); assertThat(VmValidator.validateCpuSockets(vm.getStaticData(), COMPAT_VERSION_FOR_CPU_SOCKET_TEST), isValid()); } @Test public void testVmExceedsMaxNumOfVmCpus() { setVmCpuValues(2, 3, 3); assertThat(VmValidator.validateCpuSockets(vm.getStaticData(), COMPAT_VERSION_FOR_CPU_SOCKET_TEST), failsWith(EngineMessage.ACTION_TYPE_FAILED_MAX_NUM_CPU)); } @Test public void testVmExceedsMaxNumOfSockets() { setVmCpuValues(MAX_NUM_SOCKETS + 1, 1, 1); assertThat(VmValidator.validateCpuSockets(vm.getStaticData(), COMPAT_VERSION_FOR_CPU_SOCKET_TEST), failsWith(EngineMessage.ACTION_TYPE_FAILED_MAX_NUM_SOCKETS)); } @Test public void testVmExceedsMaxNumOfCpusPerSocket() { setVmCpuValues(1, MAX_NUM_CPUS_PER_SOCKET + 1, 1); assertThat(VmValidator.validateCpuSockets(vm.getStaticData(), COMPAT_VERSION_FOR_CPU_SOCKET_TEST), failsWith(EngineMessage.ACTION_TYPE_FAILED_MAX_CPU_PER_SOCKET)); } @Test public void testVmExceedsMaxThreadsPerCpu() { setVmCpuValues(1, 1, MAX_NUM_THREADS_PER_CPU + 1); assertThat(VmValidator.validateCpuSockets(vm.getStaticData(), COMPAT_VERSION_FOR_CPU_SOCKET_TEST), failsWith(EngineMessage.ACTION_TYPE_FAILED_MAX_THREADS_PER_CPU)); } @Test public void testVmUnderMinNumOfSockets() { setVmCpuValues(1, -2, 1); assertThat(VmValidator.validateCpuSockets(vm.getStaticData(), COMPAT_VERSION_FOR_CPU_SOCKET_TEST), failsWith(EngineMessage.ACTION_TYPE_FAILED_MIN_CPU_PER_SOCKET)); } @Test public void testVmUnderMinNumOfCpusPerSocket() { setVmCpuValues(-2, 1, 1); assertThat(VmValidator.validateCpuSockets(vm.getStaticData(), COMPAT_VERSION_FOR_CPU_SOCKET_TEST), failsWith(EngineMessage.ACTION_TYPE_FAILED_MIN_NUM_SOCKETS)); } @Test public void testVmUnderMinNumOfThreadsPerCpu() { setVmCpuValues(1, 1, -2); assertThat(VmValidator.validateCpuSockets(vm.getStaticData(), COMPAT_VERSION_FOR_CPU_SOCKET_TEST), failsWith(EngineMessage.ACTION_TYPE_FAILED_MIN_THREADS_PER_CPU)); } private void validateVMPluggedDisksWithReservationStatus(boolean vmHasDisksPluggedWithReservation) { DiskVmElement dve = new DiskVmElement(null, vm.getId()); dve.setUsingScsiReservation(vmHasDisksPluggedWithReservation); when(DbFacade.getInstance().getDiskVmElementDao()).thenReturn(diskVmElementDao); when(diskVmElementDao.getAllPluggedToVm(vm.getId())).thenReturn( Collections.singletonList(dve)); if (vmHasDisksPluggedWithReservation) { // If the VM has plugged disks using ISCSI reservation the validation should fail assertThat(validator.isVmPluggedDiskNotUsingScsiReservation(), failsWith(EngineMessage.ACTION_TYPE_FAILED_VM_USES_SCSI_RESERVATION)); } else { assertThat(validator.isVmPluggedDiskNotUsingScsiReservation(), isValid()); } } @Test public void thereIsPluggedPassthroughNonMigratableVnic() { mockVnics(createNonMigratablePassthroughNics(1, true)); assertThatAllPassthroughVnicsMigratable(false); } @Test public void allPassthroughNonMigratableNicsAreUnplugged() { mockVnics(createNonMigratablePassthroughNics(2, false)); assertThatAllPassthroughVnicsMigratable(true); } @Test public void passthroughVnicsMigrationIsNotSupported() { mockVnics(createMigratablePassthroughNics(1, true)); vm.setClusterCompatibilityVersion(Version.v3_6); assertThatAllPassthroughVnicsMigratable(false); } private Stream<VmNetworkInterface> createRegularNics(int numberOfMocks) { //migratable should be ignored for regular nics, therefore its value should not matter. boolean migratable = RandomUtils.instance().nextBoolean(); return createMocks(() -> mockVnic(false, migratable, true), numberOfMocks); } private Stream<VmNetworkInterface> createNonMigratablePassthroughNics(int numberOfMocks, boolean nicIsPlugged) { return createMocks(() -> mockVnic(true, false, nicIsPlugged), numberOfMocks); } private Stream<VmNetworkInterface> createMigratablePassthroughNics(int numberOfMocks, boolean nicIsPlugged) { return createMocks(() -> mockVnic(true, true, nicIsPlugged), numberOfMocks); } private Stream<VmNetworkInterface> createMocks(Supplier<VmNetworkInterface> supplier, long count) { return Stream.generate(supplier).limit(count); } private void mockVnics(Stream<VmNetworkInterface> vnics) { List<VmNetworkInterface> vNics = vnics.collect(toList()); when(vmNetworkInterfaceDao.getAllForVm(vm.getId())).thenReturn(vNics); } private VmNetworkInterface mockVnic(boolean passthrough, boolean migratable, boolean plugged) { VmNetworkInterface vnic = mock(VmNetworkInterface.class); when(vnic.isPassthrough()).thenReturn(passthrough); when(vnic.isPlugged()).thenReturn(plugged); Guid vnicProfileId = Guid.newGuid(); when(vnic.getVnicProfileId()).thenReturn(vnicProfileId); VnicProfile profile = mock(VnicProfile.class); when(vnicProfileDao.get(vnicProfileId)).thenReturn(profile); when(profile.isMigratable()).thenReturn(migratable); return vnic; } private void assertThatAllPassthroughVnicsMigratable(boolean valid) { assertThat(validator.allPassthroughVnicsMigratable(), valid ? isValid() : failsWith(EngineMessage.ACTION_TYPE_FAILED_MIGRATION_OF_NON_MIGRATABLE_PASSTHROUGH_VNICS_IS_NOT_SUPPORTED)); } }