package org.ovirt.engine.core.bll.network.host; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; import org.ovirt.engine.core.common.businessentities.HostDevice; 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; @Singleton public class VfSchedulerImpl implements VfScheduler { private NetworkDao networkDao; private InterfaceDao interfaceDao; private HostDeviceDao hostDeviceDao; private VmDeviceDao vmDeviceDao; private NetworkDeviceHelper networkDeviceHelper; private Map<Guid, Map<Guid, Map<Guid, String>>> vmToHostToVnicToVfMap = new ConcurrentHashMap<>(); @Inject public VfSchedulerImpl(NetworkDao networkDao, InterfaceDao interfaceDao, HostDeviceDao hostDeviceDao, VmDeviceDao vmDeviceDao, NetworkDeviceHelper vfsConfigHelper) { this.networkDao = networkDao; this.interfaceDao = interfaceDao; this.hostDeviceDao = hostDeviceDao; this.vmDeviceDao = vmDeviceDao; this.networkDeviceHelper = vfsConfigHelper; } @Override public List<String> validatePassthroughVnics(Guid vmId, Guid hostId, List<VmNetworkInterface> allVmNics) { List<VmNetworkInterface> pluggedPassthroughVnics = getPluggedPassthroughVnics(allVmNics); if (pluggedPassthroughVnics.isEmpty()) { return Collections.emptyList(); } Map<Guid, Map<Guid, String>> hostToVnicToVfMap = vmToHostToVnicToVfMap.get(vmId); if (hostToVnicToVfMap == null) { hostToVnicToVfMap = new HashMap<>(); vmToHostToVnicToVfMap.put(vmId, hostToVnicToVfMap); } Map<Guid, List<String>> nicToUsedVfs = new HashMap<>(); Map<Guid, VdsNetworkInterface> fetchedNics = new HashMap<>(); List<String> problematicVnics = new ArrayList<>(); List<HostNicVfsConfig> vfsConfigs = networkDeviceHelper.getHostNicVfsConfigsWithNumVfsDataByHostId(hostId); Map<Guid, String> vnicToVfMap = new HashMap<>(); hostToVnicToVfMap.put(hostId, vnicToVfMap); for (final VmNetworkInterface vnic : pluggedPassthroughVnics) { String freeVf = findFreeVfForVnic(vfsConfigs, nicToUsedVfs, fetchedNics, vnic.getNetworkName() == null ? null : networkDao.getByName(vnic.getNetworkName()), vnic.getVmId(), true); if (freeVf == null) { problematicVnics.add(vnic.getName()); } else { vnicToVfMap.put(vnic.getId(), freeVf); } } return problematicVnics; } @Override public String findFreeVfForVnic(Guid hostId, Network vnicNetwork, Guid vmId) { List<HostNicVfsConfig> vfsConfigs = networkDeviceHelper.getHostNicVfsConfigsWithNumVfsDataByHostId(hostId); String freeVf = findFreeVfForVnic(vfsConfigs, new HashMap<>(), new HashMap<>(), vnicNetwork, vmId, false); return freeVf; } private List<VmNetworkInterface> getPluggedPassthroughVnics(List<VmNetworkInterface> vnics) { return vnics.stream().filter(vnic -> vnic.isPassthrough() && vnic.isPlugged()).collect(Collectors.toList()); } /* * @param nicToUsedVfs used to calculate all used VFs by given nic. * Mapped by nic id. Map<Guid, List<String>> nicToUsedVfs = new HashMap<>(); * This collection is changed as side-effect of calling this method. * @param fetchedNics caching. This collection is changed as side-effect of calling this method. * Not to query same nic multiple times from db. * Mapped by VdsNetworkInterface.getId() Map<Guid, VdsNetworkInterface> fetchedNics = new HashMap<>(); */ private String findFreeVfForVnic(List<HostNicVfsConfig> vfsConfigs, Map<Guid, List<String>> nicToUsedVfs, Map<Guid, VdsNetworkInterface> fetchedNics, Network vnicNetwork, Guid vmId, boolean shouldCheckDirectlyAttachedVmDevices) { for (HostNicVfsConfig vfsConfig : vfsConfigs) { if (vfsConfig.getNumOfVfs() != 0 && isNetworkInVfsConfig(vnicNetwork, vfsConfig)) { Guid nicId = vfsConfig.getNicId(); List<String> skipVfs = new ArrayList<>(); HostDevice freeVf = getFreeVf(nicId, nicToUsedVfs, fetchedNics, skipVfs); while (freeVf != null && (isSharingIommuGroup(freeVf) || (shouldCheckDirectlyAttachedVmDevices && shouldBeDirectlyAttached(freeVf.getName(), vmId)))) { skipVfs.add(freeVf.getName()); freeVf = getFreeVf(nicId, nicToUsedVfs, fetchedNics, skipVfs); } if (freeVf != null) { return freeVf.getName(); } } } return null; } private boolean isNetworkInVfsConfig(Network vnicNetwork, HostNicVfsConfig vfsConfig) { if (vnicNetwork == null) { return true; } boolean isNetworkInConfig = vfsConfig.isAllNetworksAllowed() || vfsConfig.getNetworks().contains(vnicNetwork.getId()); boolean isLabelInConfig = vnicNetwork.getLabel() != null && vfsConfig.getNetworkLabels().contains(vnicNetwork.getLabel()); return isNetworkInConfig || isLabelInConfig; } private HostDevice getFreeVf(Guid nicId, Map<Guid, List<String>> nicToUsedVfs, Map<Guid, VdsNetworkInterface> fetchedNics, List<String> skipVfs) { VdsNetworkInterface nic = getNic(nicId, fetchedNics); List<String> usedVfsByNic = nicToUsedVfs.get(nic.getId()); if (usedVfsByNic != null) { skipVfs.addAll(usedVfsByNic); } HostDevice freeVf = networkDeviceHelper.getFreeVf(nic, skipVfs); if (freeVf != null) { if (usedVfsByNic == null) { usedVfsByNic = new ArrayList<>(); nicToUsedVfs.put(nic.getId(), usedVfsByNic); } usedVfsByNic.add(freeVf.getName()); return freeVf; } return null; } private VdsNetworkInterface getNic(Guid nicId, Map<Guid, VdsNetworkInterface> fetchedNics) { VdsNetworkInterface nic = fetchedNics.get(nicId); if (nic == null) { nic = interfaceDao.get(nicId); fetchedNics.put(nicId, nic); } return nic; } @Override public Map<Guid, String> getVnicToVfMap(Guid vmId, Guid hostId) { Map<Guid, Map<Guid, String>> hostToVnicToVfMap = vmToHostToVnicToVfMap.get(vmId); return hostToVnicToVfMap == null ? null : hostToVnicToVfMap.get(hostId); } @Override public void cleanVmData(Guid vmId) { vmToHostToVnicToVfMap.remove(vmId); } private boolean isSharingIommuGroup(HostDevice device) { if (device.getIommuGroup() == null) { return false; } // Check that the device doesn't share iommu group with other devices List<HostDevice> iommoGroupDevices = hostDeviceDao.getHostDevicesByHostIdAndIommuGroup(device.getHostId(), device.getIommuGroup()); return iommoGroupDevices.size() > 1; } private boolean shouldBeDirectlyAttached(String vfName, Guid vmId) { return !vmDeviceDao.getVmDeviceByVmIdTypeAndDevice(vmId, VmDeviceGeneralType.HOSTDEV, vfName).isEmpty(); } }