Fix race condition when registering services
[controller.git] / opendaylight / sal / implementation / src / main / java / org / opendaylight / controller / sal / implementation / internal / ReadService.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.controller.sal.implementation.internal;
11
12 import java.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.concurrent.CopyOnWriteArraySet;
20
21 import org.eclipse.osgi.framework.console.CommandInterpreter;
22 import org.eclipse.osgi.framework.console.CommandProvider;
23 import org.opendaylight.controller.sal.action.Action;
24 import org.opendaylight.controller.sal.action.Controller;
25 import org.opendaylight.controller.sal.action.Flood;
26 import org.opendaylight.controller.sal.action.Output;
27 import org.opendaylight.controller.sal.action.PopVlan;
28 import org.opendaylight.controller.sal.core.ConstructionException;
29 import org.opendaylight.controller.sal.core.Node;
30 import org.opendaylight.controller.sal.core.Node.NodeIDType;
31 import org.opendaylight.controller.sal.core.NodeConnector;
32 import org.opendaylight.controller.sal.core.NodeTable;
33 import org.opendaylight.controller.sal.flowprogrammer.Flow;
34 import org.opendaylight.controller.sal.match.Match;
35 import org.opendaylight.controller.sal.match.MatchType;
36 import org.opendaylight.controller.sal.reader.FlowOnNode;
37 import org.opendaylight.controller.sal.reader.IPluginInReadService;
38 import org.opendaylight.controller.sal.reader.IPluginOutReadService;
39 import org.opendaylight.controller.sal.reader.IReadService;
40 import org.opendaylight.controller.sal.reader.IReadServiceListener;
41 import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
42 import org.opendaylight.controller.sal.reader.NodeDescription;
43 import org.opendaylight.controller.sal.reader.NodeTableStatistics;
44 import org.opendaylight.controller.sal.utils.EtherTypes;
45 import org.opendaylight.controller.sal.utils.GlobalConstants;
46 import org.opendaylight.controller.sal.utils.IPProtocols;
47 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
48 import org.opendaylight.controller.sal.utils.NodeCreator;
49 import org.opendaylight.controller.sal.utils.NodeTableCreator;
50 import org.osgi.framework.BundleContext;
51 import org.osgi.framework.FrameworkUtil;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 /**
56  * The SAL Read Service. Dispatches read requests to the proper SDN protocol
57  * plugin, and notifies any listeners on updates from any plugin readers
58  */
59 public class ReadService implements IReadService, CommandProvider, IPluginOutReadService {
60
61     protected static final Logger logger = LoggerFactory.getLogger(ReadService.class);
62     private ConcurrentHashMap<String, IPluginInReadService> pluginReader =
63         new ConcurrentHashMap<String, IPluginInReadService>();
64     private Set<IReadServiceListener> readerListeners =
65         new CopyOnWriteArraySet<IReadServiceListener>();
66
67     /**
68      * Function called by the dependency manager when all the required
69      * dependencies are satisfied
70      *
71      */
72     void init() {
73     }
74
75     /**
76      * Function called by the dependency manager when at least one
77      * dependency become unsatisfied or when the component is shutting
78      * down because for example bundle is being stopped.
79      *
80      */
81     void destroy() {
82         // In case of plugin disactivating make sure we clear the
83         // dependencies
84         this.pluginReader.clear();
85         this.readerListeners.clear();
86     }
87
88     /**
89      * Function called by dependency manager after "init ()" is called
90      * and after the services provided by the class are registered in
91      * the service registry
92      *
93      */
94     void start() {
95         registerWithOSGIConsole();
96     }
97
98     /**
99      * Function called by the dependency manager before the services
100      * exported by the component are unregistered, this will be
101      * followed by a "destroy ()" calls
102      *
103      */
104     void stop() {
105     }
106
107     // Set the reference to the plugin flow Reader service
108     public void setService(Map<?, ?> props, IPluginInReadService s) {
109         if (this.pluginReader == null) {
110             logger.error("pluginReader store null");
111             return;
112         }
113
114         logger.trace("Got a service set request {}", s);
115         String type = null;
116         for (Object e : props.entrySet()) {
117             Map.Entry<?, ?> entry = (Map.Entry<?, ?>) e;
118             logger.trace("Prop key:({}) value:({})", entry.getKey(),
119                     entry.getValue());
120         }
121
122         Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
123         if (value instanceof String) {
124             type = (String) value;
125         }
126         if (type == null) {
127             logger.error("Received a pluginReader without any "
128                     + "protocolPluginType provided");
129         } else {
130             this.pluginReader.put(type, s);
131             logger.debug("Stored the pluginReader for type: {}", type);
132         }
133     }
134
135     public void unsetService(Map<?, ?> props, IPluginInReadService s) {
136         if (this.pluginReader == null) {
137             logger.error("pluginReader store null");
138             return;
139         }
140
141         String type = null;
142         logger.debug("Received unsetpluginReader request");
143         for (Object e : props.entrySet()) {
144             Map.Entry<?, ?> entry = (Map.Entry<?, ?>) e;
145             logger.trace("Prop key:({}) value:({})", entry.getKey(),
146                     entry.getValue());
147         }
148
149         Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
150         if (value instanceof String) {
151             type = (String) value;
152         }
153         if (type == null) {
154             logger.error("Received a pluginReader without any "
155                     + "protocolPluginType provided");
156         } else if (this.pluginReader.get(type).equals(s)) {
157             this.pluginReader.remove(type);
158             logger.debug("Removed the pluginReader for type: {}", type);
159         }
160     }
161     public void setReaderListener(IReadServiceListener service) {
162         logger.trace("Got a listener set request {}", service);
163         this.readerListeners.add(service);
164     }
165
166     public void unsetReaderListener(IReadServiceListener service) {
167         logger.trace("Got a listener Unset request");
168         this.readerListeners.remove(service);
169     }
170
171     @Override
172     public FlowOnNode readFlow(Node node, Flow flow) {
173         if (pluginReader != null) {
174             if (this.pluginReader.get(node.getType()) != null) {
175                 return this.pluginReader.get(node.getType())
176                         .readFlow(node, flow, true);
177             }
178         }
179         logger.warn("Plugin {} unavailable", node.getType());
180         return null;
181     }
182
183     @Override
184     public FlowOnNode nonCachedReadFlow(Node node, Flow flow) {
185         if (pluginReader != null) {
186             if (this.pluginReader.get(node.getType()) != null) {
187                 return this.pluginReader.get(node.getType())
188                         .readFlow(node, flow, false);
189             }
190         }
191         logger.warn("Plugin {} unavailable", node.getType());
192         return null;
193     }
194
195     @Override
196     public List<FlowOnNode> readAllFlows(Node node) {
197         if (pluginReader != null) {
198             if (this.pluginReader.get(node.getType()) != null) {
199                 return this.pluginReader.get(node.getType())
200                         .readAllFlow(node, true);
201             }
202         }
203         logger.warn("Plugin {} unavailable", node.getType());
204         return null;
205     }
206
207     @Override
208     public List<FlowOnNode> nonCachedReadAllFlows(Node node) {
209         if (pluginReader != null) {
210             if (this.pluginReader.get(node.getType()) != null) {
211                 return this.pluginReader.get(node.getType())
212                         .readAllFlow(node, false);
213             }
214         }
215         logger.warn("Plugin {} unavailable", node.getType());
216         return null;
217     }
218
219     @Override
220     public NodeDescription readDescription(Node node) {
221         if (pluginReader != null) {
222             if (this.pluginReader.get(node.getType()) != null) {
223                 return this.pluginReader.get(node.getType())
224                         .readDescription(node, true);
225             }
226         }
227         logger.warn("Plugin {} unavailable", node.getType());
228         return null;
229     }
230
231     @Override
232     public NodeDescription nonCachedReadDescription(Node node) {
233         if (pluginReader != null) {
234             if (this.pluginReader.get(node.getType()) != null) {
235                 return this.pluginReader.get(node.getType())
236                         .readDescription(node, false);
237             }
238         }
239         logger.warn("Plugin {} unavailable", node.getType());
240         return null;
241     }
242
243     @Override
244     public NodeConnectorStatistics readNodeConnector(NodeConnector connector) {
245         Node node = connector.getNode();
246         if (pluginReader != null && node != null) {
247             if (this.pluginReader.get(node.getType()) != null) {
248                 return this.pluginReader.get(node.getType())
249                         .readNodeConnector(connector, true);
250             }
251         }
252         logger.warn("Plugin {} unavailable", node.getType());
253         return null;
254     }
255
256     @Override
257     public NodeConnectorStatistics nonCachedReadNodeConnector(
258             NodeConnector connector) {
259         Node node = connector.getNode();
260         if (pluginReader != null && node != null) {
261             if (this.pluginReader.get(node.getType()) != null) {
262                 return this.pluginReader.get(node.getType())
263                         .readNodeConnector(connector, false);
264             }
265         }
266         logger.warn("Plugin {} unavailable", node.getType());
267         return null;
268     }
269
270     @Override
271     public List<NodeConnectorStatistics> readNodeConnectors(Node node) {
272         if (pluginReader != null) {
273             if (this.pluginReader.get(node.getType()) != null) {
274                 return this.pluginReader.get(node.getType())
275                         .readAllNodeConnector(node, true);
276             }
277         }
278         logger.warn("Plugin {} unavailable", node.getType());
279         return null;
280     }
281
282     @Override
283     public List<NodeTableStatistics> readNodeTable(Node node) {
284         if (pluginReader != null) {
285             if (this.pluginReader.get(node.getType()) != null) {
286                 return this.pluginReader.get(node.getType())
287                         .readAllNodeTable(node, true);
288             }
289         }
290         logger.warn("Plugin {} unavailable", node.getType());
291         return null;
292     }
293
294
295     @Override
296     public NodeTableStatistics nonCachedReadNodeTable(NodeTable table) {
297         Node node = table.getNode();
298         if (pluginReader != null && node != null) {
299             if (this.pluginReader.get(node.getType()) != null) {
300                 return this.pluginReader.get(node.getType())
301                         .readNodeTable(table, false);
302             }
303         }
304         logger.warn("Plugin {} unavailable", node.getType());
305         return null;
306     }
307
308     @Override
309     public NodeTableStatistics readNodeTable(NodeTable table) {
310         Node node = table.getNode();
311         if (pluginReader != null && node != null) {
312             if (this.pluginReader.get(node.getType()) != null) {
313                 return this.pluginReader.get(node.getType())
314                         .readNodeTable(table, true);
315             }
316         }
317         logger.warn("Plugin {} unavailable", node.getType());
318         return null;
319     }
320
321     @Override
322     public List<NodeConnectorStatistics> nonCachedReadNodeConnectors(Node node) {
323         if (pluginReader != null) {
324             if (this.pluginReader.get(node.getType()) != null) {
325                 return this.pluginReader.get(node.getType())
326                         .readAllNodeConnector(node, false);
327             }
328         }
329         logger.warn("Plugin {} unavailable", node.getType());
330         return null;
331     }
332
333     @Override
334     public long getTransmitRate(NodeConnector connector) {
335         Node node = connector.getNode();
336         if (pluginReader != null && node != null) {
337             if (this.pluginReader.get(node.getType()) != null) {
338                 return this.pluginReader.get(node.getType())
339                         .getTransmitRate(connector);
340             }
341         }
342         logger.warn("Plugin {} unavailable", node.getType());
343         return 0;
344     }
345
346     @Override
347     public void nodeFlowStatisticsUpdated(Node node, List<FlowOnNode> flowStatsList) {
348         for (IReadServiceListener l : readerListeners){
349             l.nodeFlowStatisticsUpdated(node, flowStatsList);
350         }
351     }
352
353     @Override
354     public void nodeConnectorStatisticsUpdated(Node node, List<NodeConnectorStatistics> ncStatsList) {
355         for (IReadServiceListener l : readerListeners){
356             l.nodeConnectorStatisticsUpdated(node, ncStatsList);
357         }
358     }
359
360     @Override
361     public void nodeTableStatisticsUpdated(Node node, List<NodeTableStatistics> tableStatsList) {
362         for (IReadServiceListener l : readerListeners){
363             l.nodeTableStatisticsUpdated(node, tableStatsList);
364         }
365     }
366
367     @Override
368     public void descriptionStatisticsUpdated(Node node, NodeDescription nodeDescription) {
369         for (IReadServiceListener l : readerListeners){
370             l.descriptionStatisticsUpdated(node, nodeDescription);
371         }
372     }
373
374     // ---------------- OSGI TEST CODE ------------------------------//
375
376     private void registerWithOSGIConsole() {
377         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
378                 .getBundleContext();
379         bundleContext.registerService(CommandProvider.class.getName(), this,
380                 null);
381     }
382
383     @Override
384     public String getHelp() {
385         StringBuffer help = new StringBuffer();
386         help.append("---SAL Reader testing commands---\n");
387         help.append("\t readflows <sid> <cached> - Read all the (cached) flows from the openflow switch <sid>\n");
388         help.append("\t readflow  <sid> <cached> - Read the (cached) sample flow from the openflow switch <sid>\n");
389         help.append("\t readdescr <sid> <cached> - Read the (cached) description from openflow switch <sid>\n");
390         help.append("\t\t cached = (true|false). If false or not specified, the plugin cached info\n");
391         help.append("\t\t is returned. If true, the info is directly retrieved from the switch\n");
392         help.append("\t readport <sid> <port>    - Read port statistics for the specified port\n");
393         help.append("\t readports <sid>          - Read port statistics for all ports of specified switch\n");
394         help.append("\t readtable <sid> <tableid>- Read specified table statistics\n");
395
396         return help.toString();
397     }
398
399     public void _readflows(CommandInterpreter ci) {
400         String nodeId = ci.nextArgument();
401         String cacheReq = ci.nextArgument();
402         boolean cached;
403         if (nodeId == null) {
404             ci.print("Node id not specified");
405             return;
406         }
407         cached = (cacheReq == null) ? true : cacheReq.equals("true");
408         Node node = null;
409         try {
410             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
411         } catch (NumberFormatException e) {
412             logger.error("",e);
413         } catch (ConstructionException e) {
414             logger.error("",e);
415         }
416         List<FlowOnNode> list = (cached) ? this.readAllFlows(node) : this
417                 .nonCachedReadAllFlows(node);
418         if (list != null) {
419             ci.println(list.toString());
420         } else {
421             ci.println("null");
422         }
423     }
424
425     // Requests the hw view for the specific sample flow
426     public void _readflow(CommandInterpreter ci) throws UnknownHostException {
427         String nodeId = ci.nextArgument();
428         String cacheReq = ci.nextArgument();
429         boolean cached;
430         if (nodeId == null) {
431             ci.print("Node id not specified");
432             return;
433         }
434         cached = (cacheReq == null) ? true : cacheReq.equals("true");
435         Node node = null;
436         try {
437             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
438         } catch (NumberFormatException e) {
439             logger.error("",e);
440         } catch (ConstructionException e) {
441             logger.error("",e);
442         }
443         Flow flow = getSampleFlow(node);
444         FlowOnNode flowOnNode = (cached) ? this.readFlow(node, flow) : this
445                 .nonCachedReadFlow(node, flow);
446         if (flowOnNode != null) {
447             ci.println(flowOnNode.toString());
448         } else {
449             ci.println("null");
450         }
451     }
452
453     public void _readports(CommandInterpreter ci) {
454         String nodeId = ci.nextArgument();
455         String cacheReq = ci.nextArgument();
456         boolean cached;
457         if (nodeId == null) {
458             ci.print("Node id not specified");
459             return;
460         }
461         cached = (cacheReq == null) ? true : cacheReq.equals("true");
462         Node node = null;
463         try {
464             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
465         } catch (NumberFormatException e) {
466             logger.error("",e);
467         } catch (ConstructionException e) {
468             logger.error("",e);
469         }
470         List<NodeConnectorStatistics> list = (cached) ? this
471                 .readNodeConnectors(node) : this
472                 .nonCachedReadNodeConnectors(node);
473                 if (list != null) {
474                     ci.println(list.toString());
475                 } else {
476                     ci.println("null");
477                 }
478     }
479
480     public void _readport(CommandInterpreter ci) {
481         String nodeId = ci.nextArgument();
482         String portId = ci.nextArgument();
483         String cacheReq = ci.nextArgument();
484         boolean cached;
485         if (nodeId == null) {
486             ci.print("Node id not specified");
487             return;
488         }
489         if (portId == null) {
490             ci.print("Port id not specified");
491             return;
492         }
493         cached = (cacheReq == null) ? true : cacheReq.equals("true");
494         NodeConnector nodeConnector = null;
495         Node node = NodeCreator.createOFNode(Long.parseLong(nodeId));
496         nodeConnector = NodeConnectorCreator.createNodeConnector(Short
497                 .valueOf(portId), node);
498         NodeConnectorStatistics stats = (cached) ? this
499                 .readNodeConnector(nodeConnector) : this
500                 .nonCachedReadNodeConnector(nodeConnector);
501                 if (stats != null) {
502                     ci.println(stats.toString());
503                 } else {
504                     ci.println("null");
505                 }
506     }
507
508     public void _readtable(CommandInterpreter ci) {
509         String nodeId = ci.nextArgument();
510         String tableId = ci.nextArgument();
511         String cacheReq = ci.nextArgument();
512         boolean cached;
513         if (nodeId == null) {
514             ci.print("Node id not specified");
515             return;
516         }
517         if (tableId == null) {
518             ci.print("Table id not specified");
519             return;
520         }
521         cached = (cacheReq == null) ? true : cacheReq.equals("true");
522         NodeTable nodeTable = null;
523         Node node = NodeCreator.createOFNode(Long.parseLong(nodeId));
524         nodeTable = NodeTableCreator.createNodeTable(Byte
525                 .valueOf(tableId), node);
526         NodeTableStatistics stats = (cached) ? this
527                 .readNodeTable(nodeTable) : this
528                 .nonCachedReadNodeTable(nodeTable);
529                 if (stats != null) {
530                     ci.println(stats.toString());
531                 } else {
532                     ci.println("null");
533                 }
534     }
535
536     public void _readdescr(CommandInterpreter ci) {
537         String nodeId = ci.nextArgument();
538         String cacheReq = ci.nextArgument();
539         boolean cached;
540         if (nodeId == null) {
541             ci.print("Node id not specified");
542             return;
543         }
544         cached = (cacheReq == null) ? true : cacheReq.equals("true");
545
546         Node node = null;
547         try {
548             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
549         } catch (NumberFormatException e) {
550             logger.error("",e);
551         } catch (ConstructionException e) {
552             logger.error("",e);
553         }
554         NodeDescription desc = (cached) ? this.readDescription(node) : this
555                 .nonCachedReadDescription(node);
556         if (desc != null) {
557             ci.println(desc.toString());
558         } else {
559             ci.println("null");
560         }
561     }
562
563     private Flow getSampleFlow(Node node) throws UnknownHostException {
564         NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
565                 (short) 24, node);
566         NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
567                 (short) 30, node);
568         byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
569                 (byte) 0x9a, (byte) 0xbc };
570         byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
571                 (byte) 0x5e, (byte) 0x6f };
572         InetAddress srcIP = InetAddress.getByName("172.28.30.50");
573         InetAddress dstIP = InetAddress.getByName("171.71.9.52");
574         InetAddress ipMask = InetAddress.getByName("255.255.255.0");
575         InetAddress ipMask2 = InetAddress.getByName("255.0.0.0");
576         short ethertype = EtherTypes.IPv4.shortValue();
577         short vlan = (short) 27;
578         byte vlanPr = 3;
579         Byte tos = 4;
580         byte proto = IPProtocols.TCP.byteValue();
581         short src = (short) 55000;
582         short dst = 80;
583
584         /*
585          * Create a SAL Flow aFlow
586          */
587         Match match = new Match();
588         match.setField(MatchType.IN_PORT, port);
589         match.setField(MatchType.DL_SRC, srcMac);
590         match.setField(MatchType.DL_DST, dstMac);
591         match.setField(MatchType.DL_TYPE, ethertype);
592         match.setField(MatchType.DL_VLAN, vlan);
593         match.setField(MatchType.DL_VLAN_PR, vlanPr);
594         match.setField(MatchType.NW_SRC, srcIP, ipMask);
595         match.setField(MatchType.NW_DST, dstIP, ipMask2);
596         match.setField(MatchType.NW_TOS, tos);
597         match.setField(MatchType.NW_PROTO, proto);
598         match.setField(MatchType.TP_SRC, src);
599         match.setField(MatchType.TP_DST, dst);
600
601         List<Action> actions = new ArrayList<Action>();
602         actions.add(new Output(oport));
603         actions.add(new PopVlan());
604         actions.add(new Flood());
605         actions.add(new Controller());
606         return new Flow(match, actions);
607     }
608 }