2 * Copyright © 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
9 package org.opendaylight.ovsdb.southbound;
11 import java.net.UnknownHostException;
12 import java.util.Collection;
13 import java.util.HashMap;
14 import java.util.List;
16 import java.util.Map.Entry;
18 import javax.annotation.Nonnull;
20 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
23 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.ovsdb.lib.OvsdbClient;
28 import org.opendaylight.ovsdb.southbound.ovsdb.transact.BridgeOperationalState;
29 import org.opendaylight.ovsdb.southbound.ovsdb.transact.TransactCommandAggregator;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
33 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
38 import org.opendaylight.yangtools.concepts.ListenerRegistration;
39 import org.opendaylight.yangtools.yang.binding.Augmentation;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * Data-tree change listener for OVSDB.
47 public class OvsdbDataTreeChangeListener implements ClusteredDataTreeChangeListener<Node>, AutoCloseable {
49 /** Our registration. */
50 private final ListenerRegistration<DataTreeChangeListener<Node>> registration;
52 /** The connection manager. */
53 private final OvsdbConnectionManager cm;
55 /** The data broker. */
56 private final DataBroker db;
59 private static final Logger LOG = LoggerFactory.getLogger(OvsdbDataTreeChangeListener.class);
62 * Create an instance and register the listener.
64 * @param db The data broker.
65 * @param cm The connection manager.
67 OvsdbDataTreeChangeListener(DataBroker db, OvsdbConnectionManager cm) {
70 InstanceIdentifier<Node> path = InstanceIdentifier
71 .create(NetworkTopology.class)
72 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
74 DataTreeIdentifier<Node> dataTreeIdentifier =
75 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, path);
76 registration = db.registerDataTreeChangeListener(dataTreeIdentifier, this);
77 LOG.info("OVSDB topology listener has been registered.");
83 LOG.info("OVSDB topology listener has been closed.");
87 public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<Node>> changes) {
88 LOG.trace("onDataTreeChanged: {}", changes);
90 // Connect first if necessary
93 // Update connections if necessary
94 updateConnections(changes);
96 // Update the actual data
99 // Disconnect if necessary
102 LOG.trace("onDataTreeChanged: exit");
105 private void connect(@Nonnull Collection<DataTreeModification<Node>> changes) {
106 for (DataTreeModification<Node> change : changes) {
107 if (change.getRootNode().getModificationType() == DataObjectModification.ModificationType.WRITE || change
108 .getRootNode().getModificationType() == DataObjectModification.ModificationType.SUBTREE_MODIFIED) {
109 DataObjectModification<OvsdbNodeAugmentation> ovsdbNodeModification =
110 change.getRootNode().getModifiedAugmentation(OvsdbNodeAugmentation.class);
111 if (ovsdbNodeModification != null && ovsdbNodeModification.getDataBefore() == null
112 && ovsdbNodeModification.getDataAfter() != null
113 && ovsdbNodeModification.getDataAfter().getConnectionInfo() != null) {
114 OvsdbNodeAugmentation ovsdbNode = ovsdbNodeModification.getDataAfter();
115 ConnectionInfo key = ovsdbNode.getConnectionInfo();
116 InstanceIdentifier<Node> iid = cm.getInstanceIdentifier(key);
118 LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
119 + "to same device, hence dropping the request {}", key, ovsdbNode);
122 InstanceIdentifier<Node> instanceIdentifier = change.getRootPath().getRootIdentifier();
123 cm.connect(instanceIdentifier, ovsdbNode);
124 LOG.info("OVSDB node has been connected: {}",ovsdbNode);
125 } catch (UnknownHostException e) {
126 LOG.warn("Failed to connect to ovsdbNode", e);
134 private void disconnect(@Nonnull Collection<DataTreeModification<Node>> changes) {
135 for (DataTreeModification<Node> change : changes) {
136 if (change.getRootNode().getModificationType() == DataObjectModification.ModificationType.DELETE) {
137 DataObjectModification<OvsdbNodeAugmentation> ovsdbNodeModification =
138 change.getRootNode().getModifiedAugmentation(OvsdbNodeAugmentation.class);
139 if (ovsdbNodeModification != null && ovsdbNodeModification.getDataBefore() != null) {
140 OvsdbNodeAugmentation ovsdbNode = ovsdbNodeModification.getDataBefore();
141 ConnectionInfo key = ovsdbNode.getConnectionInfo();
142 InstanceIdentifier<Node> iid = cm.getInstanceIdentifier(key);
144 cm.disconnect(ovsdbNode);
145 LOG.info("OVSDB node has been disconnected:{}", ovsdbNode);
146 cm.stopConnectionReconciliationIfActive(iid.firstIdentifierOf(Node.class), ovsdbNode);
147 } catch (UnknownHostException e) {
148 LOG.warn("Failed to disconnect ovsdbNode", e);
155 private void updateConnections(@Nonnull Collection<DataTreeModification<Node>> changes) {
156 for (DataTreeModification<Node> change : changes) {
157 if (change.getRootNode().getModificationType() == DataObjectModification.ModificationType.WRITE || change
158 .getRootNode().getModificationType() == DataObjectModification.ModificationType.SUBTREE_MODIFIED) {
159 DataObjectModification<OvsdbNodeAugmentation> ovsdbNodeModification =
160 change.getRootNode().getModifiedAugmentation(OvsdbNodeAugmentation.class);
161 if (ovsdbNodeModification != null && ovsdbNodeModification.getDataBefore() != null
162 && ovsdbNodeModification.getDataAfter() != null
163 && ovsdbNodeModification.getDataAfter().getConnectionInfo() != null) {
164 OvsdbClient client = cm.getClient(ovsdbNodeModification.getDataAfter().getConnectionInfo());
165 if (client == null) {
166 if (ovsdbNodeModification.getDataBefore() != null) {
168 cm.disconnect(ovsdbNodeModification.getDataBefore());
169 cm.connect(change.getRootPath().getRootIdentifier(), ovsdbNodeModification
171 } catch (UnknownHostException e) {
172 LOG.warn("Error disconnecting from or connecting to ovsdbNode", e);
181 private void updateData(@Nonnull Collection<DataTreeModification<Node>> changes) {
182 for (Entry<InstanceIdentifier<Node>, OvsdbConnectionInstance> connectionInstanceEntry :
183 connectionInstancesFromChanges(changes).entrySet()) {
184 OvsdbConnectionInstance connectionInstance = connectionInstanceEntry.getValue();
185 connectionInstance.transact(new TransactCommandAggregator(),
186 new BridgeOperationalState(db, changes), changes);
190 private Map<InstanceIdentifier<Node>, OvsdbConnectionInstance> connectionInstancesFromChanges(
191 @Nonnull Collection<DataTreeModification<Node>> changes) {
192 Map<InstanceIdentifier<Node>,OvsdbConnectionInstance> result =
194 for (DataTreeModification<Node> change : changes) {
195 OvsdbConnectionInstance client = null;
196 Node node = change.getRootNode().getDataAfter() != null?
197 change.getRootNode().getDataAfter() : change.getRootNode().getDataBefore();
199 InstanceIdentifier<Node> nodeIid;
200 Augmentation nodeAug = node.getAugmentation(OvsdbNodeAugmentation.class) !=null?
201 node.getAugmentation(OvsdbNodeAugmentation.class):node.getAugmentation(OvsdbBridgeAugmentation.class);
203 if(nodeAug instanceof OvsdbNodeAugmentation) {
204 OvsdbNodeAugmentation ovsdbNode = (OvsdbNodeAugmentation) nodeAug;
205 if(ovsdbNode.getConnectionInfo() != null) {
206 client = cm.getConnectionInstance(ovsdbNode.getConnectionInfo());
208 client = cm.getConnectionInstance(SouthboundMapper.createInstanceIdentifier(node.getNodeId()));
211 if(nodeAug instanceof OvsdbBridgeAugmentation) {
212 OvsdbBridgeAugmentation bridgeAugmentation = (OvsdbBridgeAugmentation)nodeAug;
213 if(bridgeAugmentation.getManagedBy() != null) {
214 nodeIid = (InstanceIdentifier<Node>) bridgeAugmentation.getManagedBy().getValue();
215 client = cm.getConnectionInstance(nodeIid);
218 if( client == null ) {
219 //Try getting from change root identifier
220 client = cm.getConnectionInstance(change.getRootPath().getRootIdentifier());
223 LOG.warn("Following change don't have after/before data {}", change);
225 if (client != null) {
226 LOG.debug("Found client for {}", node);
228 * As of now data change sets are processed by single thread, so we can assume that device will
229 * be connected and ownership will be decided before sending any instructions down to the device.
230 * Note:Processing order in onDataChange() method should not change. If processing is changed to
231 * use multiple thread, we might need to take care of corner cases, where ownership is not decided
232 * but transaction are ready to go to switch. In that scenario, either we need to queue those task
233 * till ownership is decided for that specific device.
234 * Given that each DataChangeNotification is notified through separate thread, so we are already
235 * multi threaded and i don't see any need to further parallelism per DataChange
236 * notifications processing.
238 if ( cm.getHasDeviceOwnership(client.getMDConnectionInfo())) {
239 LOG.debug("*This* instance of southbound plugin is an owner of the device {}", node);
240 result.put(change.getRootPath().getRootIdentifier(), client);
242 LOG.debug("*This* instance of southbound plugin is *not* an owner of the device {}", node);
245 LOG.debug("Did not find client for {}", node);