package org.ovirt.engine.core.bll.network.dc; 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.Objects; import java.util.Set; import javax.inject.Inject; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; import org.ovirt.engine.core.bll.RenamedEntityInfoProvider; import org.ovirt.engine.core.bll.ValidationResult; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.network.AddNetworkParametersBuilder; import org.ovirt.engine.core.bll.network.HostSetupNetworksParametersBuilder; import org.ovirt.engine.core.bll.network.RemoveNetworkParametersBuilder; import org.ovirt.engine.core.bll.network.cluster.NetworkClusterHelper; import org.ovirt.engine.core.bll.validator.NetworkValidator; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.AddNetworkStoragePoolParameters; import org.ovirt.engine.core.common.action.PersistentHostSetupNetworksParameters; import org.ovirt.engine.core.common.action.VdcActionParametersBase; import org.ovirt.engine.core.common.action.VdcActionType; 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.NetworkCluster; 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.errors.EngineError; import org.ovirt.engine.core.common.errors.EngineException; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.utils.NetworkCommonUtils; import org.ovirt.engine.core.common.validation.group.UpdateEntity; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase; import org.ovirt.engine.core.dao.VdsStaticDao; import org.ovirt.engine.core.dao.VmDao; import org.ovirt.engine.core.dao.network.InterfaceDao; import org.ovirt.engine.core.dao.network.NetworkAttachmentDao; import org.ovirt.engine.core.dao.network.NetworkClusterDao; import org.ovirt.engine.core.dao.network.NetworkDao; import org.ovirt.engine.core.utils.NetworkUtils; import org.ovirt.engine.core.utils.transaction.TransactionSupport; import org.ovirt.engine.core.vdsbroker.NetworkImplementationDetailsUtils; @NonTransactiveCommandAttribute public class UpdateNetworkCommand<T extends AddNetworkStoragePoolParameters> extends NetworkModification<T> implements RenamedEntityInfoProvider { @Inject private SyncNetworkParametersBuilder syncNetworkParametersBuilder; @Inject private NetworkClusterHelper networkClusterHelper; @Inject private NetworkDao networkDao; @Inject private NetworkClusterDao networkClusterDao; @Inject private InterfaceDao interfaceDao; @Inject private VmDao vmDao; private Network oldNetwork; public UpdateNetworkCommand(T parameters, CommandContext commandContext) { super(parameters, commandContext); } @Override protected void executeCommand() { TransactionSupport.executeInNewTransaction(() -> { networkDao.update(getNetwork()); for (NetworkCluster clusterAttachment : networkClusterDao.getAllForNetwork(getNetwork().getId())) { networkClusterHelper.setStatus(clusterAttachment.getClusterId(), getNetwork()); } if (networkChangedToNonVmNetwork()) { removeVnicProfiles(); } return null; }); if (!getNetwork().isExternal()) { applyNetworkChangesToHosts(); } setSucceeded(true); } private void applyNetworkChangesToHosts() { ArrayList<VdcActionParametersBase> parameters = syncNetworkParametersBuilder.buildParameters(getNetwork(), getOldNetwork()); if (!parameters.isEmpty()) { HostSetupNetworksParametersBuilder.updateParametersSequencing(parameters); runInternalMultipleActions(VdcActionType.PersistentHostSetupNetworks, parameters); } } private boolean networkChangedToNonVmNetwork() { return getOldNetwork().isVmNetwork() && !getNetwork().isVmNetwork(); } @Override protected void setActionMessageParameters() { super.setActionMessageParameters(); addValidationMessage(EngineMessage.VAR__ACTION__UPDATE); } @Override protected boolean validate() { if (onlyPermittedFieldsChanged() && allowedNetworkLabelManipulation()) { return true; } final NetworkValidator validatorNew = new NetworkValidator(vmDao, getNetwork()); final UpdateNetworkValidator validatorOld = new UpdateNetworkValidator(getOldNetwork(), vmDao, interfaceDao); return validate(validatorNew.dataCenterExists()) && validate(validatorNew.stpForVmNetworkOnly()) && validate(validatorNew.mtuValid()) && validate(validatorNew.networkPrefixValid()) && validate(validatorNew.vlanIdNotUsed()) && validate(validatorNew.qosExistsInDc()) && validate(validatorOld.networkIsSet(getNetwork().getId())) && validate(validatorOld.notChangingDataCenterId(getNetwork().getDataCenterId())) && validate(validatorNew.networkNameNotUsed()) && validate(validatorOld.nonVmNetworkNotUsedByVms(getNetwork())) && validate(validatorOld.nonVmNetworkNotUsedByTemplates(getNetwork())) && validate(validatorOld.notRenamingUsedNetwork(getNetworkName())) && validate(validatorOld.notRenamingLabel(getNetwork().getLabel())) && (oldAndNewNetworkIsNotExternal() || validate(validatorOld.externalNetworkDetailsUnchanged(getNetwork()))); } private boolean allowedNetworkLabelManipulation() { boolean labelNotChanged = !labelChanged(getNetwork(), getOldNetwork()); return !getNetwork().isExternal() && (labelNotChanged || labelAdded(getNetwork(), getOldNetwork())); } /** * @return <code>true</code> iff only the description or comment field were changed, otherwise <code>false</code>. */ private boolean onlyPermittedFieldsChanged() { Network oldNetwork = getOldNetwork(); Network newNetwork = getNetwork(); if (oldNetwork == null || newNetwork == null) { return false; } return Objects.equals(oldNetwork.getName(), newNetwork.getName()) && Objects.equals(oldNetwork.getDataCenterId(), newNetwork.getDataCenterId()) && Objects.equals(oldNetwork.getId(), newNetwork.getId()) && Objects.equals(oldNetwork.getMtu(), newNetwork.getMtu()) && Objects.equals(oldNetwork.getName(), newNetwork.getName()) && Objects.equals(oldNetwork.getProvidedBy(), newNetwork.getProvidedBy()) && Objects.equals(oldNetwork.getStp(), newNetwork.getStp()) && Objects.equals(oldNetwork.getVlanId(), newNetwork.getVlanId()) && Objects.equals(oldNetwork.isVmNetwork(), newNetwork.isVmNetwork()); } private boolean oldAndNewNetworkIsNotExternal() { return !getOldNetwork().isExternal() && !getNetwork().isExternal(); } @Override public AuditLogType getAuditLogTypeValue() { return getSucceeded() ? AuditLogType.NETWORK_UPDATE_NETWORK : AuditLogType.NETWORK_UPDATE_NETWORK_FAILED; } @Override protected List<Class<?>> getValidationGroups() { addValidationGroup(UpdateEntity.class); return super.getValidationGroups(); } private Network getOldNetwork() { if (oldNetwork == null) { oldNetwork = networkDao.get(getNetwork().getId()); } return oldNetwork; } protected static class UpdateNetworkValidator extends NetworkValidator { private final InterfaceDao interfaceDao; public UpdateNetworkValidator( Network network, VmDao vmDao, InterfaceDao interfaceDao) { super(vmDao, network); this.interfaceDao = interfaceDao; } public ValidationResult notRenamingLabel(String newLabel) { String oldLabel = network.getLabel(); if (oldLabel == null || newLabel == null || oldLabel.equals(newLabel)) { return ValidationResult.VALID; } List<VdsNetworkInterface> nics = interfaceDao.getVdsInterfacesByNetworkId(network.getId()); for (VdsNetworkInterface nic : nics) { VdsNetworkInterface labeledNic = nic; if (NetworkCommonUtils.isVlan(nic)) { labeledNic = getBaseInterface(nic); } if (NetworkUtils.isLabeled(labeledNic) && labeledNic.getLabels().contains(oldLabel)) { return new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_NETWORK_LABEL_RENAMING_NOT_SUPPORTED); } } return ValidationResult.VALID; } private VdsNetworkInterface getBaseInterface(VdsNetworkInterface vlan) { List<VdsNetworkInterface> hostNics = interfaceDao.getAllInterfacesForVds(vlan.getVdsId()); for (VdsNetworkInterface hostNic : hostNics) { if (NetworkUtils.interfaceBasedOn(vlan, hostNic.getName())) { return hostNic; } } throw new EngineException(EngineError.LABELED_NETWORK_INTERFACE_NOT_FOUND); } public ValidationResult notRenamingUsedNetwork(String networkName) { if (StringUtils.equals(network.getName(), networkName)) { return ValidationResult.VALID; } ValidationResult result = networkNotUsedByHosts(); if (!result.isValid()) { return result; } result = networkNotUsedByVms(); if (!result.isValid()) { return result; } return networkNotUsedByTemplates(); } public ValidationResult nonVmNetworkNotUsedByVms(Network updatedNetwork) { if (networkChangedToNonVmNetwork(updatedNetwork)) { return networkNotUsedByVms(); } return ValidationResult.VALID; } private boolean networkChangedToNonVmNetwork(Network updatedNetwork) { return network.isVmNetwork() && !updatedNetwork.isVmNetwork(); } public ValidationResult nonVmNetworkNotUsedByTemplates(Network updatedNetwork) { if (networkChangedToNonVmNetwork(updatedNetwork)) { return networkNotUsedByTemplates(); } return ValidationResult.VALID; } /** * Check that the external network details that can't be updated were not updated.<br> * The check is undefined if both the validator's network and the new network are internal networks. * * @param newNetwork * The new network definition to check. * @return A valid result iff the details that shouldn't be changed remained unchanged, An error otherwise. */ public ValidationResult externalNetworkDetailsUnchanged(Network newNetwork) { return Objects.equals(network.getVlanId(), newNetwork.getVlanId()) && network.getMtu() == newNetwork.getMtu() && network.getStp() == newNetwork.getStp() && network.isVmNetwork() == newNetwork.isVmNetwork() && Objects.equals(network.getProvidedBy(), newNetwork.getProvidedBy()) ? ValidationResult.VALID : new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_EXTERNAL_NETWORK_DETAILS_CANNOT_BE_EDITED); } public ValidationResult notChangingDataCenterId(Guid dataCenterId) { final Guid oldDataCenterId = network.getDataCenterId(); return ValidationResult.failWith(EngineMessage.ACTION_TYPE_FAILED_DATA_CENTER_ID_CANNOT_BE_CHANGED) .when(!oldDataCenterId.equals(dataCenterId)); } } @Override public String getEntityType() { return VdcObjectType.Network.getVdcObjectTranslation(); } @Override public String getEntityOldName() { return getOldNetwork().getName(); } @Override public String getEntityNewName() { return getParameters().getNetwork().getName(); } @Override public void setEntityId(AuditLogableBase logable) { } private static class SyncNetworkParametersBuilder extends HostSetupNetworksParametersBuilder { @Inject private NetworkImplementationDetailsUtils networkImplementationDetailsUtils; @Inject private AddNetworkParametersBuilder addNetworkParametersBuilder; @Inject private RemoveNetworkParametersBuilder removeNetworkParametersBuilder; @Inject public SyncNetworkParametersBuilder(InterfaceDao interfaceDao, VdsStaticDao vdsStaticDao, NetworkClusterDao networkClusterDao, NetworkAttachmentDao networkAttachmentDao) { super(interfaceDao, vdsStaticDao, networkClusterDao, networkAttachmentDao); } private ArrayList<VdcActionParametersBase> buildParameters(Network network, Network oldNetwork) { ArrayList<VdcActionParametersBase> parameters = new ArrayList<>(); List<VdsNetworkInterface> nics = interfaceDao.getVdsInterfacesByNetworkId(network.getId()); // sync network on nics if the label wasn't changed if (!labelChanged(network, oldNetwork)) { createSyncNetworkParameters(network, parameters, nics); return parameters; } // add network to labeled interfaces and sync network on the rest if (labelAdded(network, oldNetwork) || labelRenamed(network, oldNetwork)) { List<VdsNetworkInterface> labeledNics = getLabeledNics(network); Map<Guid, VdsNetworkInterface> hostToNic = mapHostToNic(nics); List<VdsNetworkInterface> nicsForAdd = new ArrayList<>(); Set<VdsNetworkInterface> nicsForSync = new HashSet<>(); // nics to add network for (VdsNetworkInterface labeledNic : labeledNics) { VdsNetworkInterface nic = hostToNic.get(labeledNic.getVdsId()); // add network to labeled nic if network not configured on host if (nic == null) { nicsForAdd.add(labeledNic); } else { // sync the network nicsForSync.add(nic); } } // add the unlabeled nics to be synced for (VdsNetworkInterface nic : nics) { if (!nicsForSync.contains(nic)) { nicsForSync.add(nic); } } parameters.addAll(createAddNetworkParameters(network, nicsForAdd)); createSyncNetworkParameters(network, parameters, nicsForSync); return parameters; } // remove network from labeled interfaces if (labelRemoved(network, oldNetwork)) { List<VdsNetworkInterface> labeledNics = getLabeledNics(oldNetwork); Map<Guid, VdsNetworkInterface> hostToNic = mapHostToNic(nics); List<VdsNetworkInterface> nicsForRemove = new ArrayList<>(); Set<VdsNetworkInterface> nicsForSync = new HashSet<>(); // nics to remove the network from for (VdsNetworkInterface labeledNic : labeledNics) { VdsNetworkInterface nic = hostToNic.get(labeledNic.getVdsId()); // remove the network from labeled nic if (nic != null) { nicsForRemove.add(labeledNic); } } // add the unlabeled nics to be synced for (VdsNetworkInterface nic : nics) { if (!nicsForSync.contains(nic)) { nicsForSync.add(nic); } } parameters.addAll(createRemoveNetworkParameters(network, nicsForRemove)); createSyncNetworkParameters(network, parameters, nicsForSync); return parameters; } return parameters; } private ArrayList<VdcActionParametersBase> createAddNetworkParameters(Network network, List<VdsNetworkInterface> nicsForAdd) { return addNetworkParametersBuilder.buildParameters(network, nicsForAdd); } private ArrayList<VdcActionParametersBase> createRemoveNetworkParameters(Network oldNetwork, List<VdsNetworkInterface> nicsForRemove) { return removeNetworkParametersBuilder.buildParameters(oldNetwork, nicsForRemove); } private Map<Guid, VdsNetworkInterface> mapHostToNic(List<VdsNetworkInterface> nics) { Map<Guid, VdsNetworkInterface> hostToNic = new HashMap<>(nics.size()); for (VdsNetworkInterface nic : nics) { hostToNic.put(nic.getVdsId(), nic); } return hostToNic; } private List<VdsNetworkInterface> getLabeledNics(Network network) { List<NetworkCluster> clusters = networkClusterDao.getAllForNetwork(network.getId()); List<VdsNetworkInterface> labeledNics = new ArrayList<>(); for (NetworkCluster networkCluster : clusters) { labeledNics.addAll(interfaceDao.getAllInterfacesByLabelForCluster(networkCluster.getClusterId(), network.getLabel())); } return labeledNics; } private void createSyncNetworkParameters(Network network, ArrayList<VdcActionParametersBase> parameters, Collection<VdsNetworkInterface> nics) { Set<Guid> hostIdsToSync = new HashSet<>(); for (VdsNetworkInterface nic : nics) { NetworkImplementationDetails networkImplementationDetails = networkImplementationDetailsUtils.calculateNetworkImplementationDetails(nic, network); boolean networkShouldBeSynced = networkImplementationDetails != null && !networkImplementationDetails.isInSync(); if (networkShouldBeSynced) { hostIdsToSync.add(nic.getVdsId()); } } for (Guid hostId : hostIdsToSync) { PersistentHostSetupNetworksParameters setupNetworkParams = createHostSetupNetworksParameters(hostId); setupNetworkParams.setNetworkNames(network.getName()); NetworkAttachment attachment = getNetworkIdToAttachmentMap(hostId).get(network.getId()); attachment.setOverrideConfiguration(true); setupNetworkParams.getNetworkAttachments().add(attachment); parameters.add(setupNetworkParams); } } } private static boolean labelChanged(Network network, Network oldNetwork) { return !Objects.equals(network.getLabel(), oldNetwork.getLabel()); } private static boolean labelAdded(Network network, Network oldNetwork) { return !NetworkUtils.isLabeled(oldNetwork) && NetworkUtils.isLabeled(network); } private static boolean labelRemoved(Network network, Network oldNetwork) { return NetworkUtils.isLabeled(oldNetwork) && !NetworkUtils.isLabeled(network); } private static boolean labelRenamed(Network network, Network oldNetwork) { return NetworkUtils.isLabeled(oldNetwork) && NetworkUtils.isLabeled(network) && labelChanged(network, oldNetwork); } }