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