c472747a8e16eac2b421e17931f51a9d09f1b84f
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / InventoryServiceShim.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, 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.controller.protocol_plugin.openflow.internal;
10
11 import java.util.Date;
12 import java.util.HashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
18 import java.util.concurrent.CopyOnWriteArrayList;
19
20 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
21 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimInternalListener;
22 import org.opendaylight.controller.protocol_plugin.openflow.IStatisticsListener;
23 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
24 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
25 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
26 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitchStateListener;
27 import org.opendaylight.controller.sal.core.Actions;
28 import org.opendaylight.controller.sal.core.Buffers;
29 import org.opendaylight.controller.sal.core.Capabilities;
30 import org.opendaylight.controller.sal.core.ConstructionException;
31 import org.opendaylight.controller.sal.core.ContainerFlow;
32 import org.opendaylight.controller.sal.core.Description;
33 import org.opendaylight.controller.sal.core.IContainerListener;
34 import org.opendaylight.controller.sal.core.Node;
35 import org.opendaylight.controller.sal.core.Node.NodeIDType;
36 import org.opendaylight.controller.sal.core.NodeConnector;
37 import org.opendaylight.controller.sal.core.Property;
38 import org.opendaylight.controller.sal.core.Tables;
39 import org.opendaylight.controller.sal.core.TimeStamp;
40 import org.opendaylight.controller.sal.core.UpdateType;
41 import org.opendaylight.controller.sal.utils.GlobalConstants;
42 import org.openflow.protocol.OFMessage;
43 import org.openflow.protocol.OFPortStatus;
44 import org.openflow.protocol.OFPortStatus.OFPortReason;
45 import org.openflow.protocol.OFType;
46 import org.openflow.protocol.statistics.OFDescriptionStatistics;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 /**
51  * The class describes a shim layer that bridges inventory events from Openflow
52  * core to various listeners. The notifications are filtered based on container
53  * configurations.
54  * 
55  * 
56  */
57 public class InventoryServiceShim implements IContainerListener,
58         IMessageListener, ISwitchStateListener, IStatisticsListener {
59     protected static final Logger logger = LoggerFactory
60             .getLogger(InventoryServiceShim.class);
61     private IController controller = null;
62     private ConcurrentMap<String, IInventoryShimInternalListener> inventoryShimInternalListeners = new ConcurrentHashMap<String, IInventoryShimInternalListener>();
63     private List<IInventoryShimExternalListener> inventoryShimExternalListeners = new CopyOnWriteArrayList<IInventoryShimExternalListener>();
64     private ConcurrentMap<NodeConnector, List<String>> containerMap = new ConcurrentHashMap<NodeConnector, List<String>>();
65
66     void setController(IController s) {
67         this.controller = s;
68     }
69
70     void unsetController(IController s) {
71         if (this.controller == s) {
72             this.controller = null;
73         }
74     }
75
76     void setInventoryShimInternalListener(Map<?, ?> props,
77             IInventoryShimInternalListener s) {
78         if (props == null) {
79             logger.error("setInventoryShimInternalListener property is null");
80             return;
81         }
82         String containerName = (String) props.get("containerName");
83         if (containerName == null) {
84             logger.error("setInventoryShimInternalListener containerName not supplied");
85             return;
86         }
87         if ((this.inventoryShimInternalListeners != null)
88                 && !this.inventoryShimInternalListeners.containsValue(s)) {
89             this.inventoryShimInternalListeners.put(containerName, s);
90             logger.trace(
91                     "Added inventoryShimInternalListener for container {}",
92                     containerName);
93         }
94     }
95
96     void unsetInventoryShimInternalListener(Map<?, ?> props,
97             IInventoryShimInternalListener s) {
98         if (props == null) {
99             logger.error("unsetInventoryShimInternalListener property is null");
100             return;
101         }
102         String containerName = (String) props.get("containerName");
103         if (containerName == null) {
104             logger.error("unsetInventoryShimInternalListener containerName not supplied");
105             return;
106         }
107         if ((this.inventoryShimInternalListeners != null)
108                 && this.inventoryShimInternalListeners.get(containerName) != null
109                 && this.inventoryShimInternalListeners.get(containerName)
110                         .equals(s)) {
111             this.inventoryShimInternalListeners.remove(containerName);
112             logger.trace(
113                     "Removed inventoryShimInternalListener for container {}",
114                     containerName);
115         }
116     }
117
118     void setInventoryShimExternalListener(IInventoryShimExternalListener s) {
119         logger.trace("Set inventoryShimExternalListener");
120         if ((this.inventoryShimExternalListeners != null)
121                 && !this.inventoryShimExternalListeners.contains(s)) {
122             this.inventoryShimExternalListeners.add(s);
123         }
124     }
125
126     void unsetInventoryShimExternalListener(IInventoryShimExternalListener s) {
127         if ((this.inventoryShimExternalListeners != null)
128                 && this.inventoryShimExternalListeners.contains(s)) {
129             this.inventoryShimExternalListeners.remove(s);
130         }
131     }
132
133     /**
134      * Function called by the dependency manager when all the required
135      * dependencies are satisfied
136      * 
137      */
138     void init() {
139         this.controller.addMessageListener(OFType.PORT_STATUS, this);
140         this.controller.addSwitchStateListener(this);
141     }
142
143     /**
144      * Function called after registering the service in OSGi service registry.
145      */
146     void started() {
147         /* Start with existing switches */
148         startService();
149     }
150
151     /**
152      * Function called by the dependency manager when at least one dependency
153      * become unsatisfied or when the component is shutting down because for
154      * example bundle is being stopped.
155      * 
156      */
157     void destroy() {
158         this.controller.removeMessageListener(OFType.PACKET_IN, this);
159         this.controller.removeSwitchStateListener(this);
160
161         this.inventoryShimInternalListeners.clear();
162         this.containerMap.clear();
163         this.controller = null;
164     }
165
166     @Override
167     public void receive(ISwitch sw, OFMessage msg) {
168         try {
169             if (msg instanceof OFPortStatus) {
170                 handlePortStatusMessage(sw, (OFPortStatus) msg);
171             }
172         } catch (ConstructionException e) {
173             logger.error("",e);
174         }
175         return;
176     }
177
178     protected void handlePortStatusMessage(ISwitch sw, OFPortStatus m)
179             throws ConstructionException {
180         Node node = new Node(NodeIDType.OPENFLOW, sw.getId());
181         NodeConnector nodeConnector = PortConverter.toNodeConnector(m.getDesc()
182                 .getPortNumber(), node);
183         UpdateType type = null;
184
185         if (m.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) {
186             type = UpdateType.ADDED;
187         } else if (m.getReason() == (byte) OFPortReason.OFPPR_DELETE.ordinal()) {
188             type = UpdateType.REMOVED;
189         } else if (m.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
190             type = UpdateType.CHANGED;
191         }
192
193         logger.trace("handlePortStatusMessage {} type {}", nodeConnector, type);
194
195         if (type != null) {
196             // get node connector properties
197             Set<Property> props = InventoryServiceHelper.OFPortToProps(m
198                     .getDesc());
199             notifyInventoryShimListener(nodeConnector, type, props);
200         }
201     }
202
203     @Override
204     public void switchAdded(ISwitch sw) {
205         if (sw == null)
206             return;
207
208         // Add all the nodeConnectors of this switch
209         Map<NodeConnector, Set<Property>> ncProps = InventoryServiceHelper
210                 .OFSwitchToProps(sw);
211         for (Map.Entry<NodeConnector, Set<Property>> entry : ncProps.entrySet()) {
212             notifyInventoryShimListener(entry.getKey(), UpdateType.ADDED,
213                     entry.getValue());
214         }
215
216         // Add this node
217         addNode(sw);
218     }
219
220     @Override
221     public void switchDeleted(ISwitch sw) {
222         if (sw == null)
223             return;
224
225         removeNode(sw);
226     }
227
228     @Override
229     public void containerModeUpdated(UpdateType t) {
230         // do nothing
231     }
232
233     @Override
234     public void tagUpdated(String containerName, Node n, short oldTag,
235             short newTag, UpdateType t) {
236     }
237
238     @Override
239     public void containerFlowUpdated(String containerName,
240             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
241     }
242
243     @Override
244     public void nodeConnectorUpdated(String containerName, NodeConnector p,
245             UpdateType t) {
246         if (this.containerMap == null) {
247             logger.error("containerMap is NULL");
248             return;
249         }
250         List<String> containers = this.containerMap.get(p);
251         if (containers == null) {
252             containers = new CopyOnWriteArrayList<String>();
253         }
254         boolean updateMap = false;
255         switch (t) {
256         case ADDED:
257             if (!containers.contains(containerName)) {
258                 containers.add(containerName);
259                 updateMap = true;
260             }
261             break;
262         case REMOVED:
263             if (containers.contains(containerName)) {
264                 containers.remove(containerName);
265                 updateMap = true;
266             }
267             break;
268         case CHANGED:
269             break;
270         }
271         if (updateMap) {
272             if (containers.isEmpty()) {
273                 // Do cleanup to reduce memory footprint if no
274                 // elements to be tracked
275                 this.containerMap.remove(p);
276             } else {
277                 this.containerMap.put(p, containers);
278             }
279         }
280
281         // notify InventoryService
282         notifyInventoryShimInternalListener(containerName, p, t, null);
283     }
284
285     private void notifyInventoryShimExternalListener(Node node,
286             UpdateType type, Set<Property> props) {
287         for (IInventoryShimExternalListener s : this.inventoryShimExternalListeners) {
288             s.updateNode(node, type, props);
289         }
290     }
291
292     private void notifyInventoryShimExternalListener(
293             NodeConnector nodeConnector, UpdateType type, Set<Property> props) {
294         for (IInventoryShimExternalListener s : this.inventoryShimExternalListeners) {
295             s.updateNodeConnector(nodeConnector, type, props);
296         }
297     }
298
299     private void notifyInventoryShimInternalListener(String container,
300             NodeConnector nodeConnector, UpdateType type, Set<Property> props) {
301         IInventoryShimInternalListener inventoryShimInternalListener = inventoryShimInternalListeners
302                 .get(container);
303         if (inventoryShimInternalListener != null) {
304             inventoryShimInternalListener.updateNodeConnector(nodeConnector,
305                     type, props);
306             logger.trace(
307                     "notifyInventoryShimInternalListener {} type {} for container {}",
308                     nodeConnector, type, container);
309         }
310     }
311
312     /*
313      * Notify all internal and external listeners
314      */
315     private void notifyInventoryShimListener(NodeConnector nodeConnector,
316             UpdateType type, Set<Property> props) {
317         // Always notify default InventoryService. Store properties in default
318         // one.
319         notifyInventoryShimInternalListener(GlobalConstants.DEFAULT.toString(),
320                 nodeConnector, type, props);
321
322         // Now notify other containers
323         List<String> containers = containerMap.get(nodeConnector);
324         if (containers != null) {
325             for (String container : containers) {
326                 // no property stored in container components.
327                 notifyInventoryShimInternalListener(container, nodeConnector,
328                         type, null);
329             }
330         }
331
332         // Notify DiscoveryService
333         notifyInventoryShimExternalListener(nodeConnector, type, props);
334     }
335
336     /*
337      * Notify all internal and external listeners
338      */
339     private void notifyInventoryShimListener(Node node, UpdateType type,
340             Set<Property> props) {
341         switch (type) {
342         case ADDED:
343             // Notify only the default Inventory Service
344             IInventoryShimInternalListener inventoryShimDefaultListener = inventoryShimInternalListeners
345                     .get(GlobalConstants.DEFAULT.toString());
346             if (inventoryShimDefaultListener != null) {
347                 inventoryShimDefaultListener.updateNode(node, type, props);
348             }
349             break;
350         case REMOVED:
351             // Notify all Inventory Service containers
352             for (IInventoryShimInternalListener inventoryShimInternalListener : inventoryShimInternalListeners
353                     .values()) {
354                 inventoryShimInternalListener.updateNode(node, type, null);
355             }
356             break;
357         case CHANGED:
358             // Notify only the default Inventory Service
359             inventoryShimDefaultListener = inventoryShimInternalListeners
360                     .get(GlobalConstants.DEFAULT.toString());
361             if (inventoryShimDefaultListener != null) {
362                 inventoryShimDefaultListener.updateNode(node, type, props);
363             }
364             break;
365         default:
366             break;
367         }
368
369         // Notify external listener
370         notifyInventoryShimExternalListener(node, type, props);
371     }
372
373     private void addNode(ISwitch sw) {
374         Node node;
375         try {
376             node = new Node(NodeIDType.OPENFLOW, sw.getId());
377         } catch (ConstructionException e) {
378             logger.error("{}", e.getMessage());
379             return;
380         }
381
382         UpdateType type = UpdateType.ADDED;
383
384         Set<Property> props = new HashSet<Property>();
385         Long sid = (Long) node.getID();
386
387         Date connectedSince = controller.getSwitches().get(sid)
388                 .getConnectedDate();
389         Long connectedSinceTime = (connectedSince == null) ? 0 : connectedSince
390                 .getTime();
391         props.add(new TimeStamp(connectedSinceTime, "connectedSince"));
392
393         byte tables = sw.getTables();
394         Tables t = new Tables(tables);
395         if (t != null) {
396             props.add(t);
397         }
398         int cap = sw.getCapabilities();
399         Capabilities c = new Capabilities(cap);
400         if (c != null) {
401             props.add(c);
402         }
403         int act = sw.getActions();
404         Actions a = new Actions(act);
405         if (a != null) {
406             props.add(a);
407         }
408         int buffers = sw.getBuffers();
409         Buffers b = new Buffers(buffers);
410         if (b != null) {
411             props.add(b);
412         }
413         // Notify all internal and external listeners
414         notifyInventoryShimListener(node, type, props);
415     }
416
417     private void removeNode(ISwitch sw) {
418         Node node;
419         try {
420             node = new Node(NodeIDType.OPENFLOW, sw.getId());
421         } catch (ConstructionException e) {
422             logger.error("{}", e.getMessage());
423             return;
424         }
425
426         UpdateType type = UpdateType.REMOVED;
427
428         // Notify all internal and external listeners
429         notifyInventoryShimListener(node, type, null);
430     }
431
432     private void startService() {
433         // Get a snapshot of all the existing switches
434         Map<Long, ISwitch> switches = this.controller.getSwitches();
435         for (ISwitch sw : switches.values()) {
436             switchAdded(sw);
437         }
438     }
439
440     @Override
441     public void descriptionRefreshed(Long switchId,
442             OFDescriptionStatistics descriptionStats) {
443         Node node;
444         try {
445             node = new Node(NodeIDType.OPENFLOW, switchId);
446         } catch (ConstructionException e) {
447             logger.error("{}", e.getMessage());
448             return;
449         }
450         
451         Set<Property> properties = new HashSet<Property>(1);
452         Description desc = new Description(
453                 descriptionStats.getDatapathDescription());
454         properties.add(desc);
455         
456         // Notify all internal and external listeners
457         notifyInventoryShimListener(node, UpdateType.CHANGED, properties);
458     }  
459
460    
461 }