package org.ovirt.engine.core.bll.validator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
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 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.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.ovirt.engine.core.bll.ValidationResult;
import org.ovirt.engine.core.bll.snapshots.SnapshotsValidator;
import org.ovirt.engine.core.bll.storage.disk.DiskHandler;
import org.ovirt.engine.core.bll.validator.storage.MultipleDiskVmElementValidator;
import org.ovirt.engine.core.common.businessentities.ArchitectureType;
import org.ovirt.engine.core.common.businessentities.BootSequence;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.businessentities.storage.Disk;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.errors.EngineMessage;
import org.ovirt.engine.core.common.osinfo.OsRepository;
import org.ovirt.engine.core.common.utils.SimpleDependencyInjector;
import org.ovirt.engine.core.common.utils.customprop.VmPropertiesUtils;
import org.ovirt.engine.core.common.utils.exceptions.InitializationException;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.Version;
import org.ovirt.engine.core.dao.network.VmNicDao;
import org.ovirt.engine.core.di.InjectorRule;
import org.ovirt.engine.core.utils.MockConfigRule;
@RunWith(MockitoJUnitRunner.class)
public class RunVmValidatorTest {
private static final int _64_BIT_OS = 13;
public static final int MEMORY_LIMIT_32_BIT = 32000;
public static final int MEMORY_LIMIT_64_BIT = 640000;
@ClassRule
public static MockConfigRule mcr = new MockConfigRule(
mockConfig(ConfigValues.PredefinedVMProperties, Version.v3_6, "0"),
mockConfig(ConfigValues.UserDefinedVMProperties, Version.v3_6, "0"),
mockConfig(ConfigValues.VM32BitMaxMemorySizeInMB, "general", MEMORY_LIMIT_32_BIT),
mockConfig(ConfigValues.VM64BitMaxMemorySizeInMB, Version.v4_0, MEMORY_LIMIT_64_BIT)
);
@ClassRule
public static InjectorRule injectorRule = new InjectorRule();
@Spy
@InjectMocks
private RunVmValidator runVmValidator = new RunVmValidator();
@Mock
private SnapshotsValidator snapshotValidator;
@Mock
private DiskHandler diskHandler;
@Before
public void setup() throws InitializationException {
mockVmPropertiesUtils();
mockOsRepository();
}
@After
public void tearDown() {
SimpleDependencyInjector.getInstance().bind(OsRepository.class);
}
@Test
public void testValidEmptyCustomProerties() {
VM vm = new VM();
vm.setClusterCompatibilityVersion(Version.v4_0);
vm.setCustomProperties("");
List<String> messages = new ArrayList<>();
assertTrue(runVmValidator.validateVmProperties(vm, messages));
assertTrue(messages.isEmpty());
}
@Test
public void testWrongFormatCustomProerties() {
VM vm = new VM();
vm.setClusterCompatibilityVersion(Version.v4_0);
vm.setCustomProperties("sap_agent;"); // missing '= true'
List<String> messages = new ArrayList<>();
assertFalse(runVmValidator.validateVmProperties(vm, messages));
assertFalse(messages.isEmpty());
}
@Test
public void testNotValidCustomProerties() {
VM vm = new VM();
vm.setClusterCompatibilityVersion(Version.v4_0);
vm.setCustomProperties("property=value;");
List<String> messages = new ArrayList<>();
assertFalse(runVmValidator.validateVmProperties(vm, messages));
assertFalse(messages.isEmpty());
}
@Test
public void testValidCustomProerties() {
VM vm = new VM();
vm.setClusterCompatibilityVersion(Version.v4_0);
vm.setCustomProperties("sap_agent=true;");
List<String> messages = new ArrayList<>();
assertTrue(runVmValidator.validateVmProperties(vm, messages));
assertTrue(messages.isEmpty());
}
@Test
public void testVmFailNoDisks() {
validateResult(runVmValidator.validateBootSequence(new VM(), new ArrayList<>(), null),
false,
EngineMessage.VM_CANNOT_RUN_FROM_DISK_WITHOUT_DISK);
}
@Test
public void testVmWithDisks() {
List<Disk> disks = new ArrayList<>();
disks.add(new DiskImage());
validateResult(runVmValidator.validateBootSequence(new VM(), disks, null),
true,
null);
}
@Test
public void testNoIsoDomain() {
VM vm = new VM();
vm.setBootSequence(BootSequence.CD);
validateResult(runVmValidator.validateBootSequence(vm, new ArrayList<>(), null),
false,
EngineMessage.VM_CANNOT_RUN_FROM_CD_WITHOUT_ACTIVE_STORAGE_DOMAIN_ISO);
}
@Test
public void testNoDiskBootFromIsoDomain() {
VM vm = new VM();
vm.setBootSequence(BootSequence.CD);
validateResult(runVmValidator.validateBootSequence(vm, new ArrayList<>(), Guid.newGuid()),
true,
null);
}
@Test
public void testBootFromNetworkNoNetwork() {
VmNicDao dao = mock(VmNicDao.class);
doReturn(dao).when(runVmValidator).getVmNicDao();
VM vm = new VM();
vm.setBootSequence(BootSequence.N);
validateResult(runVmValidator.validateBootSequence(vm, new ArrayList<>(), null),
false,
EngineMessage.VM_CANNOT_RUN_FROM_NETWORK_WITHOUT_NETWORK);
}
@Test
public void canRunVmFailVmRunning() {
final VM vm = new VM();
vm.setStatus(VMStatus.Up);
validateResult(runVmValidator.vmDuringInitialization(vm),
false,
EngineMessage.ACTION_TYPE_FAILED_VM_IS_RUNNING);
}
@Test
public void canRunVmDuringInit() {
final VM vm = new VM();
doReturn(true).when(runVmValidator).isVmDuringInitiating(any(VM.class));
validateResult(runVmValidator.vmDuringInitialization(vm),
false,
EngineMessage.ACTION_TYPE_FAILED_VM_IS_RUNNING);
}
@Test
public void canRunVmNotResponding() {
final VM vm = new VM();
vm.setStatus(VMStatus.NotResponding);
validateResult(runVmValidator.vmDuringInitialization(vm),
false,
EngineMessage.ACTION_TYPE_FAILED_VM_IS_RUNNING);
}
@Test
public void testVmNotDuringInitialization() {
final VM vm = new VM();
vm.setStatus(VMStatus.Down);
doReturn(false).when(runVmValidator).isVmDuringInitiating(any(VM.class));
validateResult(runVmValidator.vmDuringInitialization(vm),
true,
null);
}
@Test
public void passNotStatelessVM() {
Random rand = new Random();
canRunVmAsStateless(rand.nextBoolean(), rand.nextBoolean(), false, false, true, null);
canRunVmAsStateless(rand.nextBoolean(), rand.nextBoolean(), false, null, true, null);
}
@Test
public void failRunStatelessSnapshotInPreview() {
Random rand = new Random();
canRunVmAsStateless(rand.nextBoolean(),
true,
true,
true,
false,
EngineMessage.ACTION_TYPE_FAILED_VM_IN_PREVIEW);
canRunVmAsStateless(rand.nextBoolean(),
true,
true,
null,
false,
EngineMessage.ACTION_TYPE_FAILED_VM_IN_PREVIEW);
canRunVmAsStateless(rand.nextBoolean(),
true,
false,
true,
false,
EngineMessage.ACTION_TYPE_FAILED_VM_IN_PREVIEW);
}
@Test
public void failRunStatelessHaVm() {
canRunVmAsStateless(true,
false,
true,
true,
false,
EngineMessage.VM_CANNOT_RUN_STATELESS_HA);
canRunVmAsStateless(true,
false,
true,
null,
false,
EngineMessage.VM_CANNOT_RUN_STATELESS_HA);
canRunVmAsStateless(true,
false,
false,
true,
false,
EngineMessage.VM_CANNOT_RUN_STATELESS_HA);
}
private void mockOsRepository() {
OsRepository osRepository = mock(OsRepository.class);
when(osRepository.get64bitOss()).thenReturn(Collections.singletonList(_64_BIT_OS));
final Map<Integer, ArchitectureType> osArchitectures =
Collections.singletonMap(_64_BIT_OS, ArchitectureType.x86_64);
when(osRepository.getOsArchitectures()).thenReturn(Collections.unmodifiableMap(osArchitectures));
SimpleDependencyInjector.getInstance().bind(OsRepository.class, osRepository);
}
@Test
public void test32BitMemoryExceedsLimit() {
VM vm = new VM();
vm.setClusterCompatibilityVersion(Version.v4_0);
vm.setVmMemSizeMb(MEMORY_LIMIT_32_BIT + 1);
validateResult(runVmValidator.validateMemorySize(vm), false, EngineMessage.ACTION_TYPE_FAILED_MEMORY_EXCEEDS_SUPPORTED_LIMIT);
}
@Test
public void test64BitMemoryExceedsLimit() {
VM vm = new VM();
vm.setClusterCompatibilityVersion(Version.v4_0);
vm.setVmMemSizeMb(MEMORY_LIMIT_64_BIT + 1);
vm.setVmOs(_64_BIT_OS);
validateResult(runVmValidator.validateMemorySize(vm), false, EngineMessage.ACTION_TYPE_FAILED_MEMORY_EXCEEDS_SUPPORTED_LIMIT);
}
@Test
public void validateDisksPassDiscardSucceeds() {
mockPassDiscardSupport(ValidationResult.VALID);
VM vm = new VM();
vm.setId(Guid.newGuid());
assertThat(runVmValidator.validateDisksPassDiscard(vm), isValid());
}
@Test
public void validateDisksPassDiscardFails() {
EngineMessage failureEngineMessage =
EngineMessage.ACTION_TYPE_FAILED_PASS_DISCARD_NOT_SUPPORTED_BY_DISK_INTERFACE;
mockPassDiscardSupport(new ValidationResult(failureEngineMessage));
VM vm = new VM();
vm.setId(Guid.newGuid());
assertThat(runVmValidator.validateDisksPassDiscard(vm), failsWith(failureEngineMessage));
}
private void mockPassDiscardSupport(ValidationResult validationResult) {
doReturn(Collections.emptyList()).when(runVmValidator).getVmDisks();
doReturn(Collections.emptyMap()).when(runVmValidator).getVmDiskVmElementMap();
MultipleDiskVmElementValidator multipleDiskVmElementValidator = mock(MultipleDiskVmElementValidator.class);
doReturn(multipleDiskVmElementValidator).when(runVmValidator).createMultipleDiskVmElementValidator(anyMap());
when(multipleDiskVmElementValidator.isPassDiscardSupportedForDestSds(anyMap())).thenReturn(validationResult);
}
private void canRunVmAsStateless(boolean autoStartUp,
final boolean vmInPreview,
boolean isVmStateless,
Boolean isStatelessParam,
boolean shouldPass,
EngineMessage message) {
Guid vmId = Guid.newGuid();
when(snapshotValidator.vmNotInPreview(vmId)).thenReturn(vmInPreview ?
new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_VM_IN_PREVIEW) : ValidationResult.VALID);
VM vm = new VM();
vm.setId(vmId);
vm.setAutoStartup(autoStartUp);
vm.setStateless(isVmStateless);
validateResult(runVmValidator.validateStatelessVm(vm, isStatelessParam),
shouldPass,
message);
}
private VmPropertiesUtils mockVmPropertiesUtils() throws InitializationException {
VmPropertiesUtils utils = spy(new VmPropertiesUtils());
doReturn("sap_agent=^(true|false)$;sndbuf=^[0-9]+$;" +
"vhost=^(([a-zA-Z0-9_]*):(true|false))(,(([a-zA-Z0-9_]*):(true|false)))*$;" +
"viodiskcache=^(none|writeback|writethrough)$").
when(utils)
.getPredefinedVMProperties(any(Version.class));
doReturn("").
when(utils)
.getUserdefinedVMProperties(any(Version.class));
doReturn(new HashSet<>(Arrays.asList(Version.v3_6, Version.v4_0))).
when(utils)
.getSupportedClusterLevels();
doReturn(utils).when(runVmValidator).getVmPropertiesUtils();
utils.init();
return utils;
}
private static void validateResult(ValidationResult validationResult, boolean isValid, EngineMessage message) {
assertEquals(isValid, validationResult.isValid());
if (!isValid) {
assertThat(validationResult, failsWith(message));
}
}
}