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