Move adsal into its own subdirectory.
[controller.git] / opendaylight / adsal / sal / implementation / src / main / java / org / opendaylight / controller / sal / implementation / internal / FlowProgrammerService.java
diff --git a/opendaylight/adsal/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/FlowProgrammerService.java b/opendaylight/adsal/sal/implementation/src/main/java/org/opendaylight/controller/sal/implementation/internal/FlowProgrammerService.java
new file mode 100644 (file)
index 0000000..60a7882
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2013-2014 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.sal.implementation.internal;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.osgi.framework.console.CommandInterpreter;
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.opendaylight.controller.sal.action.Action;
+import org.opendaylight.controller.sal.action.Controller;
+import org.opendaylight.controller.sal.action.Flood;
+import org.opendaylight.controller.sal.action.Output;
+import org.opendaylight.controller.sal.action.PopVlan;
+import org.opendaylight.controller.sal.action.SetNwDst;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.Node.NodeIDType;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.flowprogrammer.Flow;
+import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
+import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
+import org.opendaylight.controller.sal.flowprogrammer.IPluginOutFlowProgrammerService;
+import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.sal.utils.IPProtocols;
+import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
+import org.opendaylight.controller.sal.utils.Status;
+import org.opendaylight.controller.sal.utils.StatusCode;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The SAL Flow Programmer Service. It dispatches the flow programming requests
+ * to the proper SDN protocol plugin and it notifies about asynchronous messages
+ * received from the network node related to flow programming.
+ */
+public class FlowProgrammerService implements IFlowProgrammerService,
+        IPluginOutFlowProgrammerService, CommandProvider {
+
+    protected static final Logger logger = LoggerFactory
+            .getLogger(FlowProgrammerService.class);
+    private ConcurrentHashMap<String, ProtocolService<IPluginInFlowProgrammerService>> pluginFlowProgrammer;
+    private Set<IFlowProgrammerListener> listener;
+    private AtomicLong seq;
+
+    public FlowProgrammerService() {
+        pluginFlowProgrammer = new ConcurrentHashMap<String, ProtocolService<IPluginInFlowProgrammerService>>();
+        listener = new HashSet<IFlowProgrammerListener>();
+        seq = new AtomicLong();
+        /*
+         * This Request ID generator starts with 1. Each aysnc message is
+         * associated with an unique Request ID (!= 0).
+         */
+        seq.lazySet(1);
+    }
+
+    /**
+     * Function called by the dependency manager when all the required
+     * dependencies are satisfied
+     *
+     */
+    void init() {
+        logger.debug("INIT called!");
+    }
+
+    /**
+     * 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() {
+        // Clear previous registration to avoid they are left hanging
+        this.pluginFlowProgrammer.clear();
+        logger.debug("DESTROY called!");
+    }
+
+    /**
+     * 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() {
+        logger.debug("START called!");
+        // OSGI console
+        registerWithOSGIConsole();
+    }
+
+    /**
+     * 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() {
+        logger.debug("STOP called!");
+    }
+
+    // Set the reference to the plugin flow programmer
+    public void setService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
+        ProtocolService.set(this.pluginFlowProgrammer, props, s, logger);
+    }
+
+    public void unsetService(Map<String, Object> props, IPluginInFlowProgrammerService s) {
+        ProtocolService.unset(this.pluginFlowProgrammer, props, s, logger);
+    }
+
+    public void setListener(IFlowProgrammerListener s) {
+        this.listener.add(s);
+    }
+
+    public void unsetListener(IFlowProgrammerListener s) {
+        this.listener.remove(s);
+    }
+
+    @Override
+    public Status addFlow(Node node, Flow flow) {
+        if (pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().addFlow(node, flow);
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+
+    @Override
+    public Status removeFlow(Node node, Flow flow) {
+        if (pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().removeFlow(node, flow);
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+
+    @Override
+    public Status removeAllFlows(Node node) {
+        if (pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().removeAllFlows(node);
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+
+    @Override
+    public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
+        if (pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().modifyFlow(node, oldFlow, newFlow);
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+
+    @Override
+    public Status addFlowAsync(Node node, Flow flow) {
+        if (pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().addFlowAsync(node, flow, getNextRid());
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+
+    @Override
+    public Status removeFlowAsync(Node node, Flow flow) {
+        if (pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().removeFlowAsync(node, flow, getNextRid());
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+
+    @Override
+    public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow) {
+        if (pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().modifyFlowAsync(node, oldFlow, newFlow, getNextRid());
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+
+    @Override
+    public void flowRemoved(Node node, Flow flow) {
+        for (IFlowProgrammerListener l : listener) {
+            l.flowRemoved(node, flow);
+        }
+    }
+
+    @Override
+    public void flowErrorReported(Node node, long rid, Object err) {
+        logger.error("Got error {} for message rid {} from node {}",
+                new Object[] { err, rid, node });
+
+        for (IFlowProgrammerListener l : listener) {
+            l.flowErrorReported(node, rid, err);
+        }
+    }
+
+    // ---------------- OSGI TEST CODE ------------------------------//
+
+    private void registerWithOSGIConsole() {
+        BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
+                .getBundleContext();
+        bundleContext.registerService(CommandProvider.class.getName(), this,
+                null);
+    }
+
+    @Override
+    public String getHelp() {
+        StringBuffer help = new StringBuffer();
+        help.append("---SAL Flow Programmer testing commands---\n");
+        help.append("\t addflow <sid> - Add a sample flow to the openflow switch <sid>\n");
+        help.append("\t removeflow <sid> - Remove the sample flow from the openflow switch <sid>\n");
+        return help.toString();
+    }
+
+    public void _addflow(CommandInterpreter ci) throws UnknownHostException {
+        Node node = null;
+        String nodeId = ci.nextArgument();
+        if (nodeId == null) {
+            ci.print("Node id not specified");
+            return;
+        }
+        try {
+            node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+        } catch (NumberFormatException e) {
+            logger.error("",e);
+        } catch (ConstructionException e) {
+            logger.error("",e);
+        }
+        ci.println(this.addFlow(node, getSampleFlow(node)));
+    }
+
+    public void _modifyflow(CommandInterpreter ci) throws UnknownHostException {
+        Node node = null;
+        String nodeId = ci.nextArgument();
+        if (nodeId == null) {
+            ci.print("Node id not specified");
+            return;
+        }
+        try {
+            node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+        } catch (NumberFormatException e) {
+            logger.error("",e);
+        } catch (ConstructionException e) {
+            logger.error("",e);
+        }
+        Flow flowA = getSampleFlow(node);
+        Flow flowB = getSampleFlow(node);
+        Match matchB = flowB.getMatch();
+        matchB.setField(MatchType.NW_DST,
+                InetAddress.getByName("190.190.190.190"));
+        flowB.setMatch(matchB);
+        ci.println(this.modifyFlow(node, flowA, flowB));
+    }
+
+    public void _removeflow(CommandInterpreter ci) throws UnknownHostException {
+        Node node = null;
+        String nodeId = ci.nextArgument();
+        if (nodeId == null) {
+            ci.print("Node id not specified");
+            return;
+        }
+        try {
+            node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+        } catch (NumberFormatException e) {
+            logger.error("",e);
+        } catch (ConstructionException e) {
+            logger.error("",e);
+        }
+        ci.println(this.removeFlow(node, getSampleFlow(node)));
+    }
+
+    public void _addflowv6(CommandInterpreter ci) throws UnknownHostException {
+        Node node = null;
+        String nodeId = ci.nextArgument();
+        if (nodeId == null) {
+            ci.print("Node id not specified");
+            return;
+        }
+        try {
+            node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+        } catch (NumberFormatException e) {
+            logger.error("",e);
+        } catch (ConstructionException e) {
+            logger.error("",e);
+        }
+        ci.println(this.addFlow(node, getSampleFlowV6(node)));
+    }
+
+    public void _removeflowv6(CommandInterpreter ci)
+            throws UnknownHostException {
+        Node node = null;
+        String nodeId = ci.nextArgument();
+        if (nodeId == null) {
+            ci.print("Node id not specified");
+            return;
+        }
+        try {
+            node = new Node(NodeIDType.OPENFLOW, Long.valueOf(nodeId));
+        } catch (NumberFormatException e) {
+            logger.error("",e);
+        } catch (ConstructionException e) {
+            logger.error("",e);
+        }
+        ci.println(this.removeFlow(node, getSampleFlowV6(node)));
+    }
+
+    private Flow getSampleFlow(Node node) throws UnknownHostException {
+        NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
+                (short) 24, node);
+        NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
+                (short) 30, node);
+        byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
+                (byte) 0x9a, (byte) 0xbc };
+        byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
+                (byte) 0x5e, (byte) 0x6f };
+        InetAddress srcIP = InetAddress.getByName("172.28.30.50");
+        InetAddress dstIP = InetAddress.getByName("171.71.9.52");
+        InetAddress newIP = InetAddress.getByName("200.200.100.1");
+        InetAddress ipMask = InetAddress.getByName("255.255.255.0");
+        InetAddress ipMask2 = InetAddress.getByName("255.240.0.0");
+        short ethertype = EtherTypes.IPv4.shortValue();
+        short vlan = (short) 27;
+        byte vlanPr = 3;
+        Byte tos = 4;
+        byte proto = IPProtocols.TCP.byteValue();
+        short src = (short) 55000;
+        short dst = 80;
+
+        /*
+         * Create a SAL Flow aFlow
+         */
+        Match match = new Match();
+        match.setField(MatchType.IN_PORT, port);
+        match.setField(MatchType.DL_SRC, srcMac);
+        match.setField(MatchType.DL_DST, dstMac);
+        match.setField(MatchType.DL_TYPE, ethertype);
+        match.setField(MatchType.DL_VLAN, vlan);
+        match.setField(MatchType.DL_VLAN_PR, vlanPr);
+        match.setField(MatchType.NW_SRC, srcIP, ipMask);
+        match.setField(MatchType.NW_DST, dstIP, ipMask2);
+        match.setField(MatchType.NW_TOS, tos);
+        match.setField(MatchType.NW_PROTO, proto);
+        match.setField(MatchType.TP_SRC, src);
+        match.setField(MatchType.TP_DST, dst);
+
+        List<Action> actions = new ArrayList<Action>();
+        actions.add(new SetNwDst(newIP));
+        actions.add(new Output(oport));
+        actions.add(new PopVlan());
+        actions.add(new Flood());
+        actions.add(new Controller());
+
+        Flow flow = new Flow(match, actions);
+        flow.setPriority((short) 100);
+        flow.setHardTimeout((short) 360);
+        flow.setId(1234L);
+
+        return flow;
+    }
+
+    private Flow getSampleFlowV6(Node node) throws UnknownHostException {
+        NodeConnector port = NodeConnectorCreator.createOFNodeConnector(
+                (short) 24, node);
+        NodeConnector oport = NodeConnectorCreator.createOFNodeConnector(
+                (short) 30, node);
+        byte srcMac[] = { (byte) 0x12, (byte) 0x34, (byte) 0x56, (byte) 0x78,
+                (byte) 0x9a, (byte) 0xbc };
+        byte dstMac[] = { (byte) 0x1a, (byte) 0x2b, (byte) 0x3c, (byte) 0x4d,
+                (byte) 0x5e, (byte) 0x6f };
+        InetAddress srcIP = InetAddress
+                .getByName("2001:420:281:1004:407a:57f4:4d15:c355");
+        InetAddress dstIP = InetAddress
+                .getByName("2001:420:281:1004:e123:e688:d655:a1b0");
+        InetAddress ipMask = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:0:0:0:0");
+                                   // V6Match implementation assumes no mask is
+                                   // specified
+        InetAddress ipMask2 = null; // InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0");
+        short ethertype = EtherTypes.IPv6.shortValue();
+        short vlan = (short) 27;
+        byte vlanPr = (byte) 3;
+        Byte tos = 4;
+        byte proto = IPProtocols.UDP.byteValue();
+        short src = (short) 5500;
+        // short dst = 80;
+
+        /*
+         * Create a SAL Flow aFlow
+         */
+        Match match = new Match();
+        match.setField(MatchType.IN_PORT, port);
+        match.setField(MatchType.DL_SRC, srcMac);
+        match.setField(MatchType.DL_DST, dstMac);
+        match.setField(MatchType.DL_TYPE, ethertype);
+        match.setField(MatchType.DL_VLAN, vlan);
+        match.setField(MatchType.DL_VLAN_PR, vlanPr); // V6Match does not handle
+                                                      // this properly...
+        match.setField(MatchType.NW_SRC, srcIP, ipMask);
+        match.setField(MatchType.NW_DST, dstIP, ipMask2);
+        match.setField(MatchType.NW_TOS, tos);
+        match.setField(MatchType.NW_PROTO, proto);
+        match.setField(MatchType.TP_SRC, src); // V6Match does not handle this
+                                               // properly...
+        // match.setField(MatchType.TP_DST, dst); V6Match does not handle this
+        // properly...
+
+        List<Action> actions = new ArrayList<Action>();
+        actions.add(new Output(oport));
+        actions.add(new PopVlan());
+        actions.add(new Flood());
+
+        Flow flow = new Flow(match, actions);
+        flow.setPriority((short) 300);
+        flow.setHardTimeout((short) 240);
+        flow.setId(65536L);
+
+        return flow;
+    }
+
+    /**
+     * This Request ID generator starts with 1. Each aysnc message is
+     * associated with an unique Request ID (!= 0).
+     *
+     * @return Request ID
+     */
+    private long getNextRid() {
+        return seq.getAndIncrement();
+    }
+
+    @Override
+    public Status syncSendBarrierMessage(Node node) {
+        if (this.pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().syncSendBarrierMessage(node);
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+
+    @Override
+    public Status asyncSendBarrierMessage(Node node) {
+        if (this.pluginFlowProgrammer != null) {
+            ProtocolService<IPluginInFlowProgrammerService> service =
+                this.pluginFlowProgrammer.get(node.getType());
+            if (service != null) {
+                return service.getService().asyncSendBarrierMessage(node);
+            }
+        }
+        return new Status(StatusCode.NOSERVICE, "Plugin unuvailable");
+    }
+}