2 * Copyright (c) 2015 Cisco Systems, 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.groupbasedpolicy.neutron.ovsdb;
10 import javax.annotation.Nonnull;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.List;
15 import java.util.Objects;
19 import com.google.common.base.Function;
20 import com.google.common.base.Strings;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Lists;
23 import com.google.common.util.concurrent.FutureCallback;
24 import com.google.common.util.concurrent.Futures;
25 import com.google.common.util.concurrent.MoreExecutors;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
29 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
30 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
31 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
32 import org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.InventoryHelper;
33 import org.opendaylight.groupbasedpolicy.neutron.ovsdb.util.NeutronOvsdbIidFactory;
34 import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler;
35 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.neutron.ovsdb.params.rev160812.IntegrationBridgeSetting;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.DatapathTypeSystem;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentationBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeName;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeProtocolOpenflow13;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeRef;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeRef;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntry;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntryBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ProtocolEntryBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntry;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagerEntry;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigs;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.OpenvswitchOtherConfigsKey;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
58 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
63 import org.opendaylight.yangtools.yang.binding.DataObject;
64 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
68 public class OvsdbNodeListener extends DataTreeChangeHandler<Node> {
70 static final String BRIDGE_SEPARATOR = "/bridge/";
71 static final String NEUTRON_PROVIDER_MAPPINGS_KEY = "provider_mappings";
72 private static final Logger LOG = LoggerFactory.getLogger(OvsdbNodeListener.class);
73 private static final String OF_SEPARATOR = ":";
74 private static final String OF_INVENTORY_PREFIX = "openflow";
75 private static IntegrationBridgeSetting intBrSettings;
77 private final Map<OvsdbBridgeRef, String> providerPortNameByBridgeRef = new HashMap<>();
78 private final Map<InstanceIdentifier<Node>, NeutronBridgeWithExtPort> bridgeByNodeIid = new HashMap<>();
80 public OvsdbNodeListener(DataBroker dataProvider, IntegrationBridgeSetting brSettings) {
82 intBrSettings = brSettings;
83 this.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
84 InstanceIdentifier.create(NetworkTopology.class)
85 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
90 protected void onWrite(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
91 Node node = rootNode.getDataAfter();
92 OvsdbNodeAugmentation ovsdbNode = node.getAugmentation(OvsdbNodeAugmentation.class);
93 if (ovsdbNode != null) {
94 LOG.trace("OVSDB node created: {} \n {}", rootIdentifier, node);
95 DataObjectModification<OpenvswitchOtherConfigs> ovsOtherConfigModification = getProviderMappingsModification(rootNode);
96 boolean integrationBridgePresent = false;
97 if (isProviderPortNameChanged(ovsOtherConfigModification) && ovsdbNode.getManagedNodeEntry() != null) {
98 String newProviderPortName = getProviderPortName(ovsOtherConfigModification.getDataAfter());
99 LOG.debug("provider_mappings created {} on node {}", newProviderPortName, node.getNodeId().getValue());
100 for (ManagedNodeEntry mngdNodeEntry : ovsdbNode.getManagedNodeEntry()) {
101 OvsdbBridgeRef bridgeRef = mngdNodeEntry.getBridgeRef();
102 providerPortNameByBridgeRef.put(bridgeRef, newProviderPortName);
103 LOG.trace("Added Provider port name {} by OVSDB bridge ref {}", newProviderPortName,
104 mngdNodeEntry.getBridgeRef());
105 NodeKey managedNodeKey = bridgeRef.getValue().firstKeyOf(Node.class);
106 if (intBrSettings != null && managedNodeKey.getNodeId().getValue().equals(intBrSettings.getName())) {
107 integrationBridgePresent = true;
111 if (intBrSettings != null && !integrationBridgePresent) {
112 final Node bridge = createBridge(rootIdentifier,
113 managerToControllerEntries(ovsdbNode.getManagerEntry()), intBrSettings.getName());
114 InstanceIdentifier<Node> bridgeNodeIid = NeutronOvsdbIidFactory.nodeIid(
115 rootIdentifier.firstKeyOf(Topology.class).getTopologyId(), bridge.getNodeId());
116 WriteTransaction wTx = dataProvider.newWriteOnlyTransaction();
117 wTx.merge(LogicalDatastoreType.CONFIGURATION, bridgeNodeIid, bridge, true);
118 Futures.addCallback(wTx.submit(), new FutureCallback<Void>() {
121 public void onSuccess(Void result) {
122 LOG.info("Bridge {} written to datastore." + bridge.getNodeId().getValue());
126 public void onFailure(Throwable t) {
127 LOG.error("Failed to write bridge {}. Message: {}" + bridge.getNodeId().getValue(),
130 }, MoreExecutors.directExecutor());
133 OvsdbBridgeAugmentation ovsdbBridge = node.getAugmentation(OvsdbBridgeAugmentation.class);
134 if (ovsdbBridge != null) {
135 LOG.trace("OVSDB bridge created: {} \n {}", rootIdentifier, node);
136 Set<DataObjectModification<OvsdbTerminationPointAugmentation>> ovsdbTpModifications =
137 getOvsdbTpModifications(rootNode);
138 NodeId ofNodeId = buildOfNodeId(ovsdbBridge);
139 if (!ovsdbTpModifications.isEmpty() && ofNodeId != null) {
140 NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
141 bridge.ofNodeId = ofNodeId;
142 LOG.trace("OF node {} representing OVSDB bridge {}", ofNodeId.getValue(), node.getNodeId().getValue());
144 for (DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification : ovsdbTpModifications) {
145 OvsdbTerminationPointAugmentation newOvsdbTp = ovsdbTpModification.getDataAfter();
146 if (ovsdbBridge.getBridgeName().getValue().equals(newOvsdbTp.getName())) {
147 LOG.trace("Termination Point {} same as Bridge {}. Not processing", newOvsdbTp.getName(),
148 ovsdbBridge.getBridgeName().getValue());
151 String portName = newOvsdbTp.getName();
152 Long ofport = newOvsdbTp.getOfport();
153 if (isOfportOrNameChanged(ovsdbTpModification) && portName != null && ofport != null) {
154 NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
155 bridge.ofportByName.put(ofport, portName);
156 LOG.trace("OVSDB termination point with ofport {} and port-name {} created.", ofport, portName);
157 // port name is same as provider port name so the termination point represents
159 if (portName.equals(providerPortNameByBridgeRef.get(new OvsdbBridgeRef(rootIdentifier)))) {
160 NodeConnectorId ofNcId = buildOfNodeConnectorId(newOvsdbTp, ofNodeId);
161 bridge.externalIfaces.add(ofNcId);
162 InventoryHelper.addOfOverlayExternalPort(bridge.ofNodeId, ofNcId, dataProvider);
163 LOG.debug("Added of-overlay external-interface {} to node {}", ofNcId.getValue(),
165 traceBridge(rootIdentifier);
173 protected void onDelete(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
174 LOG.trace("Not implemented - OVSDB element deleted: {} \n {}", rootIdentifier, rootNode.getDataBefore());
178 protected void onSubtreeModified(DataObjectModification<Node> rootNode, InstanceIdentifier<Node> rootIdentifier) {
179 Node node = rootNode.getDataAfter();
180 OvsdbBridgeAugmentation ovsdbBridge = node.getAugmentation(OvsdbBridgeAugmentation.class);
181 if (ovsdbBridge != null) {
182 LOG.trace("OVSDB bridge updated: {} \n before {} \n after {}", rootIdentifier, rootNode.getDataBefore(),
183 rootNode.getDataAfter());
184 Set<DataObjectModification<OvsdbTerminationPointAugmentation>> ovsdbTpModifications =
185 getOvsdbTpModifications(rootNode);
186 NodeId ofNodeId = buildOfNodeId(ovsdbBridge);
187 if (!ovsdbTpModifications.isEmpty() && ofNodeId != null) {
188 NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
189 if (bridge.ofNodeId != null && !bridge.ofNodeId.equals(ofNodeId)) {
190 LOG.debug("OVSDB bridge {} has changed datapath-id. \n Old: {} \n New: {}",
191 node.getNodeId().getValue(), bridge.ofNodeId.getValue(), ofNodeId.getValue());
192 bridge.ofNodeId = ofNodeId;
195 for (DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification : ovsdbTpModifications) {
196 OvsdbTerminationPointAugmentation newOvsdbTp = ovsdbTpModification.getDataAfter();
198 if (newOvsdbTp == null) {
199 LOG.trace("Termination Point is null. Not processing");
203 if (ovsdbBridge.getBridgeName().getValue().equals(newOvsdbTp.getName())) {
204 LOG.trace("Termination Point {} same as Bridge {}. Not processing", newOvsdbTp.getName(),
205 ovsdbBridge.getBridgeName().getValue());
208 String portName = newOvsdbTp.getName();
209 Long ofport = newOvsdbTp.getOfport();
210 if (isOfportOrNameChanged(ovsdbTpModification) && portName != null && ofport != null) {
211 NeutronBridgeWithExtPort bridge = getBridge(rootIdentifier);
212 bridge.ofportByName.put(ofport, portName);
213 LOG.trace("OVSDB termination point with ofport {} and port-name {} created.", ofport, portName);
214 // port name is same as provider port name so the termination point represents
216 if (portName.equals(providerPortNameByBridgeRef.get(new OvsdbBridgeRef(rootIdentifier)))) {
217 NodeConnectorId ofNcId = buildOfNodeConnectorId(newOvsdbTp, ofNodeId);
218 bridge.externalIfaces.add(ofNcId);
219 InventoryHelper.addOfOverlayExternalPort(bridge.ofNodeId, ofNcId, dataProvider);
220 LOG.debug("Added of-overlay external-interface {} to node {}", ofNcId.getValue(),
222 traceBridge(rootIdentifier);
229 private NeutronBridgeWithExtPort getBridge(InstanceIdentifier<Node> nodeIid) {
230 NeutronBridgeWithExtPort bridge = bridgeByNodeIid.get(nodeIid);
231 if (bridge == null) {
232 bridge = new NeutronBridgeWithExtPort();
233 bridgeByNodeIid.put(nodeIid, bridge);
238 @SuppressWarnings("unchecked")
239 private static Set<DataObjectModification<OvsdbTerminationPointAugmentation>> getOvsdbTpModifications(
240 DataObjectModification<Node> rootNode) {
241 Set<DataObjectModification<OvsdbTerminationPointAugmentation>> modifications = new HashSet<>();
242 for (DataObjectModification<? extends DataObject> modifiedChild : rootNode.getModifiedChildren()) {
243 if (TerminationPoint.class.isAssignableFrom(modifiedChild.getDataType())) {
244 DataObjectModification<OvsdbTerminationPointAugmentation> modifiedAugmentation =
245 ((DataObjectModification<TerminationPoint>) modifiedChild)
246 .getModifiedAugmentation(OvsdbTerminationPointAugmentation.class);
247 if (modifiedAugmentation != null) {
248 modifications.add(modifiedAugmentation);
252 return modifications;
255 private static boolean isOfportOrNameChanged(
256 DataObjectModification<OvsdbTerminationPointAugmentation> ovsdbTpModification) {
257 if (ovsdbTpModification == null) {
260 OvsdbTerminationPointAugmentation oldTp = ovsdbTpModification.getDataBefore();
261 OvsdbTerminationPointAugmentation newTp = ovsdbTpModification.getDataAfter();
262 if (oldTp != null && newTp != null) {
263 if (oldTp.getOfport() != null && newTp.getOfport() != null && !Objects.equals(oldTp.getOfport(),
264 newTp.getOfport())) {
267 if (!(Strings.nullToEmpty(oldTp.getName())).equals(Strings.nullToEmpty(newTp.getName()))) {
271 if (isOfportOrNameNotNull(oldTp)) {
274 if (isOfportOrNameNotNull(newTp)) {
280 private static boolean isOfportOrNameNotNull(OvsdbTerminationPointAugmentation tp) {
282 if (tp.getOfport() != null) {
285 if (tp.getName() != null) {
292 private static DataObjectModification<OpenvswitchOtherConfigs> getProviderMappingsModification(
293 DataObjectModification<Node> rootNode) {
294 DataObjectModification<OvsdbNodeAugmentation> modifiedOvsdbNode =
295 rootNode.getModifiedAugmentation(OvsdbNodeAugmentation.class);
296 if (modifiedOvsdbNode == null) {
299 return modifiedOvsdbNode.getModifiedChildListItem(OpenvswitchOtherConfigs.class,
300 new OpenvswitchOtherConfigsKey(NEUTRON_PROVIDER_MAPPINGS_KEY));
303 private static boolean isProviderPortNameChanged(DataObjectModification<OpenvswitchOtherConfigs> ovsConfig) {
304 if (ovsConfig == null) {
307 OpenvswitchOtherConfigs oldConfig = ovsConfig.getDataBefore();
308 OpenvswitchOtherConfigs newConfig = ovsConfig.getDataAfter();
309 if (oldConfig != null && newConfig != null) {
310 if (!(Strings.nullToEmpty(oldConfig.getOtherConfigValue())
311 .equals(Strings.nullToEmpty(newConfig.getOtherConfigValue())))) {
314 } else if (oldConfig != null && !Strings.isNullOrEmpty(oldConfig.getOtherConfigValue())) {
316 } else if (newConfig != null && !Strings.isNullOrEmpty(newConfig.getOtherConfigValue())) {
322 private static @Nonnull String getProviderPortName(OpenvswitchOtherConfigs config) {
323 if (NEUTRON_PROVIDER_MAPPINGS_KEY.equals(config.getOtherConfigKey()) && config.getOtherConfigValue() != null) {
324 String otherConfig = config.getOtherConfigValue();
325 String[] elements = otherConfig.split(":");
326 if (elements.length == 2) {
334 * Extracts IP address from URI
336 * @param uri in format protocol:ip:port
337 * @return IPv4 or IPv6 address as {@link String}.
339 private static @Nonnull String getIpAddrFromUri(Uri uri) {
340 String otherConfig = uri.getValue();
341 String[] elements = otherConfig.split(":");
342 // IPv6 expression also contains colons
343 if (elements.length < 3) {
346 StringBuilder sb = new StringBuilder();
347 // first (protocol) and last (port) elements are filtered
348 for (int i = 1; i < elements.length - 1; i++) {
349 sb.append(elements[i]);
351 return sb.toString();
354 private List<ControllerEntry> managerToControllerEntries(List<ManagerEntry> managerEntries) {
355 return Lists.transform(managerEntries, new Function<ManagerEntry, ControllerEntry>() {
358 public ControllerEntry apply(ManagerEntry managerEntry) {
359 String ipAddr = getIpAddrFromUri(managerEntry.getTarget());
360 Uri uri = new Uri(intBrSettings.getOpenflowProtocol() + OF_SEPARATOR + ipAddr + OF_SEPARATOR
361 + intBrSettings.getOpenflowPort());
362 return new ControllerEntryBuilder().setTarget(new Uri(uri)).build();
367 private Node createBridge(InstanceIdentifier<Node> managedByIid, List<ControllerEntry> controllerEntries,
369 OvsdbBridgeAugmentation br = new OvsdbBridgeAugmentationBuilder()
370 .setBridgeName(new OvsdbBridgeName(bridgeName))
371 .setManagedBy(new OvsdbNodeRef(managedByIid))
372 .setControllerEntry(controllerEntries)
373 .setDatapathType(DatapathTypeSystem.class)
375 ImmutableList.of(new ProtocolEntryBuilder().setProtocol(
376 OvsdbBridgeProtocolOpenflow13.class).build()))
378 NodeKey managerNodeKey = managedByIid.firstKeyOf(Node.class);
379 return new NodeBuilder().setNodeId(
380 new org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId(
381 managerNodeKey.getNodeId().getValue() + BRIDGE_SEPARATOR + bridgeName))
382 .addAugmentation(OvsdbBridgeAugmentation.class, br)
386 private static NodeId buildOfNodeId(OvsdbBridgeAugmentation ovsdbBridge) {
387 if (ovsdbBridge.getDatapathId() == null) {
390 Long macLong = InventoryHelper.getLongFromDpid(ovsdbBridge.getDatapathId().getValue());
391 return new NodeId(OF_INVENTORY_PREFIX + OF_SEPARATOR + String.valueOf(macLong));
394 private static NodeConnectorId buildOfNodeConnectorId(OvsdbTerminationPointAugmentation terminationPoint,
396 if (terminationPoint.getOfport() == null) {
399 return new NodeConnectorId(nodeId.getValue() + OF_SEPARATOR + String.valueOf(terminationPoint.getOfport()));
402 private void traceBridge(InstanceIdentifier<Node> identifier) {
403 if (LOG.isTraceEnabled()) {
404 NeutronBridgeWithExtPort bridge = bridgeByNodeIid.get(identifier);
405 if (bridge == null) {
406 LOG.trace("Bridge does not exist: {}", identifier);
409 String providerPortName = providerPortNameByBridgeRef.get(new OvsdbBridgeRef(identifier));
410 LOG.trace("State of bridge:\n ID: {} \n providerPortName: {} \n {}", identifier, providerPortName,
415 private class NeutronBridgeWithExtPort {
418 Set<NodeConnectorId> externalIfaces = new HashSet<>();
419 Map<Long, String> ofportByName = new HashMap<>();
422 public String toString() {
423 return "NeutronBridgeWithExtPort:\n ofNodeId=" + ofNodeId + "\n externalIfaces=" + externalIfaces
424 + ",\n ofportByName=" + ofportByName;