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