b877eb0ee793626d2990e24855052653466aa4ba
[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         new ConcurrentHashMap<String, IReadFilterInternalListener>();
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     }
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         readFilterInternalListeners.clear();
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     @Override
162     public FlowOnNode readFlow(String container, Node node, Flow flow, boolean cached) {
163
164         if (controller == null) {
165             // Avoid to provide cached statistics if controller went down.
166             // They are not valid anymore anyway
167             logger.error("Internal plugin error");
168             return null;
169         }
170
171         long sid = (Long) node.getID();
172         OFMatch ofMatch = new FlowConverter(flow).getOFMatch();
173         List<OFStatistics> ofList;
174         if (cached == true){
175             ofList = statsMgr.getOFFlowStatistics(sid, ofMatch, flow.getPriority());
176         } else {
177             ofList = statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, ofMatch);
178             for (OFStatistics ofStat : ofList) {
179                 if (((OFFlowStatisticsReply)ofStat).getPriority() == flow.getPriority()){
180                     ofList = new ArrayList<OFStatistics>(1);
181                     ofList.add(ofStat);
182                     break;
183                 }
184             }
185         }
186
187         // Convert and filter the statistics per container
188         List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList).getFlowOnNodeList(node);
189         List<FlowOnNode> filteredList = filterFlowListPerContainer(container, node, flowOnNodeList);
190
191         return (filteredList == null || filteredList.isEmpty()) ? null : filteredList.get(0);
192     }
193
194     @Override
195     public List<FlowOnNode> readAllFlow(String container, Node node,
196             boolean cached) {
197
198         long sid = (Long) node.getID();
199         List<OFStatistics> ofList = (cached == true) ? statsMgr
200                 .getOFFlowStatistics(sid) : statsMgr.queryStatistics(sid,
201                 OFStatisticsType.FLOW, null);
202
203         // Convert and filter the statistics per container
204         List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList).getFlowOnNodeList(node);
205         List<FlowOnNode> filteredList = filterFlowListPerContainer(container, node, flowOnNodeList);
206
207         return (filteredList == null) ? null : filteredList;
208
209     }
210
211     @Override
212     public NodeDescription readDescription(Node node, boolean cached) {
213
214         if (controller == null) {
215             logger.error("Internal plugin error");
216             return null;
217         }
218
219         long sid = (Long) node.getID();
220         List<OFStatistics> ofList = (cached == true) ? statsMgr
221                 .getOFDescStatistics(sid) : statsMgr.queryStatistics(sid,
222                         OFStatisticsType.DESC, null);
223
224         return new DescStatisticsConverter(ofList).getHwDescription();
225     }
226
227     /**
228      * Filters a list of FlowOnNode elements based on the container
229      *
230      * @param container
231      * @param nodeId
232      * @param list
233      * @return
234      */
235     public List<FlowOnNode> filterFlowListPerContainer(String container,
236             Node nodeId, List<FlowOnNode> list) {
237         if (list == null) {
238             return null;
239         }
240
241         // Create new filtered list of flows
242         List<FlowOnNode> newList = new ArrayList<FlowOnNode>();
243
244         for (FlowOnNode target : list) {
245             // Check whether the described flow (match + actions) belongs to this container
246             if (flowBelongToContainer(container, nodeId, target.getFlow())) {
247                 newList.add(target);
248             }
249         }
250
251         return newList;
252     }
253
254     /**
255      * Filters a list of OFStatistics elements based on the container
256      *
257      * @param container
258      * @param nodeId
259      * @param list
260      * @return
261      */
262     public List<OFStatistics> filterPortListPerContainer(String container, long switchId, List<OFStatistics> list) {
263         if (list == null) {
264             return null;
265         }
266
267         // Create new filtered list of flows
268         List<OFStatistics> newList = new ArrayList<OFStatistics>();
269
270         for (OFStatistics stat : list) {
271             OFPortStatisticsReply target = (OFPortStatisticsReply) stat;
272             NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(
273                     target.getPortNumber(), NodeCreator.createOFNode(switchId));
274             if (containerOwnsNodeConnector(container, nc)) {
275                 newList.add(target);
276             }
277         }
278
279         return newList;
280     }
281
282
283     public List<OFStatistics> filterTableListPerContainer(
284             String container, long switchId, List<OFStatistics> list) {
285         if (list == null) {
286             return null;
287         }
288
289         // Create new filtered list of node tables
290         List<OFStatistics> newList = new ArrayList<OFStatistics>();
291
292         for (OFStatistics stat : list) {
293             OFTableStatistics target = (OFTableStatistics) stat;
294             NodeTable nt = NodeTableCreator.createOFNodeTable(target.getTableId(), NodeCreator.createOFNode(switchId));
295             if (containerOwnsNodeTable(container, nt)) {
296                 newList.add(target);
297             }
298         }
299
300         return newList;
301     }
302
303     /**
304      * Returns whether the specified flow (flow match + actions)
305      * belongs to the container
306      *
307      * @param container
308      * @param node
309      * @param flow
310      * @return true if it belongs
311      */
312     public boolean flowBelongToContainer(String container, Node node, Flow flow) {
313         // All flows belong to the default container
314         if (container.equals(GlobalConstants.DEFAULT.toString())) {
315             return true;
316         }
317         return (flowPortsBelongToContainer(container, node, flow) &&
318                 flowVlanBelongsToContainer(container, node, flow) &&
319                 isFlowAllowedByContainer(container, flow));
320     }
321
322     /**
323      * Returns whether the passed NodeConnector belongs to the container
324      *
325      * @param container container name
326      * @param p     node connector to test
327      * @return          true if belongs false otherwise
328      */
329     public boolean containerOwnsNodeConnector(String container, NodeConnector p) {
330         // All node connectors belong to the default container
331         if (container.equals(GlobalConstants.DEFAULT.toString())) {
332             return true;
333         }
334         Set<NodeConnector> portSet = containerToNc.get(container);
335         return (portSet == null) ? false : portSet.contains(p);
336     }
337
338     /**
339      * Returns whether the passed NodeConnector belongs to the container
340      *
341      * @param container container name
342      * @param table     node table to test
343      * @return          true if belongs false otherwise
344      */
345     public boolean containerOwnsNodeTable(String container, NodeTable table) {
346         // All node table belong to the default container
347         if (container.equals(GlobalConstants.DEFAULT.toString())) {
348             return true;
349         }
350         Set<NodeTable> tableSet = containerToNt.get(container);
351         return (tableSet == null) ? false : tableSet.contains(table);
352     }
353
354     /**
355      * Returns whether the container flows allow the passed flow
356      *
357      * @param container
358      * @param match
359      * @return
360      */
361     private boolean isFlowAllowedByContainer(String container, Flow flow) {
362         Set<ContainerFlow> cFlowSet = this.containerFlows.get(container);
363         if (cFlowSet == null || cFlowSet.isEmpty()) {
364             return true;
365         }
366         for (ContainerFlow cFlow : cFlowSet) {
367             if (cFlow.allowsFlow(flow)) {
368                 return true;
369             }
370         }
371         return false;
372     }
373
374     /**
375      * Check whether the vlan field in the flow match is the same
376      * of the static vlan configured for the container
377      *
378      * @param container
379      * @param node
380      * @param flow
381      * @return
382      */
383     private boolean flowVlanBelongsToContainer(String container, Node node, Flow flow) {
384         return true; // Always true for now
385     }
386
387     /**
388      * Check whether the ports in the flow match and flow actions for
389      * the specified node belong to the container
390      *
391      * @param container
392      * @param node
393      * @param flow
394      * @return
395      */
396     private boolean flowPortsBelongToContainer(String container, Node node,
397             Flow flow) {
398         Match m = flow.getMatch();
399         if (m.isPresent(MatchType.IN_PORT)) {
400             NodeConnector inPort = (NodeConnector) m.getField(MatchType.IN_PORT).getValue();
401             // If the incoming port is specified, check if it belongs to
402             if (!containerOwnsNodeConnector(container, inPort)) {
403                 return false;
404             }
405         }
406
407         // If an outgoing port is specified, it must belong to this container
408         for (Action action : flow.getActions()) {
409             if (action.getType() == ActionType.OUTPUT) {
410                 NodeConnector outPort = ((Output) action).getPort();
411                 if (!containerOwnsNodeConnector(container, outPort)) {
412                     return false;
413                 }
414             }
415         }
416         return true;
417     }
418
419     @Override
420     public void containerFlowUpdated(String containerName, ContainerFlow previousFlow,
421             ContainerFlow currentFlow, UpdateType t) {
422         Set<ContainerFlow> cFlowSet = containerFlows.get(containerName);
423         switch (t) {
424         case ADDED:
425             if (cFlowSet == null) {
426                 cFlowSet = new HashSet<ContainerFlow>();
427                 containerFlows.put(containerName, cFlowSet);
428             }
429             cFlowSet.add(currentFlow);
430         case CHANGED:
431             break;
432         case REMOVED:
433             if (cFlowSet != null) {
434                 cFlowSet.remove(currentFlow);
435             }
436             break;
437         default:
438             break;
439         }
440     }
441
442     @Override
443     public void nodeConnectorUpdated(String containerName, NodeConnector p, UpdateType type) {
444
445         switch (type) {
446         case ADDED:
447             if (!containerToNc.containsKey(containerName)) {
448                 containerToNc.put(containerName,
449                     Collections.newSetFromMap(new ConcurrentHashMap<NodeConnector,Boolean>()));
450             }
451             containerToNc.get(containerName).add(p);
452             if (!containerToNode.containsKey(containerName)) {
453                 containerToNode.put(containerName, new HashSet<Node>());
454             }
455             containerToNode.get(containerName).add(p.getNode());
456             break;
457         case REMOVED:
458             Set<NodeConnector> ncSet = containerToNc.get(containerName);
459             if (ncSet != null) {
460                 //remove this nc from container map
461                 ncSet.remove(p);
462
463                 //check if there are still ports of this node in this container
464                 //and if not, remove its mapping
465                 boolean nodeInContainer = false;
466                 Node node = p.getNode();
467                 for (NodeConnector nodeConnector : ncSet) {
468                     if (nodeConnector.getNode().equals(node)){
469                         nodeInContainer = true;
470                         break;
471                     }
472                 }
473                 if (! nodeInContainer) {
474                     Set<Node> nodeSet = containerToNode.get(containerName);
475                     if (nodeSet != null) {
476                         nodeSet.remove(node);
477                     }
478                 }
479             }
480             break;
481         case CHANGED:
482         default:
483         }
484     }
485
486     @Override
487     public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
488         // Not interested in this event
489     }
490
491     @Override
492     public void containerModeUpdated(UpdateType t) {
493         // Not interested in this event
494     }
495
496     @Override
497     public NodeConnectorStatistics readNodeConnector(String containerName, NodeConnector connector, boolean cached) {
498         if (!containerOwnsNodeConnector(containerName, connector)) {
499             return null;
500         }
501         Node node = connector.getNode();
502         long sid = (Long) node.getID();
503         short portId = (Short) connector.getID();
504         List<OFStatistics> ofList = (cached == true) ? statsMgr
505                 .getOFPortStatistics(sid, portId) : statsMgr.queryStatistics(
506                         sid, OFStatisticsType.PORT, portId);
507
508         List<NodeConnectorStatistics> ncStatistics = new PortStatisticsConverter(sid, ofList)
509                 .getNodeConnectorStatsList();
510         return (ncStatistics.isEmpty()) ? new NodeConnectorStatistics() : ncStatistics.get(0);
511     }
512
513     @Override
514     public List<NodeConnectorStatistics> readAllNodeConnector(String containerName, Node node, boolean cached) {
515
516         long sid = (Long) node.getID();
517         List<OFStatistics> ofList = (cached == true) ? statsMgr
518                 .getOFPortStatistics(sid) : statsMgr.queryStatistics(sid,
519                         OFStatisticsType.FLOW, null);
520
521         List<OFStatistics> filteredList = filterPortListPerContainer(containerName, sid, ofList);
522
523         return new PortStatisticsConverter(sid, filteredList).getNodeConnectorStatsList();
524     }
525
526     @Override
527     public long getTransmitRate(String containerName, NodeConnector connector) {
528         if (!containerOwnsNodeConnector(containerName, connector)) {
529             return 0;
530         }
531
532         long switchId = (Long) connector.getNode().getID();
533         short port = (Short) connector.getID();
534
535         return statsMgr.getTransmitRate(switchId, port);
536     }
537
538     @Override
539     public NodeTableStatistics readNodeTable(String containerName,
540             NodeTable table, boolean cached) {
541         if (!containerOwnsNodeTable(containerName, table)) {
542             return null;
543         }
544         Node node = table.getNode();
545         long sid = (Long) node.getID();
546         Byte tableId = (Byte) table.getID();
547         List<OFStatistics> ofList = (cached == true) ? statsMgr.getOFTableStatistics(sid, tableId) :
548             statsMgr.queryStatistics(sid, OFStatisticsType.TABLE, tableId);
549
550         List<NodeTableStatistics> ntStatistics = new TableStatisticsConverter(sid, ofList).getNodeTableStatsList();
551
552         return (ntStatistics.isEmpty()) ? new NodeTableStatistics() : ntStatistics.get(0);
553     }
554
555     @Override
556     public List<NodeTableStatistics> readAllNodeTable(String containerName, Node node, boolean cached) {
557         long sid = (Long) node.getID();
558         List<OFStatistics> ofList = (cached == true) ?
559                 statsMgr.getOFTableStatistics(sid) : statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, null);
560
561         List<OFStatistics> filteredList = filterTableListPerContainer(containerName, sid, ofList);
562
563         return new TableStatisticsConverter(sid, filteredList).getNodeTableStatsList();
564     }
565
566     @Override
567     public void descriptionStatisticsRefreshed(Long switchId, List<OFStatistics> description) {
568         String container;
569         IReadFilterInternalListener listener;
570         Node node = NodeCreator.createOFNode(switchId);
571         NodeDescription nodeDescription = new DescStatisticsConverter(description).getHwDescription();
572         for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
573             container = l.getKey();
574             listener = l.getValue();
575             if (container == GlobalConstants.DEFAULT.toString()
576                     || (containerToNode.containsKey(container) && containerToNode.get(container).contains(node))) {
577                 listener.nodeDescriptionStatisticsUpdated(node, nodeDescription);
578             }
579         }
580     }
581
582     @Override
583     public void flowStatisticsRefreshed(Long switchId, List<OFStatistics> flows) {
584         String container;
585         IReadFilterInternalListener listener;
586         Node node = NodeCreator.createOFNode(switchId);
587         for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
588             container = l.getKey();
589             listener = l.getValue();
590
591             // Convert and filter the statistics per container
592             List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(flows).getFlowOnNodeList(node);
593             flowOnNodeList = filterFlowListPerContainer(container, node, flowOnNodeList);
594
595             // notify listeners
596             listener.nodeFlowStatisticsUpdated(node, flowOnNodeList);
597         }
598     }
599
600     @Override
601     public void portStatisticsRefreshed(Long switchId, List<OFStatistics> ports) {
602         String container;
603         IReadFilterInternalListener listener;
604         Node node = NodeCreator.createOFNode(switchId);
605         for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
606             container = l.getKey();
607             listener = l.getValue();
608
609             // Convert and filter the statistics per container
610             List<OFStatistics> filteredPorts = filterPortListPerContainer(container, switchId, ports);
611             List<NodeConnectorStatistics> ncStatsList = new PortStatisticsConverter(switchId, filteredPorts)
612                     .getNodeConnectorStatsList();
613
614             // notify listeners
615             listener.nodeConnectorStatisticsUpdated(node, ncStatsList);
616         }
617     }
618
619     @Override
620     public void tableStatisticsRefreshed(Long switchId, List<OFStatistics> tables) {
621         String container;
622         Node node = NodeCreator.createOFNode(switchId);
623         for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
624             container = l.getKey();
625
626             // Convert and filter the statistics per container
627             List<OFStatistics> filteredList = filterTableListPerContainer(container, switchId, tables);
628             List<NodeTableStatistics> tableStatsList = new TableStatisticsConverter(switchId, filteredList)
629                     .getNodeTableStatsList();
630
631             // notify listeners
632             l.getValue().nodeTableStatisticsUpdated(node, tableStatsList);
633         }
634     }
635 }