Controller to listen to expired flow removal message
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / 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.protocol_plugin.openflow.internal;
10
11 import java.nio.ByteBuffer;
12 import java.util.ArrayList;
13 import java.util.HashSet;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
18
19 import org.opendaylight.controller.protocol_plugin.openflow.IFlowProgrammerNotifier;
20 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
21 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
22 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
23 import org.opendaylight.controller.protocol_plugin.openflow.vendorextension.v6extension.V6Error;
24 import org.openflow.protocol.OFError;
25 import org.openflow.protocol.OFFlowMod;
26 import org.openflow.protocol.OFFlowRemoved;
27 import org.openflow.protocol.OFMessage;
28 import org.openflow.protocol.OFPort;
29 import org.openflow.protocol.OFType;
30 import org.openflow.protocol.action.OFAction;
31
32 import org.opendaylight.controller.sal.core.ContainerFlow;
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.UpdateType;
38 import org.opendaylight.controller.sal.flowprogrammer.Flow;
39 import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
40 import org.opendaylight.controller.sal.match.Match;
41 import org.opendaylight.controller.sal.match.MatchType;
42 import org.opendaylight.controller.sal.utils.GlobalConstants;
43 import org.opendaylight.controller.sal.utils.NodeCreator;
44 import org.opendaylight.controller.sal.utils.StatusCode;
45 import org.opendaylight.controller.sal.utils.Status;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 /**
50  * Represents the openflow plugin component in charge of programming the flows
51  * the flow programming and relay them to functional modules above SAL.
52  */
53 public class FlowProgrammerService implements IPluginInFlowProgrammerService,
54         IMessageListener, IContainerListener {
55     private static final Logger log = LoggerFactory
56             .getLogger(FlowProgrammerService.class);
57     private IController controller;
58     private ConcurrentMap<String, IFlowProgrammerNotifier> flowProgrammerNotifiers;
59     private Map<String, Set<NodeConnector>> containerToNc;
60
61     public FlowProgrammerService() {
62         controller = null;
63         flowProgrammerNotifiers = new ConcurrentHashMap<String, IFlowProgrammerNotifier>();
64     }
65
66     public void setController(IController core) {
67         this.controller = core;
68     }
69
70     public void unsetController(IController core) {
71         if (this.controller == core) {
72             this.controller = null;
73         }
74     }
75
76     public void setFlowProgrammerNotifier(Map<String, ?> props,
77             IFlowProgrammerNotifier s) {
78         if (props == null || props.get("containerName") == null) {
79             log.error("Didn't receive the service correct properties");
80             return;
81         }
82         String containerName = (String) props.get("containerName");
83         this.flowProgrammerNotifiers.put(containerName, s);
84     }
85
86     public void unsetFlowProgrammerNotifier(Map<String, ?> props,
87             IFlowProgrammerNotifier s) {
88         if (props == null || props.get("containerName") == null) {
89             log.error("Didn't receive the service correct properties");
90             return;
91         }
92         String containerName = (String) props.get("containerName");
93         if (this.flowProgrammerNotifiers != null
94                 && this.flowProgrammerNotifiers.containsKey(containerName)
95                 && this.flowProgrammerNotifiers.get(containerName) == s) {
96             this.flowProgrammerNotifiers.remove(containerName);
97         }
98     }
99
100     /**
101      * Function called by the dependency manager when all the required
102      * dependencies are satisfied
103      * 
104      */
105     void init() {
106         this.controller.addMessageListener(OFType.FLOW_REMOVED, this);
107     }
108
109     /**
110      * Function called by the dependency manager when at least one dependency
111      * become unsatisfied or when the component is shutting down because for
112      * example bundle is being stopped.
113      * 
114      */
115     void destroy() {
116     }
117
118     /**
119      * Function called by dependency manager after "init ()" is called and after
120      * the services provided by the class are registered in the service registry
121      * 
122      */
123     void start() {
124     }
125
126     /**
127      * Function called by the dependency manager before the services exported by
128      * the component are unregistered, this will be followed by a "destroy ()"
129      * calls
130      * 
131      */
132     void stop() {
133     }
134
135     @Override
136     public Status addFlow(Node node, Flow flow) {
137         String action = "add";
138         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
139             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
140                     action, "Invalid node type"));
141         }
142
143         if (controller != null) {
144             ISwitch sw = controller.getSwitch((Long) node.getID());
145             if (sw != null) {
146                 FlowConverter x = new FlowConverter(flow);
147                 OFMessage msg = x.getOFFlowMod(OFFlowMod.OFPFC_ADD, null);
148
149                 /*
150                  * Synchronous message send
151                  */
152                 Object result = sw.syncSend(msg);
153                 if (result instanceof Boolean) {
154                     return ((Boolean) result == Boolean.TRUE) ? new Status(
155                             StatusCode.SUCCESS, null) : new Status(
156                             StatusCode.TIMEOUT, errorString(null, action,
157                                     "Request Timed Out"));
158                 } else if (result instanceof OFError) {
159                     OFError res = (OFError) result;
160                     if (res.getErrorType() == V6Error.NICIRA_VENDOR_ERRORTYPE) {
161                         V6Error er = new V6Error(res);
162                         byte[] b = res.getError();
163                         ByteBuffer bb = ByteBuffer.allocate(b.length);
164                         bb.put(b);
165                         bb.rewind();
166                         er.readFrom(bb);
167                         return new Status(StatusCode.INTERNALERROR,
168                                 errorString("program", action,
169                                         "Vendor Extension Internal Error"));
170                     }
171                     return new Status(StatusCode.INTERNALERROR, errorString(
172                             "program", action, Utils.getOFErrorString(res)));
173                 } else {
174                     return new Status(StatusCode.INTERNALERROR, errorString(
175                             "send", action, "Internal Error"));
176                 }
177             } else {
178                 return new Status(StatusCode.GONE, errorString("send", action,
179                         "Switch is not available"));
180             }
181         }
182         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
183                 "Internal plugin error"));
184     }
185
186     @Override
187     public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
188         String action = "modify";
189         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
190             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
191                     action, "Invalid node type"));
192         }
193         if (controller != null) {
194             ISwitch sw = controller.getSwitch((Long) node.getID());
195             if (sw != null) {
196                 OFMessage msg1 = null, msg2 = null;
197
198                 // If priority and match portion are the same, send a
199                 // modification message
200                 if (oldFlow.getPriority() != newFlow.getPriority()
201                         || !oldFlow.getMatch().equals(newFlow.getMatch())) {
202                     msg1 = new FlowConverter(oldFlow).getOFFlowMod(
203                             OFFlowMod.OFPFC_DELETE_STRICT, OFPort.OFPP_NONE);
204                     msg2 = new FlowConverter(newFlow).getOFFlowMod(
205                             OFFlowMod.OFPFC_ADD, null);
206                 } else {
207                     msg1 = new FlowConverter(newFlow).getOFFlowMod(
208                             OFFlowMod.OFPFC_MODIFY_STRICT, null);
209                 }
210                 /*
211                  * Synchronous message send
212                  */
213                 action = (msg2 == null) ? "modify" : "delete";
214                 Object result = sw.syncSend(msg1);
215                 if (result instanceof Boolean) {
216                     if ((Boolean) result == Boolean.FALSE) {
217                         return new Status(StatusCode.TIMEOUT, errorString(null,
218                                 action, "Request Timed Out"));
219                     } else if (msg2 == null) {
220                         return new Status(StatusCode.SUCCESS, null);
221                     }
222                 } else if (result instanceof OFError) {
223                     return new Status(StatusCode.INTERNALERROR, errorString(
224                             "program", action,
225                             Utils.getOFErrorString((OFError) result)));
226                 } else {
227                     return new Status(StatusCode.INTERNALERROR, errorString(
228                             "send", action, "Internal Error"));
229                 }
230
231                 if (msg2 != null) {
232                     action = "add";
233                     result = sw.syncSend(msg2);
234                     if (result instanceof Boolean) {
235                         return ((Boolean) result == Boolean.TRUE) ? new Status(
236                                 StatusCode.SUCCESS, null) : new Status(
237                                 StatusCode.TIMEOUT, errorString(null, action,
238                                         "Request Timed Out"));
239                     } else if (result instanceof OFError) {
240                         return new Status(StatusCode.INTERNALERROR,
241                                 errorString("program", action, Utils
242                                         .getOFErrorString((OFError) result)));
243                     } else {
244                         return new Status(StatusCode.INTERNALERROR,
245                                 errorString("send", action, "Internal Error"));
246                     }
247                 }
248             } else {
249                 return new Status(StatusCode.GONE, errorString("send", action,
250                         "Switch is not available"));
251             }
252         }
253         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
254                 "Internal plugin error"));
255     }
256
257     @Override
258     public Status removeFlow(Node node, Flow flow) {
259         String action = "remove";
260         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
261             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
262                     action, "Invalid node type"));
263         }
264         if (controller != null) {
265             ISwitch sw = controller.getSwitch((Long) node.getID());
266             if (sw != null) {
267                 OFMessage msg = new FlowConverter(flow).getOFFlowMod(
268                         OFFlowMod.OFPFC_DELETE_STRICT, OFPort.OFPP_NONE);
269                 Object result = sw.syncSend(msg);
270                 if (result instanceof Boolean) {
271                     return ((Boolean) result == Boolean.TRUE) ? new Status(
272                             StatusCode.SUCCESS, null) : new Status(
273                             StatusCode.TIMEOUT, errorString(null, action,
274                                     "Request Timed Out"));
275                 } else if (result instanceof OFError) {
276                     return new Status(StatusCode.INTERNALERROR, errorString(
277                             "program", action,
278                             Utils.getOFErrorString((OFError) result)));
279                 } else {
280                     return new Status(StatusCode.INTERNALERROR, errorString(
281                             "send", action, "Internal Error"));
282                 }
283             } else {
284                 return new Status(StatusCode.GONE, errorString("send", action,
285                         "Switch is not available"));
286             }
287         }
288         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
289                 "Internal plugin error"));
290     }
291
292     @Override
293     public Status removeAllFlows(Node node) {
294         return new Status(StatusCode.SUCCESS, null);
295     }
296
297     private String errorString(String phase, String action, String cause) {
298         return "Failed to "
299                 + ((phase != null) ? phase + " the " + action
300                         + " flow message: " : action + " the flow: ") + cause;
301     }
302
303     @Override
304     public void receive(ISwitch sw, OFMessage msg) {
305         if (msg instanceof OFFlowRemoved) {
306             handleFlowRemovedMessage(sw, (OFFlowRemoved) msg);
307         }
308     }
309
310     private void handleFlowRemovedMessage(ISwitch sw, OFFlowRemoved msg) {
311         Node node = NodeCreator.createOFNode(sw.getId());
312         Flow flow = new FlowConverter(msg.getMatch(),
313                 new ArrayList<OFAction>(0)).getFlow(node);
314         flow.setPriority(msg.getPriority());
315         flow.setIdleTimeout(msg.getIdleTimeout());
316         flow.setId(msg.getCookie());
317
318         Match match = flow.getMatch();
319         NodeConnector inPort = match.isPresent(MatchType.IN_PORT) ? (NodeConnector) match
320                 .getField(MatchType.IN_PORT).getValue() : null;
321
322         for (Map.Entry<String, IFlowProgrammerNotifier> containerNotifier : flowProgrammerNotifiers
323                 .entrySet()) {
324             String container = containerNotifier.getKey();
325             IFlowProgrammerNotifier notifier = containerNotifier.getValue();
326             /*
327              * Switch only provide us with the match information. For now let's
328              * try to identify the container membership only from the input port
329              * match field. In any case, upper layer consumers can derive
330              * whether the notification was not for them. More sophisticated
331              * filtering can be added later on.
332              */
333             if (inPort == null
334                     || container.equals(GlobalConstants.DEFAULT.toString())
335                     || this.containerToNc.get(container).contains(inPort)) {
336                 notifier.flowRemoved(node, flow);
337             }
338         }
339     }
340
341     @Override
342     public void tagUpdated(String containerName, Node n, short oldTag,
343             short newTag, UpdateType t) {
344
345     }
346
347     @Override
348     public void containerFlowUpdated(String containerName,
349             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
350     }
351
352     @Override
353     public void nodeConnectorUpdated(String containerName, NodeConnector p,
354             UpdateType type) {
355         Set<NodeConnector> target = null;
356
357         switch (type) {
358         case ADDED:
359             if (!containerToNc.containsKey(containerName)) {
360                 containerToNc.put(containerName, new HashSet<NodeConnector>());
361             }
362             containerToNc.get(containerName).add(p);
363             break;
364         case CHANGED:
365             break;
366         case REMOVED:
367             target = containerToNc.get(containerName);
368             if (target != null) {
369                 target.remove(p);
370             }
371             break;
372         default:
373         }
374
375     }
376
377     @Override
378     public void containerModeUpdated(UpdateType t) {
379
380     }
381 }