package org.ovirt.engine.core.bll.network.host; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.commons.lang.RandomStringUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.ovirt.engine.core.common.businessentities.HostDevice; import org.ovirt.engine.core.common.businessentities.VmDevice; import org.ovirt.engine.core.common.businessentities.VmDeviceGeneralType; import org.ovirt.engine.core.common.businessentities.network.HostNicVfsConfig; import org.ovirt.engine.core.common.businessentities.network.Network; import org.ovirt.engine.core.common.businessentities.network.VdsNetworkInterface; import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.HostDeviceDao; import org.ovirt.engine.core.dao.VmDeviceDao; import org.ovirt.engine.core.dao.network.InterfaceDao; import org.ovirt.engine.core.dao.network.NetworkDao; import org.ovirt.engine.core.utils.RandomUtils; @RunWith(MockitoJUnitRunner.class) public class VfSchedulerImplTest { @Mock private NetworkDeviceHelper networkDeviceHelper; @Mock private InterfaceDao interfaceDao; @Mock private HostDeviceDao hostDeviceDao; @Mock private VmDeviceDao vmDeviceDao; @Mock private NetworkDao networkDao; @Mock private Guid vmId; @Mock private Guid hostId; private VfSchedulerImpl vfScheduler; private Map<Guid, String> expectedVnicToVfMap; @Before public void setUp() { vfScheduler = new VfSchedulerImpl(networkDao, interfaceDao, hostDeviceDao, vmDeviceDao, networkDeviceHelper); expectedVnicToVfMap = new HashMap<>(); } @Test public void hostNotHaveSriovNics() { VmNetworkInterface vnic = mockVnic(true); assertHostNotValid(Collections.singletonList(vnic), Collections.singletonList(vnic.getName())); } @Test public void hostNicNotHaveVfsOnSriovNic() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 0, true, false, false, false); assertHostNotValid(Collections.singletonList(vnic), Collections.singletonList(vnic.getName())); } @Test public void hostNicNotHaveNetworkInSriovConfig() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 5, false, false, false, true); assertHostNotValid(Collections.singletonList(vnic), Collections.singletonList(vnic.getName())); } @Test public void validNetworkInSriovConfig() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 5, false, true, false, true); assertHostValid(Collections.singletonList(vnic)); } @Test public void validLabelInSriovConfig() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 1, false, false, true, true); assertHostValid(Collections.singletonList(vnic)); } @Test public void validAllNetworksAllowed() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 5, true, false, false, true); assertHostValid(Collections.singletonList(vnic)); } @Test public void hostNicNotHaveFreeVfs() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 8, true, false, false, false); assertHostNotValid(Collections.singletonList(vnic), Collections.singletonList(vnic.getName())); } @Test public void hostNicHaveOneFreeVfWhichShareIommuGroup() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 1, true, false, false, true, true, false); assertHostNotValid(Collections.singletonList(vnic), Collections.singletonList(vnic.getName())); } @Test public void hostNicHaveOneFreeVfWhichShouldBeDirectlyPassthrough() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 1, true, false, false, true, false, true); assertHostNotValid(Collections.singletonList(vnic), Collections.singletonList(vnic.getName())); } @Test public void hostNicHaveTwoFreeVfOneShouldBeDirectlyPassthrough() { VmNetworkInterface vnic = mockVnic(true); List<HostDevice> vfs = initHostWithOneVfsConfig(Collections.singletonList(vnic), 2, true, false, false, true, false, true); HostDevice freeVf = vfs.get(0); mockVfDirectlyAttached(false, freeVf); expectedVnicToVfMap.put(vnic.getId(), freeVf.getDeviceName()); assertHostValid(Collections.singletonList(vnic)); } @Test public void validVnicNotPlugged() { VmNetworkInterface vnic = mockVnic(true); when(vnic.isPlugged()).thenReturn(false); initHostWithOneVfsConfig(Collections.singletonList(vnic), 0, true, false, false, false); assertHostValid(Collections.singletonList(vnic)); } @Test public void multipleVnicsValid() { multipleVnicCommonTest(true); } @Test public void multipleVnicsNotValid() { multipleVnicCommonTest(false); } private void multipleVnicCommonTest(boolean allNicsValid) { VmNetworkInterface vnic1 = mockVnic(true, "net1"); VmNetworkInterface vnic2 = mockVnic(true, "net2"); VmNetworkInterface vnic3 = mockVnic(false); VmNetworkInterface vnic4 = mockVnic(false); HostNicVfsConfig hostNicVfsConfig1 = new HostNicVfsConfig(); updateVfsConfig(hostNicVfsConfig1, vnic1, true, false, true); HostNicVfsConfig hostNicVfsConfig2 = new HostNicVfsConfig(); updateVfsConfig(hostNicVfsConfig2, vnic2, false, allNicsValid, allNicsValid); mockVfsConfigsOnHost(Arrays.asList(hostNicVfsConfig1, hostNicVfsConfig2)); List<VmNetworkInterface> allVnics = Arrays.asList(vnic1, vnic2, vnic3, vnic4); if (allNicsValid) { assertHostValid(allVnics); } else { assertHostNotValid(allVnics, Collections.singletonList(vnic2.getName())); } } @Test public void multipleVfsConfigsFirstValid() { multipleVfsConfigsCommon(true); } @Test public void multipleVfsConfigsLastValid() { multipleVfsConfigsCommon(false); } private void multipleVfsConfigsCommon(boolean firstValid) { VmNetworkInterface vnic = mockVnic(true); HostNicVfsConfig hostNicVfsConfig1 = new HostNicVfsConfig(); updateVfsConfig(hostNicVfsConfig1, vnic, firstValid, false, firstValid); HostNicVfsConfig hostNicVfsConfig2 = new HostNicVfsConfig(); updateVfsConfig(hostNicVfsConfig2, vnic, !firstValid, false, !firstValid); mockVfsConfigsOnHost(Arrays.asList(hostNicVfsConfig1, hostNicVfsConfig2)); assertHostValid(Collections.singletonList(vnic)); } @Test public void cleanVmDataTest() { VmNetworkInterface vnic = mockVnic(true); initHostWithOneVfsConfig(Collections.singletonList(vnic), 1, true, false, false, true); assertHostValid(Collections.singletonList(vnic)); vfScheduler.cleanVmData(vmId); assertNull(vfScheduler.getVnicToVfMap(vmId, hostId)); } @Test public void findFreeVfForVnicNoFreeVfTest() { findFreeVfForVnicCommon(false); } @Test public void findFreeVfForVnicTest() { findFreeVfForVnicCommon(true); } private void findFreeVfForVnicCommon(boolean existFreeVf) { VmNetworkInterface vnic = mockVnic(true, "net1"); HostNicVfsConfig hostNicVfsConfig = new HostNicVfsConfig(); updateVfsConfig(hostNicVfsConfig, vnic, true, false, existFreeVf); mockVfsConfigsOnHost(Collections.singletonList(hostNicVfsConfig)); String freeVf = vfScheduler.findFreeVfForVnic(hostId, createNetwork(vnic.getNetworkName()), vmId); if (existFreeVf) { assertNotNull(freeVf); } else { assertNull(freeVf); } } private List<HostDevice> initHostWithOneVfsConfig(List<VmNetworkInterface> passthroughVnics, int numOfVfs, boolean allNetworksAllowed, boolean networkInSriovConfig, boolean labelInSriovConfig, boolean hasFreeVf, boolean freeVfShareIommuGroup, boolean vfDirectlyAttached) { HostNicVfsConfig hostNicVfsConfig = new HostNicVfsConfig(); List<HostDevice> vfs = passthroughVnics.stream().map(vnic -> updateVfsConfig(hostNicVfsConfig, vnic, numOfVfs, allNetworksAllowed, networkInSriovConfig, labelInSriovConfig, hasFreeVf, freeVfShareIommuGroup, vfDirectlyAttached)).collect(Collectors.toList()); mockVfsConfigsOnHost(Collections.singletonList(hostNicVfsConfig)); return vfs; } private void initHostWithOneVfsConfig(List<VmNetworkInterface> passthroughVnics, int numOfVfs, boolean allNetworksAllowed, boolean networkInSriovConfig, boolean labelInSriovConfig, boolean hasFreeVf) { initHostWithOneVfsConfig(passthroughVnics, numOfVfs, allNetworksAllowed, networkInSriovConfig, labelInSriovConfig, hasFreeVf, false, false); } private void validateVnics(List<VmNetworkInterface> vnics, List<String> excetedProblematicVnics) { List<String> problematicVnics = vfScheduler.validatePassthroughVnics(vmId, hostId, vnics); assertEquals(excetedProblematicVnics, problematicVnics); } private VmNetworkInterface mockVnic(boolean passthrough, String networkName) { VmNetworkInterface vnic = mock(VmNetworkInterface.class); when(vnic.getId()).thenReturn(Guid.newGuid()); when(vnic.getName()).thenReturn(getRandomString()); when(vnic.isPassthrough()).thenReturn(passthrough); Network network = createNetwork(networkName); when(vnic.getNetworkName()).thenReturn(network.getName()); when(vnic.isPlugged()).thenReturn(true); when(vnic.getVmId()).thenReturn(vmId); return vnic; } private VmNetworkInterface mockVnic(boolean passthrough) { return mockVnic(passthrough, getRandomString()); } private void assertHostNotValid(List<VmNetworkInterface> vnics, List<String> exceptedProblematicVnics) { validateVnics(vnics, exceptedProblematicVnics); validateVnicToVfMap(); } private void assertHostValid(List<VmNetworkInterface> vnics) { validateVnics(vnics, new ArrayList<>()); validateVnicToVfMap(); } private void validateVnicToVfMap() { Map<Guid, String> vnicToVfMap = vfScheduler.getVnicToVfMap(vmId, hostId); vnicToVfMap = vnicToVfMap == null ? new HashMap<>() : vnicToVfMap; assertEquals(expectedVnicToVfMap, vnicToVfMap); } private HostDevice updateVfsConfig(HostNicVfsConfig hostNicVfsConfig, VmNetworkInterface vnic, int numOfVfs, boolean allNetworksAllowed, boolean vnicNetworkInSriovConfig, boolean vnicLabelInSriovConfig, boolean hasFreeVf, boolean freeVfShareIommuGroup, boolean vfDirectlyAttached) { hostNicVfsConfig.setNicId(Guid.newGuid()); hostNicVfsConfig.setNumOfVfs(numOfVfs); hostNicVfsConfig.setAllNetworksAllowed(allNetworksAllowed); updateVfsConfigNetworks(hostNicVfsConfig, vnic, vnicNetworkInSriovConfig); updateVfsConfigLabels(hostNicVfsConfig, vnic, vnicLabelInSriovConfig); VdsNetworkInterface nic = new VdsNetworkInterface(); nic.setId(hostNicVfsConfig.getNicId()); when(getNic(hostNicVfsConfig)).thenReturn(nic); HostDevice vf = null; if (hasFreeVf) { vf = createFreeVf(hostNicVfsConfig); mockVfShareIommuGroup(vf, freeVfShareIommuGroup); if (!freeVfShareIommuGroup && (allNetworksAllowed || vnicNetworkInSriovConfig || vnicLabelInSriovConfig)) { if (!vfDirectlyAttached) { expectedVnicToVfMap.put(vnic.getId(), vf.getDeviceName()); } mockVfDirectlyAttached(vfDirectlyAttached, vf); } } return vf; } private void mockVfDirectlyAttached(boolean vfDirectlyAttached, HostDevice vf) { when(vmDeviceDao.getVmDeviceByVmIdTypeAndDevice(vmId, VmDeviceGeneralType.HOSTDEV, vf.getName())) .thenReturn(vfDirectlyAttached ? Collections.singletonList(new VmDevice()) : Collections.emptyList()); } private void updateVfsConfig(HostNicVfsConfig hostNicVfsConfig, VmNetworkInterface vnic, boolean allNetworksAllowed, boolean vnicNetworkInSriovConfig, boolean hasFreeVf) { updateVfsConfig(hostNicVfsConfig, vnic, 1, allNetworksAllowed, vnicNetworkInSriovConfig, false, hasFreeVf, false, false); } private void mockVfShareIommuGroup(HostDevice vf, boolean share) { vf.setIommuGroup(RandomUtils.instance().nextInt()); List<HostDevice> devices = new ArrayList<>(); devices.add(vf); if (share) { HostDevice extraIommuDevice = new HostDevice(); extraIommuDevice.setHostId(vf.getHostId()); extraIommuDevice.setIommuGroup(vf.getIommuGroup()); devices.add(extraIommuDevice); } when(hostDeviceDao.getHostDevicesByHostIdAndIommuGroup(vf.getHostId(), vf.getIommuGroup())).thenReturn(devices); } private void updateVfsConfigLabels(HostNicVfsConfig hostNicVfsConfig, VmNetworkInterface vnic, boolean vnicLabelInSriovConfig) { if (hostNicVfsConfig.getNetworkLabels() == null) { hostNicVfsConfig.setNetworkLabels(new HashSet<>()); } if (vnicLabelInSriovConfig) { hostNicVfsConfig.getNetworkLabels().add(getVnicNetwork(vnic).getLabel()); } } private void updateVfsConfigNetworks(HostNicVfsConfig hostNicVfsConfig, VmNetworkInterface vnic, boolean vnicNetworkInSriovConfig) { if (hostNicVfsConfig.getNetworks() == null) { hostNicVfsConfig.setNetworks(new HashSet<>()); } if (vnicNetworkInSriovConfig) { hostNicVfsConfig.getNetworks().add(getVnicNetwork(vnic).getId()); } } private Network createNetwork(String networkName) { Network network = new Network(); network.setId(Guid.newGuid()); network.setName(networkName); network.setLabel(getRandomString()); when(networkDao.getByName(network.getName())).thenReturn(network); return network; } private Network getVnicNetwork(VmNetworkInterface vnic) { return networkDao.getByName(vnic.getNetworkName()); } private HostDevice createVf() { HostDevice hostDevice = new HostDevice(); hostDevice.setHostId(hostId); hostDevice.setDeviceName(getRandomString()); return hostDevice; } private HostDevice createFreeVf(HostNicVfsConfig hostNicVfsConfig) { HostDevice vf = createVf(); ArgumentMatcher<List<String>> matchNotContainVf = argVf -> argVf == null || !argVf.contains(vf.getName()); when(networkDeviceHelper.getFreeVf(eq(getNic(hostNicVfsConfig)), argThat(matchNotContainVf))).thenReturn(vf); return vf; } private void mockVfsConfigsOnHost(List<HostNicVfsConfig> vfsConfigs) { when(networkDeviceHelper.getHostNicVfsConfigsWithNumVfsDataByHostId(hostId)).thenReturn(vfsConfigs); } private VdsNetworkInterface getNic(HostNicVfsConfig hostNicVfsConfig) { return interfaceDao.get(hostNicVfsConfig.getNicId()); } private String getRandomString() { return RandomStringUtils.random(10); } }