package org.ovirt.engine.core.bll.scheduling.policyunits; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.ValidationResult; import org.ovirt.engine.core.bll.network.host.VfScheduler; import org.ovirt.engine.core.bll.scheduling.PolicyUnitImpl; import org.ovirt.engine.core.bll.scheduling.SchedulingUnit; import org.ovirt.engine.core.bll.scheduling.pending.PendingResourceManager; import org.ovirt.engine.core.common.businessentities.Cluster; import org.ovirt.engine.core.common.businessentities.Entities; import org.ovirt.engine.core.common.businessentities.VDS; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.network.Ipv4BootProtocol; 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.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.scheduling.PerHostMessages; import org.ovirt.engine.core.common.scheduling.PolicyUnit; import org.ovirt.engine.core.common.scheduling.PolicyUnitType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.dao.network.InterfaceDao; import org.ovirt.engine.core.dao.network.NetworkDao; import org.ovirt.engine.core.dao.network.VmNetworkInterfaceDao; import org.ovirt.engine.core.di.Injector; import org.ovirt.engine.core.utils.NetworkUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SchedulingUnit( guid = "72163d1c-9468-4480-99d9-0888664eb143", name = "Network", description = "Filters out hosts that are missing networks required by VM NICs, or missing cluster's display network", type = PolicyUnitType.FILTER ) public class NetworkPolicyUnit extends PolicyUnitImpl { private static final Logger log = LoggerFactory.getLogger(NetworkPolicyUnit.class); public NetworkPolicyUnit(PolicyUnit policyUnit, PendingResourceManager pendingResourceManager) { super(policyUnit, pendingResourceManager); } @Override public List<VDS> filter(Cluster cluster, List<VDS> hosts, VM vm, Map<String, String> parameters, PerHostMessages messages) { if (hosts == null || hosts.isEmpty()) { return null; } List<VDS> toRemoveHostList = new ArrayList<>(); List<VmNetworkInterface> vmNICs = getVmNetworkInterfaceDao().getAllForVm(vm.getId()); Guid clusterId = hosts.get(0).getClusterId(); List<Network> clusterNetworks = getNetworkDao().getAllForCluster(clusterId); Map<String, Network> networksByName = Entities.entitiesByName(clusterNetworks); Map<Guid, List<String>> hostNics = getInterfaceDao().getHostNetworksByCluster(clusterId); Network displayNetwork = NetworkUtils.getDisplayNetwork(clusterNetworks); Map<Guid, VdsNetworkInterface> hostDisplayNics = getDisplayNics(displayNetwork); for (VDS host : hosts) { ValidationResult result = validateRequiredNetworksAvailable(host, vm, vmNICs, displayNetwork, networksByName, hostNics.get(host.getId()), hostDisplayNics.get(host.getId())); if (result.isValid()) { result = validatePassthroughVnics(vm.getId(), host, vmNICs); } if (!result.isValid()) { toRemoveHostList.add(host); messages.addMessages(host.getId(), result.getVariableReplacements()); messages.addMessages(host.getId(), result.getMessagesAsStrings()); } } hosts.removeAll(toRemoveHostList); return hosts; } public Map<Guid, VdsNetworkInterface> getDisplayNics(Network displayNetwork) { Map<Guid, VdsNetworkInterface> displayNics = new HashMap<>(); if (displayNetwork != null) { List<VdsNetworkInterface> nics = getInterfaceDao().getVdsInterfacesByNetworkId(displayNetwork.getId()); for (VdsNetworkInterface nic : nics) { displayNics.put(nic.getVdsId(), nic); } } return displayNics; } /** * Determine whether all required Networks are attached to the Host's Nics. A required Network, depending on * ConfigValue.OnlyRequiredNetworksMandatoryForVdsSelection, is defined as: 1. false: any network that is defined on * an Active vNic of the VM or the cluster's display network. 2. true: a Cluster-Required Network that is defined on * an Active vNic of the VM. * Vnic configured for 'passthrough' will skip the network existence check for the host and will be validated later by * {@code validatePassthroughVnics(...)} * * @param vds * the Host * @param vm * the VM * @param hostNetworks * the Host network names * @param displayNic * the interface on top the display network is configured * @return the result of network compatibility check */ private ValidationResult validateRequiredNetworksAvailable(VDS vds, VM vm, List<VmNetworkInterface> vmNICs, Network displayNetwork, Map<String, Network> networksByName, List<String> hostNetworks, VdsNetworkInterface displayNic) { List<String> missingIfs = new ArrayList<>(); boolean onlyRequiredNetworks = Config.<Boolean> getValue(ConfigValues.OnlyRequiredNetworksMandatoryForVdsSelection); for (final VmNetworkInterface vmIf : vmNICs) { boolean found = false; boolean skipNetworkExistenceCheckForVnicPassthrough = vmIf.isPassthrough(); if (vmIf.getNetworkName() == null) { found = true; } else { for (String networkName : hostNetworks) { if (skipNetworkExistenceCheckForVnicPassthrough || !networkRequiredOnVds(vmIf, networksByName, onlyRequiredNetworks) || StringUtils.equals(vmIf.getNetworkName(), networkName)) { found = true; break; } } } if (!found) { missingIfs.add(vmIf.getNetworkName()); } } if (!missingIfs.isEmpty()) { String nics = StringUtils.join(missingIfs, ", "); log.warn("host {} is missing networks required by VM nics {}", vds.getName(), nics); return new ValidationResult(EngineMessage.VAR__DETAIL__NETWORK_MISSING, String.format("$networkNames %1$s", nics)); } return validateDisplayNetworkAvailability(vds, onlyRequiredNetworks, displayNic, displayNetwork); } private boolean networkRequiredOnVds(VmNetworkInterface vmIface, Map<String, Network> networksByName, boolean onlyRequiredNetworks) { if (!vmIface.isPlugged()) { return false; } Network network = networksByName.get(vmIface.getNetworkName()); if (onlyRequiredNetworks) { return network.getCluster().isRequired(); } return !network.isExternal(); } /** * Determines whether the cluster's display network is defined on the host. * * @param host * the host * @param onlyRequiredNetworks * should be false, in order the method to be non-trivial. * @param displayNic * the interface on top the display network is configured * @param displayNetwork * the cluster's display network * @return the result of the display network validity check on the given host */ private ValidationResult validateDisplayNetworkAvailability(VDS host, boolean onlyRequiredNetworks, VdsNetworkInterface displayNic, Network displayNetwork) { if (onlyRequiredNetworks) { return ValidationResult.VALID; } if (displayNetwork == null) { return ValidationResult.VALID; } // Check if display network attached to host and has a proper boot protocol if (displayNic == null) { log.warn("host {} is missing the cluster's display network {}", host.getName(), displayNetwork.getName()); return new ValidationResult(EngineMessage.VAR__DETAIL__DISPLAY_NETWORK_MISSING, String.format("$DisplayNetwork %1$s", displayNetwork.getName())); } if (displayNic.getIpv4BootProtocol() == Ipv4BootProtocol.NONE) { log.warn("Host {} has the display network {} configured with improper boot protocol on interface {}.", host.getName(), displayNetwork.getName(), displayNic.getName()); return new ValidationResult(EngineMessage.VAR__DETAIL__DISPLAY_NETWORK_HAS_NO_BOOT_PROTOCOL, String.format("$DisplayNetwork %1$s", displayNetwork.getName())); } return ValidationResult.VALID; } private ValidationResult validatePassthroughVnics(Guid vmId, VDS host, List<VmNetworkInterface> vnics) { VfScheduler vfScheduler = Injector.get(VfScheduler.class); List<String> problematicVnics = vfScheduler.validatePassthroughVnics(vmId, host.getId(), vnics); if (!problematicVnics.isEmpty()) { String vnicsString = StringUtils.join(problematicVnics, ", "); log.warn("host {} doesn't contain suitable virtual functions for VM nics {}", host.getName(), vnicsString); return new ValidationResult(EngineMessage.VAR__DETAIL__NO_SUITABLE_VF, String.format("$vnicNames %1$s", vnicsString)); } return ValidationResult.VALID; } private VmNetworkInterfaceDao getVmNetworkInterfaceDao() { return DbFacade.getInstance().getVmNetworkInterfaceDao(); } private InterfaceDao getInterfaceDao() { return DbFacade.getInstance().getInterfaceDao(); } private NetworkDao getNetworkDao() { return DbFacade.getInstance().getNetworkDao(); } }