Move adsal into its own subdirectory.
[controller.git] / opendaylight / adsal / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / ReadServiceFilter.java
diff --git a/opendaylight/adsal/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/ReadServiceFilter.java b/opendaylight/adsal/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/ReadServiceFilter.java
new file mode 100644 (file)
index 0000000..e1b244f
--- /dev/null
@@ -0,0 +1,647 @@
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.protocol_plugin.openflow.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+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.IReadFilterInternalListener;
+import org.opendaylight.controller.protocol_plugin.openflow.IReadServiceFilter;
+import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.ActionType;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.core.ContainerFlow;
+import org.opendaylight.controller.sal.core.IContainerAware;
+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;
+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.
+ */
+public class ReadServiceFilter implements IReadServiceFilter, IContainerListener, IOFStatisticsListener, IContainerAware {
+    private static final Logger logger = LoggerFactory
+            .getLogger(ReadServiceFilter.class);
+    private IController controller = null;
+    private IOFStatisticsManager statsMgr = null;
+    private ConcurrentMap<String, Set<NodeConnector>> containerToNc;
+    private ConcurrentMap<String, Set<Node>> containerToNode;
+    private ConcurrentMap<String, Set<NodeTable>> containerToNt;
+    private ConcurrentMap<String, Set<ContainerFlow>> containerFlows;
+    private ConcurrentMap<String, IReadFilterInternalListener> readFilterInternalListeners =
+        new ConcurrentHashMap<String, IReadFilterInternalListener>();
+
+    public void setController(IController core) {
+        this.controller = core;
+    }
+
+    public void unsetController(IController core) {
+        if (this.controller == core) {
+            this.controller = null;
+        }
+    }
+
+    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
+     *
+     */
+    void init() {
+        containerToNc = new ConcurrentHashMap<String, Set<NodeConnector>>();
+        containerToNt = new ConcurrentHashMap<String, Set<NodeTable>>();
+        containerToNode = new ConcurrentHashMap<String, Set<Node>>();
+        containerFlows = new ConcurrentHashMap<String, Set<ContainerFlow>>();
+    }
+
+    /**
+     * Function called by the dependency manager when at least one
+     * dependency become unsatisfied or when the component is shutting
+     * down because for example bundle is being stopped.
+     *
+     */
+    void destroy() {
+        readFilterInternalListeners.clear();
+    }
+
+    /**
+     * Function called by dependency manager after "init ()" is called
+     * and after the services provided by the class are registered in
+     * the service registry
+     *
+     */
+    void start() {
+    }
+
+    /**
+     * Function called by the dependency manager before the services
+     * exported by the component are unregistered, this will be
+     * followed by a "destroy ()" calls
+     *
+     */
+    void stop() {
+    }
+
+    public void setService(IOFStatisticsManager service) {
+        this.statsMgr = service;
+    }
+
+    public void unsetService(IOFStatisticsManager service) {
+        this.statsMgr = null;
+    }
+
+    @Override
+    public FlowOnNode readFlow(String container, Node node, Flow flow, boolean cached) {
+
+        if (controller == null) {
+            // Avoid to provide cached statistics if controller went down.
+            // They are not valid anymore anyway
+            logger.error("Internal plugin error");
+            return null;
+        }
+
+        long sid = (Long) node.getID();
+        OFMatch ofMatch = new FlowConverter(flow).getOFMatch();
+        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
+        List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList).getFlowOnNodeList(node);
+        List<FlowOnNode> filteredList = filterFlowListPerContainer(container, node, flowOnNodeList);
+
+        return (filteredList.isEmpty()) ? null : filteredList.get(0);
+    }
+
+    @Override
+    public List<FlowOnNode> readAllFlow(String container, Node node,
+            boolean cached) {
+
+        long sid = (Long) node.getID();
+        List<OFStatistics> ofList = (cached == true) ? statsMgr
+                .getOFFlowStatistics(sid) : statsMgr.queryStatistics(sid,
+                OFStatisticsType.FLOW, null);
+
+        // Convert and filter the statistics per container
+        List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(ofList).getFlowOnNodeList(node);
+
+        return filterFlowListPerContainer(container, node, flowOnNodeList);
+    }
+
+    @Override
+    public NodeDescription readDescription(Node node, boolean cached) {
+
+        if (controller == null) {
+            logger.error("Internal plugin error");
+            return null;
+        }
+
+        long sid = (Long) node.getID();
+        List<OFStatistics> ofList = (cached == true) ? statsMgr
+                .getOFDescStatistics(sid) : statsMgr.queryStatistics(sid,
+                        OFStatisticsType.DESC, null);
+
+        return new DescStatisticsConverter(ofList).getHwDescription();
+    }
+
+    /**
+     * Filters a list of FlowOnNode elements based on the container
+     *
+     * @param container
+     * @param nodeId
+     * @param list
+     * @return
+     */
+    private List<FlowOnNode> filterFlowListPerContainer(String container,
+            Node nodeId, List<FlowOnNode> list) {
+        if (list == null) {
+            return Collections.emptyList();
+        }
+
+        // Create new filtered list of flows
+        List<FlowOnNode> newList = new ArrayList<FlowOnNode>();
+
+        for (FlowOnNode target : list) {
+            // Check whether the described flow (match + actions) belongs to this container
+            if (flowBelongToContainer(container, nodeId, target.getFlow())) {
+                newList.add(target);
+            }
+        }
+
+        return newList;
+    }
+
+    /**
+     * Filters a list of OFStatistics elements based on the container
+     *
+     * @param container
+     * @param nodeId
+     * @param list
+     * @return
+     */
+    private List<OFStatistics> filterPortListPerContainer(String container, long switchId, List<OFStatistics> list) {
+        if (list == null) {
+            return Collections.emptyList();
+        }
+
+        // Create new filtered list of flows
+        List<OFStatistics> newList = new ArrayList<OFStatistics>();
+
+        for (OFStatistics stat : list) {
+            OFPortStatisticsReply target = (OFPortStatisticsReply) stat;
+            NodeConnector nc = NodeConnectorCreator.createOFNodeConnector(
+                    target.getPortNumber(), NodeCreator.createOFNode(switchId));
+            if (containerOwnsNodeConnector(container, nc)) {
+                newList.add(target);
+            }
+        }
+
+        return newList;
+    }
+
+
+    private List<OFStatistics> filterTableListPerContainer(
+            String container, long switchId, List<OFStatistics> list) {
+        if (list == null) {
+            return Collections.emptyList();
+        }
+
+        // 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
+     *
+     * @param container
+     * @param node
+     * @param flow
+     * @return true if it belongs
+     */
+    public boolean flowBelongToContainer(String container, Node node, Flow flow) {
+        // All flows belong to the default container
+        if (container.equals(GlobalConstants.DEFAULT.toString())) {
+            return true;
+        }
+        return (flowPortsBelongToContainer(container, node, flow) &&
+                flowVlanBelongsToContainer(container, node, flow) &&
+                isFlowAllowedByContainer(container, flow));
+    }
+
+    /**
+     * 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
+     */
+    public boolean containerOwnsNodeConnector(String container, NodeConnector p) {
+        // All node connectors belong to the default container
+        if (container.equals(GlobalConstants.DEFAULT.toString())) {
+            return true;
+        }
+        Set<NodeConnector> portSet = containerToNc.get(container);
+        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 flows allow the passed flow
+     *
+     * @param container
+     * @param match
+     * @return
+     */
+    private boolean isFlowAllowedByContainer(String container, Flow flow) {
+        Set<ContainerFlow> cFlowSet = this.containerFlows.get(container);
+        if (cFlowSet == null || cFlowSet.isEmpty()) {
+            return true;
+        }
+        for (ContainerFlow cFlow : cFlowSet) {
+            if (cFlow.allowsFlow(flow)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check whether the vlan field in the flow match is the same
+     * of the static vlan configured for the container
+     *
+     * @param container
+     * @param node
+     * @param flow
+     * @return
+     */
+    private boolean flowVlanBelongsToContainer(String container, Node node, Flow flow) {
+        return true; // Always true for now
+    }
+
+    /**
+     * Check whether the ports in the flow match and flow actions for
+     * the specified node belong to the container
+     *
+     * @param container
+     * @param node
+     * @param flow
+     * @return
+     */
+    private boolean flowPortsBelongToContainer(String container, Node node,
+            Flow flow) {
+        Match m = flow.getMatch();
+        if (m.isPresent(MatchType.IN_PORT)) {
+            NodeConnector inPort = (NodeConnector) m.getField(MatchType.IN_PORT).getValue();
+            // If the incoming port is specified, check if it belongs to
+            if (!containerOwnsNodeConnector(container, inPort)) {
+                return false;
+            }
+        }
+
+        // If an outgoing port is specified, it must belong to this container
+        for (Action action : flow.getActions()) {
+            if (action.getType() == ActionType.OUTPUT) {
+                NodeConnector outPort = ((Output) action).getPort();
+                if (!containerOwnsNodeConnector(container, outPort)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void containerFlowUpdated(String containerName, ContainerFlow previousFlow,
+            ContainerFlow currentFlow, UpdateType t) {
+        Set<ContainerFlow> cFlowSet = containerFlows.get(containerName);
+        switch (t) {
+        case ADDED:
+            if (cFlowSet == null) {
+                cFlowSet = new HashSet<ContainerFlow>();
+                containerFlows.put(containerName, cFlowSet);
+            }
+            cFlowSet.add(currentFlow);
+        case CHANGED:
+            break;
+        case REMOVED:
+            if (cFlowSet != null) {
+                cFlowSet.remove(currentFlow);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+
+    @Override
+    public void nodeConnectorUpdated(String containerName, NodeConnector p, UpdateType type) {
+
+        switch (type) {
+        case ADDED:
+            if (!containerToNc.containsKey(containerName)) {
+                containerToNc.put(containerName,
+                    Collections.newSetFromMap(new ConcurrentHashMap<NodeConnector,Boolean>()));
+            }
+            containerToNc.get(containerName).add(p);
+            if (!containerToNode.containsKey(containerName)) {
+                containerToNode.put(containerName, new HashSet<Node>());
+            }
+            containerToNode.get(containerName).add(p.getNode());
+            break;
+        case REMOVED:
+            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) {
+        // Not interested in this event
+    }
+
+    @Override
+    public void containerModeUpdated(UpdateType t) {
+        // Not interested in this event
+    }
+
+    @Override
+    public NodeConnectorStatistics readNodeConnector(String containerName, NodeConnector connector, boolean cached) {
+        if (!containerOwnsNodeConnector(containerName, connector)) {
+            return null;
+        }
+        Node node = connector.getNode();
+        long sid = (Long) node.getID();
+        short portId = (Short) connector.getID();
+        List<OFStatistics> ofList = (cached == true) ? statsMgr
+                .getOFPortStatistics(sid, portId) : statsMgr.queryStatistics(
+                        sid, OFStatisticsType.PORT, portId);
+
+        List<NodeConnectorStatistics> ncStatistics = new PortStatisticsConverter(sid, ofList)
+                .getNodeConnectorStatsList();
+        return (ncStatistics.isEmpty()) ? new NodeConnectorStatistics() : ncStatistics.get(0);
+    }
+
+    @Override
+    public List<NodeConnectorStatistics> readAllNodeConnector(String containerName, Node node, boolean cached) {
+
+        long sid = (Long) node.getID();
+        List<OFStatistics> ofList = (cached == true) ? statsMgr
+                .getOFPortStatistics(sid) : statsMgr.queryStatistics(sid,
+                        OFStatisticsType.PORT, null);
+
+        List<OFStatistics> filteredList = filterPortListPerContainer(containerName, sid, ofList);
+
+        return new PortStatisticsConverter(sid, filteredList).getNodeConnectorStatsList();
+    }
+
+    @Override
+    public long getTransmitRate(String containerName, NodeConnector connector) {
+        if (!containerOwnsNodeConnector(containerName, connector)) {
+            return 0;
+        }
+
+        long switchId = (Long) connector.getNode().getID();
+        short port = (Short) connector.getID();
+
+        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.TABLE, 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;
+        IReadFilterInternalListener listener;
+        Node node = NodeCreator.createOFNode(switchId);
+        NodeDescription nodeDescription = new DescStatisticsConverter(description).getHwDescription();
+        for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+            container = l.getKey();
+            listener = l.getValue();
+            if (container == GlobalConstants.DEFAULT.toString()
+                    || (containerToNode.containsKey(container) && containerToNode.get(container).contains(node))) {
+                listener.nodeDescriptionStatisticsUpdated(node, nodeDescription);
+            }
+        }
+    }
+
+    @Override
+    public void flowStatisticsRefreshed(Long switchId, List<OFStatistics> flows) {
+        String container;
+        IReadFilterInternalListener listener;
+        Node node = NodeCreator.createOFNode(switchId);
+        for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+            container = l.getKey();
+            listener = l.getValue();
+
+            // Convert and filter the statistics per container
+            List<FlowOnNode> flowOnNodeList = new FlowStatisticsConverter(flows).getFlowOnNodeList(node);
+            flowOnNodeList = filterFlowListPerContainer(container, node, flowOnNodeList);
+
+            // notify listeners
+            listener.nodeFlowStatisticsUpdated(node, flowOnNodeList);
+        }
+    }
+
+    @Override
+    public void portStatisticsRefreshed(Long switchId, List<OFStatistics> ports) {
+        String container;
+        IReadFilterInternalListener listener;
+        Node node = NodeCreator.createOFNode(switchId);
+        for (Map.Entry<String, IReadFilterInternalListener> l : readFilterInternalListeners.entrySet()) {
+            container = l.getKey();
+            listener = l.getValue();
+
+            // Convert and filter the statistics per container
+            List<OFStatistics> filteredPorts = filterPortListPerContainer(container, switchId, ports);
+            List<NodeConnectorStatistics> ncStatsList = new PortStatisticsConverter(switchId, filteredPorts)
+                    .getNodeConnectorStatsList();
+
+            // notify listeners
+            listener.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);
+        }
+    }
+
+    @Override
+    public void containerCreate(String containerName) {
+        // do nothing
+    }
+
+    @Override
+    public void containerDestroy(String containerName) {
+        containerToNc.remove(containerName);
+        containerToNode.remove(containerName);
+        containerToNt.remove(containerName);
+        containerFlows.remove(containerName);
+    }
+}