Move stats caching to FM StatisticsManager
[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 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.concurrent.ConcurrentMap;
20 import java.util.concurrent.ConcurrentSkipListSet;
21
22 import org.opendaylight.controller.protocol_plugin.openflow.IOFStatisticsListener;
23 import org.opendaylight.controller.protocol_plugin.openflow.IOFStatisticsManager;
24 import org.opendaylight.controller.protocol_plugin.openflow.IReadFilterInternalListener;
25 import org.opendaylight.controller.protocol_plugin.openflow.IReadServiceFilter;
26 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
27 import org.opendaylight.controller.sal.action.Action;
28 import org.opendaylight.controller.sal.action.ActionType;
29 import org.opendaylight.controller.sal.action.Output;
30 import org.opendaylight.controller.sal.core.ContainerFlow;
31 import org.opendaylight.controller.sal.core.IContainerListener;
32 import org.opendaylight.controller.sal.core.Node;
33 import org.opendaylight.controller.sal.core.NodeConnector;
34 import org.opendaylight.controller.sal.core.NodeTable;
35 import org.opendaylight.controller.sal.core.UpdateType;
36 import org.opendaylight.controller.sal.flowprogrammer.Flow;
37 import org.opendaylight.controller.sal.match.Match;
38 import org.opendaylight.controller.sal.match.MatchType;
39 import org.opendaylight.controller.sal.reader.FlowOnNode;
40 import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
41 import org.opendaylight.controller.sal.reader.NodeDescription;
42 import org.opendaylight.controller.sal.reader.NodeTableStatistics;
43 import org.opendaylight.controller.sal.utils.GlobalConstants;
44 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
45 import org.opendaylight.controller.sal.utils.NodeCreator;
46 import org.opendaylight.controller.sal.utils.NodeTableCreator;
47 import org.openflow.protocol.OFMatch;
48 import org.openflow.protocol.statistics.OFFlowStatisticsReply;
49 import org.openflow.protocol.statistics.OFPortStatisticsReply;
50 import org.openflow.protocol.statistics.OFStatistics;
51 import org.openflow.protocol.statistics.OFStatisticsType;
52 import org.openflow.protocol.statistics.OFTableStatistics;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 /**
56  * Read Service shim layer which is in charge of filtering the flow statistics
57  * based on container. It is a Global instance.
58  *
59  *
60  *
61  */
62 public class ReadServiceFilter implements IReadServiceFilter, IContainerListener, IOFStatisticsListener {
63     private static final Logger logger = LoggerFactory
64             .getLogger(ReadServiceFilter.class);
65     private IController controller = null;
66     private IOFStatisticsManager statsMgr = null;
67     private Map<String, Set<NodeConnector>> containerToNc;
68     private Map<String, Set<Node>> containerToNode;
69     private Map<String, Set<NodeTable>> containerToNt;
70     private ConcurrentMap<String, IReadFilterInternalListener> readFilterInternalListeners;
71
72     public void setController(IController core) {
73         this.controller = core;
74     }
75
76     public void unsetController(IController core) {
77         if (this.controller == core) {
78             this.controller = null;
79         }
80     }
81
82     public void setReadFilterInternalListener(Map<?, ?> props, IReadFilterInternalListener s) {
83         if (props == null) {
84             logger.error("Failed setting Read Filter Listener, property map is null.");
85             return;
86         }
87         String containerName = (String) props.get("containerName");
88         if (containerName == null) {
89             logger.error("Failed setting Read Filter Listener, container name not supplied.");
90             return;
91         }
92         if ((this.readFilterInternalListeners != null) && !this.readFilterInternalListeners.containsValue(s)) {
93             this.readFilterInternalListeners.put(containerName, s);
94             logger.trace("Added Read Filter Listener for container {}", containerName);
95         }
96     }
97
98     public void unsetReadFilterInternalListener(Map<?, ?> props, IReadFilterInternalListener s) {
99         if (props == null) {
100             logger.error("Failed unsetting Read Filter Listener, property map is null.");
101             return;
102         }
103         String containerName = (String) props.get("containerName");
104         if (containerName == null) {
105             logger.error("Failed unsetting Read Filter Listener, containerName not supplied");
106             return;
107         }
108         if ((this.readFilterInternalListeners != null) && this.readFilterInternalListeners.get(containerName) != null
109                 && this.readFilterInternalListeners.get(containerName).equals(s)) {
110             this.readFilterInternalListeners.remove(containerName);
111             logger.trace("Removed Read Filter Listener for container {}", containerName);
112         }
113     }
114
115     /**
116      * Function called by the dependency manager when all the required
117      * dependencies are satisfied
118      *
119      */
120     void init() {
121         containerToNc = new HashMap<String, Set<NodeConnector>>();
122         containerToNt = new HashMap<String, Set<NodeTable>>();
123         containerToNode = new HashMap<String, Set<Node>>();
124         readFilterInternalListeners = new ConcurrentHashMap<String, IReadFilterInternalListener>();
125     }
126
127     /**
128      * Function called by the dependency manager when at least one
129      * dependency become unsatisfied or when the component is shutting
130      * down because for example bundle is being stopped.
131      *
132      */
133     void destroy() {
134     }
135
136     /**
137      * Function called by dependency manager after "init ()" is called
138      * and after the services provided by the class are registered in
139      * the service registry
140      *
141      */
142     void start() {
143     }
144
145     /**
146      * Function called by the dependency manager before the services
147      * exported by the component are unregistered, this will be
148      * followed by a "destroy ()" calls
149      *
150      */
151     void stop() {
152     }
153
154     public void setService(IOFStatisticsManager service) {
155         this.statsMgr = service;
156     }
157
158     public void unsetService(IOFStatisticsManager service) {
159         this.statsMgr = null;
160     }
161
162     @Override
163     public FlowOnNode readFlow(String container, Node node, Flow flow,
164             boolean cached) {
165
166         if (controller == null) {
167             // Avoid to provide cached statistics if controller went down.
168             // They are not valid anymore anyway
169             logger.error("Internal plugin error");
170             return null;
171         }
172
173         long sid = (Long) node.getID();
174         OFMatch ofMatch = new FlowConverter(flow).getOFMatch();
175         List<OFStatistics> ofList;
176         if (cached == true){
177             ofList = statsMgr.getOFFlowStatistics(sid, ofMatch, flow.getPriority());
178         } else {
179             ofList = statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, ofMatch);
180             for (OFStatistics ofStat : ofList) {
181                 if (((OFFlowStatisticsReply)ofStat).getPriority() == flow.getPriority()){
182                     ofList = new ArrayList<OFStatistics>(1);
183                     ofList.add(ofStat);
184                     break;
185                 }
186             }
187         }
188
189
190         /*
191          * Convert and filter the statistics per container
192          */
193         List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList)
194                 .getFlowOnNodeList(node);
195         List<FlowOnNode> filteredList = filterFlowListPerContainer(container,
196                 node, flowOnNodeList);
197
198         return (filteredList == null || filteredList.isEmpty()) ? null
199                 : filteredList.get(0);
200     }
201
202     @Override
203     public List<FlowOnNode> readAllFlow(String container, Node node,
204             boolean cached) {
205
206         long sid = (Long) node.getID();
207         List<OFStatistics> ofList = (cached == true) ? statsMgr
208                 .getOFFlowStatistics(sid) : statsMgr.queryStatistics(sid,
209                 OFStatisticsType.FLOW, null);
210
211         /*
212          * Convert and filter the statistics per container
213          */
214         List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList)
215                 .getFlowOnNodeList(node);
216         List<FlowOnNode> filteredList = filterFlowListPerContainer(container,
217                 node, flowOnNodeList);
218
219         return (filteredList == null) ? null : filteredList;
220
221     }
222
223     @Override
224     public NodeDescription readDescription(Node node, boolean cached) {
225
226         if (controller == null) {
227             logger.error("Internal plugin error");
228             return null;
229         }
230
231         long sid = (Long) node.getID();
232         List<OFStatistics> ofList = (cached == true) ? statsMgr
233                 .getOFDescStatistics(sid) : statsMgr.queryStatistics(sid,
234                         OFStatisticsType.DESC, null);
235
236                 return new DescStatisticsConverter(ofList).getHwDescription();
237     }
238
239     /**
240      * Filters a list of FlowOnNode elements based on the container
241      *
242      * @param container
243      * @param nodeId
244      * @param list
245      * @return
246      */
247     public List<FlowOnNode> filterFlowListPerContainer(String container,
248             Node nodeId, List<FlowOnNode> list) {
249         if (list == null) {
250             return null;
251         }
252
253         // Create new filtered list of flows
254         List<FlowOnNode> newList = new ArrayList<FlowOnNode>();
255
256         for (FlowOnNode target : list) {
257             // Check whether the described flow (match + actions) belongs to this container
258             if (flowBelongToContainer(container, nodeId, target.getFlow())) {
259                 newList.add(target);
260             }
261         }
262
263         return newList;
264     }
265
266     /**
267      * Filters a list of OFStatistics elements based on the container
268      *
269      * @param container
270      * @param nodeId
271      * @param list
272      * @return
273      */
274     public List<OFStatistics> filterPortListPerContainer(String container,
275             long switchId, List<OFStatistics> list) {
276         if (list == null) {
277             return null;
278         }
279
280         // Create new filtered list of flows
281         List<OFStatistics> newList = new ArrayList<OFStatistics>();
282
283         for (OFStatistics stat : list) {
284             OFPortStatisticsReply target = (OFPortStatisticsReply) stat;
285             NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(
286                     target.getPortNumber(), NodeCreator.createOFNode(switchId));
287             if (containerOwnsNodeConnector(container, nc)) {
288                 newList.add(target);
289             }
290         }
291
292         return newList;
293     }
294
295
296     public List<OFStatistics> filterTableListPerContainer(
297             String container, long switchId, List<OFStatistics> list) {
298         if (list == null) {
299             return null;
300         }
301
302         // Create new filtered list of node tables
303         List<OFStatistics> newList = new ArrayList<OFStatistics>();
304
305         for (OFStatistics stat : list) {
306             OFTableStatistics target = (OFTableStatistics) stat;
307             NodeTable nt = NodeTableCreator.createOFNodeTable(
308                     target.getTableId(), NodeCreator.createOFNode(switchId));
309             if (containerOwnsNodeTable(container, nt)) {
310                 newList.add(target);
311             }
312         }
313
314         return newList;
315     }
316
317     /**
318      * Returns whether the specified flow (flow match + actions)
319      * belongs to the container
320      *
321      * @param container
322      * @param node
323      * @param flow
324      * @return true if it belongs
325      */
326     public boolean flowBelongToContainer(String container, Node node, Flow flow) {
327         // All flows belong to the default container
328         if (container.equals(GlobalConstants.DEFAULT.toString())) {
329             return true;
330         }
331         return (flowPortsBelongToContainer(container, node, flow)
332                 && flowVlanBelongsToContainer(container, node, flow) && flowSpecAllowsFlow(
333                         container, flow.getMatch()));
334     }
335
336     /**
337      * Returns whether the passed NodeConnector belongs to the container
338      *
339      * @param container container name
340      * @param p     node connector to test
341      * @return          true if belongs false otherwise
342      */
343     public boolean containerOwnsNodeConnector(String container, NodeConnector p) {
344         // All node connectors belong to the default container
345         if (container.equals(GlobalConstants.DEFAULT.toString())) {
346             return true;
347         }
348         Set<NodeConnector> portSet = containerToNc.get(container);
349         return (portSet == null) ? false : portSet.contains(p);
350     }
351
352     /**
353      * Returns whether the passed NodeConnector belongs to the container
354      *
355      * @param container container name
356      * @param table     node table to test
357      * @return          true if belongs false otherwise
358      */
359     public boolean containerOwnsNodeTable(String container, NodeTable table) {
360         // All node table belong to the default container
361         if (container.equals(GlobalConstants.DEFAULT.toString())) {
362             return true;
363         }
364         Set<NodeTable> tableSet = containerToNt.get(container);
365         return (tableSet == null) ? false : tableSet.contains(table);
366     }
367
368     /**
369      * Returns whether the container flowspec allows the passed flow
370      *
371      * @param container
372      * @param match
373      * @return
374      */
375     private boolean flowSpecAllowsFlow(String container, Match match) {
376         return true; // Always true for now
377     }
378
379     /**
380      * Check whether the vlan field in the flow match is the same
381      * of the static vlan configured for the container
382      *
383      * @param container
384      * @param node
385      * @param flow
386      * @return
387      */
388     private boolean flowVlanBelongsToContainer(String container, Node node, Flow flow) {
389         return true; // Always true for now
390     }
391
392     /**
393      * Check whether the ports in the flow match and flow actions for
394      * the specified node belong to the container
395      *
396      * @param container
397      * @param node
398      * @param flow
399      * @return
400      */
401     private boolean flowPortsBelongToContainer(String container, Node node,
402             Flow flow) {
403         Match m = flow.getMatch();
404         if (m.isPresent(MatchType.IN_PORT)) {
405             NodeConnector inPort = (NodeConnector) m
406                     .getField(MatchType.IN_PORT).getValue();
407
408             // If the incoming port is specified, check if it belongs to
409             if (!containerOwnsNodeConnector(container, inPort)) {
410                 return false;
411             }
412         }
413
414         // If an outgoing port is specified, it must belong to this container
415         for (Action action : flow.getActions()) {
416             if (action.getType() == ActionType.OUTPUT) {
417                 NodeConnector outPort = ((Output) action)
418                         .getPort();
419                 if (!containerOwnsNodeConnector(container, outPort)) {
420                     return false;
421                 }
422             }
423         }
424         return true;
425     }
426
427     @Override
428     public void containerFlowUpdated(String containerName, ContainerFlow previousFlow,
429             ContainerFlow currentFlow, UpdateType t) {
430         // TODO
431     }
432
433     @Override
434     public void nodeConnectorUpdated(String containerName, NodeConnector p,
435             UpdateType type) {
436
437         switch (type) {
438         case ADDED:
439             if (!containerToNc.containsKey(containerName)) {
440                 containerToNc.put(containerName, new ConcurrentSkipListSet<NodeConnector>());
441             }
442             containerToNc.get(containerName).add(p);
443             if (!containerToNode.containsKey(containerName)) {
444                 containerToNode.put(containerName, new HashSet<Node>());
445             }
446             containerToNode.get(containerName).add(p.getNode());
447             break;
448         case REMOVED:
449             Set<NodeConnector> ncSet = containerToNc.get(containerName);
450             if (ncSet != null) {
451                 //remove this nc from container map
452                 ncSet.remove(p);
453
454                 //check if there are still ports of this node in this container
455                 //and if not, remove its mapping
456                 boolean nodeInContainer = false;
457                 Node node = p.getNode();
458                 for (NodeConnector nodeConnector : ncSet) {
459                     if (nodeConnector.getNode().equals(node)){
460                         nodeInContainer = true;
461                         break;
462                     }
463                 }
464                 if (! nodeInContainer) {
465                     Set<Node> nodeSet = containerToNode.get(containerName);
466                     if (nodeSet != null) {
467                         nodeSet.remove(node);
468                     }
469                 }
470
471             }
472             break;
473         case CHANGED:
474         default:
475         }
476     }
477
478     @Override
479     public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
480         // Not interested in this event
481     }
482
483     @Override
484     public void containerModeUpdated(UpdateType t) {
485         // Not interested in this event
486     }
487
488     @Override
489     public NodeConnectorStatistics readNodeConnector(
490             String containerName, NodeConnector connector, boolean cached) {
491         if (!containerOwnsNodeConnector(containerName, connector)) {
492             return null;
493         }
494         Node node = connector.getNode();
495         long sid = (Long) node.getID();
496         short portId = (Short) connector.getID();
497         List<OFStatistics> ofList = (cached == true) ? statsMgr
498                 .getOFPortStatistics(sid, portId) : statsMgr.queryStatistics(
499                         sid, OFStatisticsType.PORT, portId);
500
501                 List<NodeConnectorStatistics> ncStatistics = new PortStatisticsConverter(
502                         sid, ofList).getNodeConnectorStatsList();
503                 return (ncStatistics.isEmpty()) ? new NodeConnectorStatistics()
504                 : ncStatistics.get(0);
505     }
506
507     @Override
508     public List<NodeConnectorStatistics> readAllNodeConnector(
509             String containerName, Node node, boolean cached) {
510
511         long sid = (Long) node.getID();
512         List<OFStatistics> ofList = (cached == true) ? statsMgr
513                 .getOFPortStatistics(sid) : statsMgr.queryStatistics(sid,
514                         OFStatisticsType.FLOW, null);
515
516                 List<OFStatistics> filteredList = filterPortListPerContainer(
517                         containerName, sid, ofList);
518
519                 return new PortStatisticsConverter(sid, filteredList)
520                 .getNodeConnectorStatsList();
521     }
522
523     @Override
524     public long getTransmitRate(String containerName, NodeConnector connector) {
525         if (!containerOwnsNodeConnector(containerName, connector)) {
526             return 0;
527         }
528
529         long switchId = (Long) connector.getNode().getID();
530         short port = (Short) connector.getID();
531
532         return statsMgr.getTransmitRate(switchId, port);
533     }
534
535     @Override
536     public NodeTableStatistics readNodeTable(String containerName,
537             NodeTable table, boolean cached) {
538         if (!containerOwnsNodeTable(containerName, table)) {
539             return null;
540         }
541         Node node = table.getNode();
542         long sid = (Long) node.getID();
543         Byte tableId = (Byte) table.getID();
544         List<OFStatistics> ofList = (cached == true) ? statsMgr.getOFTableStatistics(sid, tableId) :
545             statsMgr.queryStatistics(sid, OFStatisticsType.TABLE, tableId);
546
547         List<NodeTableStatistics> ntStatistics =
548                 new TableStatisticsConverter(sid, ofList).getNodeTableStatsList();
549
550         return (ntStatistics.isEmpty()) ? new NodeTableStatistics() : ntStatistics.get(0);
551     }
552
553     @Override
554     public List<NodeTableStatistics> readAllNodeTable(String containerName, Node node, boolean cached) {
555         long sid = (Long) node.getID();
556         List<OFStatistics> ofList = (cached == true) ?
557                 statsMgr.getOFTableStatistics(sid) : statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, null);
558
559         List<OFStatistics> filteredList = filterTableListPerContainer(containerName, sid, ofList);
560
561         return new TableStatisticsConverter(sid, filteredList).getNodeTableStatsList();
562     }
563
564     @Override
565     public void descriptionStatisticsRefreshed(Long switchId, List<OFStatistics> description) {
566         String container;
567         Node node = NodeCreator.createOFNode(switchId);
568         NodeDescription nodeDescription = new DescStatisticsConverter(description).getHwDescription();
569         for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
570             container = l.getKey();
571             if (container == GlobalConstants.DEFAULT.toString()
572                     || (containerToNode.containsKey(container) && containerToNode.get(container).contains(node))) {
573                 l.getValue().nodeDescriptionStatisticsUpdated(node, nodeDescription);
574             }
575         }
576     }
577
578     @Override
579     public void flowStatisticsRefreshed(Long switchId, List<OFStatistics> flows) {
580         String container;
581         Node node = NodeCreator.createOFNode(switchId);
582         for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
583             container = l.getKey();
584
585             // Convert and filter the statistics per container
586             List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(flows).getFlowOnNodeList(node);
587             flowOnNodeList = filterFlowListPerContainer(container, node, flowOnNodeList);
588
589             // notify listeners
590             if (!flowOnNodeList.isEmpty()) {
591                 l.getValue().nodeFlowStatisticsUpdated(node, flowOnNodeList);
592             }
593         }
594     }
595
596     @Override
597     public void portStatisticsRefreshed(Long switchId, List<OFStatistics> ports) {
598         String container;
599         Node node = NodeCreator.createOFNode(switchId);
600         for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
601             container = l.getKey();
602
603             // Convert and filter the statistics per container
604             List<OFStatistics> filteredPorts = filterPortListPerContainer(container, switchId, ports);
605             List<NodeConnectorStatistics> ncStatsList = new PortStatisticsConverter(switchId, filteredPorts)
606                     .getNodeConnectorStatsList();
607
608             // notify listeners
609             if (!ncStatsList.isEmpty()) {
610                 l.getValue().nodeConnectorStatisticsUpdated(node, ncStatsList);
611             }
612         }
613     }
614
615     @Override
616     public void tableStatisticsRefreshed(Long switchId, List<OFStatistics> tables) {
617         String container;
618         Node node = NodeCreator.createOFNode(switchId);
619         for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
620             container = l.getKey();
621
622             // Convert and filter the statistics per container
623             List<OFStatistics> filteredList = filterTableListPerContainer(container, switchId, tables);
624             List<NodeTableStatistics> tableStatsList = new TableStatisticsConverter(switchId, filteredList)
625                     .getNodeTableStatsList();
626
627             // notify listeners
628             if (!tableStatsList.isEmpty()) {
629                 l.getValue().nodeTableStatisticsUpdated(node, tableStatsList);
630             }
631         }
632     }
633 }