package org.ovirt.engine.core.bll.network.host; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.common.businessentities.Entities; import org.ovirt.engine.core.common.businessentities.HostDevice; import org.ovirt.engine.core.common.businessentities.network.HostNicVfsConfig; import org.ovirt.engine.core.common.businessentities.network.VdsNetworkInterface; import org.ovirt.engine.core.common.utils.NetworkCommonUtils; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.HostDeviceDao; import org.ovirt.engine.core.dao.network.HostNicVfsConfigDao; import org.ovirt.engine.core.dao.network.InterfaceDao; import org.ovirt.engine.core.utils.NetworkUtils; @Singleton class NetworkDeviceHelperImpl implements NetworkDeviceHelper { private final InterfaceDao interfaceDao; private final HostDeviceDao hostDeviceDao; private final HostNicVfsConfigDao hostNicVfsConfigDao; @Inject NetworkDeviceHelperImpl(InterfaceDao interfaceDao, HostDeviceDao hostDeviceDao, HostNicVfsConfigDao hostNicVfsConfigDao) { Objects.requireNonNull(interfaceDao, "interfaceDao cannot be null"); Objects.requireNonNull(hostDeviceDao, "hostDeviceDao cannot be null"); Objects.requireNonNull(hostNicVfsConfigDao, "hostNicVfsConfigDao cannot be null"); this.interfaceDao = interfaceDao; this.hostDeviceDao = hostDeviceDao; this.hostNicVfsConfigDao = hostNicVfsConfigDao; } @Override public VdsNetworkInterface getNicByPciDevice(final HostDevice pciDevice) { return getNicByPciDevice(pciDevice, null); } @Override public VdsNetworkInterface getNicByPciDevice(final HostDevice pciDevice, final Collection<HostDevice> devices) { return getNicByPciDevice(pciDevice, devices, null); } private VdsNetworkInterface getNicByPciDevice(final HostDevice pciDevice, final Collection<HostDevice> devices, final Collection<VdsNetworkInterface> hostNics) { final HostDevice netDevice = getFirstChildDevice(pciDevice, devices); if (netDevice == null || !isNetworkDevice(netDevice)) { return null; } final Collection<VdsNetworkInterface> hostInterfaces = hostNics == null ? interfaceDao.getAllInterfacesForVds(netDevice.getHostId()) : hostNics; return hostInterfaces.stream().filter(iface -> iface.getName().equals(netDevice.getNetworkInterfaceName())) .findFirst().orElse(null); } private HostDevice getFirstChildDevice(final HostDevice pciDevice, final Collection<HostDevice> devices) { Collection<HostDevice> hostDevices = devices == null ? getDevicesByHostId(pciDevice.getHostId()) : devices; return hostDevices.stream().filter(device -> pciDevice.getDeviceName().equals(device.getParentDeviceName())) .findFirst().orElse(null); } @Override public boolean isSriovDevice(HostDevice device) { return device.getTotalVirtualFunctions() != null; } @Override public boolean isNetworkDevice(HostDevice device) { return device.getNetworkInterfaceName() != null; } @Override public void updateHostNicVfsConfigWithNumVfsData(HostNicVfsConfig hostNicVfsConfig) { VdsNetworkInterface nic = getNicById(hostNicVfsConfig.getNicId()); updateVfsConfigWithNumOfVfsData(hostNicVfsConfig, nic, getDevicesByHostId(nic.getVdsId())); } @Override public List<HostNicVfsConfig> getHostNicVfsConfigsWithNumVfsDataByHostId(Guid hostId) { List<HostNicVfsConfig> hostNicVfsConfigList = hostNicVfsConfigDao.getAllVfsConfigByHostId(hostId); List<HostDevice> deviceList = getDevicesByHostId(hostId); for (HostNicVfsConfig hostNicVfsConfig : hostNicVfsConfigList) { updateVfsConfigWithNumOfVfsData(hostNicVfsConfig, null, deviceList); } return hostNicVfsConfigList; } private void updateVfsConfigWithNumOfVfsData(HostNicVfsConfig hostNicVfsConfig, VdsNetworkInterface nic, List<HostDevice> deviceList) { if (nic == null) { nic = getNicById(hostNicVfsConfig.getNicId()); } HostDevice pciDevice = getPciDeviceByNic(nic, deviceList); hostNicVfsConfig.setMaxNumOfVfs(getMaxNumOfVfs(pciDevice)); hostNicVfsConfig.setNumOfVfs(getNumOfVfs(pciDevice, deviceList)); } private HostDevice getPciDeviceByNic(final VdsNetworkInterface nic, List<HostDevice> deviceList) { return getPciDeviceByNic(nic, deviceList, Entities.entitiesByName(deviceList)); } private HostDevice getPciDeviceByNic(final VdsNetworkInterface nic, List<HostDevice> deviceList, Map<String, HostDevice> devicesByName) { final String nicName = nic.getName(); final HostDevice netDevice = deviceList.stream() .filter(device -> nicName.equals(device.getNetworkInterfaceName())).findFirst().orElse(null); Objects.requireNonNull(netDevice, String.format("Host \"%s\": nic \"%s\" doesn't have a net device", nic.getVdsName(), nicName)); final String parentDeviceName = netDevice.getParentDeviceName(); final HostDevice pciDevice = devicesByName.get(parentDeviceName); Objects.requireNonNull(pciDevice, String.format("Host \"%s\": net device \"%s\" doesn't have a parent pci device \"%s\"", nic.getVdsName(), netDevice.getName(), parentDeviceName)); return pciDevice; } private int getMaxNumOfVfs(HostDevice pciDevice) { return pciDevice.getTotalVirtualFunctions(); } private int getNumOfVfs(HostDevice pciDevice, List<HostDevice> deviceList) { List<HostDevice> vfs = getVfs(pciDevice, deviceList); return vfs.size(); } private VdsNetworkInterface getNicById(Guid nicId) { return interfaceDao.get(nicId); } private List<HostDevice> getDevicesByHostId(Guid hostId) { return hostDeviceDao.getHostDevicesByHostId(hostId); } private List<HostDevice> getVfs(final HostDevice pciDevice, List<HostDevice> deviceList) { return deviceList.stream() .filter(device -> pciDevice.getDeviceName().equals(device.getParentPhysicalFunction())) .collect(Collectors.toList()); } @Override public boolean areAllVfsFree(VdsNetworkInterface nic) { HostDevice nonFreeVf = getVf(nic, false); return nonFreeVf == null; } @Override public boolean isDeviceNetworkFree(HostDevice hostDevice) { HostDevice firstChild = getFirstChildDevice(hostDevice, null); if (firstChild == null || !isNetworkDevice(firstChild)) { return true; } return isNetworkDeviceFree(hostDevice); } private boolean isVfFree(HostDevice vf) { // Check if the VF is attached directly to a VM if (vf.getVmId() != null) { return false; } return isNetworkDeviceFree(vf); } private boolean isNetworkDeviceFree(HostDevice pciDevice) { // Check that there is no macvtap device on top of the VM- // nics with macvtap attached are not reported via the getVdsCaps VdsNetworkInterface vfNic = getNicByPciDevice(pciDevice); return vfNic != null && !isNetworkAttached(vfNic) && !isVlanDeviceAttached(vfNic) && !vfNic.isPartOfBond(); } @Override public HostDevice getFreeVf(VdsNetworkInterface nic, List<String> excludeVfs) { return getVf(nic, true, excludeVfs); } private HostDevice getVf(VdsNetworkInterface nic, final boolean shouldBeFree, final List<String> excludeVfs) { List<HostDevice> deviceList = getDevicesByHostId(nic.getVdsId()); HostDevice pciDevice = getPciDeviceByNic(nic, deviceList); if (pciDevice == null) { throw new NullPointerException("nic doesn't have a pci device"); } if (!isSriovDevice(pciDevice)) { throw new UnsupportedOperationException("'getVf' method should be called only for 'sriov' nics"); } List<HostDevice> vfs = getVfs(pciDevice, deviceList); return vfs.stream() .filter(vf -> isVfFree(vf) == shouldBeFree && (excludeVfs == null || !excludeVfs.contains(vf.getDeviceName()))) .findFirst() .orElse(null); } private HostDevice getVf(VdsNetworkInterface nic, final boolean shouldBeFree) { return getVf(nic, shouldBeFree, null); } private boolean isNetworkAttached(VdsNetworkInterface vfNic) { return vfNic.getNetworkName() != null; } boolean isVlanDeviceAttached(VdsNetworkInterface vfNic) { return NetworkUtils.interfaceHasVlan(vfNic, interfaceDao.getAllInterfacesForVds(vfNic.getVdsId())); } @Override public String getPciDeviceNameByNic(VdsNetworkInterface nic) { return getPciDeviceByNic(nic, getDevicesByHostId(nic.getVdsId())).getDeviceName(); } @Override public void setVmIdOnVfs(Guid hostId, Guid vmId, final Set<String> vfsNames) { List<HostDevice> hostDevices = hostDeviceDao.getHostDevicesByHostId(hostId); List<HostDevice> vfs = hostDevices.stream() .filter(device -> vfsNames.contains(device.getDeviceName()) && isVf(device)) .collect(Collectors.toList()); if (vmId != null) { HostDevice alreadyTakenVf = vfs.stream().filter(vf -> vf.getVmId() != null).findFirst().orElse(null); if (alreadyTakenVf != null) { throw new IllegalStateException( String.format("VF %s is already taken by VM %s", alreadyTakenVf.getName(), alreadyTakenVf.getVmId())); } } setVmIdOnVfsDevices(vmId, new HashSet<>(vfs)); } private void setVmIdOnVfsDevices(Guid vmId, Set<HostDevice> vfs) { for (HostDevice vf : vfs) { hostDeviceDao.setVmIdOnHostDevice(vf.getId(), vmId); } } @Override public Guid removeVmIdFromVfs(final Guid vmId) { List<HostDevice> hostDevices = hostDeviceDao.getAll(); List<HostDevice> vfsUsedByVm = hostDevices.stream() .filter(device -> vmId.equals(device.getVmId()) && isVf(device)).collect(Collectors.toList()); Guid hostId = vfsUsedByVm.isEmpty() ? null : vfsUsedByVm.get(0).getHostId(); if (hostId != null) { setVmIdOnVfsDevices(null, new HashSet<>(vfsUsedByVm)); } return hostId; } @Override public Map<Guid, Guid> getVfMap(final Guid hostId) { final List<VdsNetworkInterface> hostNics = interfaceDao.getAllInterfacesForVds(hostId); final List<HostDevice> hostDevices = hostDeviceDao.getHostDevicesByHostId(hostId); final Map<String, HostDevice> hostDevicesByName = Entities.entitiesByName(hostDevices); return hostNics.stream() .filter(new VfNicPredicate(hostDevices, hostDevicesByName)) .collect(Collectors.toMap(VdsNetworkInterface::getId, new VfNicToPfNicMapper(hostDevices, hostDevicesByName, hostNics))); } private boolean isVf(HostDevice device) { return StringUtils.isNotBlank(device.getParentPhysicalFunction()); } private class VfNicToPfNicMapper implements Function<VdsNetworkInterface, Guid> { private final List<HostDevice> hostDevices; private final Map<String, HostDevice> hostDevicesByName; private final List<VdsNetworkInterface> hostNics; public VfNicToPfNicMapper(List<HostDevice> hostDevices, Map<String, HostDevice> hostDevicesByName, List<VdsNetworkInterface> hostNics) { this.hostDevices = hostDevices; this.hostDevicesByName = hostDevicesByName; this.hostNics = hostNics; } @Override public Guid apply(VdsNetworkInterface nic) { final HostDevice vfPciDevice = getPciDeviceByNic(nic, hostDevices, hostDevicesByName); final HostDevice pfPciDevice = hostDevicesByName.get(vfPciDevice.getParentPhysicalFunction()); final VdsNetworkInterface pfNic = getNicByPciDevice(pfPciDevice, hostDevices, hostNics); return pfNic == null ? null : pfNic.getId(); } } private class VfNicPredicate implements Predicate<VdsNetworkInterface> { private final List<HostDevice> hostDevices; private final Map<String, HostDevice> hostDevicesByName; public VfNicPredicate(List<HostDevice> hostDevices, Map<String, HostDevice> hostDevicesByName) { this.hostDevices = hostDevices; this.hostDevicesByName = hostDevicesByName; } @Override public boolean test(VdsNetworkInterface nic) { if (nic.isBond() || NetworkCommonUtils.isVlan(nic)) { return false; } try { final HostDevice nicPciDevice = getPciDeviceByNic(nic, hostDevices, hostDevicesByName); return isVf(nicPciDevice); } catch (Exception e) { return false; } } } }