Merge "Bug 1811 Fixed the pipeline by moving L2Fwd to the end."
[ovsdb.git] / plugin-mdsal-adapter / src / main / java / org / opendaylight / ovsdb / plugin / md / OvsdbInventoryManager.java
1 package org.opendaylight.ovsdb.plugin.md;
2
3 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
4 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
5 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
6 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
7 import org.opendaylight.controller.sal.utils.HexEncode;
8 import org.opendaylight.ovsdb.lib.notation.Row;
9 import org.opendaylight.ovsdb.plugin.api.OvsdbConfigurationService;
10 import org.opendaylight.ovsdb.plugin.api.OvsdbInventoryListener;
11 import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
12 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
13 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
14 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
15 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovsdb.node.inventory.rev140731.OvsdbCapableNode;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovsdb.node.inventory.rev140731.OvsdbCapableNodeBuilder;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovsdb.node.inventory.rev140731.OvsdbManagedNode;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovsdb.node.inventory.rev140731.OvsdbManagedNodeBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovsdb.node.inventory.rev140731.nodes.node.OvsdbBridge;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.ovsdb.node.inventory.rev140731.nodes.node.OvsdbBridgeBuilder;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
25
26 import com.google.common.base.Optional;
27 import com.google.common.base.Preconditions;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import java.net.InetAddress;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Set;
35 import java.util.concurrent.ExecutionException;
36
37 /**
38  * Handle OVSDB Inventory Updates and create the necessary entries in the MD-SAL config datastore
39  */
40 public class OvsdbInventoryManager implements OvsdbInventoryListener {
41
42     // Dependencies injected by OSGi
43     private volatile OvsdbBindingAwareProvider provider;
44     private volatile OvsdbConfigurationService ovsdbConfigurationService;
45
46     static final String OVS_NODE_PREFIX = "openvswitch:";
47     static final String OPENFLOW_NODE_PREFIX = "openflow:";
48
49     static final Logger logger = LoggerFactory.getLogger(OvsdbInventoryManager.class);
50
51
52     /**
53      * Called by the framework when the bundle is started
54      */
55     public void start() {
56         //ToDo: Add existing nodes from inventory
57         //This case is required for surviving controller reboot
58     }
59
60     /**
61      * When an AD-SAL node is added by the OVSDB Inventory Service, Add an MD-SAL node
62      *
63      * @param node    The AD-SAL node
64      * @param address The {@link java.net.InetAddress} of the Node
65      * @param port    The ephemeral port number used by this connection
66      */
67     @Override
68     public synchronized void nodeAdded(org.opendaylight.controller.sal.core.Node node,
69                                        InetAddress address,
70                                        int port) {
71         DataBroker dataBroker = provider.getDataBroker();
72         Preconditions.checkNotNull(dataBroker);
73
74         NodeId nodeId = new NodeId(OVS_NODE_PREFIX + node.getNodeIDString());
75         NodeKey nodeKey = new NodeKey(nodeId);
76
77         OvsdbCapableNode ovsdbNode = new OvsdbCapableNodeBuilder()
78                 .setIpAddress(Utils.convertIpAddress(address))
79                 .setPort(new PortNumber(port))
80                 .setManagedNodes(new ArrayList<NodeId>())
81                 .build();
82
83         Node newNode = new NodeBuilder()
84                 .setId(nodeId)
85                 .setKey(nodeKey)
86                 .addAugmentation(OvsdbCapableNode.class, ovsdbNode)
87                 .build();
88
89         InstanceIdentifier<Node> path = InstanceIdentifier.builder(Nodes.class)
90                 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, nodeKey)
91                 .toInstance();
92
93         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
94         tx.put(LogicalDatastoreType.CONFIGURATION, path, newNode, true);
95         try {
96             tx.submit().get();
97             logger.debug("Removed Node {}", path.toString());
98         } catch (InterruptedException | ExecutionException e) {
99             logger.error(e.getMessage(), e);
100         }
101     }
102
103     /**
104      * When an AD-SAL node is removed by the OVSDB Inventory Service, Remove the MD-SAL node
105      *
106      * @param node The AD-SAL node
107      */
108     @Override
109     public synchronized void nodeRemoved(org.opendaylight.controller.sal.core.Node node) {
110         DataBroker dataBroker = provider.getDataBroker();
111         Preconditions.checkNotNull(dataBroker);
112
113         NodeId nodeId = new NodeId(new NodeId(OVS_NODE_PREFIX + node.getNodeIDString()));
114         NodeKey nodeKey = new NodeKey(nodeId);
115
116         InstanceIdentifier<Node> path = InstanceIdentifier.builder(Nodes.class)
117                 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class, nodeKey)
118                 .toInstance();
119
120         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
121         tx.delete(LogicalDatastoreType.CONFIGURATION, path);
122         try {
123             tx.submit().get();
124             logger.debug("Removed Node {}", path.toString());
125         } catch (InterruptedException | ExecutionException e) {
126             logger.error(e.getMessage(), e);
127         }
128     }
129
130     /**
131      * Handle OVSDB row removed When a Bridge row is removed, the OpenFlow Node is deleted The parent OVSDB node is
132      * updated and the OpenFlow node removed from it's managed-nodes list
133      *
134      * @param node      The AD-SAL node
135      * @param tableName The name of modified table
136      * @param uuid      The UUID of the deleted row
137      * @param row       The deleted Row
138      */
139     @Override
140     public synchronized void rowRemoved(org.opendaylight.controller.sal.core.Node node,
141                                         String tableName,
142                                         String uuid,
143                                         Row row,
144                                         Object context) {
145         if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Bridge.class))) {
146             logger.debug("OVSDB Bridge Row removed on node {}", node.toString());
147             DataBroker dataBroker = provider.getDataBroker();
148             Preconditions.checkNotNull(dataBroker);
149
150             Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, row);
151             Set<String> dpidString = bridge.getDatapathIdColumn().getData();
152             Long dpid = HexEncode.stringToLong((String) dpidString.toArray()[0]);
153
154             NodeId openflowNodeId = new NodeId(OPENFLOW_NODE_PREFIX + dpid.toString());
155             NodeKey openflowNodeKey = new NodeKey(openflowNodeId);
156
157             InstanceIdentifier<Node> openflowNodePath = InstanceIdentifier.builder(Nodes.class)
158                     .child(Node.class, openflowNodeKey)
159                     .toInstance();
160
161             NodeId ovsdbNodeId = new NodeId(OVS_NODE_PREFIX + node.getNodeIDString());
162             NodeKey ovsdbNodeKey = new NodeKey(ovsdbNodeId);
163
164             InstanceIdentifier<OvsdbCapableNode> ovsdbNodePath = InstanceIdentifier.builder(Nodes.class)
165                     .child(Node.class, ovsdbNodeKey)
166                     .augmentation(OvsdbCapableNode.class)
167                     .toInstance();
168
169             // Read the current OVSDB Node from the DataStore
170             ReadWriteTransaction tx = dataBroker.newReadWriteTransaction();
171             OvsdbCapableNode ovsdbNode;
172             try {
173                 Optional<OvsdbCapableNode> data = tx.read(LogicalDatastoreType.CONFIGURATION, ovsdbNodePath).get();
174                 if (!data.isPresent()) {
175                     logger.error("OVSDB node not updated. Parent node for {} does not exist", ovsdbNodePath.toString());
176                     return;
177                 }
178                 ovsdbNode = data.get();
179             } catch (InterruptedException | ExecutionException e) {
180                 logger.error("OVSDB node not updated. Parent node for {} does not exist", ovsdbNodePath.toString());
181                 return;
182             }
183
184             // Update the list of Nodes
185             List<NodeId> managedNodesList = ovsdbNode.getManagedNodes();
186             managedNodesList.remove(openflowNodeId);
187
188             // Write changes to DataStore
189             OvsdbCapableNode updatedNode = new OvsdbCapableNodeBuilder(ovsdbNode)
190                     .setManagedNodes(managedNodesList)
191                     .build();
192             tx.delete(LogicalDatastoreType.CONFIGURATION, openflowNodePath);
193             tx.put(LogicalDatastoreType.CONFIGURATION, ovsdbNodePath, updatedNode);
194
195             try {
196                 tx.submit().get();
197                 logger.debug("Transaction success for delete of {} and update of {}",
198                              openflowNodePath.toString(),
199                              ovsdbNodePath.toString());
200             } catch (InterruptedException | ExecutionException e) {
201                 logger.error(e.getMessage(), e);
202             }
203         }
204     }
205
206     /**
207      * Handle OVSDB row updates When a Bridge row is updated and it contains a DPID then add a new OpenFlow node to the
208      * inventory A relationship is created between the OpenFlow and OVSDB nodes
209      *
210      * @param node      The AD-SAL node
211      * @param tableName The name of the updated table
212      * @param uuid      The UUID of the updated row
213      * @param old       The old contents of the row
214      * @param row       The updated Row
215      */
216     @Override
217     public synchronized void rowUpdated(org.opendaylight.controller.sal.core.Node node,
218                                         String tableName,
219                                         String uuid,
220                                         Row old,
221                                         Row row) {
222         logger.debug("OVSDB Bridge Row updated on node {}", node.toString());
223         if (tableName.equalsIgnoreCase(ovsdbConfigurationService.getTableName(node, Bridge.class))) {
224             DataBroker dataBroker = provider.getDataBroker();
225             Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, row);
226
227             Set<String> dpidString = bridge.getDatapathIdColumn().getData();
228             Long dpid;
229             try {
230                 dpid = HexEncode.stringToLong((String) dpidString.toArray()[0]);
231             } catch (ArrayIndexOutOfBoundsException e) {
232                 return;
233             }
234
235             NodeId openflowNodeId = new NodeId(OPENFLOW_NODE_PREFIX + dpid.toString());
236             NodeKey openflowNodeKey = new NodeKey(openflowNodeId);
237
238             InstanceIdentifier<OvsdbManagedNode> openflowNodepath = InstanceIdentifier.builder(Nodes.class)
239                     .child(Node.class, openflowNodeKey)
240                     .augmentation(OvsdbManagedNode.class)
241                     .toInstance();
242
243             NodeId ovsdbNodeId = new NodeId(OVS_NODE_PREFIX + node.getNodeIDString());
244             NodeKey ovsdbNodeKey = new NodeKey(ovsdbNodeId);
245
246             InstanceIdentifier<OvsdbCapableNode> ovsdbNodePath = InstanceIdentifier.builder(Nodes.class)
247                     .child(Node.class, ovsdbNodeKey)
248                     .augmentation(OvsdbCapableNode.class)
249                     .toInstance();
250
251             // Create an OvsdbBridge object using the information from the update
252             OvsdbBridge ovsdbBridge = new OvsdbBridgeBuilder()
253                     .setBridgeName(bridge.getName())
254                     .setBridgeUuid(uuid)
255                     .setManagedBy(ovsdbNodeId)
256                     .build();
257
258             // Add the bridge to the OvsdbManagedNode
259             OvsdbManagedNode ovsdbManagedNode = new OvsdbManagedNodeBuilder()
260                     .setOvsdbBridge(ovsdbBridge)
261                     .build();
262
263             // Read the current OVSDB Node from the DataStore
264             ReadWriteTransaction tx = dataBroker.newReadWriteTransaction();
265             OvsdbCapableNode ovsdbNode;
266             try {
267                 Optional<OvsdbCapableNode> data = tx.read(LogicalDatastoreType.CONFIGURATION, ovsdbNodePath).get();
268                 if (!data.isPresent()) {
269                     logger.error("OVSDB node not updated. Parent node for {} does not exist", ovsdbNodePath.toString());
270                     return;
271                 }
272                 ovsdbNode = data.get();
273             } catch (InterruptedException | ExecutionException e) {
274                 throw new RuntimeException("Node does not exist");
275             }
276
277             // Update the list of Nodes
278             List<NodeId> managedNodesList = ovsdbNode.getManagedNodes();
279             managedNodesList.add(openflowNodeId);
280
281             // Create a delta object
282             OvsdbCapableNode updatedNode = new OvsdbCapableNodeBuilder(ovsdbNode)
283                     .setManagedNodes(managedNodesList)
284                     .build();
285
286             // Create parent if we get to this node before openflowplugin
287             tx.put(LogicalDatastoreType.CONFIGURATION, openflowNodepath, ovsdbManagedNode, true);
288             tx.put(LogicalDatastoreType.CONFIGURATION, ovsdbNodePath, updatedNode);
289
290             try {
291                 tx.submit().get();
292                 logger.debug("Transaction success for addition of {} and update of {}",
293                              openflowNodepath.toString(),
294                              ovsdbNodePath.toString());
295             } catch (InterruptedException | ExecutionException e) {
296                 logger.error(e.getMessage(), e);
297             }
298         }
299     }
300
301     @Override
302     public synchronized void rowAdded(org.opendaylight.controller.sal.core.Node node,
303                                       String tableName,
304                                       String uuid,
305                                       Row row) {
306         // noop
307     }
308 }