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

Reply via email to