d5a80370fbf106c5f03f87ef3f9d477f87131a0d
[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.IContainerAware;
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, IContainerAware {
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         new ConcurrentHashMap<String, IReadFilterInternalListener>();
70
71     public void setController(IController core) {
72         this.controller = core;
73     }
74
75     public void unsetController(IController core) {
76         if (this.controller == core) {
77             this.controller = null;
78         }
79     }
80
81     public void setReadFilterInternalListener(Map<?, ?> props, IReadFilterInternalListener s) {
82         if (props == null) {
83             logger.error("Failed setting Read Filter Listener, property map is null.");
84             return;
85         }
86         String containerName = (String) props.get("containerName");
87         if (containerName == null) {
88             logger.error("Failed setting Read Filter Listener, container name not supplied.");
89             return;
90         }
91         if ((this.readFilterInternalListeners != null) && !this.readFilterInternalListeners.containsValue(s)) {
92             this.readFilterInternalListeners.put(containerName, s);
93             logger.trace("Added Read Filter Listener for container {}", containerName);
94         }
95     }
96
97     public void unsetReadFilterInternalListener(Map<?, ?> props, IReadFilterInternalListener s) {
98         if (props == null) {
99             logger.error("Failed unsetting Read Filter Listener, property map is null.");
100             return;
101         }
102         String containerName = (String) props.get("containerName");
103         if (containerName == null) {
104             logger.error("Failed unsetting Read Filter Listener, containerName not supplied");
105             return;
106         }
107         if ((this.readFilterInternalListeners != null) && this.readFilterInternalListeners.get(containerName) != null
108                 && this.readFilterInternalListeners.get(containerName).equals(s)) {
109             this.readFilterInternalListeners.remove(containerName);
110             logger.trace("Removed Read Filter Listener for container {}", containerName);
111         }
112     }
113
114     /**
115      * Function called by the dependency manager when all the required
116      * dependencies are satisfied
117      *
118      */
119     void init() {
120         containerToNc = new ConcurrentHashMap<String, Set<NodeConnector>>();
121         containerToNt = new ConcurrentHashMap<String, Set<NodeTable>>();
122         containerToNode = new ConcurrentHashMap<String, Set<Node>>();
123         containerFlows = new ConcurrentHashMap<String, Set<ContainerFlow>>();
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         readFilterInternalListeners.clear();
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, boolean cached) {
164
165         if (controller == null) {
166             // Avoid to provide cached statistics if controller went down.
167             // They are not valid anymore anyway
168             logger.error("Internal plugin error");
169             return null;
170         }
171
172         long sid = (Long) node.getID();
173         OFMatch ofMatch = new FlowConverter(flow).getOFMatch();
174         List<OFStatistics> ofList;
175         if (cached == true){
176             ofList = statsMgr.getOFFlowStatistics(sid, ofMatch, flow.getPriority());
177         } else {
178             ofList = statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, ofMatch);
179             for (OFStatistics ofStat : ofList) {
180                 if (((OFFlowStatisticsReply)ofStat).getPriority() == flow.getPriority()){
181                     ofList = new ArrayList<OFStatistics>(1);
182                     ofList.add(ofStat);
183                     break;
184                 }
185             }
186         }
187
188         // Convert and filter the statistics per container
189         List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList).getFlowOnNodeList(node);
190         List<FlowOnNode> filteredList = filterFlowListPerContainer(container, node, flowOnNodeList);
191
192         return (filteredList.isEmpty()) ? null : filteredList.get(0);
193     }
194
195     @Override
196     public List<FlowOnNode> readAllFlow(String container, Node node,
197             boolean cached) {
198
199         long sid = (Long) node.getID();
200         List<OFStatistics> ofList = (cached == true) ? statsMgr
201                 .getOFFlowStatistics(sid) : statsMgr.queryStatistics(sid,
202                 OFStatisticsType.FLOW, null);
203
204         // Convert and filter the statistics per container
205         List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList).getFlowOnNodeList(node);
206
207         return filterFlowListPerContainer(container, node, flowOnNodeList);
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     private List<FlowOnNode> filterFlowListPerContainer(String container,
235             Node nodeId, List<FlowOnNode> list) {
236         if (list == null) {
237             return Collections.emptyList();
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     private List<OFStatistics> filterPortListPerContainer(String container, long switchId, List<OFStatistics> list) {
262         if (list == null) {
263             return Collections.emptyList();
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     private List<OFStatistics> filterTableListPerContainer(
283             String container, long switchId, List<OFStatistics> list) {
284         if (list == null) {
285             return Collections.emptyList();
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.TABLE, 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
635     @Override
636     public void containerCreate(String containerName) {
637         // do nothing
638     }
639
640     @Override
641     public void containerDestroy(String containerName) {
642         containerToNc.remove(containerName);
643         containerToNode.remove(containerName);
644         containerToNt.remove(containerName);
645         containerFlows.remove(containerName);
646     }
647 }