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