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