2 * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.elan.internal;
10 import com.google.common.base.Optional;
11 import com.google.common.base.Splitter;
12 import com.google.common.base.Strings;
14 import java.math.BigInteger;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.List;
19 import java.util.Random;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.genius.interfacemanager.globals.IfmConstants;
22 import org.opendaylight.ovsdb.utils.config.ConfigProperties;
23 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
24 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeBase;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeNetdev;
27 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * This class provides functions for creating bridges via OVSDB, specifically the br-int bridge.
33 * Note and TODO: br-ex is temporary. vpnservice does not require it but for the time being it is
34 * left here because devstack expects it.
36 public class ElanBridgeManager {
37 private static final Logger LOG = LoggerFactory.getLogger(ElanBridgeManager.class);
39 public static final String PROVIDER_MAPPINGS_KEY = "provider_mappings";
40 private static final String INTEGRATION_BRIDGE = "br-int";
41 private static final String INT_SIDE_PATCH_PORT_SUFFIX = "-expatch";
42 private static final String EX_SIDE_PATCH_PORT_SUFFIX = "-patch";
44 private final MdsalUtils mdsalUtils;
45 final SouthboundUtils southboundUtils;
46 private Random random;
49 * Construct a new ElanBridgeManager.
50 * @param dataBroker DataBroker
52 public ElanBridgeManager(DataBroker dataBroker) {
53 //TODO: ClusterAware!!!??
54 this.mdsalUtils = new MdsalUtils(dataBroker);
55 this.southboundUtils = new SouthboundUtils(mdsalUtils);
56 this.random = new Random(System.currentTimeMillis());
60 * Is OVS running in userspace mode?
61 * @return true if the ovsdb.userspace.enabled variable is set to true
63 public boolean isUserSpaceEnabled() {
64 final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.userspace.enabled");
65 return enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes");
69 * Is the Node object an OVSDB node.
70 * @param node unidentified node object
71 * @return true if the Node is an OVSDB node
73 public boolean isOvsdbNode(Node node) {
74 return southboundUtils.extractNodeAugmentation(node) != null;
78 * Is this Node the integration bridge (br-int).
79 * @param node unidentified noe object
80 * @return true if the Node is a bridge and it is the integration bridge
82 public boolean isIntegrationBridge(Node node) {
83 if (!isBridgeNode(node)) {
87 String bridgeName = southboundUtils.extractBridgeName(node);
88 if (bridgeName == null) {
92 return bridgeName.equals(INTEGRATION_BRIDGE);
96 * Is this node a bridge.
97 * @param node unidentified node object
98 * @return true if this node is a bridge
100 public boolean isBridgeNode(Node node) {
101 return southboundUtils.extractBridgeAugmentation(node) != null;
105 * Advance the "preperation" of the OVSDB node. This re-entrant method advances the state of an OVSDB
106 * node towards the prepared state where all bridges and patch ports are created and active. This method
107 * should be invoked for the OVSDB node and the integration bridge node BUT IT IS SAFE TO INVOKE IT ON ANY NODE.
109 * @param generateIntBridgeMac whether or not the int bridge's mac should be set to a random value
111 public void processNodePrep(Node node, boolean generateIntBridgeMac) {
112 if (isOvsdbNode(node)) {
113 ensureBridgesExist(node, generateIntBridgeMac);
115 //if br-int already exists, we can add provider networks
116 Node brIntNode = southboundUtils.readBridgeNode(node, INTEGRATION_BRIDGE);
117 if (brIntNode != null) {
118 if (!addControllerToBridge(node, INTEGRATION_BRIDGE)) {
119 LOG.error("Failed to set controller to existing integration bridge {}", brIntNode);
122 prepareIntegrationBridge(node, brIntNode);
127 Node ovsdbNode = southboundUtils.readOvsdbNode(node);
128 if (ovsdbNode == null) {
129 LOG.error("Node is neither bridge nor ovsdb {}", node);
133 if (isIntegrationBridge(node)) {
134 prepareIntegrationBridge(ovsdbNode, node);
139 private void prepareIntegrationBridge(Node ovsdbNode, Node brIntNode) {
140 Optional<Map<String, String>> providerMappings = getOpenvswitchOtherConfigMap(ovsdbNode, PROVIDER_MAPPINGS_KEY);
142 for (String value : providerMappings.or(Collections.emptyMap()).values()) {
143 if (southboundUtils.extractTerminationPointAugmentation(brIntNode, value) != null) {
144 LOG.debug("prepareIntegrationBridge: port {} already exists on {}", value, INTEGRATION_BRIDGE);
148 Node exBridgeNode = southboundUtils.readBridgeNode(ovsdbNode, value);
149 if (exBridgeNode != null) {
150 LOG.debug("prepareIntegrationBridge: bridge {} found. Patching to {}", value, INTEGRATION_BRIDGE);
151 patchBridgeToBrInt(brIntNode, exBridgeNode, value);
153 LOG.debug("prepareIntegrationBridge: adding interface {} to {}", value, INTEGRATION_BRIDGE);
154 if (!addPortToBridge(brIntNode, INTEGRATION_BRIDGE, value)) {
155 LOG.error("Failed to add {} port to {}", value, brIntNode);
163 private void patchBridgeToBrInt(Node intBridgeNode, Node exBridgeNode, String physnetBridgeName) {
165 String portNameInt = getIntSidePatchPortName(physnetBridgeName);
166 String portNameExt = getExSidePatchPortName(physnetBridgeName);
167 if (!addPatchPort(intBridgeNode, INTEGRATION_BRIDGE, portNameInt, portNameExt)) {
168 LOG.error("Failed to add patch port {} to {}", portNameInt, intBridgeNode);
172 if (!addPatchPort(exBridgeNode, physnetBridgeName, portNameExt, portNameInt)) {
173 LOG.error("Failed to add patch port {} to {}", portNameExt, exBridgeNode);
178 private void ensureBridgesExist(Node ovsdbNode, boolean generateIntBridgeMac) {
181 createIntegrationBridge(ovsdbNode, generateIntBridgeMac);
182 } catch (Exception e) {
183 LOG.error("Error creating bridge on " + ovsdbNode, e);
187 private boolean createIntegrationBridge(Node ovsdbNode, boolean generateIntBridgeMac) {
188 LOG.debug("ElanBridgeManager.createIntegrationBridge, skipping if exists");
189 if (!addBridge(ovsdbNode, INTEGRATION_BRIDGE,
190 generateIntBridgeMac ? generateRandomMac() : null)) {
191 LOG.warn("Integration Bridge Creation failed");
198 * Add a bridge to the OVSDB node but check that it does not exist in the
199 * CONFIGURATION or OPERATIONAL md-sals first.
201 * @param ovsdbNode Which OVSDB node
202 * @param bridgeName Name of the bridge
203 * @param mac mac address to set on the bridge or null
204 * @return true if no errors occurred
206 public boolean addBridge(Node ovsdbNode, String bridgeName, String mac) {
208 if (!southboundUtils.isBridgeOnOvsdbNode(ovsdbNode, bridgeName)
209 || southboundUtils.getBridgeFromConfig(ovsdbNode, bridgeName) == null) {
210 Class<? extends DatapathTypeBase> dpType = null;
211 if (isUserSpaceEnabled()) {
212 dpType = DatapathTypeNetdev.class;
214 rv = southboundUtils.addBridge(ovsdbNode, bridgeName,
215 southboundUtils.getControllersFromOvsdbNode(ovsdbNode), dpType, mac);
220 private boolean addControllerToBridge(Node ovsdbNode,String bridgeName) {
221 return southboundUtils.setBridgeController(ovsdbNode,
222 bridgeName, southboundUtils.getControllersFromOvsdbNode(ovsdbNode));
226 * Extract OpenvSwitch other-config to key value map.
227 * @param node OVSDB node
228 * @param key key to extract from other-config
229 * @return Optional of key-value Map
231 public Optional<Map<String, String>> getOpenvswitchOtherConfigMap(Node node, String key) {
232 String providerMappings = southboundUtils.getOpenvswitchOtherConfig(node, key);
233 return extractMultiKeyValueToMap(providerMappings);
237 * Get the OVS node physical interface name from provider mappings.
238 * @param node OVSDB node
239 * @param physicalNetworkName name of physical network
240 * @return physical network name
242 public String getProviderMappingValue(Node node, String physicalNetworkName) {
243 Optional<Map<String, String>> providerMappings = getOpenvswitchOtherConfigMap(node, PROVIDER_MAPPINGS_KEY);
244 if (!providerMappings.isPresent()) {
245 LOG.trace("Physical network {} not found in {}", physicalNetworkName, PROVIDER_MAPPINGS_KEY);
249 return providerMappings.get().get(physicalNetworkName);
253 * Get the name of the port in br-int for the given provider-mapping value. This is either a patch port to a bridge
254 * with providerMappingValue - patch-<providerMappingValue> or simply a port with the same name as
255 * providerMappingValue
256 * @param ovsdbNode ovsdbNode
257 * @param providerMappingValue this is the last part of provider_mappings=net_name:THIS
258 * @return the name of the port on br-int
260 public String getIntBridgePortNameFor(Node ovsdbNode, String providerMappingValue) {
261 String res = providerMappingValue;
262 if (southboundUtils.isBridgeOnOvsdbNode(ovsdbNode, providerMappingValue)) {
263 res = getIntSidePatchPortName(providerMappingValue);
270 * Get the name of the patch-port which is patched to the bridge containing
273 * @param interfaceName The external interface
274 * @return interface name
276 public static String getIntSidePatchPortName(String interfaceName) {
277 return interfaceName + INT_SIDE_PATCH_PORT_SUFFIX;
280 private String getExSidePatchPortName(String physicalInterfaceName) {
281 return physicalInterfaceName + EX_SIDE_PATCH_PORT_SUFFIX;
285 * Add a port to a bridge.
286 * @param node the bridge node
287 * @param bridgeName name of the bridge
288 * @param portName name of port to add
289 * @return true if successful in writing to mdsal
291 public boolean addPortToBridge(Node node, String bridgeName, String portName) {
294 if (southboundUtils.extractTerminationPointAugmentation(node, portName) == null) {
295 rv = southboundUtils.addTerminationPoint(node, bridgeName, portName, null);
298 LOG.debug("addPortToBridge: node: {}, bridge: {}, portname: {} status: success",
299 node.getNodeId().getValue(), bridgeName, portName);
301 LOG.error("addPortToBridge: node: {}, bridge: {}, portname: {} status: FAILED",
302 node.getNodeId().getValue(), bridgeName, portName);
305 LOG.trace("addPortToBridge: node: {}, bridge: {}, portname: {} status: not_needed",
306 node.getNodeId().getValue(), bridgeName, portName);
313 * Add a patch port to a bridge.
314 * @param node the bridge node
315 * @param bridgeName name of the bridge
316 * @param portName name of the port
317 * @param peerPortName name of the port's peer (the other side)
318 * @return true if successful
320 public boolean addPatchPort(Node node, String bridgeName, String portName, String peerPortName) {
323 if (southboundUtils.extractTerminationPointAugmentation(node, portName) == null) {
324 rv = southboundUtils.addPatchTerminationPoint(node, bridgeName, portName, peerPortName);
327 LOG.info("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: success",
328 node.getNodeId().getValue(), bridgeName, portName, peerPortName);
330 LOG.error("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: FAILED",
331 node.getNodeId().getValue(), bridgeName, portName, peerPortName);
334 LOG.trace("addPatchPort: node: {}, bridge: {}, portname: {} peer: {} status: not_needed",
335 node.getNodeId().getValue(), bridgeName, portName, peerPortName);
341 private String generateRandomMac() {
342 byte[] macBytes = new byte[6];
343 random.nextBytes(macBytes);
344 macBytes[0] &= 0xfc; //the two low bits of the first byte need to be zero
346 StringBuilder stringBuilder = new StringBuilder();
350 stringBuilder.append(String.format("%02x", macBytes[index++]));
354 stringBuilder.append(':');
357 return stringBuilder.toString();
360 private static Optional<Map<String, String>> extractMultiKeyValueToMap(String multiKeyValueStr) {
361 if (Strings.isNullOrEmpty(multiKeyValueStr)) {
362 return Optional.absent();
365 Map<String, String> valueMap = new HashMap<>();
366 Splitter splitter = Splitter.on(",");
367 for (String keyValue : splitter.split(multiKeyValueStr)) {
368 String[] split = keyValue.split(":", 2);
369 if (split != null && split.length == 2) {
370 valueMap.put(split[0], split[1]);
374 return Optional.of(valueMap);
377 public Node getBridgeNode(BigInteger dpId) {
378 List<Node> ovsdbNodes = southboundUtils.getOvsdbNodes();
379 if (null == ovsdbNodes) {
380 LOG.debug("Could not find any (?) ovsdb nodes");
384 for (Node node : ovsdbNodes) {
385 if(!isIntegrationBridge(node)) {
389 long nodeDpid = southboundUtils.getDataPathId(node);
390 if (dpId.equals(BigInteger.valueOf(nodeDpid))) {
398 public String getProviderInterfaceName(BigInteger dpId, String physicalNetworkName) {
401 brNode = getBridgeNode(dpId);
402 if (brNode == null) {
403 LOG.debug("Could not find bridge node for {}", dpId);
407 return getProviderInterfaceName(brNode, physicalNetworkName);
410 public String getProviderInterfaceName(Node bridgeNode, String physicalNetworkName) {
411 if (physicalNetworkName == null) {
415 String providerMappingValue = getProviderMappingValue(bridgeNode, physicalNetworkName);
416 if (providerMappingValue == null) {
417 LOG.trace("No provider mapping found for physicalNetworkName {} node {}", physicalNetworkName,
418 bridgeNode.getNodeId().getValue());
422 return southboundUtils.getDataPathId(bridgeNode) + IfmConstants.OF_URI_SEPARATOR
423 + getIntBridgePortNameFor(bridgeNode, providerMappingValue);