--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.elan.internal;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.ovsdb.utils.config.ConfigProperties;
+import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
+import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeNetdev;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+
+import java.util.Collections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class provides functions for creating bridges via OVSDB, specifically the br-int bridge.
+ * Note and TODO: br-ex is temporary. vpnservice does not require it but for the time being it is
+ * left here because devstack expects it.
+ */
+public class ElanBridgeManager {
+ private static final Logger LOG = LoggerFactory.getLogger(ElanBridgeManager.class);
+
+ public static final String PROVIDER_MAPPINGS_KEY = "provider_mappings";
+ private static final String INTEGRATION_BRIDGE = "br-int";
+ private static final String EXTERNAL_BRIDGE = "br-ex";
+ private static final String INT_SIDE_PATCH_PORT_SUFFIX = "-expatch";
+ private static final String EX_SIDE_PATCH_PORT_SUFFIX = "-patch";
+
+ private final MdsalUtils mdsalUtils;
+ final SouthboundUtils southboundUtils;
+ private Random random;
+
+ /**
+ * Construct a new ElanBridgeManager
+ * @param dataBroker
+ */
+ public ElanBridgeManager(DataBroker dataBroker) {
+ //TODO: ClusterAware!!!??
+ this.mdsalUtils = new MdsalUtils(dataBroker);
+ this.southboundUtils = new SouthboundUtils(mdsalUtils);
+ this.random = new Random(System.currentTimeMillis());
+ }
+
+ /**
+ * Is OVS running in userspace mode?
+ * @return true if the ovsdb.userspace.enabled variable is set to true
+ */
+ public boolean isUserSpaceEnabled() {
+ final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.userspace.enabled");
+ return enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes");
+ }
+
+ /**
+ * Is the Node object an OVSDB node?
+ * @param node unidentified node object
+ * @return true if the Node is an OVSDB node
+ */
+ public boolean isOvsdbNode(Node node) {
+ return southboundUtils.extractNodeAugmentation(node) != null;
+ }
+
+ /**
+ * Is this Node the integration bridge (br-int)
+ * @param node unidentified noe object
+ * @return true if the Node is a bridge and it is the integration bridge
+ */
+ public boolean isIntegrationBridge(Node node) {
+ if (!isBridgeNode(node)) {
+ return false;
+ }
+
+ String bridgeName = southboundUtils.extractBridgeName(node);
+ if (bridgeName == null) {
+ return false;
+ }
+
+ return bridgeName.equals(INTEGRATION_BRIDGE);
+ }
+
+ /**
+ * Is this node a bridge?
+ * @param node unidentified node object
+ * @return true if this node is a bridge
+ */
+ public boolean isBridgeNode(Node node) {
+ return southboundUtils.extractBridgeAugmentation(node) != null;
+ }
+
+ /**
+ * Advance the "preperation" of the OVSDB node. This re-entrant method advances the state of an OVSDB
+ * node towards the prepared state where all bridges and patch ports are created and active. This method
+ * should be invoked for the OVSDB node and the integration bridge node BUT IT IS SAFE TO INVOKE IT ON ANY NODE.
+ * @param node A node
+ * @param generateIntBridgeMac whether or not the int bridge's mac should be set to a random value
+ */
+ public void processNodePrep(Node node, boolean generateIntBridgeMac) {
+ if (isOvsdbNode(node)) {
+ ensureBridgesExist(node, generateIntBridgeMac);
+
+ //if br-int already exists, we can add provider networks
+ Node brIntNode = southboundUtils.readBridgeNode(node, INTEGRATION_BRIDGE);
+ if (brIntNode != null) {
+ if(!addControllerToBridge(node, brIntNode, INTEGRATION_BRIDGE)) {
+ LOG.error("Failed to set controller to existing integration bridge {}", brIntNode);
+ }
+
+ prepareIntegrationBridge(node, brIntNode);
+ }
+ return;
+ }
+
+ Node ovsdbNode = southboundUtils.readOvsdbNode(node);
+ if (ovsdbNode == null) {
+ LOG.error("Node is neither bridge nor ovsdb {}", node);
+ return;
+ }
+
+ if (isIntegrationBridge(node)) {
+ prepareIntegrationBridge(ovsdbNode, node);
+ }
+
+ }
+
+ private void prepareIntegrationBridge(Node ovsdbNode, Node brIntNode) {
+ Optional<Map<String, String>> providerMappings = getOpenvswitchOtherConfigMap(ovsdbNode, PROVIDER_MAPPINGS_KEY);
+
+ for (String value : providerMappings.or(Collections.emptyMap()).values()) {
+ if (southboundUtils.extractTerminationPointAugmentation(brIntNode, value) != null) {
+ LOG.debug("prepareIntegrationBridge: port {} already exists on {}", value, INTEGRATION_BRIDGE);
+ continue;
+ }
+
+ Node exBridgeNode = southboundUtils.readBridgeNode(ovsdbNode, value);
+ if (exBridgeNode != null) {
+ LOG.debug("prepareIntegrationBridge: bridge {} found. Patching to {}", value, INTEGRATION_BRIDGE);
+ patchBridgeToBrInt(brIntNode, exBridgeNode, value);
+ } else {
+ LOG.debug("prepareIntegrationBridge: adding interface {} to {}", value, INTEGRATION_BRIDGE);
+ if (!addPortToBridge(brIntNode, INTEGRATION_BRIDGE, value)) {
+ LOG.error("Failed to add {} port to {}", value, brIntNode);
+ }
+ }
+
+ }
+
+ }
+
+ private void patchBridgeToBrInt(Node intBridgeNode, Node exBridgeNode, String physnetBridgeName) {
+
+ String portNameInt = getIntSidePatchPortName(physnetBridgeName);
+ String portNameExt = getExSidePatchPortName(physnetBridgeName);
+ if (!addPatchPort(intBridgeNode, INTEGRATION_BRIDGE, portNameInt, portNameExt)) {
+ LOG.error("Failed to add patch port {} to {}", portNameInt, intBridgeNode);
+ return;
+ }
+
+ if (!addPatchPort(exBridgeNode, physnetBridgeName, portNameExt, portNameInt)) {
+ LOG.error("Failed to add patch port {} to {}", portNameExt, exBridgeNode);
+ return;
+ }
+ }
+
+ private void ensureBridgesExist(Node ovsdbNode, boolean generateIntBridgeMac) {
+
+ try {
+ createIntegrationBridge(ovsdbNode, generateIntBridgeMac);
+ //TODO: Get rid of this:
+ createExternalBridge(ovsdbNode);
+ } catch (Exception e) {
+ LOG.error("Error creating bridge on " + ovsdbNode, e);
+ }
+ }
+
+ private boolean createIntegrationBridge(Node ovsdbNode, boolean generateIntBridgeMac) {
+ LOG.debug("ElanBridgeManager.createIntegrationBridge, skipping if exists");
+ if (!addBridge(ovsdbNode, INTEGRATION_BRIDGE,
+ generateIntBridgeMac ? generateRandomMac() : null)) {
+ LOG.warn("Integration Bridge Creation failed");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean createExternalBridge(Node ovsdbNode) {
+ LOG.debug("ElanBridgeManager.createExternalBridge, skipping if exists");
+ if (!addBridge(ovsdbNode, EXTERNAL_BRIDGE, null)) {
+ LOG.warn("External Bridge Creation failed");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add a bridge to the OVSDB node but check that it does not exist in the CONFIGURATION or OPERATIONAL md-sals first
+ * @param ovsdbNode Which OVSDB node
+ * @param bridgeName Name of the bridge
+ * @param mac mac address to set on the bridge or null
+ * @return true if no errors occurred
+ */
+ public boolean addBridge(Node ovsdbNode, String bridgeName, String mac) {
+ boolean rv = true;
+ if ((!southboundUtils.isBridgeOnOvsdbNode(ovsdbNode, bridgeName)) ||
+ (southboundUtils.getBridgeFromConfig(ovsdbNode, bridgeName) == null)) {
+ Class<? extends DatapathTypeBase> dpType = null;
+ if (isUserSpaceEnabled()) {
+ dpType = DatapathTypeNetdev.class;
+ }
+ rv = southboundUtils.addBridge(ovsdbNode, bridgeName, southboundUtils.getControllersFromOvsdbNode(ovsdbNode), dpType, mac);
+ }
+ return rv;
+ }
+
+ private boolean addControllerToBridge(Node ovsdbNode, Node bridgeNode, String bridgeName) {
+ return southboundUtils.setBridgeController(ovsdbNode, bridgeNode,
+ bridgeName, southboundUtils.getControllersFromOvsdbNode(ovsdbNode));
+ }
+
+ /**
+ * Extract OpenvSwitch other-config to key value map.
+ * @param node OVSDB node
+ * @param key key to extract from other-config
+ * @return Optional of key-value Map
+ */
+ public Optional<Map<String, String>> getOpenvswitchOtherConfigMap(Node node, String key) {
+ String providerMappings = southboundUtils.getOpenvswitchOtherConfig(node, key);
+ return extractMultiKeyValueToMap(providerMappings);
+ }
+
+ /**
+ * Get the OVS node physical interface name from provider mappings.
+ * @param node OVSDB node
+ * @param physicalNetworkName name of physical network
+ */
+ public String getProviderMappingValue(Node node, String physicalNetworkName) {
+ Optional<Map<String, String>> providerMappings = getOpenvswitchOtherConfigMap(node, PROVIDER_MAPPINGS_KEY);
+ if (!providerMappings.isPresent()) {
+ LOG.trace("Physical network {} not found in {}", physicalNetworkName, PROVIDER_MAPPINGS_KEY);
+ return null;
+ }
+
+ return providerMappings.get().get(physicalNetworkName);
+ }
+
+ /**
+ * Get the name of the port in br-int for the given provider-mapping value. This is either a patch port to a bridge
+ * with providerMappingValue - patch-<providerMappingValue> or simply a port with the same name as
+ * providerMappingValue
+ * @param ovsdbNode ovsdbNode
+ * @param providerMappingValue this is the last part of provider_mappings=net_name:THIS
+ * @return the name of the port on br-int
+ */
+ public String getIntBridgePortNameFor(Node ovsdbNode, String providerMappingValue) {
+ String res = providerMappingValue;
+ if (southboundUtils.isBridgeOnOvsdbNode(ovsdbNode, providerMappingValue)) {
+ res = getIntSidePatchPortName(providerMappingValue);
+ }
+
+ return res;
+ }
+ /**
+ * Get the name of the patch-port which is patched to the bridge containing interfaceName
+ * @param interfaceName The external interface
+ * @return
+ */
+ public static String getIntSidePatchPortName(String interfaceName) {
+ return interfaceName + INT_SIDE_PATCH_PORT_SUFFIX;
+ }
+
+ private String getExSidePatchPortName(String physicalInterfaceName) {
+ return physicalInterfaceName + EX_SIDE_PATCH_PORT_SUFFIX;
+ }
+
+ /**
+ * Add a port to a bridge
+ * @param node the bridge node
+ * @param bridgeName name of the bridge
+ * @param portName name of port to add
+ * @return true if successful in writing to mdsal
+ */
+ public boolean addPortToBridge (Node node, String bridgeName, String portName) {
+ boolean rv = true;
+
+ if (southboundUtils.extractTerminationPointAugmentation(node, portName) == null) {
+ rv = southboundUtils.addTerminationPoint(node, bridgeName, portName, null);
+
+ if (rv) {
+ LOG.debug("addPortToBridge: node: {}, bridge: {}, portname: {} status: success",
+ node.getNodeId().getValue(), bridgeName, portName);
+ } else {
+ LOG.error("addPortToBridge: node: {}, bridge: {}, portname: {} status: FAILED",
+ node.getNodeId().getValue(), bridgeName, portName);
+ }
+ } else {
+ LOG.trace("addPortToBridge: node: {}, bridge: {}, portname: {} status: not_needed",
+ node.getNodeId().getValue(), bridgeName, portName);
+ }
+
+ return rv;
+ }
+
+ /**
+ * Add a patch port to a bridge
+ * @param node the bridge node
+ * @param bridgeName name of the bridge
+ * @param portName name of the port
+ * @param peerPortName name of the port's peer (the other side)
+ * @return true if successful
+ */
+ public boolean addPatchPort (Node node, String bridgeName, String portName, String peerPortName) {
+ boolean rv = true;
+
+ if (southboundUtils.extractTerminationPointAugmentation(node, portName) == null) {
+ rv = southboundUtils.addPatchTerminationPoint(node, bridgeName, portName, peerPortName);
+
+ if (rv) {
+ LOG.info("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: success",
+ node.getNodeId().getValue(), bridgeName, portName, peerPortName);
+ } else {
+ LOG.error("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: FAILED",
+ node.getNodeId().getValue(), bridgeName, portName, peerPortName);
+ }
+ } else {
+ LOG.trace("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: not_needed",
+ node.getNodeId().getValue(), bridgeName, portName, peerPortName);
+ }
+
+ return rv;
+ }
+
+ private String generateRandomMac() {
+ byte[] macBytes = new byte[6];
+ random.nextBytes(macBytes);
+ macBytes[0] &= 0xfc; //the two low bits of the first byte need to be zero
+
+ StringBuilder stringBuilder = new StringBuilder();
+
+ int i = 0;
+ while(true) {
+ stringBuilder.append(String.format("%02x", macBytes[i++]));
+ if (i >= 6) {
+ break;
+ }
+ stringBuilder.append(':');
+ }
+
+ return stringBuilder.toString();
+ }
+
+ private static Optional<Map<String, String>> extractMultiKeyValueToMap(String multiKeyValueStr) {
+ if (Strings.isNullOrEmpty(multiKeyValueStr)) {
+ return Optional.absent();
+ }
+
+ Map<String, String> valueMap = new HashMap<>();
+ Splitter splitter = Splitter.on(",");
+ for (String keyValue : splitter.split(multiKeyValueStr)) {
+ String[] split = keyValue.split(":", 2);
+ if (split != null && split.length == 2) {
+ valueMap.put(split[0], split[1]);
+ }
+ }
+
+ return Optional.of(valueMap);
+ }
+}
import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.netvirt.elan.utils.BridgeUtils;
-import org.opendaylight.netvirt.elan.utils.ElanConstants;
+import org.opendaylight.netvirt.elanmanager.api.IElanService;
import org.opendaylight.genius.mdsalutil.AbstractDataChangeListener;
import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
private static final Logger logger = LoggerFactory.getLogger(ElanOvsdbNodeListener.class);
private ListenerRegistration<DataChangeListener> listenerRegistration;
- private BridgeUtils bridgeUtils;
+ private ElanBridgeManager bridgeMgr;
+ private IElanService elanProvider;
private boolean generateIntBridgeMac;
/**
* @param db the DataBroker
* @param generateIntBridgeMac true if the integration bridge should be given a fixed, random MAC address
*/
- public ElanOvsdbNodeListener(final DataBroker db, boolean generateIntBridgeMac) {
+ public ElanOvsdbNodeListener(final DataBroker db, boolean generateIntBridgeMac, ElanBridgeManager bridgeMgr, IElanService elanProvider) {
super(Node.class);
- this.bridgeUtils = new BridgeUtils(db);
+ this.bridgeMgr = bridgeMgr;
+ this.elanProvider = elanProvider;
this.generateIntBridgeMac = generateIntBridgeMac;
registerListener(db);
}
@Override
protected void update(InstanceIdentifier<Node> identifier, Node original, Node update) {
+ logger.debug("ElanOvsdbNodeListener.update, updated node detected. original: {} new: {}", original, update);
+ doNodeUpdate(update);
}
@Override
- protected void add(InstanceIdentifier<Node> identifier, Node add) {
- logger.debug("ElanOvsdbNodeListener.add, new bridge detected{}", add);
- bridgeUtils.prepareNode(add, generateIntBridgeMac);
+ protected void add(InstanceIdentifier<Node> identifier, Node node) {
+ logger.debug("ElanOvsdbNodeListener.add, new node detected {}", node);
+ doNodeUpdate(node);
+ }
+
+ private void doNodeUpdate(Node node) {
+ bridgeMgr.processNodePrep(node, generateIntBridgeMac);
}
}
private OdlInterfaceRpcService interfaceManagerRpcService;
private ElanInstanceManager elanInstanceManager;
private ElanForwardingEntriesHandler elanForwardingEntriesHandler;
+ private ElanBridgeManager bridgeMgr;
+
public IdManagerService getIdManager() {
return idManager;
}
getDataStoreJobCoordinator();
broker = session.getSALService(DataBroker.class);
- elanOvsdbNodeListener = new ElanOvsdbNodeListener(broker, generateIntBridgeMac);
+ bridgeMgr = new ElanBridgeManager(broker);
+ elanOvsdbNodeListener = new ElanOvsdbNodeListener(broker, generateIntBridgeMac, bridgeMgr, this);
ElanUtils.setElanServiceProvider(this);
elanForwardingEntriesHandler = ElanForwardingEntriesHandler.getElanForwardingEntriesHandler(this);
elanInterfaceManager = ElanInterfaceManager.getElanInterfaceManager(this);
+++ /dev/null
-/*
- * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-package org.opendaylight.netvirt.elan.utils;
-
-import com.google.common.collect.ImmutableBiMap;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.ovsdb.utils.config.ConfigProperties;
-import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
-import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeBase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeNetdev;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.InterfaceTypeDpdk;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentationBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeName;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeProtocolBase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbFailModeBase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbFailModeSecure;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbFailModeStandalone;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.BridgeOtherConfigs;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.BridgeOtherConfigsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntryBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ProtocolEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ProtocolEntryBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.InterfaceTypeEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntry;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This class provides functions for creating bridges via OVSDB, specifically the br-int bridge.
- * Note and TODO: br-ex is temporary. vpnservice does not require it but for the time being it is
- * left here because devstack expects it.
- */
-public class BridgeUtils {
- private static final Logger LOG = LoggerFactory.getLogger(BridgeUtils.class);
-
- private static final ImmutableBiMap<Class<? extends OvsdbFailModeBase>,String> OVSDB_FAIL_MODE_MAP
- = new ImmutableBiMap.Builder<Class<? extends OvsdbFailModeBase>,String>()
- .put(OvsdbFailModeStandalone.class,"standalone")
- .put(OvsdbFailModeSecure.class,"secure")
- .build();
-
- private static final String DISABLE_IN_BAND = "disable-in-band";
- private static final String INTEGRATION_BRIDGE = "br-int";
- private static final String EXTERNAL_BRIDGE = "br-ex";
-
- private final MdsalUtils mdsalUtils;
- private final SouthboundUtils southboundUtils;
- private Random random;
-
-
- /**
- * Construct a new BridgeUtils
- * @param dataBroker
- */
- public BridgeUtils(DataBroker dataBroker) {
- //TODO: ClusterAware!!!??
- this.mdsalUtils = new MdsalUtils(dataBroker);
- this.southboundUtils = new SouthboundUtils(mdsalUtils);
- this.random = new Random(System.currentTimeMillis());
- }
-
- /**
- * Is OVS running in userspace mode?
- * @return true if the ovsdb.userspace.enabled variable is set to true
- */
- public boolean isUserSpaceEnabled() {
- final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.userspace.enabled");
- return enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes");
- }
-
- /**
- * Is vpnservice handling L3 as well? This function is deprocated because br-ex is temporary as mentioned above
- * @return true if the ovsdb.l3.fwd.enable is set to true
- */
- @Deprecated
- public boolean isL3ForwardingEnabled() {
- final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.l3.fwd.enabled");
- return enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes");
- }
-
- /**
- * Prepare the OVSDB node for netvirt, create br-int
- * @param ovsdbNode The OVSDB node
- * @param generateIntBridgeMac should BrigeUtil set br-int's mac address to a random address. If this vlaue is false, the dpid may change as ports are added to the bridge
- */
- public void prepareNode(Node ovsdbNode, boolean generateIntBridgeMac) {
- try {
- createIntegrationBridge(ovsdbNode, generateIntBridgeMac);
- } catch (Exception e) {
- LOG.error("Error creating Integration Bridge on {}", ovsdbNode, e);
- return;
- }
-
- try {
- if (isL3ForwardingEnabled()) {
- createExternalBridge(ovsdbNode);
- }
- } catch (Exception e) {
- LOG.error("Error creating External Bridge on {}", ovsdbNode, e);
- return;
- }
- // this node is an ovsdb node so it doesn't have a bridge
- // so either look up the bridges or just wait for the bridge update to come in
- // and add the flows there.
- //networkingProviderManager.getProvider(node).initializeFlowRules(node);
- }
-
- private boolean createIntegrationBridge(Node ovsdbNode, boolean generateIntBridgeMac) {
- LOG.debug("BridgeUtils.createIntegrationBridge, skipping if exists");
- if (!addBridge(ovsdbNode, INTEGRATION_BRIDGE,
- generateIntBridgeMac ? generateRandomMac() : null)) {
- LOG.warn("Integration Bridge Creation failed");
- return false;
- }
- return true;
- }
-
- private boolean createExternalBridge(Node ovsdbNode) {
- LOG.debug("BridgeUtils.createExternalBridge, skipping if exists");
- if (!addBridge(ovsdbNode, EXTERNAL_BRIDGE, null)) {
- LOG.warn("External Bridge Creation failed");
- return false;
- }
- return true;
- }
-
- /**
- * Add a bridge to the OVSDB node but check that it does not exist in the CONFIGURATION or OPERATIONAL md-sals first
- * @param ovsdbNode Which OVSDB node
- * @param bridgeName Name of the bridge
- * @param mac mac address to set on the bridge or null
- * @return true if no errors occurred
- */
- public boolean addBridge(Node ovsdbNode, String bridgeName, String mac) {
- boolean rv = true;
- if ((!isBridgeOnOvsdbNode(ovsdbNode, bridgeName)) ||
- (getBridgeFromConfig(ovsdbNode, bridgeName) == null)) {
- Class<? extends DatapathTypeBase> dpType = null;
- if (isUserSpaceEnabled()) {
- dpType = DatapathTypeNetdev.class;
- }
- rv = addBridge(ovsdbNode, bridgeName, southboundUtils.getControllersFromOvsdbNode(ovsdbNode), dpType, mac);
- }
- return rv;
- }
-
- /**
- * Add a bridge to the OVSDB node
- * @param ovsdbNode Which OVSDB node
- * @param bridgeName Name of the bridge
- * @param controllersStr OVSDB's controller
- * @param dpType datapath type
- * @param mac mac address to set on the bridge or null
- * @return true if the write to md-sal was successful
- */
- public boolean addBridge(Node ovsdbNode, String bridgeName, List<String> controllersStr,
- final Class<? extends DatapathTypeBase> dpType, String mac) {
- boolean result = false;
-
- LOG.debug("addBridge: node: {}, bridgeName: {}, controller(s): {}", ovsdbNode, bridgeName, controllersStr);
- ConnectionInfo connectionInfo = southboundUtils.getConnectionInfo(ovsdbNode);
- if (connectionInfo == null) {
- throw new InvalidParameterException("Could not find ConnectionInfo");
- }
-
- NodeBuilder bridgeNodeBuilder = new NodeBuilder();
- InstanceIdentifier<Node> bridgeIid = southboundUtils.createInstanceIdentifier(ovsdbNode.getKey(), bridgeName);
- NodeId bridgeNodeId = southboundUtils.createManagedNodeId(bridgeIid);
- bridgeNodeBuilder.setNodeId(bridgeNodeId);
-
- OvsdbBridgeAugmentationBuilder ovsdbBridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
- ovsdbBridgeAugmentationBuilder.setControllerEntry(createControllerEntries(controllersStr));
- ovsdbBridgeAugmentationBuilder.setBridgeName(new OvsdbBridgeName(bridgeName));
- ovsdbBridgeAugmentationBuilder.setProtocolEntry(createMdsalProtocols());
- ovsdbBridgeAugmentationBuilder.setFailMode( OVSDB_FAIL_MODE_MAP.inverse().get("secure"));
-
- BridgeOtherConfigsBuilder bridgeOtherConfigsBuilder = new BridgeOtherConfigsBuilder();
- bridgeOtherConfigsBuilder.setBridgeOtherConfigKey(DISABLE_IN_BAND);
- bridgeOtherConfigsBuilder.setBridgeOtherConfigValue("true");
-
- List<BridgeOtherConfigs> bridgeOtherConfigsList = new ArrayList<>();
- bridgeOtherConfigsList.add(bridgeOtherConfigsBuilder.build());
- if (mac != null) {
- BridgeOtherConfigsBuilder macOtherConfigBuilder = new BridgeOtherConfigsBuilder();
- macOtherConfigBuilder.setBridgeOtherConfigKey("hwaddr");
- macOtherConfigBuilder.setBridgeOtherConfigValue(mac);
- bridgeOtherConfigsList.add(macOtherConfigBuilder.build());
- }
-
- ovsdbBridgeAugmentationBuilder.setBridgeOtherConfigs(bridgeOtherConfigsList);
- setManagedByForBridge(ovsdbBridgeAugmentationBuilder, ovsdbNode.getKey());
- if (dpType != null) {
- ovsdbBridgeAugmentationBuilder.setDatapathType(dpType);
- }
-
- if (isOvsdbNodeDpdk(ovsdbNode)) {
- ovsdbBridgeAugmentationBuilder.setDatapathType(DatapathTypeNetdev.class);
- }
-
- bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, ovsdbBridgeAugmentationBuilder.build());
-
- Node node = bridgeNodeBuilder.build();
- result = mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, bridgeIid, node);
- LOG.debug("addBridge: result: {}", result);
-
- return result;
- }
-
- private List<ControllerEntry> createControllerEntries(List<String> controllersStr) {
- List<ControllerEntry> controllerEntries = new ArrayList<>();
- if (controllersStr != null) {
- for (String controllerStr : controllersStr) {
- ControllerEntryBuilder controllerEntryBuilder = new ControllerEntryBuilder();
- controllerEntryBuilder.setTarget(new Uri(controllerStr));
- controllerEntries.add(controllerEntryBuilder.build());
- }
- }
- return controllerEntries;
- }
-
- private List<ProtocolEntry> createMdsalProtocols() {
- List<ProtocolEntry> protocolList = new ArrayList<>();
- ImmutableBiMap<String, Class<? extends OvsdbBridgeProtocolBase>> mapper =
- southboundUtils.OVSDB_PROTOCOL_MAP.inverse();
- protocolList.add(new ProtocolEntryBuilder().
- setProtocol(mapper.get("OpenFlow13")).build());
- return protocolList;
- }
-
- private void setManagedByForBridge(OvsdbBridgeAugmentationBuilder ovsdbBridgeAugmentationBuilder,
- NodeKey ovsdbNodeKey) {
- InstanceIdentifier<Node> connectionNodePath = southboundUtils.createInstanceIdentifier(ovsdbNodeKey.getNodeId());
- ovsdbBridgeAugmentationBuilder.setManagedBy(new OvsdbNodeRef(connectionNodePath));
- }
-
- private boolean isOvsdbNodeDpdk(Node ovsdbNode) {
- boolean found = false;
- OvsdbNodeAugmentation ovsdbNodeAugmentation = southboundUtils.extractNodeAugmentation(ovsdbNode);
- if (ovsdbNodeAugmentation != null) {
- List<InterfaceTypeEntry> ifTypes = ovsdbNodeAugmentation.getInterfaceTypeEntry();
- if (ifTypes != null) {
- for (InterfaceTypeEntry ifType : ifTypes) {
- if (ifType.getInterfaceType().equals(InterfaceTypeDpdk.class)) {
- found = true;
- break;
- }
- }
- }
- }
- return found;
- }
-
- private boolean isBridgeOnOvsdbNode(Node ovsdbNode, String bridgeName) {
- boolean found = false;
- //TODO: MAKE SURE extract function is right
- OvsdbNodeAugmentation ovsdbNodeAugmentation = southboundUtils.extractNodeAugmentation(ovsdbNode);
- if (ovsdbNodeAugmentation != null) {
- List<ManagedNodeEntry> managedNodes = ovsdbNodeAugmentation.getManagedNodeEntry();
- if (managedNodes != null) {
- for (ManagedNodeEntry managedNode : managedNodes) {
- InstanceIdentifier<?> bridgeIid = managedNode.getBridgeRef().getValue();
- if (bridgeIid.toString().contains(bridgeName)) {
- found = true;
- break;
- }
- }
- }
- }
- return found;
- }
-
- private OvsdbBridgeAugmentation getBridgeFromConfig(Node node, String bridge) {
- OvsdbBridgeAugmentation ovsdbBridgeAugmentation = null;
- InstanceIdentifier<Node> bridgeIid =
- southboundUtils.createInstanceIdentifier(node.getKey(), bridge);
- Node bridgeNode = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, bridgeIid);
- if (bridgeNode != null) {
- ovsdbBridgeAugmentation = bridgeNode.getAugmentation(OvsdbBridgeAugmentation.class);
- }
- return ovsdbBridgeAugmentation;
- }
-
- private String generateRandomMac() {
- byte[] macBytes = new byte[6];
- random.nextBytes(macBytes);
- macBytes[0] &= 0xfc; //the two low bits of the first byte need to be zero
-
- StringBuilder stringBuilder = new StringBuilder();
-
- int i = 0;
- while(true) {
- stringBuilder.append(String.format("%02x", macBytes[i++]));
- if (i >= 6) {
- break;
- }
- stringBuilder.append(':');
- }
-
- return stringBuilder.toString();
- }
-}