Merge "Identifying and ignoring uninterested SouthBound updates."
[ovsdb.git] / ovsdb / src / main / java / org / opendaylight / ovsdb / plugin / InventoryService.java
1 package org.opendaylight.ovsdb.plugin;
2
3 import java.util.Collection;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.concurrent.ConcurrentHashMap;
9 import java.util.concurrent.ConcurrentMap;
10 import java.util.concurrent.CopyOnWriteArraySet;
11 import java.util.concurrent.Executors;
12 import java.util.concurrent.ScheduledExecutorService;
13 import java.util.concurrent.TimeUnit;
14
15 import org.opendaylight.controller.sal.core.ConstructionException;
16 import org.opendaylight.controller.sal.core.Description;
17 import org.opendaylight.controller.sal.core.Node;
18 import org.opendaylight.controller.sal.core.NodeConnector;
19 import org.opendaylight.controller.sal.core.Property;
20 import org.opendaylight.controller.sal.core.UpdateType;
21 import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
22 import org.opendaylight.controller.sal.inventory.IPluginOutInventoryService;
23 import org.opendaylight.controller.sal.utils.HexEncode;
24 import org.opendaylight.controller.sal.utils.ServiceHelper;
25 import org.opendaylight.ovsdb.lib.database.DatabaseSchema;
26 import org.opendaylight.ovsdb.lib.message.TableUpdate;
27 import org.opendaylight.ovsdb.lib.message.TableUpdate.Row;
28 import org.opendaylight.ovsdb.lib.message.TableUpdates;
29 import org.opendaylight.ovsdb.lib.notation.OvsDBSet;
30 import org.opendaylight.ovsdb.lib.table.Bridge;
31 import org.opendaylight.ovsdb.lib.table.internal.Table;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import com.google.common.collect.Maps;
36
37 /**
38  * Stub Implementation for IPluginInReadService used by SAL
39  *
40  *
41  */
42 public class InventoryService implements IPluginInInventoryService, InventoryServiceInternal {
43     private static final Logger logger = LoggerFactory
44             .getLogger(InventoryService.class);
45     private final Set<IPluginOutInventoryService> pluginOutInventoryServices =
46             new CopyOnWriteArraySet<IPluginOutInventoryService>();
47     private ConcurrentMap<Node, Map<String, Property>> nodeProps;
48     private ConcurrentMap<NodeConnector, Map<String, Property>> nodeConnectorProps;
49     private Map<Node, NodeDB> dbCache = Maps.newHashMap();
50     private ScheduledExecutorService executor;
51
52     /**
53      * Function called by the dependency manager when all the required
54      * dependencies are satisfied
55      *
56      */
57     public void init() {
58         nodeProps = new ConcurrentHashMap<Node, Map<String, Property>>();
59         nodeConnectorProps = new ConcurrentHashMap<NodeConnector, Map<String, Property>>();
60         Node.NodeIDType.registerIDType("OVS", String.class);
61         NodeConnector.NodeConnectorIDType.registerIDType("OVS", String.class, "OVS");
62         this.executor = Executors.newSingleThreadScheduledExecutor();
63     }
64
65     /**
66      * Function called by the dependency manager when at least one dependency
67      * become unsatisfied or when the component is shutting down because for
68      * example bundle is being stopped.
69      *
70      */
71     public void destroy() {
72     }
73
74     /**
75      * Function called by dependency manager after "init ()" is called and after
76      * the services provided by the class are registered in the service registry
77      *
78      */
79     public void start() {
80     }
81
82     /**
83      * Function called by the dependency manager before the services exported by
84      * the component are unregistered, this will be followed by a "destroy ()"
85      * calls
86      *
87      */
88     public void stop() {
89         this.executor.shutdownNow();
90     }
91
92     public void setPluginOutInventoryServices(IPluginOutInventoryService service) {
93         if (this.pluginOutInventoryServices != null) {
94             this.pluginOutInventoryServices.add(service);
95         }
96     }
97
98     public void unsetPluginOutInventoryServices(
99             IPluginOutInventoryService service) {
100         if (this.pluginOutInventoryServices != null) {
101             this.pluginOutInventoryServices.remove(service);
102         }
103     }
104
105     /**
106      * Retrieve nodes from openflow
107      */
108     @Override
109     public ConcurrentMap<Node, Map<String, Property>> getNodeProps() {
110         return nodeProps;
111     }
112
113     /**
114      * Retrieve nodeConnectors from openflow
115      */
116     @Override
117     public ConcurrentMap<NodeConnector, Map<String, Property>> getNodeConnectorProps(
118             Boolean refresh) {
119         return nodeConnectorProps;
120     }
121
122
123     @Override
124     public Map<String, Map<String, Table<?>>> getCache(Node n) {
125         NodeDB db = dbCache.get(n);
126         if (db == null) return null;
127         return db.getTableCache();
128     }
129
130
131     @Override
132     public Map<String, Table<?>> getTableCache(Node n, String tableName) {
133         NodeDB db = dbCache.get(n);
134         if (db == null) return null;
135         return db.getTableCache(tableName);
136     }
137
138
139     @Override
140     public Table<?> getRow(Node n, String tableName, String uuid) {
141         NodeDB db = dbCache.get(n);
142         if (db == null) return null;
143         return db.getRow(tableName, uuid);
144     }
145
146     @Override
147     public void updateRow(Node n, String tableName, String uuid, Table<?> row) {
148         NodeDB db = dbCache.get(n);
149         if (db == null) {
150             db = new NodeDB();
151             dbCache.put(n, db);
152         }
153         db.updateRow(tableName, uuid, row);
154     }
155
156     @Override
157     public void removeRow(Node n, String tableName, String uuid) {
158         NodeDB db = dbCache.get(n);
159         if (db != null) db.removeRow(tableName, uuid);
160     }
161
162     @Override
163     public void processTableUpdates(Node n, TableUpdates tableUpdates) {
164         NodeDB db = dbCache.get(n);
165         if (db == null) {
166             db = new NodeDB();
167             dbCache.put(n, db);
168         }
169
170         OVSDBInventoryListener inventoryListener = (OVSDBInventoryListener)ServiceHelper.getGlobalInstance(OVSDBInventoryListener.class, this);
171         Set<Table.Name> available = tableUpdates.availableUpdates();
172         for (Table.Name name : available) {
173             TableUpdate tableUpdate = tableUpdates.getUpdate(name);
174             Collection<TableUpdate.Row<?>> rows = tableUpdate.getRows();
175             for (Row<?> row : rows) {
176                 String uuid = row.getId();
177                 Table<?> newRow = (Table<?>)row.getNew();
178                 Table<?> oldRow = (Table<?>)row.getOld();
179                 if (newRow != null) {
180                     db.updateRow(name.getName(), uuid, newRow);
181                     if (name.getName().equalsIgnoreCase("bridge")) {
182                         logger.debug("Received Bridge Table udpate for node {}", n);
183                         // OVSDB has the Bridge name info while OpenFlow Spec is not
184                         // Clear on that. From a user/manageability standpoint, it is easier
185                         // to handle Bridge names compared to dpids.
186                         // Updating the Openflow bridge name via the SAL Description update.
187                         updateOFBridgeName(n, (Bridge)newRow);
188                     }
189                     if ((oldRow == null) && (inventoryListener != null)) {
190                         inventoryListener.rowAdded(n, name.getName(), uuid, newRow);
191                     } else if (inventoryListener != null) {
192                         inventoryListener.rowUpdated(n, name.getName(), uuid, oldRow, newRow);
193                     }
194                 } else if (oldRow != null) {
195                     if (inventoryListener != null) {
196                         inventoryListener.rowRemoved(n, name.getName(), uuid, oldRow);
197                     }
198                     db.removeRow(name.getName(), uuid);
199                 }
200             }
201         }
202     }
203
204     private void updateOFBridgeName(final Node node, final Bridge bridge) {
205         Runnable updateNameRunnable = new Runnable() {
206             @Override
207             public void run() {
208                 OvsDBSet<String> dpids = bridge.getDatapath_id();
209                 String bridgeName = bridge.getName();
210                 if (dpids == null || bridgeName == null) return;
211                 for (String dpid : dpids) {
212                     Long dpidLong = Long.valueOf(HexEncode.stringToLong(dpid));
213                     try {
214                         Node ofNode = new Node(Node.NodeIDType.OPENFLOW, dpidLong);
215                         Description descProp = new Description(bridgeName);
216                         Set<Property> props = new HashSet<Property>();
217                         props.add(descProp);
218
219                         IPluginOutInventoryService salInventoryService = (IPluginOutInventoryService) ServiceHelper.getInstance(
220                                 IPluginOutInventoryService.class, "default", this);
221                         if (salInventoryService != null) {
222                             logger.debug("Updating Bridge Name {} on OF node {}", bridgeName, ofNode);
223                             salInventoryService.updateNode(ofNode, UpdateType.CHANGED, props);
224                         } else {
225                             logger.error("Error accessing SAL Inventory plugin");
226                         }
227                     } catch (ConstructionException e) {
228                         logger.error("Failed to construct node for " + dpid, e);
229                     }
230                 }
231             }
232         };
233         executor.execute(updateNameRunnable);
234         // Add a delay & re-execute to compensate for the current OpenFlow plugin bug of
235         // overriding the Description with a Null value after the first statistics timer.
236         // Hopefully this will be resolved in the newer version of the Openflow plugin.
237         executor.schedule(updateNameRunnable, 30, TimeUnit.SECONDS);
238     }
239
240     @Override
241     public void printCache(Node n) {
242         NodeDB db = dbCache.get(n);
243         if (db != null) db.printTableCache();
244     }
245
246     @Override
247     public void addNode(Node node, Set<Property> props) {
248         addNodeProperty(node, UpdateType.ADDED, props);
249     }
250
251     @Override
252     public void notifyNodeAdded(Node node) {
253         OVSDBInventoryListener inventoryListener = (OVSDBInventoryListener)ServiceHelper.getGlobalInstance(OVSDBInventoryListener.class, this);
254         if (inventoryListener != null) {
255             inventoryListener.nodeAdded(node);
256         }
257     }
258
259     @Override
260     public void addNodeProperty(Node node, UpdateType type, Set<Property> props) {
261         Map<String, Property> nProp = nodeProps.get(node);
262         if (nProp == null) nProp = new HashMap<String, Property>();
263         for (Property prop : props) {
264             nProp.put(prop.getName(), prop);
265         }
266         nodeProps.put(node, nProp);
267         for (IPluginOutInventoryService service : pluginOutInventoryServices) {
268             service.updateNode(node, type, props);
269         }
270     }
271
272     @Override
273     public DatabaseSchema getDatabaseSchema(Node n) {
274         NodeDB db = dbCache.get(n);
275         if (db != null) return db.getSchema();
276         return null;
277     }
278
279     @Override
280     public void updateDatabaseSchema(Node n, DatabaseSchema schema) {
281         NodeDB db = dbCache.get(n);
282         if (db == null) {
283             db = new NodeDB();
284             dbCache.put(n, db);
285         }
286         db.setSchema(schema);
287     }
288
289     @Override
290     public void removeNode(Node node) {
291         OVSDBInventoryListener inventoryListener = (OVSDBInventoryListener)ServiceHelper.getGlobalInstance(OVSDBInventoryListener.class, this);
292         if (inventoryListener != null) {
293             inventoryListener.nodeRemoved(node);
294         }
295
296         for (IPluginOutInventoryService service : pluginOutInventoryServices) {
297             service.updateNode(node, UpdateType.REMOVED, null);
298         }
299         nodeProps.remove(node);
300         dbCache.remove(node);
301     }
302 }