package org.ovirt.engine.core.vdsbroker.vdsbroker;
import static org.ovirt.engine.core.common.businessentities.network.NetworkStatus.OPERATIONAL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.ovirt.engine.core.bll.network.cluster.ManagementNetworkUtil;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.businessentities.Entities;
import org.ovirt.engine.core.common.businessentities.NonOperationalReason;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
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.VdsNetworkInterface.NetworkImplementationDetails;
import org.ovirt.engine.core.common.vdscommands.UserConfiguredNetworkData;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogDirector;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogable;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableImpl;
import org.ovirt.engine.core.dao.VmDynamicDao;
import org.ovirt.engine.core.dao.network.InterfaceDao;
import org.ovirt.engine.core.dao.network.NetworkAttachmentDao;
import org.ovirt.engine.core.dao.network.NetworkDao;
import org.ovirt.engine.core.utils.NetworkUtils;
import org.ovirt.engine.core.vdsbroker.NetworkImplementationDetailsUtils;
import org.ovirt.engine.core.vdsbroker.ResourceManager;
import org.ovirt.engine.core.vdsbroker.vdsbroker.predicates.DisplayInterfaceEqualityPredicate;
import org.ovirt.engine.core.vdsbroker.vdsbroker.predicates.IsNetworkOnInterfacePredicate;
@Singleton
final class HostNetworkTopologyPersisterImpl implements HostNetworkTopologyPersister {
private final VmDynamicDao vmDynamicDao;
private final InterfaceDao interfaceDao;
private final NetworkDao networkDao;
private final Instance<ResourceManager> resourceManager;
private final ManagementNetworkUtil managementNetworkUtil;
private final AuditLogDirector auditLogDirector = new AuditLogDirector();
private final NetworkAttachmentDao networkAttachmentDao;
private final NetworkImplementationDetailsUtils networkImplementationDetailsUtils;
@Inject
HostNetworkTopologyPersisterImpl(VmDynamicDao vmDynamicDao,
InterfaceDao interfaceDao,
NetworkAttachmentDao networkAttachmentDao,
NetworkDao networkDao,
Instance<ResourceManager> resourceManager,
NetworkImplementationDetailsUtils networkImplementationDetailsUtils,
ManagementNetworkUtil managementNetworkUtil) {
Validate.notNull(networkDao, "networkAttachmentDao can not be null");
Validate.notNull(networkDao, "networkDao can not be null");
Validate.notNull(interfaceDao, "interfaceDao can not be null");
Validate.notNull(vmDynamicDao, "vmDynamicDao can not be null");
Validate.notNull(resourceManager, "resourceManager can not be null");
Validate.notNull(networkImplementationDetailsUtils, "networkImplementationDetailsUtils can not be null");
Validate.notNull(managementNetworkUtil, "managementNetworkUtil can not be null");
this.vmDynamicDao = vmDynamicDao;
this.interfaceDao = interfaceDao;
this.networkDao = networkDao;
this.resourceManager = resourceManager;
this.managementNetworkUtil = managementNetworkUtil;
this.networkAttachmentDao = networkAttachmentDao;
this.networkImplementationDetailsUtils = networkImplementationDetailsUtils;
}
@Override
public NonOperationalReason persistAndEnforceNetworkCompliance(VDS host,
boolean skipManagementNetwork,
UserConfiguredNetworkData userConfiguredData) {
List<VdsNetworkInterface> dbIfaces = interfaceDao.getAllInterfacesForVds(host.getId());
List<Network> clusterNetworks = networkDao.getAllForCluster(host.getClusterId());
persistTopology(host, dbIfaces, clusterNetworks, userConfiguredData);
NonOperationalReason nonOperationalReason =
enforceNetworkCompliance(host, skipManagementNetwork, clusterNetworks);
auditNetworkCompliance(host, dbIfaces, clusterNetworks);
return nonOperationalReason;
}
private NonOperationalReason enforceNetworkCompliance(VDS host,
boolean skipManagementNetwork,
List<Network> clusterNetworks) {
if (host.getStatus() != VDSStatus.Maintenance) {
if (skipManagementNetwork) {
skipManagementNetworkCheck(host.getInterfaces(), clusterNetworks, host.getClusterId());
}
Map<String, String> customLogValues;
// here we check if the host networks match it's cluster networks
String networks = getMissingOperationalClusterNetworks(host, clusterNetworks);
if (networks.length() > 0) {
customLogValues = new HashMap<>();
customLogValues.put("Networks", networks);
setNonOperational(host, NonOperationalReason.NETWORK_UNREACHABLE, customLogValues);
return NonOperationalReason.NETWORK_UNREACHABLE;
}
// Check that VM networks are implemented above a bridge.
networks = getVmNetworksImplementedAsBridgeless(host, clusterNetworks);
if (networks.length() > 0) {
customLogValues = new HashMap<>();
customLogValues.put("Networks", networks);
setNonOperational(host, NonOperationalReason.VM_NETWORK_IS_BRIDGELESS, customLogValues);
return NonOperationalReason.VM_NETWORK_IS_BRIDGELESS;
}
}
return NonOperationalReason.NONE;
}
private void auditNetworkCompliance(VDS host,
List<VdsNetworkInterface> dbIfaces,
List<Network> clusterNetworks) {
if (host.getStatus() == VDSStatus.Maintenance) {
return;
}
final Map<String, Network> clusterNetworksByName = Entities.entitiesByName(clusterNetworks);
final Collection<Network> dbHostNetworks = findNetworksOnInterfaces(dbIfaces, clusterNetworksByName);
logChangedDisplayNetwork(host, dbHostNetworks, dbIfaces);
logUnsynchronizedNetworks(host, clusterNetworksByName);
}
@Override
public NonOperationalReason persistAndEnforceNetworkCompliance(VDS host) {
return persistAndEnforceNetworkCompliance(host, false, new UserConfiguredNetworkData());
}
private void skipManagementNetworkCheck(List<VdsNetworkInterface> ifaces, List<Network> clusterNetworks, Guid clusterId) {
final Network managementNetwork = managementNetworkUtil.getManagementNetwork(clusterId);
final String managementNetworkName = managementNetwork.getName();
for (VdsNetworkInterface iface : ifaces) {
if (managementNetworkName.equals(iface.getNetworkName())) {
return;
}
}
for (Iterator<Network> iterator = clusterNetworks.iterator(); iterator.hasNext();) {
Network network = iterator.next();
if (managementNetworkName.equals(network.getName())) {
iterator.remove();
break;
}
}
}
private void logChangedDisplayNetwork(VDS host,
Collection<Network> engineHostNetworks,
Collection<VdsNetworkInterface> engineInterfaces) {
if (isVmRunningOnHost(host.getId())) {
final Network engineDisplayNetwork = findDisplayNetwork(host.getClusterId(), engineHostNetworks);
if (engineDisplayNetwork == null) {
return;
}
final IsNetworkOnInterfacePredicate isNetworkOnInterfacePredicate =
new IsNetworkOnInterfacePredicate(engineDisplayNetwork.getName());
final VdsNetworkInterface vdsmDisplayInterface =
host.getInterfaces().stream().filter(isNetworkOnInterfacePredicate).findFirst().orElse(null);
final VdsNetworkInterface engineDisplayInterface =
engineInterfaces.stream().filter(isNetworkOnInterfacePredicate).findFirst().orElse(null);
final DisplayInterfaceEqualityPredicate displayIneterfaceEqualityPredicate =
new DisplayInterfaceEqualityPredicate(engineDisplayInterface);
if (vdsmDisplayInterface == null // the display interface is't on host anymore
|| !displayIneterfaceEqualityPredicate.test(vdsmDisplayInterface)) {
final AuditLogable loggable = createAuditLogForHost(host);
auditLogDirector.log(loggable, AuditLogType.NETWORK_UPDATE_DISPLAY_FOR_HOST_WITH_ACTIVE_VM);
}
}
}
private AuditLogable createAuditLogForHost(VDS host) {
final AuditLogable loggable = new AuditLogableImpl();
loggable.setVdsId(host.getId());
loggable.setVdsName(host.getName());
return loggable;
}
private boolean isVmRunningOnHost(Guid hostId) {
return !vmDynamicDao.getAllRunningForVds(hostId).isEmpty();
}
private Collection<Network> findNetworksOnInterfaces(Collection<VdsNetworkInterface> ifaces,
Map<String, Network> clusterNetworksByName) {
final Collection<Network> networks = new ArrayList<>();
for (VdsNetworkInterface iface : ifaces) {
final String interfaceNetworkName = iface.getNetworkName();
if (clusterNetworksByName.containsKey(interfaceNetworkName)) {
final Network network = clusterNetworksByName.get(interfaceNetworkName);
networks.add(network);
}
}
return networks;
}
private Network findDisplayNetwork(Guid clusterId, Collection<Network> networks) {
Network managementNetwork = null;
for (Network network : networks) {
if (network.getCluster().isDisplay()) {
return network;
}
if (managementNetworkUtil.isManagementNetwork(network.getId(), clusterId)) {
managementNetwork = network;
}
}
return managementNetwork;
}
private void logUnsynchronizedNetworks(VDS host, Map<String, Network> networks) {
List<String> networkNames = new ArrayList<>();
for (VdsNetworkInterface iface : host.getInterfaces()) {
Network network = networks.get(iface.getNetworkName());
NetworkImplementationDetails networkImplementationDetails =
networkImplementationDetailsUtils.calculateNetworkImplementationDetails(iface, network);
if (networkImplementationDetails != null
&& !networkImplementationDetails.isInSync()
&& networkImplementationDetails.isManaged()) {
networkNames.add(iface.getNetworkName());
}
}
if (!networkNames.isEmpty()) {
final AuditLogable logable = createAuditLogForHost(host);
logable.addCustomValue("Networks", StringUtils.join(networkNames, ","));
auditLogDirector.log(logable, AuditLogType.VDS_NETWORKS_OUT_OF_SYNC);
}
}
/**
* Persists the network topology as reported by vdsm, with respect to the following:
* <ul>
* <li>Pre-configured provided interfaces will maintain their settings (labels, properties and QoS)</li>
* <li>Network attachments will be created for nics without it and on which a known network is configured</li>
* <li>A nic which existed on db and wasn't reported will be removed with its network attachment</li>
* </ul>
*
* @param host
* the host for which the network topology should be persisted and contains the list of the reported nics
* @param dbNics
* network interfaces from the database prior to vdsm report
* @param clusterNetworks
* the networks which assigned to the host's cluster
* @param userConfiguredData
* The network configuration as provided by the user, for which engine managed data will be preserved.
*/
private void persistTopology(VDS host,
List<VdsNetworkInterface> dbNics,
List<Network> clusterNetworks,
UserConfiguredNetworkData userConfiguredData) {
final HostNetworkInterfacesPersister networkInterfacesPersister = new HostNetworkInterfacesPersisterImpl(
interfaceDao,
host.getInterfaces(),
dbNics,
userConfiguredData.getUserOverriddenNicValuesByNicName());
networkInterfacesPersister.persistTopology();
createHostNetworkAttachmentsPersister(host, clusterNetworks, userConfiguredData).persistNetworkAttachments();
}
private HostNetworkAttachmentsPersister createHostNetworkAttachmentsPersister(VDS host,
List<Network> clusterNetworks,
UserConfiguredNetworkData userConfiguredData) {
return new HostNetworkAttachmentsPersister(networkAttachmentDao,
host.getId(),
host.getInterfaces(),
userConfiguredData.getNetworkAttachments(),
userConfiguredData.getRemovedNetworkAttachments(),
clusterNetworks);
}
private String getVmNetworksImplementedAsBridgeless(VDS host, List<Network> clusterNetworks) {
Map<String, VdsNetworkInterface> interfacesByNetworkName =
NetworkUtils.hostInterfacesByNetworkName(host.getInterfaces());
List<String> networkNames = new ArrayList<>();
for (Network net : clusterNetworks) {
if (net.isVmNetwork()
&& interfacesByNetworkName.containsKey(net.getName())
&& !interfacesByNetworkName.get(net.getName()).isBridged()) {
networkNames.add(net.getName());
}
}
return StringUtils.join(networkNames, ",");
}
private String getMissingOperationalClusterNetworks(VDS host, List<Network> clusterNetworks) {
List<String> networkNames = new ArrayList<>();
for (Network net : clusterNetworks) {
if (net.getCluster().getStatus() == OPERATIONAL &&
net.getCluster().isRequired() &&
!host.getNetworkNames().contains(net.getName())) {
networkNames.add(net.getName());
}
}
return StringUtils.join(networkNames, ",");
}
private void setNonOperational(VDS host, NonOperationalReason reason, Map<String, String> customLogValues) {
resourceManager.get().getEventListener().vdsNonOperational(host.getId(), reason, true, Guid.Empty, customLogValues);
}
}