Add 'TableStatistics' to SAL and Northbound Statistics API.
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / ReadServiceFilter.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.protocol_plugin.openflow.internal;
11
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.opendaylight.controller.protocol_plugin.openflow.IOFStatisticsManager;
20 import org.opendaylight.controller.protocol_plugin.openflow.IPluginReadServiceFilter;
21 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
22 import org.openflow.protocol.OFMatch;
23 import org.openflow.protocol.statistics.OFPortStatisticsReply;
24 import org.openflow.protocol.statistics.OFStatistics;
25 import org.openflow.protocol.statistics.OFStatisticsType;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 import org.opendaylight.controller.sal.action.Action;
30 import org.opendaylight.controller.sal.action.ActionType;
31 import org.opendaylight.controller.sal.action.Output;
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.NodeConnector;
36 import org.opendaylight.controller.sal.core.NodeTable;
37 import org.opendaylight.controller.sal.core.UpdateType;
38 import org.opendaylight.controller.sal.flowprogrammer.Flow;
39 import org.opendaylight.controller.sal.match.Match;
40 import org.opendaylight.controller.sal.match.MatchType;
41 import org.opendaylight.controller.sal.reader.FlowOnNode;
42 import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
43 import org.opendaylight.controller.sal.reader.NodeDescription;
44 import org.opendaylight.controller.sal.reader.NodeTableStatistics;
45 import org.opendaylight.controller.sal.utils.GlobalConstants;
46 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
47 import org.opendaylight.controller.sal.utils.NodeCreator;
48 import org.opendaylight.controller.sal.utils.NodeTableCreator;
49 import org.openflow.protocol.statistics.OFTableStatistics;
50 /**
51  * Read Service shim layer which is in charge of filtering the flow statistics
52  * based on container. It is a Global instance.
53  *
54  *
55  *
56  */
57 public class ReadServiceFilter implements IPluginReadServiceFilter,
58         IContainerListener {
59     private static final Logger logger = LoggerFactory
60             .getLogger(ReadServiceFilter.class);
61     private IController controller = null;
62     private IOFStatisticsManager statsMgr = null;
63     private Map<String, Set<NodeConnector>> containerToNc;
64     private Map<String, Set<NodeTable>> containerToNt;
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     /**
77      * Function called by the dependency manager when all the required
78      * dependencies are satisfied
79      *
80      */
81     void init() {
82         containerToNc = new HashMap<String, Set<NodeConnector>>();
83         containerToNt = new HashMap<String, Set<NodeTable>>();
84     }
85
86     /**
87      * Function called by the dependency manager when at least one
88      * dependency become unsatisfied or when the component is shutting
89      * down because for example bundle is being stopped.
90      *
91      */
92     void destroy() {
93     }
94
95     /**
96      * Function called by dependency manager after "init ()" is called
97      * and after the services provided by the class are registered in
98      * the service registry
99      *
100      */
101     void start() {
102     }
103
104     /**
105      * Function called by the dependency manager before the services
106      * exported by the component are unregistered, this will be
107      * followed by a "destroy ()" calls
108      *
109      */
110     void stop() {
111     }
112
113     public void setService(IOFStatisticsManager service) {
114         this.statsMgr = service;
115     }
116
117     public void unsetService(IOFStatisticsManager service) {
118         this.statsMgr = null;
119     }
120
121     @Override
122     public FlowOnNode readFlow(String container, Node node, Flow flow,
123             boolean cached) {
124
125         if (controller == null) {
126             // Avoid to provide cached statistics if controller went down.
127             // They are not valid anymore anyway
128             logger.error("Internal plugin error");
129             return null;
130         }
131
132         long sid = (Long) node.getID();
133         OFMatch ofMatch = new FlowConverter(flow).getOFMatch();
134         List<OFStatistics> ofList = (cached == true) ? statsMgr
135                 .getOFFlowStatistics(sid, ofMatch) : statsMgr.queryStatistics(
136                 sid, OFStatisticsType.FLOW, ofMatch);
137
138         /*
139          * Convert and filter the statistics per container
140          */
141         List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList)
142                 .getFlowOnNodeList(node);
143         List<FlowOnNode> filteredList = filterFlowListPerContainer(container,
144                 node, flowOnNodeList);
145
146         return (filteredList == null || filteredList.isEmpty()) ? null
147                 : filteredList.get(0);
148     }
149
150     @Override
151     public List<FlowOnNode> readAllFlow(String container, Node node,
152             boolean cached) {
153
154         long sid = (Long) node.getID();
155         List<OFStatistics> ofList = (cached == true) ? statsMgr
156                 .getOFFlowStatistics(sid) : statsMgr.queryStatistics(sid,
157                 OFStatisticsType.FLOW, null);
158
159         /*
160          * Convert and filter the statistics per container
161          */
162         List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList)
163                 .getFlowOnNodeList(node);
164         List<FlowOnNode> filteredList = filterFlowListPerContainer(container,
165                 node, flowOnNodeList);
166
167         return (filteredList == null) ? null : filteredList;
168
169     }
170
171     @Override
172     public NodeDescription readDescription(Node node, boolean cached) {
173
174         if (controller == null) {
175             logger.error("Internal plugin error");
176             return null;
177         }
178
179         long sid = (Long) node.getID();
180         List<OFStatistics> ofList = (cached == true) ? statsMgr
181                 .getOFDescStatistics(sid) : statsMgr.queryStatistics(sid,
182                         OFStatisticsType.DESC, null);
183
184                 return new DescStatisticsConverter(ofList).getHwDescription();
185     }
186
187     /**
188      * Filters a list of FlowOnNode elements based on the container
189      *
190      * @param container
191      * @param nodeId
192      * @param list
193      * @return
194      */
195     public List<FlowOnNode> filterFlowListPerContainer(String container,
196             Node nodeId, List<FlowOnNode> list) {
197         if (list == null) {
198             return null;
199         }
200
201         // Create new filtered list of flows
202         List<FlowOnNode> newList = new ArrayList<FlowOnNode>();
203
204         for (FlowOnNode target : list) {
205             // Check whether the described flow (match + actions) belongs to this container
206             if (flowBelongToContainer(container, nodeId, target.getFlow())) {
207                 newList.add(target);
208             }
209         }
210
211         return newList;
212     }
213
214     /**
215      * Filters a list of FlowOnNode elements based on the container
216      *
217      * @param container
218      * @param nodeId
219      * @param list
220      * @return
221      */
222     public List<OFStatistics> filterPortListPerContainer(String container,
223             long switchId, List<OFStatistics> list) {
224         if (list == null) {
225             return null;
226         }
227
228         // Create new filtered list of flows
229         List<OFStatistics> newList = new ArrayList<OFStatistics>();
230
231         for (OFStatistics stat : list) {
232             OFPortStatisticsReply target = (OFPortStatisticsReply) stat;
233             NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(
234                     target.getPortNumber(), NodeCreator.createOFNode(switchId));
235             if (containerOwnsNodeConnector(container, nc)) {
236                 newList.add(target);
237             }
238         }
239
240         return newList;
241     }
242
243
244     public List<OFStatistics> filterTableListPerContainer(
245             String container, long switchId, List<OFStatistics> list) {
246         if (list == null) {
247             return null;
248         }
249
250         // Create new filtered list of node tables
251         List<OFStatistics> newList = new ArrayList<OFStatistics>();
252
253         for (OFStatistics stat : list) {
254             OFTableStatistics target = (OFTableStatistics) stat;
255             NodeTable nt = NodeTableCreator.createOFNodeTable(
256                     target.getTableId(), NodeCreator.createOFNode(switchId));
257             if (containerOwnsNodeTable(container, nt)) {
258                 newList.add(target);
259             }
260         }
261
262         return newList;
263     }
264
265     /**
266      * Returns whether the specified flow (flow match + actions)
267      * belongs to the container
268      *
269      * @param container
270      * @param node
271      * @param flow
272      * @return true if it belongs
273      */
274     public boolean flowBelongToContainer(String container, Node node, Flow flow) {
275         // All flows belong to the default container
276         if (container.equals(GlobalConstants.DEFAULT.toString())) {
277             return true;
278         }
279         return (flowPortsBelongToContainer(container, node, flow)
280                 && flowVlanBelongsToContainer(container, node, flow) && flowSpecAllowsFlow(
281                         container, flow.getMatch()));
282     }
283
284     /**
285      * Returns whether the passed NodeConnector belongs to the container
286      *
287      * @param container container name
288      * @param p         node connector to test
289      * @return          true if belongs false otherwise
290      */
291     public boolean containerOwnsNodeConnector(String container, NodeConnector p) {
292         // All node connectors belong to the default container
293         if (container.equals(GlobalConstants.DEFAULT.toString())) {
294             return true;
295         }
296         Set<NodeConnector> portSet = containerToNc.get(container);
297         return (portSet == null) ? false : portSet.contains(p);
298     }
299
300     /**
301      * Returns whether the passed NodeConnector belongs to the container
302      *
303      * @param container container name
304      * @param table             node table to test
305      * @return          true if belongs false otherwise
306      */
307     public boolean containerOwnsNodeTable(String container, NodeTable table) {
308         // All node table belong to the default container
309         if (container.equals(GlobalConstants.DEFAULT.toString())) {
310             return true;
311         }
312         Set<NodeTable> tableSet = containerToNt.get(container);
313         return (tableSet == null) ? false : tableSet.contains(table);
314     }
315
316     /**
317      * Returns whether the container flowspec allows the passed flow
318      *
319      * @param container
320      * @param match
321      * @return
322      */
323     private boolean flowSpecAllowsFlow(String container, Match match) {
324         return true; // Always true for now
325     }
326
327     /**
328      * Check whether the vlan field in the flow match is the same
329      * of the static vlan configured for the container
330      *
331      * @param container
332      * @param node
333      * @param flow
334      * @return
335      */
336     private boolean flowVlanBelongsToContainer(String container, Node node,
337             Flow flow) {
338         return true; // Always true for now
339     }
340
341     /**
342      * Check whether the ports in the flow match and flow actions for
343      * the specified node belong to the container
344      *
345      * @param container
346      * @param node
347      * @param flow
348      * @return
349      */
350     private boolean flowPortsBelongToContainer(String container, Node node,
351             Flow flow) {
352         Match m = flow.getMatch();
353         if (m.isPresent(MatchType.IN_PORT)) {
354             NodeConnector inPort = (NodeConnector) m
355                     .getField(MatchType.IN_PORT).getValue();
356
357             // If the incoming port is specified, check if it belongs to
358             if (!containerOwnsNodeConnector(container, inPort)) {
359                 return false;
360             }
361         }
362
363         // If an outgoing port is specified, it must belong to this container
364         for (Action action : flow.getActions()) {
365             if (action.getType() == ActionType.OUTPUT) {
366                 NodeConnector outPort = (NodeConnector) ((Output) action)
367                         .getPort();
368                 if (!containerOwnsNodeConnector(container, outPort)) {
369                     return false;
370                 }
371             }
372         }
373         return true;
374     }
375
376     @Override
377     public void containerFlowUpdated(String containerName,
378             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
379
380     }
381
382     @Override
383     public void nodeConnectorUpdated(String containerName, NodeConnector p,
384             UpdateType type) {
385         Set<NodeConnector> target = null;
386
387         switch (type) {
388         case ADDED:
389             if (!containerToNc.containsKey(containerName)) {
390                 containerToNc.put(containerName, new HashSet<NodeConnector>());
391             }
392             containerToNc.get(containerName).add(p);
393             break;
394         case CHANGED:
395             break;
396         case REMOVED:
397             target = containerToNc.get(containerName);
398             if (target != null) {
399                 target.remove(p);
400             }
401             break;
402         default:
403         }
404     }
405
406     @Override
407     public void tagUpdated(String containerName, Node n, short oldTag,
408             short newTag, UpdateType t) {
409         // Not interested in this event
410     }
411
412     @Override
413     public void containerModeUpdated(UpdateType t) {
414         // do nothing
415     }
416
417     @Override
418     public NodeConnectorStatistics readNodeConnector(String containerName,
419             NodeConnector connector, boolean cached) {
420         if (!containerOwnsNodeConnector(containerName, connector)) {
421             return null;
422         }
423         Node node = connector.getNode();
424         long sid = (Long) node.getID();
425         short portId = (Short) connector.getID();
426         List<OFStatistics> ofList = (cached == true) ? statsMgr
427                 .getOFPortStatistics(sid, portId) : statsMgr.queryStatistics(
428                         sid, OFStatisticsType.PORT, portId);
429
430                 List<NodeConnectorStatistics> ncStatistics = new PortStatisticsConverter(
431                         sid, ofList).getNodeConnectorStatsList();
432                 return (ncStatistics.isEmpty()) ? new NodeConnectorStatistics()
433                 : ncStatistics.get(0);
434     }
435
436     @Override
437     public List<NodeConnectorStatistics> readAllNodeConnector(
438             String containerName, Node node, boolean cached) {
439
440         long sid = (Long) node.getID();
441         List<OFStatistics> ofList = (cached == true) ? statsMgr
442                 .getOFPortStatistics(sid) : statsMgr.queryStatistics(sid,
443                         OFStatisticsType.FLOW, null);
444
445                 List<OFStatistics> filteredList = filterPortListPerContainer(
446                         containerName, sid, ofList);
447
448                 return new PortStatisticsConverter(sid, filteredList)
449                 .getNodeConnectorStatsList();
450     }
451
452     @Override
453     public long getTransmitRate(String containerName, NodeConnector connector) {
454         if (!containerOwnsNodeConnector(containerName, connector)) {
455             return 0;
456         }
457
458         long switchId = (Long) connector.getNode().getID();
459         short port = (Short) connector.getID();
460
461         return statsMgr.getTransmitRate(switchId, port);
462     }
463
464     @Override
465     public NodeTableStatistics readNodeTable(String containerName,
466             NodeTable table, boolean cached) {
467         if (!containerOwnsNodeTable(containerName, table)) {
468             return null;
469         }
470         Node node = table.getNode();
471         long sid = (Long) node.getID();
472         Byte tableId = (Byte) table.getID();
473         List<OFStatistics> ofList = (cached == true) ? statsMgr
474                 .getOFTableStatistics(sid, tableId) : statsMgr.queryStatistics(
475                         sid, OFStatisticsType.TABLE, tableId);
476
477                 List<NodeTableStatistics> ntStatistics = new TableStatisticsConverter(
478                         sid, ofList).getNodeTableStatsList();
479
480                 return (ntStatistics.isEmpty()) ? new NodeTableStatistics()
481                 : ntStatistics.get(0);
482     }
483
484     @Override
485     public List<NodeTableStatistics> readAllNodeTable(String containerName,
486             Node node, boolean cached) {
487         long sid = (Long) node.getID();
488         List<OFStatistics> ofList = (cached == true) ? statsMgr
489                 .getOFTableStatistics(sid) : statsMgr.queryStatistics(sid,
490                         OFStatisticsType.FLOW, null);
491
492                 List<OFStatistics> filteredList = filterTableListPerContainer(
493                         containerName, sid, ofList);
494
495                 return new TableStatisticsConverter(sid, filteredList)
496                 .getNodeTableStatsList();
497     }
498
499 }