Merge "Fix for the "without any protocolPluginType provided" error"
[controller.git] / opendaylight / sal / implementation / src / main / java / org / opendaylight / controller / sal / implementation / internal / FlowProgrammerService.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.sal.implementation.internal;
10
11 import java.util.Map;
12 import java.util.concurrent.ConcurrentHashMap;
13 import java.util.concurrent.atomic.AtomicLong;
14 import java.net.InetAddress;
15 import java.net.UnknownHostException;
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Set;
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.action.SetNwDst;
29 import org.opendaylight.controller.sal.core.ConstructionException;
30 import org.opendaylight.controller.sal.core.Node;
31 import org.opendaylight.controller.sal.core.Node.NodeIDType;
32 import org.opendaylight.controller.sal.core.NodeConnector;
33 import org.opendaylight.controller.sal.flowprogrammer.Flow;
34 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
35 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
36 import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
37 import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
38 import org.opendaylight.controller.sal.match.Match;
39 import org.opendaylight.controller.sal.match.MatchType;
40 import org.opendaylight.controller.sal.utils.EtherTypes;
41 import org.opendaylight.controller.sal.utils.GlobalConstants;
42 import org.opendaylight.controller.sal.utils.IPProtocols;
43 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
44 import org.opendaylight.controller.sal.utils.Status;
45 import org.opendaylight.controller.sal.utils.StatusCode;
46 import org.osgi.framework.BundleContext;
47 import org.osgi.framework.FrameworkUtil;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 /**
52  * The SAL Flow Programmer Service. It dispatches the flow programming requests
53  * to the proper SDN protocol plugin and it notifies about asynchronous messages
54  * received from the network node related to flow programming.
55  */
56 public class FlowProgrammerService implements IFlowProgrammerService,
57         IPluginOutFlowProgrammerService, CommandProvider {
58
59     protected static final Logger logger = LoggerFactory
60             .getLogger(FlowProgrammerService.class);
61     private ConcurrentHashMap<String, IPluginInFlowProgrammerService> pluginFlowProgrammer;
62     private Set<IFlowProgrammerListener> listener;
63     private AtomicLong seq;
64
65     public FlowProgrammerService() {
66         pluginFlowProgrammer = new ConcurrentHashMap<String, IPluginInFlowProgrammerService>();
67         listener = new HashSet<IFlowProgrammerListener>();
68         seq = new AtomicLong();
69         /*
70          * This Request ID generator starts with 1. Each aysnc message is
71          * associated with an unique Request ID (!= 0).
72          */
73         seq.lazySet(1);
74     }
75
76     /**
77      * Function called by the dependency manager when all the required
78      * dependencies are satisfied
79      *
80      */
81     void init() {
82         logger.debug("INIT called!");
83     }
84
85     /**
86      * Function called by the dependency manager when at least one dependency
87      * become unsatisfied or when the component is shutting down because for
88      * example bundle is being stopped.
89      *
90      */
91     void destroy() {
92         // Clear previous registration to avoid they are left hanging
93         this.pluginFlowProgrammer.clear();
94         logger.debug("DESTROY called!");
95     }
96
97     /**
98      * Function called by dependency manager after "init ()" is called and after
99      * the services provided by the class are registered in the service registry
100      *
101      */
102     void start() {
103         logger.debug("START called!");
104         // OSGI console
105         registerWithOSGIConsole();
106     }
107
108     /**
109      * Function called by the dependency manager before the services exported by
110      * the component are unregistered, this will be followed by a "destroy ()"
111      * calls
112      *
113      */
114     void stop() {
115         logger.debug("STOP called!");
116     }
117
118     // Set the reference to the plugin flow programmer
119     public void setService(Map props, IPluginInFlowProgrammerService s) {
120         if (this.pluginFlowProgrammer == null) {
121             logger.error("pluginFlowProgrammer store null");
122             return;
123         }
124
125         logger.trace("Got a service set request {}", s);
126         String type = null;
127         for (Object e : props.entrySet()) {
128             Map.Entry entry = (Map.Entry) e;
129             logger.trace("Prop key:({}) value:({})", entry.getKey(),
130                     entry.getValue());
131         }
132
133         Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
134         if (value instanceof String) {
135             type = (String) value;
136         }
137         if (type == null) {
138             logger.error("Received a pluginFlowProgrammer without any "
139                     + "protocolPluginType provided");
140         } else {
141             this.pluginFlowProgrammer.put(type, s);
142             logger.debug("Stored the pluginFlowProgrammer for type: {}", type);
143         }
144     }
145
146     public void unsetService(Map props, IPluginInFlowProgrammerService s) {
147         if (this.pluginFlowProgrammer == null) {
148             logger.error("pluginFlowProgrammer store null");
149             return;
150         }
151
152         String type = null;
153         logger.debug("Received unsetpluginFlowProgrammer request");
154         for (Object e : props.entrySet()) {
155             Map.Entry entry = (Map.Entry) e;
156             logger.trace("Prop key:({}) value:({})", entry.getKey(),
157                     entry.getValue());
158         }
159
160         Object value = props.get(GlobalConstants.PROTOCOLPLUGINTYPE.toString());
161         if (value instanceof String) {
162             type = (String) value;
163         }
164         if (type == null) {
165             logger.error("Received a pluginFlowProgrammer without any "
166                     + "protocolPluginType provided");
167         } else if (this.pluginFlowProgrammer.get(type).equals(s)) {
168             this.pluginFlowProgrammer.remove(type);
169             logger.debug("Removed the pluginFlowProgrammer for type: {}", type);
170         }
171     }
172
173     public void setListener(IFlowProgrammerListener s) {
174         this.listener.add(s);
175     }
176
177     public void unsetListener(IFlowProgrammerListener s) {
178         this.listener.remove(s);
179     }
180
181     @Override
182     public Status addFlow(Node node, Flow flow) {
183         if (pluginFlowProgrammer != null) {
184             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
185                 return this.pluginFlowProgrammer.get(node.getType()).addFlow(
186                         node, flow);
187             }
188         }
189         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
190     }
191
192     @Override
193     public Status removeFlow(Node node, Flow flow) {
194         if (pluginFlowProgrammer != null) {
195             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
196                 return this.pluginFlowProgrammer.get(node.getType())
197                         .removeFlow(node, flow);
198             }
199         }
200         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
201     }
202
203     @Override
204     public Status removeAllFlows(Node node) {
205         if (pluginFlowProgrammer != null) {
206             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
207                 return this.pluginFlowProgrammer.get(node.getType())
208                         .removeAllFlows(node);
209             }
210         }
211         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
212     }
213
214     @Override
215     public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
216         if (pluginFlowProgrammer != null) {
217             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
218                 return this.pluginFlowProgrammer.get(node.getType())
219                         .modifyFlow(node, oldFlow, newFlow);
220             }
221         }
222         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
223     }
224
225     @Override
226     public Status addFlowAsync(Node node, Flow flow) {
227         if (pluginFlowProgrammer != null) {
228             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
229                 return this.pluginFlowProgrammer.get(node.getType()).addFlowAsync(
230                         node, flow, getNextRid());
231             }
232         }
233         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
234     }
235
236     @Override
237     public Status removeFlowAsync(Node node, Flow flow) {
238         if (pluginFlowProgrammer != null) {
239             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
240                 return this.pluginFlowProgrammer.get(node.getType())
241                         .removeFlowAsync(node, flow, getNextRid());
242             }
243         }
244         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
245     }
246
247     @Override
248     public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow) {
249         if (pluginFlowProgrammer != null) {
250             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
251                 return this.pluginFlowProgrammer.get(node.getType())
252                         .modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
253             }
254         }
255         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
256     }
257
258     @Override
259     public void flowRemoved(Node node, Flow flow) {
260         for (IFlowProgrammerListener l : listener) {
261             l.flowRemoved(node, flow);
262         }
263     }
264
265     @Override
266     public void flowErrorReported(Node node, long rid, Object err) {
267         logger.error("Got error {} for message rid {} from node {}",
268                 new Object[] { err, rid, node });
269
270         for (IFlowProgrammerListener l : listener) {
271             l.flowErrorReported(node, rid, err);
272         }
273     }
274
275     // ---------------- OSGI TEST CODE ------------------------------//
276
277     private void registerWithOSGIConsole() {
278         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
279                 .getBundleContext();
280         bundleContext.registerService(CommandProvider.class.getName(), this,
281                 null);
282     }
283
284     @Override
285     public String getHelp() {
286         StringBuffer help = new StringBuffer();
287         help.append("---SAL Flow Programmer testing commands---\n");
288         help.append("\t addflow <sid> - Add a sample flow to the openflow switch <sid>\n");
289         help.append("\t removeflow <sid> - Remove the sample flow from the openflow switch <sid>\n");
290         return help.toString();
291     }
292
293     public void _addflow(CommandInterpreter ci) throws UnknownHostException {
294         Node node = null;
295         String nodeId = ci.nextArgument();
296         if (nodeId == null) {
297             ci.print("Node id not specified");
298             return;
299         }
300         try {
301             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
302         } catch (NumberFormatException e) {
303             logger.error("",e);
304         } catch (ConstructionException e) {
305             logger.error("",e);
306         }
307         ci.println(this.addFlow(node, getSampleFlow(node)));
308     }
309
310     public void _modifyflow(CommandInterpreter ci) throws UnknownHostException {
311         Node node = null;
312         String nodeId = ci.nextArgument();
313         if (nodeId == null) {
314             ci.print("Node id not specified");
315             return;
316         }
317         try {
318             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
319         } catch (NumberFormatException e) {
320             logger.error("",e);
321         } catch (ConstructionException e) {
322             logger.error("",e);
323         }
324         Flow flowA = getSampleFlow(node);
325         Flow flowB = getSampleFlow(node);
326         Match matchB = flowB.getMatch();
327         matchB.setField(MatchType.NW_DST,
328                 InetAddress.getByName("190.190.190.190"));
329         flowB.setMatch(matchB);
330         ci.println(this.modifyFlow(node, flowA, flowB));
331     }
332
333     public void _removeflow(CommandInterpreter ci) throws UnknownHostException {
334         Node node = null;
335         String nodeId = ci.nextArgument();
336         if (nodeId == null) {
337             ci.print("Node id not specified");
338             return;
339         }
340         try {
341             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
342         } catch (NumberFormatException e) {
343             logger.error("",e);
344         } catch (ConstructionException e) {
345             logger.error("",e);
346         }
347         ci.println(this.removeFlow(node, getSampleFlow(node)));
348     }
349
350     public void _addflowv6(CommandInterpreter ci) throws UnknownHostException {
351         Node node = null;
352         String nodeId = ci.nextArgument();
353         if (nodeId == null) {
354             ci.print("Node id not specified");
355             return;
356         }
357         try {
358             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
359         } catch (NumberFormatException e) {
360             logger.error("",e);
361         } catch (ConstructionException e) {
362             logger.error("",e);
363         }
364         ci.println(this.addFlow(node, getSampleFlowV6(node)));
365     }
366
367     public void _removeflowv6(CommandInterpreter ci)
368             throws UnknownHostException {
369         Node node = null;
370         String nodeId = ci.nextArgument();
371         if (nodeId == null) {
372             ci.print("Node id not specified");
373             return;
374         }
375         try {
376             node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
377         } catch (NumberFormatException e) {
378             logger.error("",e);
379         } catch (ConstructionException e) {
380             logger.error("",e);
381         }
382         ci.println(this.removeFlow(node, getSampleFlowV6(node)));
383     }
384
385     private Flow getSampleFlow(Node node) throws UnknownHostException {
386         NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
387                 (short) 24, node);
388         NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
389                 (short) 30, node);
390         byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
391                 (byte) 0x9a, (byte) 0xbc };
392         byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
393                 (byte) 0x5e, (byte) 0x6f };
394         InetAddress srcIP = InetAddress.getByName("172.28.30.50");
395         InetAddress dstIP = InetAddress.getByName("171.71.9.52");
396         InetAddress newIP = InetAddress.getByName("200.200.100.1");
397         InetAddress ipMask = InetAddress.getByName("255.255.255.0");
398         InetAddress ipMask2 = InetAddress.getByName("255.240.0.0");
399         short ethertype = EtherTypes.IPv4.shortValue();
400         short vlan = (short) 27;
401         byte vlanPr = 3;
402         Byte tos = 4;
403         byte proto = IPProtocols.TCP.byteValue();
404         short src = (short) 55000;
405         short dst = 80;
406
407         /*
408          * Create a SAL Flow aFlow
409          */
410         Match match = new Match();
411         match.setField(MatchType.IN_PORT, port);
412         match.setField(MatchType.DL_SRC, srcMac);
413         match.setField(MatchType.DL_DST, dstMac);
414         match.setField(MatchType.DL_TYPE, ethertype);
415         match.setField(MatchType.DL_VLAN, vlan);
416         match.setField(MatchType.DL_VLAN_PR, vlanPr);
417         match.setField(MatchType.NW_SRC, srcIP, ipMask);
418         match.setField(MatchType.NW_DST, dstIP, ipMask2);
419         match.setField(MatchType.NW_TOS, tos);
420         match.setField(MatchType.NW_PROTO, proto);
421         match.setField(MatchType.TP_SRC, src);
422         match.setField(MatchType.TP_DST, dst);
423
424         List<Action> actions = new ArrayList<Action>();
425         actions.add(new SetNwDst(newIP));
426         actions.add(new Output(oport));
427         actions.add(new PopVlan());
428         actions.add(new Flood());
429         actions.add(new Controller());
430
431         Flow flow = new Flow(match, actions);
432         flow.setPriority((short) 100);
433         flow.setHardTimeout((short) 360);
434
435         return flow;
436     }
437
438     private Flow getSampleFlowV6(Node node) throws UnknownHostException {
439         NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
440                 (short) 24, node);
441         NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
442                 (short) 30, node);
443         byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
444                 (byte) 0x9a, (byte) 0xbc };
445         byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
446                 (byte) 0x5e, (byte) 0x6f };
447         InetAddress srcIP = InetAddress
448                 .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
449         InetAddress dstIP = InetAddress
450                 .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
451         InetAddress ipMask = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
452                                    // V6Match implementation assumes no mask is
453                                    // specified
454         InetAddress ipMask2 = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
455         short ethertype = EtherTypes.IPv6.shortValue();
456         short vlan = (short) 27;
457         byte vlanPr = (byte) 3;
458         Byte tos = 4;
459         byte proto = IPProtocols.UDP.byteValue();
460         short src = (short) 5500;
461         // short dst = 80;
462
463         /*
464          * Create a SAL Flow aFlow
465          */
466         Match match = new Match();
467         match.setField(MatchType.IN_PORT, port);
468         match.setField(MatchType.DL_SRC, srcMac);
469         match.setField(MatchType.DL_DST, dstMac);
470         match.setField(MatchType.DL_TYPE, ethertype);
471         match.setField(MatchType.DL_VLAN, vlan);
472         match.setField(MatchType.DL_VLAN_PR, vlanPr); // V6Match does not handle
473                                                       // this properly...
474         match.setField(MatchType.NW_SRC, srcIP, ipMask);
475         match.setField(MatchType.NW_DST, dstIP, ipMask2);
476         match.setField(MatchType.NW_TOS, tos);
477         match.setField(MatchType.NW_PROTO, proto);
478         match.setField(MatchType.TP_SRC, src); // V6Match does not handle this
479                                                // properly...
480         // match.setField(MatchType.TP_DST, dst); V6Match does not handle this
481         // properly...
482
483         List<Action> actions = new ArrayList<Action>();
484         actions.add(new Output(oport));
485         actions.add(new PopVlan());
486         actions.add(new Flood());
487
488         Flow flow = new Flow(match, actions);
489         flow.setPriority((short) 300);
490         flow.setHardTimeout((short) 240);
491
492         return flow;
493     }
494
495     /**
496      * This Request ID generator starts with 1. Each aysnc message is
497      * associated with an unique Request ID (!= 0).
498      *
499      * @return Request ID
500      */
501     private long getNextRid() {
502         return seq.getAndIncrement();
503     }
504
505     @Override
506     public Status syncSendBarrierMessage(Node node) {
507         if (this.pluginFlowProgrammer != null) {
508             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
509                 return this.pluginFlowProgrammer.get(node.getType())
510                         .syncSendBarrierMessage(node);
511             }
512         }
513         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
514     }
515
516     @Override
517     public Status asyncSendBarrierMessage(Node node) {
518         if (this.pluginFlowProgrammer != null) {
519             if (this.pluginFlowProgrammer.get(node.getType()) != null) {
520                 return this.pluginFlowProgrammer.get(node.getType())
521                         .asyncSendBarrierMessage(node);
522             }
523         }
524         return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
525     }
526 }