package org.ovirt.engine.core.bll; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; 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.when; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.ovirt.engine.core.bll.snapshots.SnapshotsValidator; import org.ovirt.engine.core.bll.storage.domain.IsoDomainListSynchronizer; import org.ovirt.engine.core.bll.validator.RunVmValidator; import org.ovirt.engine.core.common.action.RunVmParams; import org.ovirt.engine.core.common.action.RunVmParams.RunVmFlow; import org.ovirt.engine.core.common.businessentities.Cluster; import org.ovirt.engine.core.common.businessentities.IVdsAsyncCommand; import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotStatus; import org.ovirt.engine.core.common.businessentities.StoragePool; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.VMStatus; import org.ovirt.engine.core.common.businessentities.VmDevice; import org.ovirt.engine.core.common.businessentities.VmDeviceGeneralType; import org.ovirt.engine.core.common.businessentities.VmPayload; import org.ovirt.engine.core.common.businessentities.VmRngDevice; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.interfaces.VDSBrokerFrontend; import org.ovirt.engine.core.common.osinfo.OsRepository; import org.ovirt.engine.core.common.utils.SimpleDependencyInjector; import org.ovirt.engine.core.common.utils.VmDeviceType; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.common.vdscommands.VDSParametersBase; import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; import org.ovirt.engine.core.common.vdscommands.VdsAndVmIDVDSParametersBase; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.Version; 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.MockConfigRule; @RunWith(Theories.class) public class RunVmCommandTest extends BaseCommandTest { @ClassRule public static MockConfigRule mcr = new MockConfigRule(); /** * The command under test. */ @Spy @InjectMocks private RunVmCommand<RunVmParams> command = new RunVmCommand<>(new RunVmParams(Guid.newGuid()), null); @Mock private VDSBrokerFrontend vdsBrokerFrontend; @Mock private VmDao vmDao; @Mock private SnapshotDao snapshotDAO; @Mock private VmDeviceDao deviceDao; @Mock private IsoDomainListSynchronizer isoDomainListSynchronizer; @Mock OsRepository osRepository; @Mock CpuFlagsManagerHandler cpuFlagsManagerHandler; @Mock private SnapshotsValidator snapshotsValidator; @Spy @InjectMocks VmHandler vmHandler; private static final String ACTIVE_ISO_PREFIX = "/rhev/data-center/mnt/some_computer/f6bccab4-e2f5-4e02-bba0-5748a7bc07b6/images/11111111-1111-1111-1111-111111111111"; private static final String INACTIVE_ISO_PREFIX = ""; public static final String CPU_ID = "mock-cpu-id"; public void mockBackend() { VDSReturnValue vdsReturnValue = new VDSReturnValue(); vdsReturnValue.setReturnValue(true); when(vdsBrokerFrontend.runVdsCommand(any(VDSCommandType.class), any(VDSParametersBase.class))).thenReturn(vdsReturnValue); // Set Valid Iso Prefix setIsoPrefixVDSMethod(ACTIVE_ISO_PREFIX); // Set create Vm. setCreateVmVDSMethod(); } /** * Set create VM to return VM with status Up. */ private void setCreateVmVDSMethod() { VDSReturnValue returnValue = new VDSReturnValue(); returnValue.setReturnValue(VMStatus.Up); when(vdsBrokerFrontend.runAsyncVdsCommand(eq(VDSCommandType.Create), any(VdsAndVmIDVDSParametersBase.class), any(IVdsAsyncCommand.class))).thenReturn(returnValue); } /** * Set the Iso prefix. * * @param isoPrefix * - Valid Iso patch or blank (when the Iso is not active. */ private void setIsoPrefixVDSMethod(final String isoPrefix) { doReturn(isoPrefix).when(command).getIsoPrefix(any(Guid.class), any(Guid.class)); } @Test public void validateSimpleInitrdAndKernelName() throws Exception { String Initrd = "/boot/initrd.initrd"; String Kernel = "/boot/kernel.image"; VM vm = createVmForTesting(Initrd, Kernel); assertEquals(Initrd, vm.getInitrdUrl()); assertEquals(Kernel, vm.getKernelUrl()); } @Test public void validateIsoPrefix() throws Exception { String initrd = "initrd"; String kernel = "kernel"; VM vm = createVmForTesting(RunVmCommand.ISO_PREFIX + initrd, RunVmCommand.ISO_PREFIX + kernel); assertEquals(ACTIVE_ISO_PREFIX + "/" + initrd, vm.getInitrdUrl()); assertEquals(ACTIVE_ISO_PREFIX + "/" + kernel, vm.getKernelUrl()); } @Test public void validateIsoPrefixForKernelAndNoPrefixForInitrd() throws Exception { String initrd = "initrd"; String kernel = "kernel"; VM vm = createVmForTesting(initrd, RunVmCommand.ISO_PREFIX + kernel); assertEquals(initrd, vm.getInitrdUrl()); assertEquals(ACTIVE_ISO_PREFIX + "/" + kernel, vm.getKernelUrl()); } @Test public void validateIsoPrefixForInitrdAndNoPrefixForKernel() throws Exception { String initrd = "initrd"; String kernel = "kernel"; VM vm = createVmForTesting(RunVmCommand.ISO_PREFIX + initrd, kernel); assertEquals(ACTIVE_ISO_PREFIX + "/" + initrd, vm.getInitrdUrl()); assertEquals(kernel, vm.getKernelUrl()); } @Test public void validateIsoPrefixNameForKernelAndNullForInitrd() throws Exception { String kernel = "kernel"; VM vm = createVmForTesting(null, RunVmCommand.ISO_PREFIX + kernel); assertNull(vm.getInitrdUrl()); assertEquals(vm.getKernelUrl(), ACTIVE_ISO_PREFIX + "/" + kernel); } @Test public void validateIsoPrefixCaseSensitive() throws Exception { String initrd = "ISO://"; VM vm = createVmForTesting(initrd, null); assertEquals("", vm.getInitrdUrl()); } @Test public void validateIsoPrefixForOnlyIsoPrefixInKernelAndInitrd() throws Exception { String initrd = RunVmCommand.ISO_PREFIX; String kernelUrl = RunVmCommand.ISO_PREFIX; VM vm = createVmForTesting(initrd, kernelUrl); assertEquals("", vm.getInitrdUrl()); assertEquals("", vm.getKernelUrl()); } @Test public void checkIsoPrefixForNastyCharacters() throws Exception { String initrd = "@#$!"; String kernelUrl = " "; VM vm = createVmForTesting(initrd, kernelUrl); assertEquals(initrd, vm.getInitrdUrl()); assertEquals(kernelUrl, vm.getKernelUrl()); } @Test public void validateIsoPrefixNameForInitrdAndNullForKernel() throws Exception { String initrd = "initrd"; VM vm = createVmForTesting(RunVmCommand.ISO_PREFIX + initrd, null); assertEquals(ACTIVE_ISO_PREFIX + "/" + initrd, vm.getInitrdUrl()); assertNull(vm.getKernelUrl()); } @Test public void validateIsoPrefixWhenNoActiveIso() throws Exception { // Set Valid Iso Prefix setIsoPrefixVDSMethod(INACTIVE_ISO_PREFIX); String initrd = "initrd"; VM vm = createVmForTesting(RunVmCommand.ISO_PREFIX + initrd, null); assertEquals(INACTIVE_ISO_PREFIX + "/" + initrd, vm.getInitrdUrl()); } @Test public void validateIsoPrefixWithTrippleSlash() throws Exception { String initrd = RunVmCommand.ISO_PREFIX + "/"; VM vm = createVmForTesting(initrd, null); assertEquals(ACTIVE_ISO_PREFIX + "/", vm.getInitrdUrl()); } @Test public void validateIsoPrefixInTheMiddleOfTheInitrdAndKerenelName() throws Exception { String initrd = "initrd " + RunVmCommand.ISO_PREFIX; String kernelUrl = "kernelUrl " + RunVmCommand.ISO_PREFIX; VM vm = createVmForTesting(initrd, kernelUrl); assertEquals(initrd, vm.getInitrdUrl()); assertEquals(kernelUrl, vm.getKernelUrl()); } @Test public void validateInitrdWithSlashOnly() throws Exception { String initrd = "/"; VM vm = createVmForTesting(initrd, null); assertEquals("/", vm.getInitrdUrl()); } @Test public void validateIsoPrefixWithBackSlash() throws Exception { String initrd = "iso:\\"; VM vm = createVmForTesting(initrd, null); assertEquals("iso:\\", vm.getInitrdUrl()); } @Test public void validateBootPrefixForInitrdAndKernelImage() throws Exception { String initrd = "/boot"; String kernelImage = "/boot"; VM vm = createVmForTesting(initrd, kernelImage); assertEquals(initrd, vm.getInitrdUrl()); assertEquals(kernelImage, vm.getKernelUrl()); } @Test public void validateInitrdAndKernelImageWithOneCharacter() throws Exception { String initrd = "i"; String kernelImage = "k"; VM vm = createVmForTesting(initrd, kernelImage); assertEquals("i", vm.getInitrdUrl()); assertEquals("k", vm.getKernelUrl()); } private VM createVmForTesting(String initrd, String kernel) { mockVm(); // Set parameter command.getVm().setInitrdUrl(initrd); command.getVm().setKernelUrl(kernel); command.createVm(); return vmDao.get(command.getParameters().getVmId()); } /** * Mock a VM. */ private VM mockVm() { VM vm = new VM(); vm.setStatus(VMStatus.Down); when(vmDao.get(command.getParameters().getVmId())).thenReturn(vm); command.setCluster(new Cluster()); // Avoid referencing the unmockable static VmHandler.updateCurrentCd doNothing().when(command).updateCurrentCd(anyString()); doReturn(null).when(command).getMemoryFromActiveSnapshot(); when(snapshotDAO.exists(any(Guid.class), any(SnapshotStatus.class))).thenReturn(false); return vm; } @Before public void setUp() { mockCpuFlagsManagerHandler(); when(osRepository.isWindows(anyInt())).thenReturn(false); when(osRepository.isCpuSupported(anyInt(), any(), any())).thenReturn(true); SimpleDependencyInjector.getInstance().bind(OsRepository.class, osRepository); injectorRule.bind(CpuFlagsManagerHandler.class, cpuFlagsManagerHandler); vmHandler.init(); mockSuccessfulRunVmValidator(); doNothing().when(command).initParametersForExternalNetworks(); doReturn(Collections.emptyMap()).when(command).flushPassthroughVnicToVfMap(); mockBackend(); } private void mockCpuFlagsManagerHandler() { when(cpuFlagsManagerHandler.getCpuId(any(), any())).thenReturn(CPU_ID); } @Test public void testValidate() { final VM vm = new VM(); vm.setStatus(VMStatus.Down); command.setVm(vm); command.setStoragePool(new StoragePool()); doReturn(true).when(command).checkRngDeviceClusterCompatibility(); doReturn(true).when(command).checkPayload(any(VmPayload.class), anyString()); command.setCluster(new Cluster()); ValidateTestUtils.runAndAssertValidateSuccess(command); } @DataPoints public static VmRngDevice.Source[] rngSources = VmRngDevice.Source.values(); @DataPoints public static Set<VmRngDevice.Source>[] rngSourcesSubsets; static { HashSet<VmRngDevice.Source> sourcesEmpty = new HashSet<>(); HashSet<VmRngDevice.Source> sourcesRandom = new HashSet<>(); sourcesRandom.add(VmRngDevice.Source.RANDOM); HashSet<VmRngDevice.Source> sourcesHwRng = new HashSet<>(); sourcesHwRng.add(VmRngDevice.Source.HWRNG); HashSet<VmRngDevice.Source> sourcesAll = new HashSet<>(); sourcesAll.add(VmRngDevice.Source.RANDOM); sourcesAll.add(VmRngDevice.Source.HWRNG); rngSourcesSubsets = new Set[] { sourcesEmpty, sourcesRandom, sourcesHwRng, sourcesAll }; } @Theory public void testValidateUnsupportedRng(VmRngDevice.Source vmRngSource, Set<VmRngDevice.Source> clusterReqSources) { final VM vm = new VM(); vm.setStatus(VMStatus.Down); vm.setId(command.getVmId()); command.setVm(vm); Cluster cluster = mock(Cluster.class); when(cluster.getRequiredRngSources()).thenReturn(clusterReqSources); command.setCluster(cluster); VmRngDevice rngDevice = new VmRngDevice(); rngDevice.setSource(vmRngSource); VmDevice rngAsDevice = new VmDevice(); rngAsDevice.setSpecParams(rngDevice.getSpecParams()); when(deviceDao.getVmDeviceByVmIdTypeAndDevice(command.getVmId(), VmDeviceGeneralType.RNG, VmDeviceType.VIRTIO.getName())) .thenReturn(Collections.singletonList(rngAsDevice)); assertThat(command.checkRngDeviceClusterCompatibility(), is(clusterReqSources.contains(vmRngSource))); } @Test public void testFlowOnResume() { final VM vm = new VM(); vm.setStatus(VMStatus.Paused); command.setVm(vm); assertEquals(RunVmFlow.RESUME_PAUSE, command.getFlow()); } @Test public void testFlowOnDehibernate() { final VM vm = new VM(); vm.setStatus(VMStatus.Suspended); command.setVm(vm); assertEquals(RunVmFlow.RESUME_HIBERNATE, command.getFlow()); } @Test public void testFlowOnStatelessNoDisks() { final VM vm = new VM(); vm.setStatus(VMStatus.Down); vm.getDiskList().clear(); command.setVm(vm); command.getParameters().setRunAsStateless(true); doNothing().when(command).fetchVmDisksFromDb(); doReturn(false).when(command).isStatelessSnapshotExistsForVm(); assertEquals(RunVmFlow.CREATE_STATELESS_IMAGES, command.getFlow()); } @Test public void testFlowOnStatelessWithDisks() { final VM vm = new VM(); vm.setStatus(VMStatus.Down); vm.getDiskList().add(new DiskImage()); command.setVm(vm); command.getParameters().setRunAsStateless(true); doReturn(false).when(command).isStatelessSnapshotExistsForVm(); doNothing().when(command).fetchVmDisksFromDb(); assertEquals(RunVmFlow.CREATE_STATELESS_IMAGES, command.getFlow()); } @Test public void testFlowOnRemoveStatelessImages() { final VM vm = new VM(); vm.setStatus(VMStatus.Down); command.setVm(vm); command.getParameters().setRunAsStateless(false); command.setInternalExecution(false); doReturn(true).when(command).isStatelessSnapshotExistsForVm(); doReturn(false).when(command).isVmPartOfManualPool(); assertEquals(RunVmFlow.REMOVE_STATELESS_IMAGES, command.getFlow()); } @Test public void testFlowOnStatelessWithStatelessSnapshot() { final VM vm = new VM(); vm.setStatus(VMStatus.Down); vm.getDiskList().add(new DiskImage()); command.setVm(vm); command.getParameters().setRunAsStateless(true); doReturn(true).when(command).isStatelessSnapshotExistsForVm(); doNothing().when(command).fetchVmDisksFromDb(); assertEquals(RunVmFlow.REMOVE_STATELESS_IMAGES, command.getFlow()); } @Test public void testFlowOnStatelessExistsForManualPool() { final VM vm = new VM(); vm.setStatus(VMStatus.Down); command.setVm(vm); command.getParameters().setRunAsStateless(false); command.setInternalExecution(false); doReturn(true).when(command).isStatelessSnapshotExistsForVm(); doReturn(true).when(command).isVmPartOfManualPool(); assertEquals(RunVmFlow.RUN, command.getFlow()); } @Test public void testEffectiveEmulatedMachineWithCustomSet() { final VM vm = new VM(); final Cluster cluster = new Cluster(); cluster.setEmulatedMachine("cluster-pc-i440fx-rhel7.3.0"); vm.setCustomEmulatedMachine("testpc-i440fx-rhel7.3.0"); command.setCluster(cluster); command.setVm(vm); assertEquals("testpc-i440fx-rhel7.3.0", command.getEffectiveEmulatedMachine()); } @Test public void testEffectiveEmulatedMachineWithoutCustomSet() { final VM vm = new VM(); final Cluster cluster = new Cluster(); cluster.setEmulatedMachine("cluster-pc-i440fx-rhel7.3.0"); command.setCluster(cluster); command.setVm(vm); assertEquals("cluster-pc-i440fx-rhel7.3.0", command.getEffectiveEmulatedMachine()); } @Test public void testEffectiveEmulatedMachineCCV() { final VM vm = new VM(); final Cluster cluster = new Cluster(); cluster.setEmulatedMachine("pc-i440fx-rhel7.3.0"); vm.setCustomCompatibilityVersion(Version.v4_0); command.setCluster(cluster); command.setVm(vm); List<String> supported = Arrays.asList("pc-i440fx-rhel7.2.0", "pc-i440fx-2.1", "pseries-rhel7.2.0"); mcr.mockConfigValue(ConfigValues.ClusterEmulatedMachines, Version.v4_0, supported); assertEquals("pc-i440fx-rhel7.2.0", command.getEffectiveEmulatedMachine()); } private RunVmValidator mockSuccessfulRunVmValidator() { RunVmValidator runVmValidator = mock(RunVmValidator.class); when(runVmValidator.canRunVm( anyList(), any(StoragePool.class), anyList(), anyList(), any(Cluster.class), anyBoolean())) .thenReturn(true); doReturn(runVmValidator).when(command).getRunVmValidator(); return runVmValidator; } @Test public void testFindBestMatchForEmulateMachine() { String original = "pc-i440fx-rhel7.3.0"; String bestMatch = "pc-i440fx-rhel7.2.0"; List<String> candidates = Arrays.asList("pc-i440fx-2.1", bestMatch, "pseries-rhel7.2.0"); assertEquals(bestMatch, command.findBestMatchForEmulatedMachine(original, candidates)); } @Test public void testFindBestMatchForEmulateMachineKeepsCurrent() { String original = "pc-i440fx-rhel7.3.0"; List<String> candidates = Arrays.asList("pc-i440fx-2.1", original, "pseries-rhel7.2.0"); assertEquals(original, command.findBestMatchForEmulatedMachine(original, candidates)); } }