package org.ovirt.engine.core.bll.storage.disk; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; 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 java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; 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.BaseCommandTest; import org.ovirt.engine.core.bll.ValidateTestUtils; import org.ovirt.engine.core.bll.ValidationResult; import org.ovirt.engine.core.bll.VmCommand; import org.ovirt.engine.core.bll.quota.QuotaManager; import org.ovirt.engine.core.bll.snapshots.SnapshotsValidator; import org.ovirt.engine.core.bll.utils.VmDeviceUtils; import org.ovirt.engine.core.bll.validator.storage.DiskValidator; import org.ovirt.engine.core.bll.validator.storage.DiskVmElementValidator; import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator; import org.ovirt.engine.core.common.action.AddDiskParameters; import org.ovirt.engine.core.common.businessentities.Quota; import org.ovirt.engine.core.common.businessentities.StorageDomain; import org.ovirt.engine.core.common.businessentities.StorageDomainStatus; import org.ovirt.engine.core.common.businessentities.StoragePool; import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMap; import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMapId; import org.ovirt.engine.core.common.businessentities.StoragePoolStatus; import org.ovirt.engine.core.common.businessentities.StorageServerConnections; import org.ovirt.engine.core.common.businessentities.VDS; 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.businessentities.storage.DiskInterface; import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement; import org.ovirt.engine.core.common.businessentities.storage.LUNs; import org.ovirt.engine.core.common.businessentities.storage.LunDisk; import org.ovirt.engine.core.common.businessentities.storage.ScsiGenericIO; 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.config.Config; 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.compat.Guid; import org.ovirt.engine.core.compat.Version; import org.ovirt.engine.core.dao.DiskLunMapDao; import org.ovirt.engine.core.dao.DiskVmElementDao; import org.ovirt.engine.core.dao.QuotaDao; import org.ovirt.engine.core.dao.StorageDomainDao; import org.ovirt.engine.core.dao.StoragePoolDao; import org.ovirt.engine.core.dao.StoragePoolIsoMapDao; import org.ovirt.engine.core.dao.VdsDao; import org.ovirt.engine.core.dao.VmDao; import org.ovirt.engine.core.dao.network.VmNicDao; import org.ovirt.engine.core.utils.MockConfigRule; public class AddDiskCommandTest extends BaseCommandTest { private static final int MAX_PCI_SLOTS = 26; private static final Guid vmId = Guid.newGuid(); @ClassRule public static MockConfigRule mcr = new MockConfigRule(); @Mock private DiskVmElementDao diskVmElementDao; @Mock private StorageDomainDao storageDomainDao; @Mock private StoragePoolIsoMapDao storagePoolIsoMapDao; @Mock private VmNicDao vmNicDao; @Mock private DiskLunMapDao diskLunMapDao; @Mock private VmDao vmDao; @Mock private StoragePoolDao storagePoolDao; @Mock private VdsDao vdsDao; @Mock private QuotaDao quotaDao; @Mock private OsRepository osRepository; @Mock private VmDeviceUtils vmDeviceUtils; @Mock private DiskVmElementValidator diskVmElementValidator; @Mock private QuotaManager quotaManager; @Mock private SnapshotsValidator snapshotsValidator; /** * The command under test. */ @Spy @InjectMocks private AddDiskCommand<AddDiskParameters> command = new AddDiskCommand<>(createParameters(), null); @Test public void validateSucceedsOnDiskDomainCheckWhenNoDisks() throws Exception { Guid storageId = Guid.newGuid(); initializeCommand(storageId); mockEntities(storageId); mockVm(); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void validateFailWithUnsupportedDiskInterface() throws Exception { Guid storageId = Guid.newGuid(); initializeCommand(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockMaxPciSlots(); mockVm(); when(diskVmElementValidator.isDiskInterfaceSupported(any())).thenReturn(new ValidationResult(EngineMessage.ACTION_TYPE_DISK_INTERFACE_UNSUPPORTED)); when(command.getDiskVmElementValidator(any(Disk.class), any(DiskVmElement.class))).thenReturn(diskVmElementValidator); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.ACTION_TYPE_DISK_INTERFACE_UNSUPPORTED); } @Test public void validateSucceedsOnDiskDomainCheckWhenEmptyStorageGuidInParams() throws Exception { initializeCommand(Guid.Empty); Guid storageId = Guid.newGuid(); mockVmWithDisk(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockInterfaceList(); mockMaxPciSlots(); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void validateSucceedsOnDiskDomainCheckWhenStorageGuidInParamsMatches() throws Exception { Guid storageId = Guid.newGuid(); initializeCommand(storageId); mockVmWithDisk(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockInterfaceList(); mockMaxPciSlots(); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void validateSucceedsOnDiskDomainCheckWhenStorageGuidInParamsMismatches() throws Exception { Guid storageId = Guid.newGuid(); initializeCommand(storageId); mockVmWithDisk(Guid.newGuid()); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockInterfaceList(); mockMaxPciSlots(); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void validateFailsOnNullDiskInterface() throws Exception { Guid storageId = Guid.newGuid(); DiskImage image = new DiskImage(); image.setVolumeFormat(VolumeFormat.COW); image.setVolumeType(VolumeType.Preallocated); command.getParameters().setDiskInfo(image); command.getParameters().setStorageDomainId(storageId); command.getParameters().getDiskVmElement().setDiskInterface(null); assertFalse(command.validateInputs()); ValidateTestUtils.assertValidationMessages ("Wrong validation method", command, EngineMessage.VALIDATION_DISK_INTERFACE_NOT_NULL); } @Test public void validateSpaceValidationSucceeds() { Guid storageId = Guid.newGuid(); initializeCommand(storageId, VolumeType.Preallocated); mockEntities(storageId); mockVm(); doReturn(mockStorageDomainValidator()).when(command).createStorageDomainValidator(); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void validateSpaceValidationFails() { Guid storageId = Guid.newGuid(); initializeCommand(storageId, VolumeType.Sparse); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockMaxPciSlots(); mockVm(); doReturn(mockStorageDomainValidatorWithoutSpace()).when(command).createStorageDomainValidator(); ValidateTestUtils.runAndAssertValidateFailure (command, EngineMessage.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_STORAGE_DOMAIN); } /** * Validate should succeed when the requested disk space is less or equal than 'MaxBlockDiskSize' */ @Test public void validateMaxBlockDiskSizeCheckSucceeds() { Guid storageId = Guid.newGuid(); DiskImage disk = new DiskImage(); disk.setSizeInGigabytes(Config.<Integer>getValue(ConfigValues.MaxBlockDiskSize)); command.getParameters().setStorageDomainId(storageId); command.getParameters().setDiskInfo(disk); mockStorageDomain(storageId, StorageType.ISCSI); mockStoragePoolIsoMap(); mockInterfaceList(); mockMaxPciSlots(); mockVm(); ValidateTestUtils.runAndAssertValidateSuccess(command); } /** * Validate should fail when the requested disk space is larger than 'MaxBlockDiskSize' */ @Test public void validateMaxBlockDiskSizeCheckFails() { Guid storageId = Guid.newGuid(); DiskImage disk = new DiskImage(); disk.setSizeInGigabytes(Config.<Integer>getValue(ConfigValues.MaxBlockDiskSize) * 2L); command.getParameters().setStorageDomainId(storageId); command.getParameters().setDiskInfo(disk); mockStorageDomain(storageId, StorageType.ISCSI); mockStoragePoolIsoMap(); mockMaxPciSlots(); mockVm(); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.ACTION_TYPE_FAILED_DISK_MAX_SIZE_EXCEEDED); } /** * Validate should succeed when creating a Shareable Disk with RAW volume format */ @Test public void validateShareableDiskVolumeFormatSucceeds() { DiskImage image = new DiskImage(); image.setShareable(true); image.setVolumeFormat(VolumeFormat.RAW); Guid storageId = Guid.newGuid(); command.getParameters().setDiskInfo(image); command.getParameters().setStorageDomainId(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockInterfaceList(); mockMaxPciSlots(); mockVm(); ValidateTestUtils.runAndAssertValidateSuccess(command); } /** * Validate should fail when creating a Shareable Disk with COW volume format */ @Test public void validateShareableDiskVolumeFormatFails() { DiskImage image = new DiskImage(); image.setShareable(true); image.setVolumeFormat(VolumeFormat.COW); Guid storageId = Guid.newGuid(); command.getParameters().setDiskInfo(image); command.getParameters().setStorageDomainId(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockMaxPciSlots(); mockVm(); ValidateTestUtils.runAndAssertValidateFailure (command, EngineMessage.SHAREABLE_DISK_IS_NOT_SUPPORTED_BY_VOLUME_FORMAT); } @Test public void validateShareableDiskOnGlusterFails() { DiskImage image = new DiskImage(); image.setShareable(true); image.setVolumeFormat(VolumeFormat.RAW); Guid storageId = Guid.newGuid(); command.getParameters().setDiskInfo(image); command.getParameters().setStorageDomainId(storageId); mockVm(); mockStorageDomain(storageId, StorageType.GLUSTERFS); mockStoragePoolIsoMap(); mockMaxPciSlots(); ValidateTestUtils.runAndAssertValidateFailure (command, EngineMessage.ACTION_TYPE_FAILED_SHAREABLE_DISKS_NOT_SUPPORTED_ON_GLUSTER_DOMAIN); } /** * Initialize the command for testing, using the given storage domain id for the parameters. * * @param storageId * Storage domain id for the parameters */ private void initializeCommand(Guid storageId) { initializeCommand(storageId, VolumeType.Unassigned); } private void initializeCommand(Guid storageId, VolumeType volumeType) { AddDiskParameters parameters = command.getParameters(); DiskImage disk = new DiskImage(); disk.setVolumeType(volumeType); parameters.setDiskInfo(disk); command.getParameters().setStorageDomainId(storageId); } @Before public void initializeMocks() { doNothing().when(command).updateDisksFromDb(); doReturn(true).when(command).checkImageConfiguration(); doReturn(false).when(command).isVirtioScsiControllerAttached(any(Guid.class)); doReturn(false).when(command).hasWatchdog(any(Guid.class)); doReturn(false).when(command).isBalloonEnabled(any(Guid.class)); doReturn(false).when(command).isSoundDeviceEnabled(any(Guid.class)); doReturn(true).when(command).setAndValidateDiskProfiles(); doReturn(true).when(command).validateQuota(); doAnswer(invocation -> invocation.getArguments()[0] != null ? invocation.getArguments()[0] : Guid.newGuid()) .when(quotaManager).getDefaultQuotaIfNull(any(Guid.class), any(Guid.class)); SimpleDependencyInjector.getInstance().bind(OsRepository.class, osRepository); injectorRule.bind(VmDeviceUtils.class, vmDeviceUtils); } /** * Mock a VM that has a disk. * * @param storageId * Storage domain id of the disk. */ private void mockVmWithDisk(Guid storageId) { DiskImage image = new DiskImage(); image.setId(Guid.newGuid()); image.setStorageIds(new ArrayList<>(Collections.singletonList(storageId))); DiskVmElement dve = new DiskVmElement(image.getId(), vmId); image.setDiskVmElements(Collections.singletonList(dve)); mockVm().getDiskMap().put(image.getId(), image); } /** * Mock a good {@link StoragePoolIsoMap}. */ private void mockStoragePoolIsoMap() { StoragePoolIsoMap spim = new StoragePoolIsoMap(); when(storagePoolIsoMapDao.get(any(StoragePoolIsoMapId.class))).thenReturn(spim); } protected void mockInterfaceList() { SimpleDependencyInjector.getInstance().bind(OsRepository.class, osRepository); List<String> diskInterfaces = Arrays.asList("IDE", "VirtIO", "VirtIO_SCSI"); when(osRepository.getDiskInterfaces(anyInt(), any())).thenReturn(diskInterfaces); } /** * Mock a VM. */ private VM mockVm() { VM vm = new VM(); vm.setId(vmId); vm.setStatus(VMStatus.Down); vm.setStoragePoolId(Guid.newGuid()); when(vmDao.get(command.getParameters().getVmId())).thenReturn(vm); return vm; } private static StorageDomainValidator mockStorageDomainValidatorWithoutSpace() { StorageDomainValidator storageDomainValidator = mockStorageDomainValidator(); when(storageDomainValidator.hasSpaceForNewDisk(any(DiskImage.class))).thenReturn( new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_DISK_SPACE_LOW_ON_STORAGE_DOMAIN)); return storageDomainValidator; } private static StorageDomainValidator mockStorageDomainValidator() { return mock(StorageDomainValidator.class); } private DiskValidator spyDiskValidator(Disk disk) { DiskValidator diskValidator = spy(new DiskValidator(disk)); doReturn(diskValidator).when(command).getDiskValidator(disk); return diskValidator; } private void mockMaxPciSlots() { SimpleDependencyInjector.getInstance().bind(OsRepository.class, osRepository); doReturn(MAX_PCI_SLOTS).when(osRepository).getMaxPciDevices(anyInt(), any()); } private VDS mockVds() { Guid vdsId = Guid.newGuid(); VDS vds = new VDS(); vds.setId(vdsId); when(vdsDao.get(vdsId)).thenReturn(vds); return vds; } /** * Mock a {@link StoragePool}. */ private StoragePool mockStoragePool() { Guid storagePoolId = Guid.newGuid(); StoragePool storagePool = new StoragePool(); storagePool.setId(storagePoolId); storagePool.setStatus(StoragePoolStatus.Up); when(storagePoolDao.get(storagePoolId)).thenReturn(storagePool); return storagePool; } /** * Mock a {@link StorageDomain}. * * @param storageId * Id of the domain. */ private StorageDomain mockStorageDomain(Guid storageId) { return mockStorageDomain(storageId, StorageType.UNKNOWN); } private StorageDomain mockStorageDomain(Guid storageId, StorageType storageType) { StoragePool storagePool = mockStoragePool(); Guid storagePoolId = storagePool.getId(); StorageDomain sd = new StorageDomain(); sd.setId(storageId); sd.setStoragePoolId(storagePoolId); sd.setStatus(StorageDomainStatus.Active); sd.setStorageType(storageType); when(storageDomainDao.get(storageId)).thenReturn(sd); when(storageDomainDao.getAllForStorageDomain(storageId)).thenReturn(Collections.singletonList(sd)); when(storageDomainDao.getForStoragePool(storageId, storagePoolId)).thenReturn(sd); return sd; } /** * @return Valid parameters for the command. */ private static AddDiskParameters createParameters() { DiskImage image = new DiskImage(); DiskVmElement dve = new DiskVmElement(null, vmId); dve.setDiskInterface(DiskInterface.IDE); return new AddDiskParameters(dve, image); } private static LunDisk createISCSILunDisk() { LunDisk disk = new LunDisk(); LUNs lun = new LUNs(); lun.setLUNId("lunid"); lun.setLunType(StorageType.ISCSI); StorageServerConnections connection = new StorageServerConnections(); connection.setIqn("a"); connection.setConnection("0.0.0.0"); connection.setPort("1234"); ArrayList<StorageServerConnections> connections = new ArrayList<>(); connections.add(connection); lun.setLunConnections(connections); disk.setLun(lun); disk.setId(Guid.newGuid()); return disk; } @Test public void testIscsiLunCanBeAdded() { LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setUsingScsiReservation(false); assertTrue("checkIfLunDiskCanBeAdded() failed for valid iscsi lun", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); } private LunDisk createISCSILunDisk(ScsiGenericIO sgio) { LunDisk disk = createISCSILunDisk(); disk.setSgio(sgio); return disk; } @Test public void testIscsiLunCannotBeAddedIfSgioIsFilteredAndScsiReservationEnabled() { LunDisk disk = createISCSILunDisk(ScsiGenericIO.FILTERED); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setUsingScsiReservation(true); mockVm(); mockInterfaceList(); assertFalse("Lun disk added successfully WHILE sgio is filtered and scsi reservation is enabled", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); ValidateTestUtils.assertValidationMessages("checkIfLunDiskCanBeAdded() failed but correct can do action hasn't been added to the return response", command, EngineMessage.ACTION_TYPE_FAILED_SGIO_IS_FILTERED); } @Test public void testIscsiLunCanBeAddedIfScsiPassthroughEnabledAndScsiReservationEnabled() { LunDisk disk = createISCSILunDisk(ScsiGenericIO.UNFILTERED); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setUsingScsiReservation(true); mockVm(); mockInterfaceList(); assertTrue("Failed to add Lun disk when scsi passthrough and scsi reservation are enabled", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); } @Test public void testUnknownTypeLunCantBeAdded() { LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); disk.getLun().setLunType(StorageType.UNKNOWN); assertFalse("checkIfLunDiskCanBeAdded() succeded for LUN with UNKNOWN type", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); ValidateTestUtils.assertValidationMessages("checkIfLunDiskCanBeAdded() failed but correct can do action hasn't been added to the return response", command, EngineMessage.ACTION_TYPE_FAILED_DISK_LUN_HAS_NO_VALID_TYPE); } @Test public void testIscsiLunDiskWithNoIqnCantBeAdded() { LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); disk.getLun().getLunConnections().get(0).setIqn(null); assertFalse("checkIfLunDiskCanBeAdded() succeded for ISCSI lun which LUNs has storage_server_connection with a null iqn", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); ValidateTestUtils.assertValidationMessages("checkIfLunDiskCanBeAdded() failed but correct can do action hasn't been added to the return response", command, EngineMessage.ACTION_TYPE_FAILED_DISK_LUN_ISCSI_MISSING_CONNECTION_PARAMS); clearValidationMessages(); disk.getLun().getLunConnections().get(0).setIqn(""); assertFalse("checkIfLunDiskCanBeAdded() succeded for ISCSI lun which LUNs has storage_server_connection with an empty iqn", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); ValidateTestUtils.assertValidationMessages("checkIfLunDiskCanBeAdded() failed but correct can do action hasn't been added to the return response", command, EngineMessage.ACTION_TYPE_FAILED_DISK_LUN_ISCSI_MISSING_CONNECTION_PARAMS); } @Test public void testIscsiLunDiskWithNoAddressCantBeAdded() { LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); disk.getLun().getLunConnections().get(0).setConnection(null); assertFalse("checkIfLunDiskCanBeAdded() succeded for ISCSI lun which LUNs has storage_server_connection with a null address", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); ValidateTestUtils.assertValidationMessages("checkIfLunDiskCanBeAdded() failed but correct can do action hasn't been added to the return response", command, EngineMessage.ACTION_TYPE_FAILED_DISK_LUN_ISCSI_MISSING_CONNECTION_PARAMS); clearValidationMessages(); disk.getLun().getLunConnections().get(0).setConnection(""); assertFalse("checkIfLunDiskCanBeAdded() succeded for ISCSI lun which LUNs has storage_server_connection with a empty address", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); ValidateTestUtils.assertValidationMessages("checkIfLunDiskCanBeAdded() failed but correct can do action hasn't been added to the return response", command, EngineMessage.ACTION_TYPE_FAILED_DISK_LUN_ISCSI_MISSING_CONNECTION_PARAMS); } @Test public void testIscsiLunDiskWithNoPortCantBeAdded() { LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); disk.getLun().getLunConnections().get(0).setPort(null); assertFalse("checkIfLunDiskCanBeAdded() succeded for ISCSI lun which LUNs has storage_server_connection with a null port", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); ValidateTestUtils.assertValidationMessages("checkIfLunDiskCanBeAdded() failed but correct can do action hasn't been added to the return response", command, EngineMessage.ACTION_TYPE_FAILED_DISK_LUN_ISCSI_MISSING_CONNECTION_PARAMS); clearValidationMessages(); disk.getLun().getLunConnections().get(0).setPort(""); assertFalse("checkIfLunDiskCanBeAdded() succeded for ISCSI lun which LUNs has storage_server_connection with a empty port", command.checkIfLunDiskCanBeAdded(spyDiskValidator(disk))); ValidateTestUtils.assertValidationMessages("checkIfLunDiskCanBeAdded() failed but correct can do action hasn't been added to the return response", command, EngineMessage.ACTION_TYPE_FAILED_DISK_LUN_ISCSI_MISSING_CONNECTION_PARAMS); } @Test public void testLunDiskValid() { VDS vds = mockVds(); LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); command.getParameters().setVdsId(vds.getId()); command.setVds(vds); mockVm(); mockMaxPciSlots(); mockInterfaceList(); List<LUNs> luns = Collections.singletonList(disk.getLun()); doReturn(luns).when(command).executeGetDeviceList(any(Guid.class), any(StorageType.class), anyString()); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void testGetLunDiskSucceeds() { VDS vds = mockVds(); LunDisk disk = createISCSILunDisk(); List<LUNs> luns = Collections.singletonList(disk.getLun()); initializeCommand(Guid.newGuid()); doReturn(luns).when(command).executeGetDeviceList(any(Guid.class), any(StorageType.class), anyString()); assertEquals(disk.getLun(), command.getLunDisk(disk.getLun(), vds)); } @Test public void testLunDiskInvalid() { VDS vds = mockVds(); LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); command.getParameters().setVdsId(vds.getId()); command.setVds(vds); mockMaxPciSlots(); mockInterfaceList(); mockVm(); List<LUNs> luns = Collections.emptyList(); doReturn(luns).when(command).executeGetDeviceList(any(Guid.class), any(StorageType.class), anyString()); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.ACTION_TYPE_FAILED_DISK_LUN_INVALID); } @Test public void testGetLunDiskFails() { VDS vds = mockVds(); LunDisk disk = createISCSILunDisk(); List<LUNs> luns = Collections.emptyList(); initializeCommand(Guid.newGuid()); doReturn(luns).when(command).executeGetDeviceList(any(Guid.class), any(StorageType.class), anyString()); assertNull(command.getLunDisk(disk.getLun(), vds)); } @Test public void testAddingIDELunExceedsSlotLimit() { mockInterfaceList(); LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setDiskInterface(DiskInterface.IDE); VM vm = mockVm(); mockMaxPciSlots(); // use maximum slots for IDE - validate expected to succeed. mockOtherVmDisks(vm, VmCommand.MAX_IDE_SLOTS - 1, DiskInterface.IDE); ValidateTestUtils.runAndAssertValidateSuccess(command); LunDisk newDisk = createISCSILunDisk(); DiskVmElement dve = new DiskVmElement(disk.getId(), vmId); dve.setDiskInterface(DiskInterface.IDE); newDisk.setDiskVmElements(Collections.singletonList(dve)); vm.getDiskMap().put(newDisk.getId(), newDisk); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.ACTION_TYPE_FAILED_EXCEEDED_MAX_IDE_SLOTS); } @Test public void testAddingPCILunExceedsSlotLimit() { mockInterfaceList(); LunDisk disk = createISCSILunDisk(); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setDiskInterface(DiskInterface.VirtIO); VM vm = mockVm(); mockMaxPciSlots(); // use maximum slots for PCI. validate expected to succeed. mockOtherVmDisks(vm, MAX_PCI_SLOTS - 2, DiskInterface.VirtIO); ValidateTestUtils.runAndAssertValidateSuccess(command); LunDisk newDisk = createISCSILunDisk(); DiskVmElement dve = new DiskVmElement(disk.getId(), vmId); dve.setDiskInterface(DiskInterface.VirtIO); newDisk.setDiskVmElements(Collections.singletonList(dve)); vm.getDiskMap().put(newDisk.getId(), newDisk); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.ACTION_TYPE_FAILED_EXCEEDED_MAX_PCI_SLOTS); } private void mockOtherVmDisks(VM vm, int numOfDisks, DiskInterface iface) { List<DiskVmElement> otherDisks = new ArrayList<>(numOfDisks); for (int i = 0; i < numOfDisks; i++) { DiskVmElement dve = new DiskVmElement(Guid.newGuid(), vm.getId()); dve.setDiskInterface(iface); otherDisks.add(dve); } doReturn(otherDisks).when(diskVmElementDao).getAllForVm(vmId); } @Test public void testVirtIoScsiNotSupportedByOs() { DiskImage disk = new DiskImage(); Guid storageId = Guid.newGuid(); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setDiskInterface(DiskInterface.VirtIO_SCSI); command.getParameters().setStorageDomainId(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockVm(); mockMaxPciSlots(); when(osRepository.getDiskInterfaces(anyInt(), any(Version.class))).thenReturn( Collections.singletonList("VirtIO")); doReturn(true).when(vmDeviceUtils).hasVirtioScsiController(any(Guid.class)); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.ACTION_TYPE_FAILED_GUEST_OS_VERSION_IS_NOT_SUPPORTED); } @Test public void testVirtioScsiDiskWithoutControllerCantBeAdded() { DiskImage disk = new DiskImage(); Guid storageId = Guid.newGuid(); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setDiskInterface(DiskInterface.VirtIO_SCSI); command.getParameters().setStorageDomainId(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockVm(); mockMaxPciSlots(); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.CANNOT_PERFORM_ACTION_VIRTIO_SCSI_IS_DISABLED); } @Test public void testDiskImageWithSgioCantBeAdded() { DiskImage disk = new DiskImage(); disk.setSgio(ScsiGenericIO.UNFILTERED); Guid storageId = Guid.newGuid(); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setDiskInterface(DiskInterface.VirtIO_SCSI); command.getParameters().setStorageDomainId(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockMaxPciSlots(); mockVm(); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.SCSI_GENERIC_IO_IS_NOT_SUPPORTED_FOR_IMAGE_DISK); } @Test public void testLunDiskWithSgioCanBeAdded() { LunDisk disk = createISCSILunDisk(); disk.setSgio(ScsiGenericIO.UNFILTERED); command.getParameters().setDiskInfo(disk); command.getParameters().getDiskVmElement().setDiskInterface(DiskInterface.VirtIO_SCSI); mockVm(); mockMaxPciSlots(); when(osRepository.getDiskInterfaces(anyInt(), any(Version.class))).thenReturn( Collections.singletonList("VirtIO_SCSI")); doReturn(true).when(vmDeviceUtils).hasVirtioScsiController(any(Guid.class)); mockInterfaceList(); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void testValidateFailOnAddFloatingDiskWithPlugSet() { DiskImage disk = new DiskImage(); command.getParameters().setDiskInfo(disk); command.getParameters().setVmId(Guid.Empty); command.getParameters().setPlugDiskToVm(true); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.CANNOT_ADD_FLOATING_DISK_WITH_PLUG_VM_SET); } @Test public void testValidateSuccessOnAddFloatingDiskWithPlugUnset() { DiskImage disk = new DiskImage(); command.getParameters().setDiskInfo(disk); command.getParameters().setVmId(Guid.Empty); command.getParameters().setPlugDiskToVm(false); Guid storageId = Guid.newGuid(); command.getParameters().setStorageDomainId(storageId); mockStorageDomain(storageId); mockStoragePoolIsoMap(); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void testValidateFailReadOnlyOnInterface() { command.getParameters().setStorageDomainId(Guid.newGuid()); mockVm(); doReturn(true).when(command).isDiskPassPciAndIdeLimit(); doReturn(new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_INTERFACE_DOES_NOT_SUPPORT_READ_ONLY_ATTR)). when(diskVmElementValidator).isReadOnlyPropertyCompatibleWithInterface(); doReturn(diskVmElementValidator).when(command).getDiskVmElementValidator(any(Disk.class), any(DiskVmElement.class)); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.ACTION_TYPE_FAILED_INTERFACE_DOES_NOT_SUPPORT_READ_ONLY_ATTR); } @Test public void testValidateSucceedReadOnly() { Guid storageId = Guid.newGuid(); initializeCommand(storageId); mockVm(); mockEntities(storageId); doReturn(true).when(command).isDiskPassPciAndIdeLimit(); doReturn(true).when(command).checkIfImageDiskCanBeAdded(any(), any()); doReturn(diskVmElementValidator).when(command).getDiskVmElementValidator(any(Disk.class), any(DiskVmElement.class)); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void testExistingQuota() { Quota quota = new Quota(); quota.setId(Guid.newGuid()); DiskImage img = new DiskImage(); img.setQuotaId(quota.getId()); command.getParameters().setDiskInfo(img); Guid storageId = Guid.newGuid(); command.getParameters().setStorageDomainId(storageId); StoragePool pool = mockStoragePool(); command.setStoragePoolId(pool.getId()); quota.setStoragePoolId(pool.getId()); mockVm(); mockEntities(storageId); when(quotaDao.getById(quota.getId())).thenReturn(quota); doCallRealMethod().when(command).validateQuota(); ValidateTestUtils.runAndAssertValidateSuccess(command); } @Test public void testNonExistingQuota() { DiskImage img = new DiskImage(); img.setQuotaId(Guid.newGuid()); command.getParameters().setDiskInfo(img); Guid storageId = Guid.newGuid(); command.getParameters().setStorageDomainId(storageId); mockEntities(storageId); mockVm(); doCallRealMethod().when(command).validateQuota(); ValidateTestUtils.runAndAssertValidateFailure(command, EngineMessage.ACTION_TYPE_FAILED_QUOTA_NOT_EXIST); } @Test public void testValidateFailsForPassDiscard() { initializeCommand(Guid.newGuid()); mockVm(); StoragePool storagePool = new StoragePool(); storagePool.setCompatibilityVersion(Version.v4_1); command.setStoragePool(storagePool); command.getParameters().getDiskVmElement().setPassDiscard(true); doReturn(diskVmElementValidator).when(command).getDiskVmElementValidator( any(Disk.class), any(DiskVmElement.class)); doReturn(new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_PASS_DISCARD_NOT_SUPPORTED_BY_DISK_INTERFACE)) .when(diskVmElementValidator).isPassDiscardSupported(any(Guid.class)); ValidateTestUtils.runAndAssertValidateFailure( command, EngineMessage.ACTION_TYPE_FAILED_PASS_DISCARD_NOT_SUPPORTED_BY_DISK_INTERFACE); } private void clearValidationMessages(){ command.getReturnValue() .getValidationMessages() .clear(); } private void mockEntities(Guid storageId) { mockStorageDomain(storageId); mockStoragePoolIsoMap(); mockInterfaceList(); mockMaxPciSlots(); } }