Moti Asayag has uploaded a new change for review. Change subject: engine: HostSetupNetorks command #DRAFT# ......................................................................
engine: HostSetupNetorks command #DRAFT# A replacement to the old SetupNetworkCommand which aims to behave as setupnetworks VDSM API compliant. Change-Id: I60077019f308f371f21fb7b5545ba48adb38bd06 Signed-off-by: Moti Asayag <masa...@redhat.com> --- A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/host/HostSetupNetworksCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/host/HostSetupNetworksValidator.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java 3 files changed, 962 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/33/33333/1 diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/host/HostSetupNetworksCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/host/HostSetupNetworksCommand.java new file mode 100644 index 0000000..0200c47 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/host/HostSetupNetworksCommand.java @@ -0,0 +1,321 @@ +package org.ovirt.engine.core.bll.network.host; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.commons.lang.StringUtils; +import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; +import org.ovirt.engine.core.bll.VdsCommand; +import org.ovirt.engine.core.bll.VdsHandler; +import org.ovirt.engine.core.bll.context.CommandContext; +import org.ovirt.engine.core.bll.network.cluster.NetworkClusterHelper; +import org.ovirt.engine.core.common.FeatureSupported; +import org.ovirt.engine.core.common.action.HostSetupNetworksParameters; +import org.ovirt.engine.core.common.businessentities.BusinessEntityMap; +import org.ovirt.engine.core.common.businessentities.Entities; +import org.ovirt.engine.core.common.businessentities.VDS; +import org.ovirt.engine.core.common.businessentities.VDSStatus; +import org.ovirt.engine.core.common.businessentities.network.Bond; +import org.ovirt.engine.core.common.businessentities.network.IpConfiguration; +import org.ovirt.engine.core.common.businessentities.network.Network; +import org.ovirt.engine.core.common.businessentities.network.NetworkAttachment; +import org.ovirt.engine.core.common.businessentities.network.NetworkBootProtocol; +import org.ovirt.engine.core.common.businessentities.network.VdsNetworkInterface; +import org.ovirt.engine.core.common.config.Config; +import org.ovirt.engine.core.common.config.ConfigValues; +import org.ovirt.engine.core.common.errors.VdcBLLException; +import org.ovirt.engine.core.common.errors.VdcBllErrors; +import org.ovirt.engine.core.common.errors.VdcBllMessages; +import org.ovirt.engine.core.common.interfaces.FutureVDSCall; +import org.ovirt.engine.core.common.vdscommands.FutureVDSCommandType; +import org.ovirt.engine.core.common.vdscommands.HostNetwork; +import org.ovirt.engine.core.common.vdscommands.HostSetupNetworksVdsCommandParameters; +import org.ovirt.engine.core.common.vdscommands.VDSCommandType; +import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; +import org.ovirt.engine.core.common.vdscommands.VdsIdAndVdsVDSCommandParametersBase; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.compat.Version; +import org.ovirt.engine.core.dao.network.NetworkQoSDao; +import org.ovirt.engine.core.utils.NetworkUtils; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; +import org.ovirt.engine.core.utils.transaction.TransactionMethod; +import org.ovirt.engine.core.utils.transaction.TransactionSupport; +import org.ovirt.engine.core.vdsbroker.vdsbroker.CollectVdsNetworkDataVDSCommand; + +@NonTransactiveCommandAttribute +public class HostSetupNetworksCommand<T extends HostSetupNetworksParameters> extends VdsCommand<T> { + + static final List<VDSStatus> SUPPORTED_HOST_STATUSES = + Arrays.asList(VDSStatus.Maintenance, VDSStatus.Up, VDSStatus.NonOperational); + private static final Log log = LogFactory.getLog(HostSetupNetworksCommand.class); + private HostSetupNetworksValidator validator; + private Map<Guid, Network> clusterNetworks; + private Set<String> removedNetworks; + private Set<String> removedBonds; + private List<VdsNetworkInterface> existingNics; + private List<NetworkAttachment> existingAttachments; + + public HostSetupNetworksCommand(T parameters) { + this(parameters, null); + } + + public HostSetupNetworksCommand(T parameters, CommandContext commandContext) { + super(parameters, commandContext); + setVdsId(parameters.getVdsId()); + } + + @Override + protected void setActionMessageParameters() { + addCanDoActionMessage(VdcBllMessages.VAR__ACTION__SETUP); + addCanDoActionMessage(VdcBllMessages.VAR__TYPE__NETWORKS); + } + + @Override + protected boolean canDoAction() { + VDS host = getVds(); + + if (host == null) { + addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_HOST_NOT_EXIST); + return false; + } + + if (!hostStatusLegalForSetupNetworks(host)) { + addCanDoActionMessage(VdcBllMessages.VAR__HOST_STATUS__UP_MAINTENANCE_OR_NON_OPERATIONAL); + addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_VDS_STATUS_ILLEGAL); + return false; + } + + NetworkAttachmentCompleter completer = new NetworkAttachmentCompleter(getExistingNics()); + completer.completeNicNames(getParameters().getNetworkAttachments()); + completer.completeBondNames(getParameters().getBonds()); + completer.completeBondNames(getParameters().getRemovedBonds()); + + validator = + new HostSetupNetworksValidator(host, + getParameters(), + getExistingNics(), + getExistingAttachments(), + getClusterNetworks()); + List<String> validationMessages = validator.validate(); + + if (!validationMessages.isEmpty()) { + for (String msg : validationMessages) { + addCanDoActionMessage(msg); + } + + return false; + } + + return true; + } + + protected boolean hostStatusLegalForSetupNetworks(VDS vds) { + return SUPPORTED_HOST_STATUSES.contains(vds.getStatus()) + || vds.getStatus() == VDSStatus.Installing && isInternalExecution(); + } + + @Override + protected void executeCommand() { + if (noChangesDetected()) { + log.infoFormat("No changes were detected in setup networks for host {0} (ID: {1})", + getVdsName(), + getVdsId()); + setSucceeded(true); + return; + } + + final HostSetupNetworksVdsCommandParameters hostCmdParams = new HostSetupNetworksVdsCommandParameters( + getVdsId(), + getNetworks(), + getRemovedNetworks(), + getParameters().getBonds(), + getRemovedBonds()); + + hostCmdParams.setCheckConnectivity(getParameters().isCheckConnectivity()); + + int timeout = getSetupNetworksTimeout(); + hostCmdParams.setConectivityTimeout(timeout); + + FutureVDSCall<VDSReturnValue> setupNetworksTask = + getBackend().getResourceManager() + .runFutureVdsCommand(FutureVDSCommandType.HostSetupNetworks, hostCmdParams); + + try { + VDSReturnValue retVal = setupNetworksTask.get(timeout, TimeUnit.SECONDS); + if (retVal != null) { + if (!retVal.getSucceeded() && retVal.getVdsError() == null && getParameters().isCheckConnectivity()) { + throw new VdcBLLException(VdcBllErrors.SETUP_NETWORKS_ROLLBACK, retVal.getExceptionString()); + } + + VdsHandler.handleVdsResult(retVal); + + if (retVal.getSucceeded()) { + persistNetworkChanges(); + setSucceeded(true); + } + } + } catch (TimeoutException e) { + log.debugFormat("Host Setup networks command timed out for {0} seconds", timeout); + } + } + + protected Integer getSetupNetworksTimeout() { + return getParameters().getConectivityTimeout() != null ? getParameters().getConectivityTimeout() + : Config.<Integer> getValue(ConfigValues.NetworkConnectivityCheckTimeoutInSeconds); + } + + private boolean defaultRouteRequired(Network network, IpConfiguration ipConfiguration) { + return NetworkUtils.isManagementNetwork(network) + && ipConfiguration != null + && (ipConfiguration.getBootProtocol() == NetworkBootProtocol.DHCP + || ipConfiguration.getBootProtocol() == NetworkBootProtocol.STATIC_IP + && StringUtils.isNotEmpty(ipConfiguration.getGateway())); + } + + private boolean noChangesDetected() { + return getNetworks().isEmpty() + && getRemovedNetworks().isEmpty() + && getParameters().getBonds().isEmpty() + && getRemovedBonds().isEmpty(); + } + + private Set<String> getRemovedBonds() { + if (removedBonds == null) { + removedBonds = new HashSet<>(getParameters().getRemovedBonds().size()); + for (Bond bond : getParameters().getRemovedBonds()) { + removedBonds.add(bond.getName()); + } + } + + return removedBonds; + } + + private List<VdsNetworkInterface> getExistingNics() { + if (existingNics == null) { + existingNics = getDbFacade().getInterfaceDao().getAllInterfacesForVds(getVdsId()); + NetworkQoSDao qosDao = getDbFacade().getNetworkQosDao(); + + for (VdsNetworkInterface iface : existingNics) { + Network network = getClusterNetworks().get(iface.getNetworkName()); + iface.setNetworkImplementationDetails(NetworkUtils.calculateNetworkImplementationDetails(network, + network == null ? null : qosDao.get(network.getQosId()), + iface)); + } + } + + return existingNics; + } + + private List<NetworkAttachment> getExistingAttachments() { + if (existingAttachments == null) { + existingAttachments = getDbFacade().getNetworkAttachmentDao().getAllForHost(getVdsId()); + } + + return existingAttachments; + } + + private Set<String> getRemovedNetworks() { + if (removedNetworks == null) { + removedNetworks = new HashSet<>(getParameters().getRemovedNetworkAttachments().size()); + for (NetworkAttachment attachment : getParameters().getRemovedNetworkAttachments()) { + removedNetworks.add(getClusterNetworks().get(attachment.getNetworkId()).getName()); + } + } + + return removedNetworks; + } + + private Map<Guid, Network> getClusterNetworks() { + if (clusterNetworks == null) { + clusterNetworks = Entities.businessEntitiesById(getNetworkDAO().getAllForCluster(getVdsGroupId())); + } + + return clusterNetworks; + } + + private List<HostNetwork> getNetworks() { + boolean defaultRouteSupported = false; + Set<Version> supportedClusterVersionsSet = getVds().getSupportedClusterVersionsSet(); + if (supportedClusterVersionsSet == null || supportedClusterVersionsSet.isEmpty()) { + log.warnFormat("Host {0} ({1}) doesn't contain Supported Cluster Versions, therefore 'defaultRoute'" + + " will not be sent via the SetupNetworks", getVdsName(), getVdsId()); + } else if (FeatureSupported.defaultRoute(Collections.max(supportedClusterVersionsSet))) { + defaultRouteSupported = true; + } + + List<HostNetwork> networksToConfigure = new ArrayList<>(getParameters().getNetworkAttachments().size()); + BusinessEntityMap<VdsNetworkInterface> nics = new BusinessEntityMap<>(getExistingNics()); + + for (NetworkAttachment attachment : getParameters().getNetworkAttachments()) { + HostNetwork networkToConfigure = new HostNetwork(); + Network network = getClusterNetworks().get(attachment.getNetworkId()); + networkToConfigure.setNetwork(network); + networkToConfigure.setBonding(isBonding(attachment, nics)); + + if (defaultRouteSupported && defaultRouteRequired(network, attachment.getIpConfiguration())) { + networkToConfigure.setDefaultRoute(true); + } + + networksToConfigure.add(networkToConfigure); + } + + return networksToConfigure; + } + + private boolean isBonding(NetworkAttachment attachment, BusinessEntityMap<VdsNetworkInterface> nics) { + for (Bond bond : getParameters().getBonds()) { + if (bond.getName() != null && bond.getName().equals(attachment.getNicName())) { + return true; + } + } + + VdsNetworkInterface attachedNic = nics.get(attachment.getId(), attachment.getNicName()); + assert (attachedNic != null); + return Boolean.TRUE.equals(attachedNic.getBonded()); + } + + private void persistNetworkChanges() { + TransactionSupport.executeInNewTransaction(new TransactionMethod<Void>() { + @Override + public Void runInTransaction() { + + VDSReturnValue returnValue = + runVdsCommand(VDSCommandType.GetCapabilities, new VdsIdAndVdsVDSCommandParametersBase(getVds())); + getVdsDynamicDao().updateNetConfigDirty(getVds().getId(), getVds().getNetConfigDirty()); + VDS updatedHost = (VDS) returnValue.getReturnValue(); + + // save the new network topology to DB + CollectVdsNetworkDataVDSCommand.persistAndEnforceNetworkCompliance(updatedHost, + false, + null); + + // TODO persist network attachments for interfaces + + // Update cluster networks (i.e. check if need to activate each new network) + for (Network net : getModifiedNetworks()) { + NetworkClusterHelper.setStatus(getVdsGroupId(), net); + } + + return null; + } + + private List<Network> getModifiedNetworks() { + List<Network> modifiedNetworks = new ArrayList<>(getParameters().getNetworkAttachments().size()); + for (NetworkAttachment attachment : getParameters().getNetworkAttachments()) { + modifiedNetworks.add(getClusterNetworks().get(attachment)); + } + + return modifiedNetworks; + } + }); + } + +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/host/HostSetupNetworksValidator.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/host/HostSetupNetworksValidator.java new file mode 100644 index 0000000..c8f7400 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/network/host/HostSetupNetworksValidator.java @@ -0,0 +1,635 @@ +package org.ovirt.engine.core.bll.network.host; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; + +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.StringUtils; +import org.ovirt.engine.core.bll.Backend; +import org.ovirt.engine.core.bll.ValidationResult; +import org.ovirt.engine.core.bll.network.VmInterfaceManager; +import org.ovirt.engine.core.bll.validator.NetworkAttachmentValidator; +import org.ovirt.engine.core.bll.validator.NetworkQosValidator; +import org.ovirt.engine.core.common.FeatureSupported; +import org.ovirt.engine.core.common.action.HostSetupNetworksParameters; +import org.ovirt.engine.core.common.businessentities.BusinessEntityMap; +import org.ovirt.engine.core.common.businessentities.Entities; +import org.ovirt.engine.core.common.businessentities.VDS; +import org.ovirt.engine.core.common.businessentities.network.Bond; +import org.ovirt.engine.core.common.businessentities.network.Network; +import org.ovirt.engine.core.common.businessentities.network.NetworkAttachment; +import org.ovirt.engine.core.common.businessentities.network.NetworkBootProtocol; +import org.ovirt.engine.core.common.businessentities.network.VdsNetworkInterface; +import org.ovirt.engine.core.common.config.Config; +import org.ovirt.engine.core.common.config.ConfigValues; +import org.ovirt.engine.core.common.errors.VdcBllMessages; +import org.ovirt.engine.core.common.utils.customprop.SimpleCustomPropertiesUtil; +import org.ovirt.engine.core.common.utils.customprop.ValidationError; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.utils.NetworkUtils; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class HostSetupNetworksValidator { + protected static final String VIOLATING_ENTITIES_LIST_FORMAT = "${0}_LIST {1}"; + private static final Log log = LogFactory.getLog(HostSetupNetworksValidator.class); + private HostSetupNetworksParameters params; + private VDS host; + private Map<VdcBllMessages, List<String>> violations = new HashMap<VdcBllMessages, List<String>>(); + private Map<String, VdsNetworkInterface> existingIfaces; + private Map<String, Network> existingClusterNetworks; + + private List<Network> modifiedNetworks = new ArrayList<Network>(); + private Map<String, VdsNetworkInterface> removedBonds = new HashMap<String, VdsNetworkInterface>(); + private List<VdsNetworkInterface> modifiedInterfaces = new ArrayList<>(); + private List<NetworkAttachment> existingAttachments; + + /** All network`s names that are attached to some sort of interface. */ + private Set<String> attachedNetworksNames = new HashSet<String>(); + + private Map<String, List<NetworkType>> ifacesWithExclusiveNetwork = new HashMap<String, List<NetworkType>>(); + + private boolean hostNetworkQosSupported; + private boolean networkCustomPropertiesSupported; + private Map<Guid, Network> clusterNetworks; + + public HostSetupNetworksValidator(VDS host, + HostSetupNetworksParameters params, + List<VdsNetworkInterface> existingNics, + List<NetworkAttachment> existingAttachments, + Map<Guid, Network> clusterNetworks) { + this.host = host; + this.params = params; + this.existingAttachments = existingAttachments; + this.existingIfaces = Entities.entitiesByName(existingNics); + this.clusterNetworks = clusterNetworks; + + setSupportedFeatures(); + } + + private void setSupportedFeatures() { + hostNetworkQosSupported = FeatureSupported.hostNetworkQos(host.getVdsGroupCompatibilityVersion()); + networkCustomPropertiesSupported = + FeatureSupported.networkCustomProperties(host.getVdsGroupCompatibilityVersion()); + } + + protected List<String> translateErrorMessages(List<String> messages) { + return Backend.getInstance().getErrorsTranslator().TranslateErrorText(messages); + } + + public List<String> validate() { + Map<Guid, NetworkAttachment> attachmentsById = Entities.businessEntitiesById(existingAttachments); + BusinessEntityMap<Bond> removedBonds = new BusinessEntityMap<>(params.getRemovedBonds()); + + if (validModifiedNetworkAttachments(attachmentsById) + && validRemovedNetworkAttachments(attachmentsById, removedBonds) + && validModifiedBonds(removedBonds) + && validRemovedBonds(removedBonds) + && validateNotRemovingUsedNetworkByVms()) { + + Collection<NetworkAttachment> attachmentsToConfigure = getAttachmentsToConfigure(removedBonds); + if (networksUniquelyConfiguredOnHost(attachmentsToConfigure)) { + + } + + Map<String, List<Network>> nicsToNetworks = new HashMap<>(); + for (NetworkAttachment attachment : attachmentsToConfigure) { + String nicName = attachment.getNicName(); + if (!nicsToNetworks.containsKey(nicName)) { + nicsToNetworks.put(nicName, new ArrayList<Network>()); + } + + Network networkToConfigure = clusterNetworks.get(attachment.getNetworkId()); + nicsToNetworks.get(nicName).add(networkToConfigure); + } + + for (Entry<String, List<Network>> nicToNetworks : nicsToNetworks) { + String nic = nicToNetworks.getKey(); + List<Network> networksOnNic = nicToNetworks.getValue(); + } + + // TODO: verify attachment can co-exist on the nic + validateNetworkExclusiveOnIface(iface, + determineNetworkType(network.getVlanId(), network.isVmNetwork())); + + + if (existingIface != null && existingIface.getNetworkImplementationDetails() != null + && !existingIface.getNetworkImplementationDetails().isInSync()) { + iface.setVlanId(existingIface.getVlanId()); + if (networkShouldBeSynced(networkName)) { + modifiedNetworks.add(network); + if (network.getQosId() != null && !hostNetworkQosSupported) { + addViolation(VdcBllMessages.ACTION_TYPE_FAILED_HOST_NETWORK_QOS_NOT_SUPPORTED, networkName); + } + } else if (networkWasModified(iface)) { + addViolation(VdcBllMessages.NETWORKS_NOT_IN_SYNC, networkName); + } + } + + + } + + validateMTU(); + validateNetworkQos(); + validateCustomProperties(); + + return translateViolations(); + } + + protected boolean networksUniquelyConfiguredOnHost(Collection<NetworkAttachment> attachmentsToConfigure) { + boolean passed = true; + Set<Guid> networkIds = new HashSet<>(attachmentsToConfigure.size()); + for (NetworkAttachment attachment : attachmentsToConfigure) { + if (networkIds.contains(attachment.getNetworkId())) { + Network network = clusterNetworks.get(attachment.getNetworkId()); + addViolation(VdcBllMessages.NETWORKS_ALREADY_ATTACHED_TO_IFACES, network.getName()); + passed = false; + } else { + networkIds.add(attachment.getNetworkId()); + } + } + + return passed; + } + + private boolean validateNotRemovingUsedNetworkByVms() { + boolean passed = true; + Collection<String> removedNetworks = new HashSet<String>(); + for (NetworkAttachment removedAttachment : params.getRemovedNetworkAttachments()) { + removedNetworks.add(clusterNetworks.get(removedAttachment.getNetworkId()).getName()); + } + + List<String> vmNames = getVmInterfaceManager().findActiveVmsUsingNetworks(host.getId(), removedNetworks); + for (String vmName : vmNames) { + addViolation(VdcBllMessages.NETWORK_CANNOT_DETACH_NETWORK_USED_BY_VMS, vmName); + passed = false; + } + + return passed; + } + + private boolean validRemovedBonds(BusinessEntityMap<Bond> removedBonds) { + Set<String> requiredNicsNames = getRemovedBondsUsedByNetworks(removedBonds); + + boolean passed = true; + for (Bond removedBond : params.getRemovedBonds()) { + if (removedBond.getName() == null) { + addViolation(VdcBllMessages.HOST_NETWORK_INTERFACE_NOT_EXIST, null); + passed = false; + continue; + } + + VdsNetworkInterface existingBond = existingIfaces.get(removedBond.getName()); + if (existingBond != null && !isBond(existingBond)) { + addViolation(VdcBllMessages.NETWORK_INTERFACE_IS_NOT_BOND, removedBond.getName()); + passed = false; + continue; + } + + if (requiredNicsNames.contains(removedBond.getName())) { + addViolation(VdcBllMessages.BOND_USED_BY_NETWORK_ATTACHMENTS, removedBond.getName()); + passed = false; + continue; + } + } + + return passed; + } + + protected Set<String> getRemovedBondsUsedByNetworks(BusinessEntityMap<Bond> removedBonds) { + Collection<NetworkAttachment> attachmentsToConfigure = getAttachmentsToConfigure(removedBonds); + Set<String> requiredNicsNames = new HashSet<>(); + for (NetworkAttachment attachment : attachmentsToConfigure) { + requiredNicsNames.add(attachment.getNicName()); + } + return requiredNicsNames; + } + + protected Collection<NetworkAttachment> getAttachmentsToConfigure(BusinessEntityMap<Bond> removedBonds) { + Map<Guid, NetworkAttachment> attachmentsToConfigure = Entities.businessEntitiesById(existingAttachments); + // ignore removed attachments + for (NetworkAttachment removedAttachment : params.getRemovedNetworkAttachments()) { + attachmentsToConfigure.remove(removedAttachment.getId()); + } + + List<NetworkAttachment> newAttachments = new ArrayList<>(); + + // add attachments which planned to be configured on removed bonds and ignore those which aren't + for (NetworkAttachment attachment : params.getNetworkAttachments()) { + + // new attachment to add + if (attachment.getId() == null) { + newAttachments.add(attachment); + continue; + } + + if (removedBonds.containsKey(attachment.getNicName())) { + attachmentsToConfigure.put(attachment.getId(), attachment); + } else { + attachmentsToConfigure.remove(attachment.getId()); + } + } + + Collection<NetworkAttachment> candidateAttachments = attachmentsToConfigure.values(); + candidateAttachments.addAll(newAttachments); + return candidateAttachments; + } + + private boolean validModifiedBonds(BusinessEntityMap<Bond> removedBonds) { + boolean passed = true; + for (Bond modifiedBond : params.getBonds()) { + if (modifiedBond.getName() == null) { + addViolation(VdcBllMessages.HOST_NETWORK_INTERFACE_NOT_EXIST, null); + passed = false; + continue; + } + + VdsNetworkInterface existingBond = existingIfaces.get(modifiedBond.getName()); + if (existingBond != null && !isBond(existingBond)) { + addViolation(VdcBllMessages.NETWORK_INTERFACE_IS_NOT_BOND, modifiedBond.getName()); + passed = false; + continue; + } + + // verify bond-slave count legal + if (modifiedBond.getSlaves().size() < 2) { + addViolation(VdcBllMessages.NETWORK_BONDS_INVALID_SLAVE_COUNT, modifiedBond.getName()); + passed = false; + continue; + } + + for (String slaveName : modifiedBond.getSlaves()) { + VdsNetworkInterface slave = getExistingIfaces().get(slaveName); + if (slave == null) { + addViolation(VdcBllMessages.HOST_NETWORK_INTERFACE_NOT_EXIST, slaveName); + passed = false; + continue; + } + + if (NetworkUtils.isVlan(slave) || isBond(slave)) { + addViolation(VdcBllMessages.NETWORK_INTERFACE_BOND_OR_VLAN_CANNOT_BE_SLAVE, slave.getName()); + passed = false; + continue; + } + + String slaveBondName = slave.getBondName(); + if (slave.isBondSlave() && (!slaveBondName.equals(modifiedBond.getName()) + || !slaveUsedByRemovedBond(slaveBondName))) { + addViolation(VdcBllMessages.NETWORK_INTERFACE_ALREADY_IN_BOND, slaveName); + passed = false; + continue; + } + + if (slave.getNetworkName() != null && !nicUsedByRemovedNetwork(slave)){ + addViolation(VdcBllMessages.NETWORK_INTERFACE_ATTACHED_TO_NETWORK_CANNOT_BE_SLAVE, slave.getName()); + passed = false; + continue; + } + } + } + + return passed; + } + + private boolean nicUsedByRemovedNetwork(VdsNetworkInterface slave) { + for (NetworkAttachment attachment : params.getRemovedNetworkAttachments()) { + if (Objects.equals(slave.getName(), attachment.getNicName())) { + return true; + } + } + + return false; + } + + private boolean slaveUsedByRemovedBond(String slaveBondName) { + for (Bond removedBond : params.getRemovedBonds()) { + if (slaveBondName.equals(removedBond.getName())) { + return true; + } + } + + return false; + } + + private boolean validModifiedNetworkAttachments(Map<Guid, NetworkAttachment> attachmentsById) { + boolean passed = true; + for (NetworkAttachment attachment : params.getNetworkAttachments()) { + NetworkAttachmentValidator validator = new NetworkAttachmentValidator(attachment, host); + if (!validate(validator.networkAttachmentIsSet()) + && validate(validator.networkExists(), attachment.getNetworkId().toString()) + && validate(validator.notExternalNetwork(), attachment.getNetworkId().toString()) + && validate(validator.networkAttachedToCluster(), attachment.getNetworkId().toString()) + && validate(validator.ipConfiguredForStaticBootProtocol(), attachment.getNetworkId().toString()) + && validate(validator.bootProtocolSetForDisplayNetwork(), attachment.getNetworkId().toString()) + && validate(validator.nicExists(), attachment.getNetworkId().toString()) + && validate(validator.networkIpAddressWasSameAsHostnameAndChanged(getExistingIfaces())) + && validate(validator.networkNotChanged(attachmentsById.get(attachment.getId()))) + && validate(validator.validateGateway())) { + passed = false; + } + } + + return passed; + } + + private boolean validRemovedNetworkAttachments(Map<Guid, NetworkAttachment> attachmentsById, + BusinessEntityMap<Bond> removedBonds) { + boolean passed = true; + for (NetworkAttachment attachment : params.getRemovedNetworkAttachments()) { + NetworkAttachment attachmentToValidate = attachmentsById.get(attachment.getId()); + NetworkAttachmentValidator validator = new NetworkAttachmentValidator(attachmentToValidate, host); + if (!validate(validator.networkAttachmentIsSet()) + && validate(validator.notExternalNetwork()) + && validate(validator.notManagementNetwork()) + && notRemovingLabeledNetworks(attachment, getExistingIfaces(), removedBonds)) { + passed = false; + } + } + + return passed; + } + + private boolean notRemovingLabeledNetworks(NetworkAttachment attachment, + Map<String, VdsNetworkInterface> existingNics, + BusinessEntityMap<Bond> removedBonds) { + Network removedNetwork = clusterNetworks.get(attachment.getNetworkId()); + if (!NetworkUtils.isLabeled(removedNetwork)) { + return true; + } + + VdsNetworkInterface nic = existingNics.get(attachment.getNicName()); + if (nic != null && !removedBonds.containsKey(nic.getName())) { + if (NetworkUtils.isLabeled(nic) && nic.getLabels().contains(removedNetwork.getLabel())) { + addViolation(VdcBllMessages.ACTION_TYPE_FAILED_CANNOT_REMOVE_LABELED_NETWORK_FROM_NIC, + removedNetwork.getName()); + return false; + } + } + + return true; + } + + /** + * Validates there is no differences on MTU value between non-VM network to Vlans over the same interface/bond + */ + private void validateMTU() { + Map<String, VdsNetworkInterface> ifacesByNetworkName = + Entities.hostInterfacesByNetworkName(params.getInterfaces()); + Set<String> checkedNetworks = new HashSet<String>(getNetworks().size()); + + for (Network network : getNetworks()) { + if (!checkedNetworks.contains(network.getName())) { + List<Network> networksOnInterface = findNetworksOnInterface(ifacesByNetworkName.get(network.getName())); + boolean mtuMismatched = false; + for (Network net : networksOnInterface) { + checkedNetworks.add(net.getName()); + if (net.getMtu() != network.getMtu() + && (NetworkUtils.isNonVmNonVlanNetwork(network) || NetworkUtils.isNonVmNonVlanNetwork(net))) { + mtuMismatched = true; + } + } + if (mtuMismatched) { + reportMTUDifferences(networksOnInterface); + } + } + } + } + + private void reportMTUDifferences(List<Network> ifaceNetworks) { + List<String> mtuDiffNetworks = new ArrayList<String>(); + for (Network net : ifaceNetworks) { + mtuDiffNetworks.add(String.format("%s(%s)", + net.getName(), + net.getMtu() == 0 ? "default" : String.valueOf(net.getMtu()))); + } + addViolation(VdcBllMessages.NETWORK_MTU_DIFFERENCES, + String.format("[%s]", StringUtils.join(mtuDiffNetworks, ", "))); + } + + /** + * Validates that the feature is supported if any QoS configuration was specified, and that the values associated + * with it are valid. + */ + private void validateNetworkQos() { + for (VdsNetworkInterface iface : params.getInterfaces()) { + if (iface.isQosOverridden()) { + if (!hostNetworkQosSupported) { + addViolation(VdcBllMessages.ACTION_TYPE_FAILED_HOST_NETWORK_QOS_NOT_SUPPORTED, + iface.getNetworkName()); + } + + NetworkQosValidator qosValidator = new NetworkQosValidator(iface.getQos()); + if (qosValidator.allValuesPresent() != ValidationResult.VALID) { + addViolation(VdcBllMessages.ACTION_TYPE_FAILED_HOST_NETWORK_QOS_MISSING_VALUES, + iface.getNetworkName()); + } + if (qosValidator.peakConsistentWithAverage() != ValidationResult.VALID) { + addViolation(VdcBllMessages.ACTION_TYPE_FAILED_HOST_NETWORK_QOS_PEAK_LOWER_THAN_AVERAGE, + iface.getNetworkName()); + } + } + } + } + + private void validateCustomProperties() { + String version = host.getVdsGroupCompatibilityVersion().getValue(); + SimpleCustomPropertiesUtil util = SimpleCustomPropertiesUtil.getInstance(); + Map<String, String> validProperties = + util.convertProperties(Config.<String> getValue(ConfigValues.PreDefinedNetworkCustomProperties, version)); + validProperties.putAll(util.convertProperties(Config.<String> getValue(ConfigValues.UserDefinedNetworkCustomProperties, + version))); + Map<String, String> validPropertiesNonVm = new HashMap<String, String>(validProperties); + validPropertiesNonVm.remove("bridge_opts"); + for (VdsNetworkInterface iface : params.getInterfaces()) { + String networkName = iface.getNetworkName(); + if (iface.hasCustomProperties() && StringUtils.isNotEmpty(networkName)) { + if (!networkCustomPropertiesSupported) { + addViolation(VdcBllMessages.ACTION_TYPE_FAILED_NETWORK_CUSTOM_PROPERTIES_NOT_SUPPORTED, networkName); + } + + Network network = existingClusterNetworks.get(networkName); + List<ValidationError> errors = + util.validateProperties(network == null || network.isVmNetwork() ? validProperties + : validPropertiesNonVm, iface.getCustomProperties()); + if (!errors.isEmpty()) { + addViolation(VdcBllMessages.ACTION_TYPE_FAILED_NETWORK_CUSTOM_PROPERTIES_BAD_INPUT, networkName); + List<String> messages = new ArrayList<>(); + util.handleCustomPropertiesError(errors, messages); + log.error(StringUtils.join(translateErrorMessages(messages), ',')); + } + } + } + } + + private void addViolation(VdcBllMessages violation, String violatingEntity) { + List<String> violatingEntities = violations.get(violation); + if (violatingEntities == null) { + violatingEntities = new ArrayList<String>(); + violations.put(violation, violatingEntities); + } + + if (violatingEntity != null) { + violatingEntities.add(violatingEntity); + } + } + + private boolean validate(ValidationResult validationResult, String violatingEntity) { + if (!validationResult.isValid()) { + addViolation(validationResult.getMessage(), violatingEntity); + } + + return validationResult.isValid(); + } + + private boolean validate(ValidationResult validationResult) { + if (!validationResult.isValid()) { + addViolation(validationResult.getMessage(), null); + } + + return validationResult.isValid(); + } + + private List<String> translateViolations() { + List<String> violationMessages = new ArrayList<String>(violations.size() * 2); + for (Map.Entry<VdcBllMessages, List<String>> v : violations.entrySet()) { + String violationName = v.getKey().name(); + violationMessages.add(violationName); + violationMessages.add(MessageFormat.format(VIOLATING_ENTITIES_LIST_FORMAT, + violationName, + StringUtils.join(v.getValue(), ", "))); + } + + return violationMessages; + } + + private Map<String, VdsNetworkInterface> getExistingIfaces() { + return existingIfaces; + } + + private VdsNetworkInterface getExistingIfaceByNetwork(String network) { + for (VdsNetworkInterface iface : getExistingIfaces().values()) { + if (network.equals(iface.getNetworkName())) { + return iface; + } + } + + return null; + } + + private NetworkType determineNetworkType(Integer vlanId, boolean vmNetwork) { + return vlanId != null ? NetworkType.VLAN : vmNetwork ? NetworkType.VM : NetworkType.NON_VM; + } + + /** + * Make sure that if the given interface has a VM network on it then there is nothing else on the interface, or if + * the given interface is a VLAN network, than there is no VM network on the interface.<br> + * Other combinations are either legal or illegal but are not a concern of this method. + */ + private void validateNetworkExclusiveOnIface(String nicName, List<NetworkType> networksOnIface) { + NetworkType networkType + + if (networksOnIface == null) { + networksOnIface = new ArrayList<HostSetupNetworksValidator.NetworkType>(); + ifacesWithExclusiveNetwork.put(ifaceName, networksOnIface); + } + + if ((networkType == NetworkType.VLAN && networksOnIface.contains(NetworkType.VM)) + || (networkType == NetworkType.VM && !networksOnIface.isEmpty())) { + addViolation(VdcBllMessages.NETWORK_INTERFACES_NOT_EXCLUSIVELY_USED_BY_NETWORK, ifaceName); + } + + networksOnIface.add(networkType); + } + + /** + * Make sure the network is not an external network, which we can't set up. + * + * @param iface + * The interface. + * @param network + * The network to check. + */ + private void validateNetworkInternal(Network network) { + if (network.getProvidedBy() != null) { + addViolation(VdcBllMessages.ACTION_TYPE_FAILED_EXTERNAL_NETWORKS_CANNOT_BE_PROVISIONED, network.getName()); + } + } + + /** + * Check if the network parameters on the given interface were modified (or network was added). + * + * @param iface + * The interface to check. + * @return <code>true</code> if the network parameters were changed, or the network wan't on the given interface. + * <code>false</code> if it existed and didn't change. + */ + private boolean networkWasModified(VdsNetworkInterface iface) { + VdsNetworkInterface existingIface = getExistingIfaces().get(iface.getName()); + + if (existingIface == null) { + return true; + } + + return !ObjectUtils.equals(iface.getNetworkName(), existingIface.getNetworkName()) + || iface.getBootProtocol() != existingIface.getBootProtocol() + || staticBootProtoPropertiesChanged(iface, existingIface) + || !ObjectUtils.equals(iface.getQos(), existingIface.getQos()) + || customPropertiesChanged(iface, existingIface); + } + + /** + * @param iface + * New interface definition. + * @param existingIface + * Existing interface definition. + * @return <code>true</code> if the boot protocol is static, and one of the properties has changed. + * <code>false</code> otherwise. + */ + private boolean staticBootProtoPropertiesChanged(VdsNetworkInterface iface, VdsNetworkInterface existingIface) { + return iface.getBootProtocol() == NetworkBootProtocol.STATIC_IP + && (!ObjectUtils.equals(iface.getAddress(), existingIface.getAddress()) + || !ObjectUtils.equals(iface.getGateway(), existingIface.getGateway()) + || !ObjectUtils.equals(iface.getSubnet(), existingIface.getSubnet())); + } + + /** + * @param iface + * New interface definition. + * @param existingIface + * Existing interface definition. + * @return <code>true</code> iff the custom properties have changed (null and empty map are considered equal). + */ + private boolean customPropertiesChanged(VdsNetworkInterface iface, VdsNetworkInterface existingIface) { + return (iface.hasCustomProperties() != existingIface.hasCustomProperties()) + || (iface.hasCustomProperties() + && !iface.getCustomProperties().equals(existingIface.getCustomProperties())); + } + + private boolean isBond(VdsNetworkInterface iface) { + return Boolean.TRUE.equals(iface.getBonded()); + } + + public Map<String, VdsNetworkInterface> getAffectedInterfaces() { + List<VdsNetworkInterface> affectedNics = new ArrayList<>(modifiedInterfaces); + affectedNics.addAll(removedBonds.values()); + return Entities.entitiesByName(affectedNics); + } + + public VmInterfaceManager getVmInterfaceManager() { + return new VmInterfaceManager(); + } + + private enum NetworkType { + VM, + NON_VM, + VLAN + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java index 53b0b26..6849180 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/errors/VdcBllMessages.java @@ -491,6 +491,7 @@ NETWORK_NOT_EXISTS(ErrorType.BAD_PARAMETERS), ACTION_TYPE_FAILED_NETWORK_QOS_NOT_EXISTS(ErrorType.BAD_PARAMETERS), NETWORK_NOT_EXISTS_IN_CLUSTER(ErrorType.BAD_PARAMETERS), + NETWORK_ATTACHMENT_NOT_EXISTS(ErrorType.BAD_PARAMETERS), NETWORK_OLD_NETWORK_NOT_SPECIFIED(ErrorType.BAD_PARAMETERS), NETWORK_ALREADY_ATTACHED_TO_CLUSTER(ErrorType.CONFLICT), NETWORK_CAN_NOT_REMOVE_DEFAULT_NETWORK(ErrorType.CONSTRAINT_VIOLATION), @@ -518,6 +519,7 @@ NETWORK_BOND_NOT_EXISTS(ErrorType.BAD_PARAMETERS), NETWORK_BOND_NOT_ATTACH_TO_NETWORK(ErrorType.BAD_PARAMETERS), NETWORK_BOND_HAVE_ATTACHED_VLANS(ErrorType.CONFLICT), + NETWORK_INTERFACE_IS_NOT_BOND(ErrorType.CONFLICT), NETWORK_INTERFACE_NOT_EXISTS(ErrorType.BAD_PARAMETERS), NETWORK_INTERFACE_IN_USE_BY_VM(ErrorType.CONFLICT), NETWORK_CANNOT_ADD_INTERFACE_WHEN_VM_STATUS_NOT_UP_DOWN_LOCKED(ErrorType.CONFLICT), @@ -530,6 +532,9 @@ NETWORKS_DONT_EXIST_IN_CLUSTER(ErrorType.CONFLICT), NETWORKS_DONT_EXIST_IN_DATA_CENTER(ErrorType.CONFLICT), NETWORK_BONDS_INVALID_SLAVE_COUNT(ErrorType.BAD_PARAMETERS), + NETWORK_INTERFACE_BOND_OR_VLAN_CANNOT_BE_SLAVE(ErrorType.CONFLICT), + NETWORK_INTERFACE_ATTACHED_TO_NETWORK_CANNOT_BE_SLAVE(ErrorType.CONFLICT), + BOND_USED_BY_NETWORK_ATTACHMENTS(ErrorType.CONFLICT), NETWORK_INTERFACES_NOT_EXCLUSIVELY_USED_BY_NETWORK(ErrorType.CONFLICT), NETWORK_CANNOT_DETACH_NETWORK_USED_BY_VMS(ErrorType.CONFLICT), NON_VM_NETWORK_CANNOT_SUPPORT_STP(ErrorType.CONFLICT), @@ -545,6 +550,7 @@ INTERFACE_NOT_LABELED(ErrorType.CONFLICT), LABELED_NETWORK_ATTACHED_TO_WRONG_INTERFACE(ErrorType.CONFLICT), OTHER_INTERFACE_ALREADY_LABELED(ErrorType.CONFLICT), + CANNOT_CHANGE_ATTACHED_NETWORK(ErrorType.BAD_PARAMETERS), ACTION_TYPE_FAILED_VNIC_PROFILE_NOT_EXISTS(ErrorType.BAD_PARAMETERS), ACTION_TYPE_FAILED_VNIC_PROFILE_NAME_IN_USE(ErrorType.CONFLICT), ACTION_TYPE_FAILED_VNIC_PROFILE_IN_USE(ErrorType.CONFLICT), -- To view, visit http://gerrit.ovirt.org/33333 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I60077019f308f371f21fb7b5545ba48adb38bd06 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Moti Asayag <masa...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches