Bug fix: flow statistics are not notified if empty
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / ReadServiceFilter.java
index 1b71c3bec34f70b382af55c094e7acb7f86f0fdf..7f9a13e92ac3be846342a7615e37ec658d8b9fa0 100644 (file)
 package org.opendaylight.controller.protocol_plugin.openflow.internal;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
+import org.opendaylight.controller.protocol_plugin.openflow.IOFStatisticsListener;
 import org.opendaylight.controller.protocol_plugin.openflow.IOFStatisticsManager;
-import org.opendaylight.controller.protocol_plugin.openflow.IPluginReadServiceFilter;
+import org.opendaylight.controller.protocol_plugin.openflow.IReadFilterInternalListener;
+import org.opendaylight.controller.protocol_plugin.openflow.IReadServiceFilter;
 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
-import org.openflow.protocol.OFMatch;
-import org.openflow.protocol.statistics.OFPortStatisticsReply;
-import org.openflow.protocol.statistics.OFStatistics;
-import org.openflow.protocol.statistics.OFStatisticsType;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.opendaylight.controller.sal.action.Action;
 import org.opendaylight.controller.sal.action.ActionType;
 import org.opendaylight.controller.sal.action.Output;
@@ -33,6 +31,7 @@ import org.opendaylight.controller.sal.core.ContainerFlow;
 import org.opendaylight.controller.sal.core.IContainerListener;
 import org.opendaylight.controller.sal.core.Node;
 import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.core.NodeTable;
 import org.opendaylight.controller.sal.core.UpdateType;
 import org.opendaylight.controller.sal.flowprogrammer.Flow;
 import org.opendaylight.controller.sal.match.Match;
@@ -40,10 +39,19 @@ import org.opendaylight.controller.sal.match.MatchType;
 import org.opendaylight.controller.sal.reader.FlowOnNode;
 import org.opendaylight.controller.sal.reader.NodeConnectorStatistics;
 import org.opendaylight.controller.sal.reader.NodeDescription;
+import org.opendaylight.controller.sal.reader.NodeTableStatistics;
 import org.opendaylight.controller.sal.utils.GlobalConstants;
 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
 import org.opendaylight.controller.sal.utils.NodeCreator;
-
+import org.opendaylight.controller.sal.utils.NodeTableCreator;
+import org.openflow.protocol.OFMatch;
+import org.openflow.protocol.statistics.OFFlowStatisticsReply;
+import org.openflow.protocol.statistics.OFPortStatisticsReply;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.openflow.protocol.statistics.OFStatisticsType;
+import org.openflow.protocol.statistics.OFTableStatistics;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 /**
  * Read Service shim layer which is in charge of filtering the flow statistics
  * based on container. It is a Global instance.
@@ -51,13 +59,15 @@ import org.opendaylight.controller.sal.utils.NodeCreator;
  *
  *
  */
-public class ReadServiceFilter implements IPluginReadServiceFilter,
-        IContainerListener {
+public class ReadServiceFilter implements IReadServiceFilter, IContainerListener, IOFStatisticsListener {
     private static final Logger logger = LoggerFactory
             .getLogger(ReadServiceFilter.class);
     private IController controller = null;
     private IOFStatisticsManager statsMgr = null;
     private Map<String, Set<NodeConnector>> containerToNc;
+    private Map<String, Set<Node>> containerToNode;
+    private Map<String, Set<NodeTable>> containerToNt;
+    private ConcurrentMap<String, IReadFilterInternalListener> readFilterInternalListeners;
 
     public void setController(IController core) {
         this.controller = core;
@@ -69,6 +79,39 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         }
     }
 
+    public void setReadFilterInternalListener(Map<?, ?> props, IReadFilterInternalListener s) {
+        if (props == null) {
+            logger.error("Failed setting Read Filter Listener, property map is null.");
+            return;
+        }
+        String containerName = (String) props.get("containerName");
+        if (containerName == null) {
+            logger.error("Failed setting Read Filter Listener, container name not supplied.");
+            return;
+        }
+        if ((this.readFilterInternalListeners != null) && !this.readFilterInternalListeners.containsValue(s)) {
+            this.readFilterInternalListeners.put(containerName, s);
+            logger.trace("Added Read Filter Listener for container {}", containerName);
+        }
+    }
+
+    public void unsetReadFilterInternalListener(Map<?, ?> props, IReadFilterInternalListener s) {
+        if (props == null) {
+            logger.error("Failed unsetting Read Filter Listener, property map is null.");
+            return;
+        }
+        String containerName = (String) props.get("containerName");
+        if (containerName == null) {
+            logger.error("Failed unsetting Read Filter Listener, containerName not supplied");
+            return;
+        }
+        if ((this.readFilterInternalListeners != null) && this.readFilterInternalListeners.get(containerName) != null
+                && this.readFilterInternalListeners.get(containerName).equals(s)) {
+            this.readFilterInternalListeners.remove(containerName);
+            logger.trace("Removed Read Filter Listener for container {}", containerName);
+        }
+    }
+
     /**
      * Function called by the dependency manager when all the required
      * dependencies are satisfied
@@ -76,6 +119,9 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
      */
     void init() {
         containerToNc = new HashMap<String, Set<NodeConnector>>();
+        containerToNt = new HashMap<String, Set<NodeTable>>();
+        containerToNode = new HashMap<String, Set<Node>>();
+        readFilterInternalListeners = new ConcurrentHashMap<String, IReadFilterInternalListener>();
     }
 
     /**
@@ -126,9 +172,20 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
 
         long sid = (Long) node.getID();
         OFMatch ofMatch = new FlowConverter(flow).getOFMatch();
-        List<OFStatistics> ofList = (cached == true) ? statsMgr
-                .getOFFlowStatistics(sid, ofMatch) : statsMgr.queryStatistics(
-                sid, OFStatisticsType.FLOW, ofMatch);
+        List<OFStatistics> ofList;
+        if (cached == true){
+            ofList = statsMgr.getOFFlowStatistics(sid, ofMatch, flow.getPriority());
+        } else {
+            ofList = statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, ofMatch);
+            for (OFStatistics ofStat : ofList) {
+                if (((OFFlowStatisticsReply)ofStat).getPriority() == flow.getPriority()){
+                    ofList = new ArrayList<OFStatistics>(1);
+                    ofList.add(ofStat);
+                    break;
+                }
+            }
+        }
+
 
         /*
          * Convert and filter the statistics per container
@@ -174,9 +231,9 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         long sid = (Long) node.getID();
         List<OFStatistics> ofList = (cached == true) ? statsMgr
                 .getOFDescStatistics(sid) : statsMgr.queryStatistics(sid,
-                OFStatisticsType.DESC, null);
+                        OFStatisticsType.DESC, null);
 
-        return new DescStatisticsConverter(ofList).getHwDescription();
+                return new DescStatisticsConverter(ofList).getHwDescription();
     }
 
     /**
@@ -207,7 +264,7 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
     }
 
     /**
-     * Filters a list of FlowOnNode elements based on the container
+     * Filters a list of OFStatistics elements based on the container
      *
      * @param container
      * @param nodeId
@@ -235,6 +292,28 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         return newList;
     }
 
+
+    public List<OFStatistics> filterTableListPerContainer(
+            String container, long switchId, List<OFStatistics> list) {
+        if (list == null) {
+            return null;
+        }
+
+        // Create new filtered list of node tables
+        List<OFStatistics> newList = new ArrayList<OFStatistics>();
+
+        for (OFStatistics stat : list) {
+            OFTableStatistics target = (OFTableStatistics) stat;
+            NodeTable nt = NodeTableCreator.createOFNodeTable(
+                    target.getTableId(), NodeCreator.createOFNode(switchId));
+            if (containerOwnsNodeTable(container, nt)) {
+                newList.add(target);
+            }
+        }
+
+        return newList;
+    }
+
     /**
      * Returns whether the specified flow (flow match + actions)
      * belongs to the container
@@ -251,15 +330,15 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         }
         return (flowPortsBelongToContainer(container, node, flow)
                 && flowVlanBelongsToContainer(container, node, flow) && flowSpecAllowsFlow(
-                container, flow.getMatch()));
+                        container, flow.getMatch()));
     }
 
     /**
      * Returns whether the passed NodeConnector belongs to the container
      *
-     * @param container        container name
-     * @param p                node connector to test
-     * @return                 true if belongs false otherwise
+     * @param container container name
+     * @param p     node connector to test
+     * @return          true if belongs false otherwise
      */
     public boolean containerOwnsNodeConnector(String container, NodeConnector p) {
         // All node connectors belong to the default container
@@ -270,6 +349,22 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         return (portSet == null) ? false : portSet.contains(p);
     }
 
+    /**
+     * Returns whether the passed NodeConnector belongs to the container
+     *
+     * @param container container name
+     * @param table     node table to test
+     * @return          true if belongs false otherwise
+     */
+    public boolean containerOwnsNodeTable(String container, NodeTable table) {
+        // All node table belong to the default container
+        if (container.equals(GlobalConstants.DEFAULT.toString())) {
+            return true;
+        }
+        Set<NodeTable> tableSet = containerToNt.get(container);
+        return (tableSet == null) ? false : tableSet.contains(table);
+    }
+
     /**
      * Returns whether the container flowspec allows the passed flow
      *
@@ -290,8 +385,7 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
      * @param flow
      * @return
      */
-    private boolean flowVlanBelongsToContainer(String container, Node node,
-            Flow flow) {
+    private boolean flowVlanBelongsToContainer(String container, Node node, Flow flow) {
         return true; // Always true for now
     }
 
@@ -320,7 +414,7 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         // If an outgoing port is specified, it must belong to this container
         for (Action action : flow.getActions()) {
             if (action.getType() == ActionType.OUTPUT) {
-                NodeConnector outPort = (NodeConnector) ((Output) action)
+                NodeConnector outPort = ((Output) action)
                         .getPort();
                 if (!containerOwnsNodeConnector(container, outPort)) {
                     return false;
@@ -331,49 +425,69 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
     }
 
     @Override
-    public void containerFlowUpdated(String containerName,
-            ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
-
+    public void containerFlowUpdated(String containerName, ContainerFlow previousFlow,
+            ContainerFlow currentFlow, UpdateType t) {
+        // TODO
     }
 
     @Override
     public void nodeConnectorUpdated(String containerName, NodeConnector p,
             UpdateType type) {
-        Set<NodeConnector> target = null;
 
         switch (type) {
         case ADDED:
             if (!containerToNc.containsKey(containerName)) {
-                containerToNc.put(containerName, new HashSet<NodeConnector>());
+                containerToNc.put(containerName,
+                    Collections.newSetFromMap(new ConcurrentHashMap<NodeConnector,Boolean>()));
             }
             containerToNc.get(containerName).add(p);
-            break;
-        case CHANGED:
+            if (!containerToNode.containsKey(containerName)) {
+                containerToNode.put(containerName, new HashSet<Node>());
+            }
+            containerToNode.get(containerName).add(p.getNode());
             break;
         case REMOVED:
-            target = containerToNc.get(containerName);
-            if (target != null) {
-                target.remove(p);
+            Set<NodeConnector> ncSet = containerToNc.get(containerName);
+            if (ncSet != null) {
+                //remove this nc from container map
+                ncSet.remove(p);
+
+                //check if there are still ports of this node in this container
+                //and if not, remove its mapping
+                boolean nodeInContainer = false;
+                Node node = p.getNode();
+                for (NodeConnector nodeConnector : ncSet) {
+                    if (nodeConnector.getNode().equals(node)){
+                        nodeInContainer = true;
+                        break;
+                    }
+                }
+                if (! nodeInContainer) {
+                    Set<Node> nodeSet = containerToNode.get(containerName);
+                    if (nodeSet != null) {
+                        nodeSet.remove(node);
+                    }
+                }
             }
             break;
+        case CHANGED:
         default:
         }
     }
 
     @Override
-    public void tagUpdated(String containerName, Node n, short oldTag,
-            short newTag, UpdateType t) {
+    public void tagUpdated(String containerName, Node n, short oldTag, short newTag, UpdateType t) {
         // Not interested in this event
     }
 
     @Override
     public void containerModeUpdated(UpdateType t) {
-        // do nothing
+        // Not interested in this event
     }
 
     @Override
-    public NodeConnectorStatistics readNodeConnector(String containerName,
-            NodeConnector connector, boolean cached) {
+    public NodeConnectorStatistics readNodeConnector(
+            String containerName, NodeConnector connector, boolean cached) {
         if (!containerOwnsNodeConnector(containerName, connector)) {
             return null;
         }
@@ -382,11 +496,11 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         short portId = (Short) connector.getID();
         List<OFStatistics> ofList = (cached == true) ? statsMgr
                 .getOFPortStatistics(sid, portId) : statsMgr.queryStatistics(
-                sid, OFStatisticsType.PORT, portId);
+                        sid, OFStatisticsType.PORT, portId);
 
-        List<NodeConnectorStatistics> ncStatistics = new PortStatisticsConverter(
-                sid, ofList).getNodeConnectorStatsList();
-        return (ncStatistics.isEmpty()) ? new NodeConnectorStatistics()
+                List<NodeConnectorStatistics> ncStatistics = new PortStatisticsConverter(
+                        sid, ofList).getNodeConnectorStatsList();
+                return (ncStatistics.isEmpty()) ? new NodeConnectorStatistics()
                 : ncStatistics.get(0);
     }
 
@@ -397,12 +511,12 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         long sid = (Long) node.getID();
         List<OFStatistics> ofList = (cached == true) ? statsMgr
                 .getOFPortStatistics(sid) : statsMgr.queryStatistics(sid,
-                OFStatisticsType.FLOW, null);
+                        OFStatisticsType.FLOW, null);
 
-        List<OFStatistics> filteredList = filterPortListPerContainer(
-                containerName, sid, ofList);
+                List<OFStatistics> filteredList = filterPortListPerContainer(
+                        containerName, sid, ofList);
 
-        return new PortStatisticsConverter(sid, filteredList)
+                return new PortStatisticsConverter(sid, filteredList)
                 .getNodeConnectorStatsList();
     }
 
@@ -418,4 +532,97 @@ public class ReadServiceFilter implements IPluginReadServiceFilter,
         return statsMgr.getTransmitRate(switchId, port);
     }
 
+    @Override
+    public NodeTableStatistics readNodeTable(String containerName,
+            NodeTable table, boolean cached) {
+        if (!containerOwnsNodeTable(containerName, table)) {
+            return null;
+        }
+        Node node = table.getNode();
+        long sid = (Long) node.getID();
+        Byte tableId = (Byte) table.getID();
+        List<OFStatistics> ofList = (cached == true) ? statsMgr.getOFTableStatistics(sid, tableId) :
+            statsMgr.queryStatistics(sid, OFStatisticsType.TABLE, tableId);
+
+        List<NodeTableStatistics> ntStatistics =
+                new TableStatisticsConverter(sid, ofList).getNodeTableStatsList();
+
+        return (ntStatistics.isEmpty()) ? new NodeTableStatistics() : ntStatistics.get(0);
+    }
+
+    @Override
+    public List<NodeTableStatistics> readAllNodeTable(String containerName, Node node, boolean cached) {
+        long sid = (Long) node.getID();
+        List<OFStatistics> ofList = (cached == true) ?
+                statsMgr.getOFTableStatistics(sid) : statsMgr.queryStatistics(sid, OFStatisticsType.FLOW, null);
+
+        List<OFStatistics> filteredList = filterTableListPerContainer(containerName, sid, ofList);
+
+        return new TableStatisticsConverter(sid, filteredList).getNodeTableStatsList();
+    }
+
+    @Override
+    public void descriptionStatisticsRefreshed(Long switchId, List<OFStatistics> description) {
+        String container;
+        Node node = NodeCreator.createOFNode(switchId);
+        NodeDescription nodeDescription = new DescStatisticsConverter(description).getHwDescription();
+        for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+            container = l.getKey();
+            if (container == GlobalConstants.DEFAULT.toString()
+                    || (containerToNode.containsKey(container) && containerToNode.get(container).contains(node))) {
+                l.getValue().nodeDescriptionStatisticsUpdated(node, nodeDescription);
+            }
+        }
+    }
+
+    @Override
+    public void flowStatisticsRefreshed(Long switchId, List<OFStatistics> flows) {
+        String container;
+        Node node = NodeCreator.createOFNode(switchId);
+        for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+            container = l.getKey();
+
+            // Convert and filter the statistics per container
+            List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(flows).getFlowOnNodeList(node);
+            flowOnNodeList = filterFlowListPerContainer(container, node, flowOnNodeList);
+
+            // notify listeners
+            l.getValue().nodeFlowStatisticsUpdated(node, flowOnNodeList);
+        }
+    }
+
+    @Override
+    public void portStatisticsRefreshed(Long switchId, List<OFStatistics> ports) {
+        String container;
+        Node node = NodeCreator.createOFNode(switchId);
+        for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+            container = l.getKey();
+
+            // Convert and filter the statistics per container
+            List<OFStatistics> filteredPorts = filterPortListPerContainer(container, switchId, ports);
+            List<NodeConnectorStatistics> ncStatsList = new PortStatisticsConverter(switchId, filteredPorts)
+                    .getNodeConnectorStatsList();
+
+            // notify listeners
+            l.getValue().nodeConnectorStatisticsUpdated(node, ncStatsList);
+
+        }
+    }
+
+    @Override
+    public void tableStatisticsRefreshed(Long switchId, List<OFStatistics> tables) {
+        String container;
+        Node node = NodeCreator.createOFNode(switchId);
+        for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+            container = l.getKey();
+
+            // Convert and filter the statistics per container
+            List<OFStatistics> filteredList = filterTableListPerContainer(container, switchId, tables);
+            List<NodeTableStatistics> tableStatsList = new TableStatisticsConverter(switchId, filteredList)
+                    .getNodeTableStatsList();
+
+            // notify listeners
+            l.getValue().nodeTableStatisticsUpdated(node, tableStatsList);
+        }
+    }
 }