Adding multipart learning switch 69/5069/6
authorTony Tkacik <ttkacik@cisco.com>
Tue, 4 Feb 2014 00:54:47 +0000 (01:54 +0100)
committerMichal Rehak <mirehak@cisco.com>
Thu, 6 Feb 2014 08:34:36 +0000 (09:34 +0100)
- improved logging
- wrapped simple implementation
- tidying up names
- fixed stop
- limited to IPv4 traffic
- fixed expire times (=0) for flow forwarding packets to controller
- Introduced InstanceIdentifier utility class.
- Separated utility functions to thematic classes.

Change-Id: I20bfb99797294767fbb0e290089cb2c701876ff4
Signed-off-by: Michal Rehak <mirehak@cisco.com>
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
17 files changed:
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/Activator.java
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/DataChangeListenerRegistrationHolder.java [moved from samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/DataChangeListenerRegistrationPublisher.java with 79% similarity]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/FlowCommitWrapper.java [moved from samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/OFDataStoreAccessor.java with 79% similarity]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/FlowCommitWrapperImpl.java [moved from samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/OFDataStoreAccessorImpl.java with 87% similarity]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/FlowUtils.java [new file with mode: 0644]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/InstanceIdentifierUtils.java [new file with mode: 0644]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/LearningSwitchHandler.java [moved from samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/SimpleLearningSwitchHandler.java with 75% similarity]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/LearningSwitchHandlerSimpleImpl.java [new file with mode: 0644]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/LearningSwitchManager.java [new file with mode: 0644]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/LearningSwitchManagerSimpleImpl.java [moved from samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/SimpleLearningSwitchManager.java with 89% similarity]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/OFFlowUtil.java [deleted file]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/PacketUtils.java [new file with mode: 0644]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/SimpleLearningSwitchHandlerImpl.java [deleted file]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/WakeupOnNode.java
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/LearningSwitchManagerMultiImpl.java [new file with mode: 0644]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/MultipleLearningSwitchHandlerFacadeImpl.java [new file with mode: 0644]
samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/PacketInDispatcherImpl.java [new file with mode: 0644]

index a1ed16bf8cfced667e6811c6d1a1fd0c5c75357a..75e65f708b0a99854ce89954e17d80b686f61d06 100644 (file)
@@ -11,6 +11,7 @@ import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareConsumer;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext;
 import org.opendaylight.controller.sal.binding.api.NotificationService;
 import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.openflowplugin.learningswitch.multi.LearningSwitchManagerMultiImpl;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
@@ -18,35 +19,58 @@ import org.slf4j.LoggerFactory;
 
 /**
  * learning switch activator
+ * 
+ * Activator is derived from AbstractBindingAwareConsumer, which takes care
+ * of looking up MD-SAL in Service Registry and registering consumer
+ * when MD-SAL is present.
  */
 public class Activator extends AbstractBindingAwareConsumer implements AutoCloseable {
     
     private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
 
-    private SimpleLearningSwitchManager learningSwitch;
+    private LearningSwitchManager learningSwitch;
 
     
     @Override
     protected void startImpl(BundleContext context) {
         LOG.info("startImpl() passing");
-        learningSwitch = new SimpleLearningSwitchManager();
     }
     
+    /**
+     * Invoked when consumer is registered to the MD-SAL.
+     * 
+     */
     @Override
     public void onSessionInitialized(ConsumerContext session) {
         LOG.info("inSessionInitialized() passing");
-        learningSwitch.setData(session.getSALService(DataBrokerService.class));
+        /**
+         * We create instance of our LearningSwitchManager
+         * and set all required dependencies,
+         * 
+         * which are 
+         *   Data Broker (data storage service) - for configuring flows and reading stored switch state
+         *   PacketProcessingService - for sending out packets
+         *   NotificationService - for receiving notifications such as packet in.
+         * 
+         */
+        learningSwitch = new LearningSwitchManagerMultiImpl();
+        learningSwitch.setDataBroker(session.getSALService(DataBrokerService.class));
         learningSwitch.setPacketProcessingService(session.getRpcService(PacketProcessingService.class));
         learningSwitch.setNotificationService(session.getSALService(NotificationService.class));
         learningSwitch.start();
     }
 
     @Override
-    public void close() throws Exception {
+    public void close() {
         LOG.info("close() passing");
         if (learningSwitch != null) {
             learningSwitch.stop();
         }
     }
-
+    
+    @Override
+    protected void stopImpl(BundleContext context) {
+        close();
+        super.stopImpl(context);
+    }
 }
@@ -14,11 +14,11 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration;
 /**
  * 
  */
-public interface DataChangeListenerRegistrationPublisher {
+public interface DataChangeListenerRegistrationHolder {
 
     /**
      * @return the dataChangeListenerRegistration
      */
-    public abstract ListenerRegistration<DataChangeListener> getDataChangeListenerRegistration();
+    ListenerRegistration<DataChangeListener> getDataChangeListenerRegistration();
 
 }
similarity index 79%
rename from samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/OFDataStoreAccessor.java
rename to samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/FlowCommitWrapper.java
index 2847f854373fc2d9553f89cfbeb6e8357ce24564..3294a1bd12eaff39dd785d3780f5e91fdb013e10 100644 (file)
@@ -18,14 +18,17 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 /**
  * 
  */
-public interface OFDataStoreAccessor {
+public interface FlowCommitWrapper {
 
     /**
+     * Starts and commits data change transaction which 
+     * modifies provided flow path with supplied body.
+     * 
      * @param flowPath 
-     * @param allToCtrlFlow 
+     * @param flowBody 
      * @return transaction commit 
      * 
      */
-    Future<RpcResult<TransactionStatus>> writeFlowToConfig(InstanceIdentifier<Flow> flowPath, Flow allToCtrlFlow);
+    Future<RpcResult<TransactionStatus>> writeFlowToConfig(InstanceIdentifier<Flow> flowPath, Flow flowBody);
 
 }
@@ -20,24 +20,23 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 /**
  * 
  */
-public class OFDataStoreAccessorImpl implements OFDataStoreAccessor {
+public class FlowCommitWrapperImpl implements FlowCommitWrapper {
     
     private DataBrokerService dataBrokerService;
 
     /**
      * @param dataBrokerService
      */
-    public OFDataStoreAccessorImpl(DataBrokerService dataBrokerService) {
+    public FlowCommitWrapperImpl(DataBrokerService dataBrokerService) {
         this.dataBrokerService = dataBrokerService;
     }
 
     @Override
     public Future<RpcResult<TransactionStatus>> writeFlowToConfig(InstanceIdentifier<Flow> flowPath, 
-            Flow allToCtrlFlow) {
+            Flow flowBody) {
         DataModificationTransaction addFlowTransaction = dataBrokerService.beginTransaction();
-        addFlowTransaction.putConfigurationData(flowPath, allToCtrlFlow);
+        addFlowTransaction.putConfigurationData(flowPath, flowBody);
         return addFlowTransaction.commit();
     }
-    
 
 }
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/FlowUtils.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/FlowUtils.java
new file mode 100644 (file)
index 0000000..5ea397f
--- /dev/null
@@ -0,0 +1,157 @@
+package org.opendaylight.openflowplugin.learningswitch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
+
+import com.google.common.collect.ImmutableList;
+
+public class FlowUtils {
+    /**
+     * @param tableId
+     * @param priority
+     * @param srcMac
+     * @param dstMac
+     * @param dstPort
+     * @return {@link FlowBuilder} forwarding all packets to controller port
+     */
+    public static FlowBuilder createDirectMacToMacFlow(Short tableId, int priority, MacAddress srcMac,
+            MacAddress dstMac, NodeConnectorRef dstPort) {
+        FlowBuilder macToMacFlow = new FlowBuilder() //
+                .setTableId(tableId) //
+                .setFlowName("mac2mac");
+        macToMacFlow.setId(new FlowId(Long.toString(macToMacFlow.hashCode())));
+
+        EthernetMatch ethernetMatch = new EthernetMatchBuilder() //
+                .setEthernetSource(new EthernetSourceBuilder() //
+                        .setAddress(srcMac) //
+                        .build()) //
+                .setEthernetDestination(new EthernetDestinationBuilder() //
+                        .setAddress(dstMac) //
+                        .build()) //
+                .build();
+
+        MatchBuilder match = new MatchBuilder();
+        match.setEthernetMatch(ethernetMatch);
+
+        Uri outputPort = dstPort.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
+
+        Action outputToControllerAction = new ActionBuilder() //
+                .setAction(new OutputActionCaseBuilder() //
+                        .setOutputAction(new OutputActionBuilder() //
+                                .setMaxLength(new Integer(0xffff)) //
+                                .setOutputNodeConnector(outputPort) //
+                                .build()) //
+                        .build()) //
+                .build();
+
+        // Create an Apply Action
+        ApplyActions applyActions = new ApplyActionsBuilder().setAction(ImmutableList.of(outputToControllerAction))
+                .build();
+
+        // Wrap our Apply Action in an Instruction
+        Instruction applyActionsInstruction = new InstructionBuilder() //
+                .setInstruction(new ApplyActionsCaseBuilder()//
+                        .setApplyActions(applyActions) //
+                        .build()) //
+                .build();
+
+        // Put our Instruction in a list of Instructions
+
+        macToMacFlow //
+                .setMatch(new MatchBuilder() //
+                        .setEthernetMatch(ethernetMatch) //
+                        .build()) //
+                .setInstructions(new InstructionsBuilder() //
+                        .setInstruction(ImmutableList.of(applyActionsInstruction)) //
+                        .build()) //
+                .setPriority(priority) //
+                .setBufferId(0L) //
+                .setHardTimeout(0) //
+                .setIdleTimeout(0) //
+                .setFlags(new FlowModFlags(false, false, false, false, false));
+
+        return macToMacFlow;
+    }
+
+    /**
+     * @param tableId
+     * @param priority
+     * @param flowId
+     * @return {@link FlowBuilder} forwarding all packets to controller port
+     */
+    public static FlowBuilder createFwdAllToControllerFlow(Short tableId, int priority, FlowId flowId) {
+        FlowBuilder allToCtrlFlow = new FlowBuilder().setTableId(tableId).setFlowName("allPacketsToCtrl").setId(flowId)
+                .setKey(new FlowKey(flowId));
+
+        MatchBuilder matchBuilder = new MatchBuilder();
+
+        // Create output action -> send to controller
+        OutputActionBuilder output = new OutputActionBuilder();
+        output.setMaxLength(new Integer(0xffff));
+        Uri controllerPort = new Uri(OutputPortValues.CONTROLLER.toString());
+        output.setOutputNodeConnector(controllerPort);
+
+        ActionBuilder ab = new ActionBuilder();
+        ab.setAction(new OutputActionCaseBuilder().setOutputAction(output.build()).build());
+        ab.setOrder(0);
+        ab.setKey(new ActionKey(0));
+
+        List<Action> actionList = new ArrayList<Action>();
+        actionList.add(ab.build());
+
+        // Create an Apply Action
+        ApplyActionsBuilder aab = new ApplyActionsBuilder();
+        aab.setAction(actionList);
+
+        // Wrap our Apply Action in an Instruction
+        InstructionBuilder ib = new InstructionBuilder();
+        ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
+        ib.setOrder(0);
+        ib.setKey(new InstructionKey(0));
+
+        // Put our Instruction in a list of Instructions
+        InstructionsBuilder isb = new InstructionsBuilder();
+        List<Instruction> instructions = new ArrayList<Instruction>();
+        instructions.add(ib.build());
+        isb.setInstruction(instructions);
+
+        allToCtrlFlow //
+            .setMatch(matchBuilder.build()) //
+            .setInstructions(isb.build()) //
+            .setPriority(priority) //
+            .setBufferId(0L) //
+            .setHardTimeout(0) //
+            .setIdleTimeout(0) //
+            .setFlags(new FlowModFlags(false, false, false, false, false));
+
+        return allToCtrlFlow;
+    }
+}
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/InstanceIdentifierUtils.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/InstanceIdentifierUtils.java
new file mode 100644 (file)
index 0000000..d1cc038
--- /dev/null
@@ -0,0 +1,95 @@
+package org.opendaylight.openflowplugin.learningswitch;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+
+public class InstanceIdentifierUtils {
+
+    /**
+     * Creates an Instance Identifier (path) for node with specified id
+     * 
+     * @param nodeId
+     * @return
+     */
+    public static final InstanceIdentifier<Node> createNodePath(NodeId nodeId) {
+        return InstanceIdentifier.builder(Nodes.class) //
+                .child(Node.class, new NodeKey(nodeId)) //
+                .build();
+    }
+
+    /**
+     * Shorten's node child path to node path.
+     * 
+     * @param nodeChild child of node, from which we want node path.
+     * @return
+     */
+    public static final InstanceIdentifier<Node> getNodePath(InstanceIdentifier<?> nodeChild) {
+        return nodeChild.firstIdentifierOf(Node.class);
+    }
+    
+    
+    /**
+     * Creates a table path by appending table specific location to node path
+     * 
+     * @param nodePath
+     * @param tableKey
+     * @return
+     */
+    public static final InstanceIdentifier<Table> createTablePath(InstanceIdentifier<Node> nodePath,TableKey tableKey) {
+        return InstanceIdentifier.builder(nodePath)
+                .augmentation(FlowCapableNode.class)
+                .child(Table.class, tableKey)
+                .build();
+    }
+
+    /**
+     * Creates a path for particular flow, by appending flow-specific information
+     * to table path.
+     * 
+     * @param flowId
+     * @param tablePathArg 
+     * @return path to flow
+     */
+    public static InstanceIdentifier<Flow> createFlowPath(InstanceIdentifier<Table> table,FlowKey flowKey) {
+        return InstanceIdentifier.builder(table)
+                .child(Flow.class, flowKey)
+                .build();
+    }
+
+    /**
+     * Extract table id from table path.
+     * 
+     * @param tablePath
+     * @return
+     */
+    public static Short getTableId(InstanceIdentifier<Table> tablePath) {
+        return tablePath.firstKeyOf(Table.class, TableKey.class).getId();
+    }
+    
+    /**
+     * Extracts NodeConnectorKey from node connector path.
+     * 
+     */
+    public static NodeConnectorKey getNodeConnectorKey(InstanceIdentifier<?> nodeConnectorPath) {
+        return nodeConnectorPath.firstKeyOf(NodeConnector.class, NodeConnectorKey.class);
+    }
+    
+    
+    //
+    public static final InstanceIdentifier<NodeConnector> createNodeConnectorPath(InstanceIdentifier<Node> nodeKey,NodeConnectorKey nodeConnectorKey) {
+        return InstanceIdentifier.builder(nodeKey) //
+                .child(NodeConnector.class,nodeConnectorKey) //
+                .build();
+    }
+}
@@ -5,18 +5,16 @@
  * 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.openflowplugin.learningswitch;
 
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 /**
  * 
  */
-public interface SimpleLearningSwitchHandler extends PacketProcessingListener {
+public interface LearningSwitchHandler {
 
     /**
      * @param tablePath 
@@ -31,10 +29,10 @@ public interface SimpleLearningSwitchHandler extends PacketProcessingListener {
    /**
     * @param dataStoreAccessor the dataStoreAccessor to set
     */
-   void setDataStoreAccessor(OFDataStoreAccessor dataStoreAccessor);
+   void setDataStoreAccessor(FlowCommitWrapper dataStoreAccessor);
 
    /**
     * @param registrationPublisher the registrationPublisher to set
     */
-   void setRegistrationPublisher(DataChangeListenerRegistrationPublisher registrationPublisher);
+   void setRegistrationPublisher(DataChangeListenerRegistrationHolder registrationPublisher);
 }
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/LearningSwitchHandlerSimpleImpl.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/LearningSwitchHandlerSimpleImpl.java
new file mode 100644 (file)
index 0000000..2bd2bd2
--- /dev/null
@@ -0,0 +1,241 @@
+/**
+ * Copyright (c) 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.openflowplugin.learningswitch;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple Learning Switch implementation which does mac learning for one switch.
+ * 
+ * 
+ */
+public class LearningSwitchHandlerSimpleImpl implements LearningSwitchHandler, PacketProcessingListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(LearningSwitchHandler.class);
+
+    private static final byte[] ETH_TYPE_IPV4 = new byte[] { 0x08, 0x00 };
+
+    private static final int DIRECT_FLOW_PRIORITY = 512;
+
+    private DataChangeListenerRegistrationHolder registrationPublisher;
+    private FlowCommitWrapper dataStoreAccessor;
+    private PacketProcessingService packetProcessingService;
+
+    private boolean iAmLearning = false;
+
+    private NodeId nodeId;
+    private AtomicLong flowIdInc = new AtomicLong();
+    private InstanceIdentifier<Node> nodePath;
+    private InstanceIdentifier<Table> tablePath;
+
+    private Map<MacAddress, NodeConnectorRef> mac2portMapping;
+    private Set<String> coveredMacPaths;
+
+    @Override
+    public synchronized void onSwitchAppeared(InstanceIdentifier<Table> appearedTablePath) {
+        if (iAmLearning) {
+            LOG.debug("already learning a node, skipping {}", nodeId.getValue());
+            return;
+        }
+
+        LOG.debug("expected table acquired, learning ..");
+
+        // disable listening - simple learning handles only one node (switch)
+        if (registrationPublisher != null) {
+            try {
+                LOG.debug("closing dataChangeListenerRegistration");
+                registrationPublisher.getDataChangeListenerRegistration().close();
+            } catch (Exception e) {
+                LOG.error("closing registration upon flowCapable node update listener failed: " + e.getMessage(), e);
+            }
+        }
+
+        iAmLearning = true;
+        
+        tablePath = appearedTablePath;
+        nodePath = tablePath.firstIdentifierOf(Node.class);
+        nodeId = nodePath.firstKeyOf(Node.class, NodeKey.class).getId();
+        mac2portMapping = new HashMap<>();
+        coveredMacPaths = new HashSet<>();
+
+        // start forwarding all packages to controller
+        FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
+        FlowKey flowKey = new FlowKey(flowId);
+        InstanceIdentifier<Flow> flowPath = InstanceIdentifierUtils.createFlowPath(tablePath, flowKey);
+
+        int priority = 0;
+        // create flow in table with id = 0, priority = 4 (other params are
+        // defaulted in OFDataStoreUtil)
+        FlowBuilder allToCtrlFlow = FlowUtils.createFwdAllToControllerFlow(
+                InstanceIdentifierUtils.getTableId(tablePath), priority, flowId);
+
+        LOG.debug("writing packetForwardToController flow");
+        dataStoreAccessor.writeFlowToConfig(flowPath, allToCtrlFlow.build());
+    }
+
+    @Override
+    public void setRegistrationPublisher(DataChangeListenerRegistrationHolder registrationPublisher) {
+        this.registrationPublisher = registrationPublisher;
+    }
+
+    @Override
+    public void setDataStoreAccessor(FlowCommitWrapper dataStoreAccessor) {
+        this.dataStoreAccessor = dataStoreAccessor;
+    }
+
+    @Override
+    public void setPacketProcessingService(PacketProcessingService packetProcessingService) {
+        this.packetProcessingService = packetProcessingService;
+    }
+
+    @Override
+    public void onPacketReceived(PacketReceived notification) {
+        if (!iAmLearning) {
+            // ignoring packets - this should not happen
+            return;
+        }
+
+        LOG.debug("Received packet via match: {}", notification.getMatch());
+
+        // detect and compare node - we support one switch
+        if (!nodePath.contains(notification.getIngress().getValue())) {
+            return;
+        }
+
+        // read src MAC and dst MAC
+        byte[] dstMacRaw = PacketUtils.extractDstMac(notification.getPayload());
+        byte[] srcMacRaw = PacketUtils.extractSrcMac(notification.getPayload());
+        byte[] etherType = PacketUtils.extractEtherType(notification.getPayload());
+
+        MacAddress dstMac = PacketUtils.rawMacToMac(dstMacRaw);
+        MacAddress srcMac = PacketUtils.rawMacToMac(srcMacRaw);
+
+        NodeConnectorKey ingressKey = InstanceIdentifierUtils.getNodeConnectorKey(notification.getIngress().getValue());
+
+        LOG.debug("Received packet from MAC match: {}, ingress: {}", srcMac, ingressKey.getId());
+        LOG.debug("Received packet to   MAC match: {}", dstMac);
+        LOG.debug("Ethertype: {}", Integer.toHexString(0x0000ffff & ByteBuffer.wrap(etherType).getShort()));
+
+        // learn by IPv4 traffic only
+        if (Arrays.equals(ETH_TYPE_IPV4, etherType)) {
+            NodeConnectorRef previousPort = mac2portMapping.put(srcMac, notification.getIngress());
+            if (previousPort != null && !notification.getIngress().equals(previousPort)) {
+                NodeConnectorKey previousPortKey = InstanceIdentifierUtils.getNodeConnectorKey(previousPort.getValue());
+                LOG.debug("mac2port mapping changed by mac {}: {} -> {}", srcMac, previousPortKey, ingressKey.getId());
+            }
+            // if dst MAC mapped:
+            NodeConnectorRef destNodeConnector = mac2portMapping.get(dstMac);
+            if (destNodeConnector != null) {
+                synchronized (coveredMacPaths) {
+                    if (!destNodeConnector.equals(notification.getIngress())) {
+                        // add flow
+                        addBridgeFlow(srcMac, dstMac, destNodeConnector);
+                        addBridgeFlow(dstMac, srcMac, notification.getIngress());
+                    } else {
+                        LOG.debug("useless rule ignoring - both MACs are behind the same port");
+                    }
+                }
+                LOG.debug("packetIn-directing.. to {}",
+                        InstanceIdentifierUtils.getNodeConnectorKey(destNodeConnector.getValue()).getId());
+                sendPacketOut(notification.getPayload(), notification.getIngress(), destNodeConnector);
+            } else {
+                // flood
+                LOG.debug("packetIn-still flooding.. ");
+                flood(notification.getPayload(), notification.getIngress());
+            }
+        } else {
+            // non IPv4 package
+            flood(notification.getPayload(), notification.getIngress());
+        }
+
+    }
+
+    /**
+     * @param srcMac
+     * @param dstMac
+     * @param destNodeConnector
+     */
+    private void addBridgeFlow(MacAddress srcMac, MacAddress dstMac, NodeConnectorRef destNodeConnector) {
+        synchronized (coveredMacPaths) {
+            String macPath = srcMac.toString() + dstMac.toString();
+            if (!coveredMacPaths.contains(macPath)) {
+                LOG.debug("covering mac path: {} by [{}]", macPath,
+                        destNodeConnector.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId());
+
+                coveredMacPaths.add(macPath);
+                FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
+                FlowKey flowKey = new FlowKey(flowId);
+                /**
+                 * Path to the flow we want to program.
+                 */
+                InstanceIdentifier<Flow> flowPath = InstanceIdentifierUtils.createFlowPath(tablePath, flowKey);
+
+                Short tableId = InstanceIdentifierUtils.getTableId(tablePath);
+                FlowBuilder srcToDstFlow = FlowUtils.createDirectMacToMacFlow(tableId, DIRECT_FLOW_PRIORITY, srcMac,
+                        dstMac, destNodeConnector);
+
+                dataStoreAccessor.writeFlowToConfig(flowPath, srcToDstFlow.build());
+            }
+        }
+    }
+
+    private void flood(byte[] payload, NodeConnectorRef ingress) {
+        NodeConnectorKey nodeConnectorKey = new NodeConnectorKey(nodeConnectorId("0xfffffffb"));
+        InstanceIdentifier<?> nodeConnectorPath = InstanceIdentifierUtils.createNodeConnectorPath(nodePath, nodeConnectorKey);
+        NodeConnectorRef egressConnectorRef = new NodeConnectorRef(nodeConnectorPath);
+
+        sendPacketOut(payload, ingress, egressConnectorRef);
+    }
+
+    private NodeConnectorId nodeConnectorId(String connectorId) {
+        NodeKey nodeKey = nodePath.firstKeyOf(Node.class, NodeKey.class);
+        StringBuilder stringId = new StringBuilder(nodeKey.getId().getValue()).append(":").append(connectorId);
+        return new NodeConnectorId(stringId.toString());
+    }
+
+    private void sendPacketOut(byte[] payload, NodeConnectorRef ingress, NodeConnectorRef egress) {
+        InstanceIdentifier<Node> egressNodePath = InstanceIdentifierUtils.getNodePath(egress.getValue());
+        TransmitPacketInput input = new TransmitPacketInputBuilder() //
+                .setPayload(payload) //
+                .setNode(new NodeRef(egressNodePath)) //
+                .setEgress(egress) //
+                .setIngress(ingress) //
+                .build();
+        packetProcessingService.transmitPacket(input);
+    }
+}
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/LearningSwitchManager.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/LearningSwitchManager.java
new file mode 100644 (file)
index 0000000..9316506
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 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.openflowplugin.learningswitch;
+
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+
+/**
+ * 
+ */
+public interface LearningSwitchManager {
+
+    /**
+     * stop manager
+     */
+    void stop();
+
+    /**
+     * start manager
+     */
+    void start();
+
+    /**
+     * Set's Data Broker dependency.
+     * 
+     * Data Broker is used to access overal operational and configuration
+     * tree.
+     * 
+     *  In simple Learning Switch handler, data broker is used to listen 
+     *  for changes in Openflow tables and to configure flows which will
+     *  be provisioned down to the Openflow switch.
+     * 
+     * inject {@link DataBrokerService}
+     * @param data
+     */
+    void setDataBroker(DataBrokerService data);
+
+    /**
+     * Set's Packet Processing dependency.
+     * 
+     * Packet Processing service is used to send packet Out on Openflow
+     * switch.
+     * 
+     * inject {@link PacketProcessingService}
+     * 
+     * @param packetProcessingService
+     */
+    void setPacketProcessingService(
+            PacketProcessingService packetProcessingService);
+
+    /**
+     * Set's Notification service dependency.
+     * 
+     * Notification service is used to register for listening 
+     * packet-in notifications.
+     * 
+     * inject {@link NotificationService}
+     * @param notificationService
+     */
+    void setNotificationService(NotificationService notificationService);
+}
@@ -31,10 +31,11 @@ import org.slf4j.LoggerFactory;
  * corresponding MACs)</li>
  * </ul>
  */
-public class SimpleLearningSwitchManager implements DataChangeListenerRegistrationPublisher {
+public class LearningSwitchManagerSimpleImpl implements DataChangeListenerRegistrationHolder,
+        LearningSwitchManager {
     
     protected static final Logger LOG = LoggerFactory
-            .getLogger(SimpleLearningSwitchManager.class);
+            .getLogger(LearningSwitchManagerSimpleImpl.class);
 
     private NotificationService notificationService;
     private PacketProcessingService packetProcessingService;
@@ -47,6 +48,7 @@ public class SimpleLearningSwitchManager implements DataChangeListenerRegistrati
     /**
      * @param notificationService the notificationService to set
      */
+    @Override
     public void setNotificationService(NotificationService notificationService) {
         this.notificationService = notificationService;
     }
@@ -54,6 +56,7 @@ public class SimpleLearningSwitchManager implements DataChangeListenerRegistrati
     /**
      * @param packetProcessingService the packetProcessingService to set
      */
+    @Override
     public void setPacketProcessingService(
             PacketProcessingService packetProcessingService) {
         this.packetProcessingService = packetProcessingService;
@@ -62,22 +65,23 @@ public class SimpleLearningSwitchManager implements DataChangeListenerRegistrati
     /**
      * @param data the data to set
      */
-    public void setData(DataBrokerService data) {
+    @Override
+    public void setDataBroker(DataBrokerService data) {
         this.data = data;
     }
 
     /**
      * starting learning switch
      */
+    @Override
     public void start() {
         LOG.debug("start() -->");
-        OFDataStoreAccessor dataStoreAccessor = new OFDataStoreAccessorImpl(data);
+        FlowCommitWrapper dataStoreAccessor = new FlowCommitWrapperImpl(data);
 
-        SimpleLearningSwitchHandler learningSwitchHandler = new SimpleLearningSwitchHandlerImpl();
+        LearningSwitchHandlerSimpleImpl learningSwitchHandler = new LearningSwitchHandlerSimpleImpl();
         learningSwitchHandler.setRegistrationPublisher(this);
         learningSwitchHandler.setDataStoreAccessor(dataStoreAccessor);
         learningSwitchHandler.setPacketProcessingService(packetProcessingService);
-        //FIXME: should register for packets from particular node
         packetInRegistration = notificationService.registerNotificationListener(learningSwitchHandler);
         
         WakeupOnNode wakeupListener = new WakeupOnNode();
@@ -94,6 +98,7 @@ public class SimpleLearningSwitchManager implements DataChangeListenerRegistrati
     /**
      * stopping learning switch 
      */
+    @Override
     public void stop() {
         LOG.debug("stop() -->");
         //TODO: remove flow (created in #start())
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/OFFlowUtil.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/OFFlowUtil.java
deleted file mode 100644 (file)
index a2a5ed3..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/**
- * Copyright (c) 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.openflowplugin.learningswitch;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-/**
- * 
- */
-public abstract class OFFlowUtil {
-
-    /**
-     * @param tableId
-     * @param priority 
-     * @param srcMac 
-     * @param dstMac 
-     * @param dstPort 
-     * @return {@link FlowBuilder} forwarding all packets to controller port
-     */
-    public static FlowBuilder createDirectMacToMacFlow(Short tableId, int priority,
-            MacAddress srcMac, MacAddress dstMac, NodeConnectorRef dstPort) {
-        FlowBuilder allToCtrlFlow = new FlowBuilder();
-        allToCtrlFlow.setTableId(tableId);
-        allToCtrlFlow.setFlowName("mac2mac");
-        allToCtrlFlow.setId(new FlowId(Long.toString(allToCtrlFlow.hashCode())));
-        
-        MatchBuilder match = new MatchBuilder();
-        EthernetMatchBuilder ethernetMatch = new EthernetMatchBuilder();
-        EthernetSourceBuilder ethSourceBuilder = new EthernetSourceBuilder();
-        ethSourceBuilder.setAddress(srcMac);
-        ethernetMatch.setEthernetSource(ethSourceBuilder.build());
-        EthernetDestinationBuilder ethDestinationBuilder = new EthernetDestinationBuilder();
-        ethDestinationBuilder.setAddress(dstMac);
-        ethernetMatch.setEthernetDestination(ethDestinationBuilder.build());
-        match.setEthernetMatch(ethernetMatch.build());
-       
-        OutputActionBuilder output = new OutputActionBuilder();
-        output.setMaxLength(new Integer(0xffff));
-        Uri outputPort = dstPort.getValue().firstKeyOf(
-                NodeConnector.class, NodeConnectorKey.class).getId();
-        output.setOutputNodeConnector(outputPort);
-        ActionBuilder ab = new ActionBuilder();
-        ab.setAction(new OutputActionCaseBuilder().setOutputAction(output.build()).build());
-        
-        // Add our drop action to a list
-        ArrayList<Action> actionList = new ArrayList<>();
-        actionList.add(ab.build());
-        
-        // Create an Apply Action
-        ApplyActionsBuilder aab = new ApplyActionsBuilder();
-        aab.setAction(actionList);
-        
-        // Wrap our Apply Action in an Instruction
-        InstructionBuilder ib = new InstructionBuilder();
-        ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
-        
-        // Put our Instruction in a list of Instructions
-        InstructionsBuilder isb = new InstructionsBuilder();
-        ArrayList<Instruction> instructions = new ArrayList<Instruction>();
-        instructions.add(ib.build());
-        isb.setInstruction(instructions);
-        
-        allToCtrlFlow.setMatch(match.build())
-           .setInstructions(isb.build())
-           .setPriority(priority)
-           .setBufferId(0L)
-           .setHardTimeout(0)
-           .setIdleTimeout(0)
-           .setFlags(new FlowModFlags(false, false, false, false, false));
-        
-        return allToCtrlFlow;
-    }
-    
-    /**
-     * @param tableId
-     * @param priority
-     * @param flowId 
-     * @return {@link FlowBuilder} forwarding all packets to controller port
-     */
-    public static FlowBuilder createFwdAllToControllerFlow(Short tableId, int priority, FlowId flowId) {
-        FlowBuilder allToCtrlFlow = new FlowBuilder();
-        allToCtrlFlow.setTableId(tableId);
-        allToCtrlFlow.setFlowName("allPacketsToCtrl");
-        allToCtrlFlow.setId(flowId);
-        allToCtrlFlow.setKey(new FlowKey(flowId));
-        
-        MatchBuilder emptyMatchBuilder = new MatchBuilder();
-
-        // Create output action -> send to controller
-        OutputActionBuilder output = new OutputActionBuilder();
-        output.setMaxLength(new Integer(0xffff));
-        Uri controllerPort = new Uri(OutputPortValues.CONTROLLER.toString());
-        output.setOutputNodeConnector(controllerPort);
-        
-        ActionBuilder ab = new ActionBuilder();
-        ab.setAction(new OutputActionCaseBuilder().setOutputAction(output.build()).build());
-        ab.setOrder(0);
-        ab.setKey(new ActionKey(0));
-        
-        List<Action> actionList = new ArrayList<Action>();
-        actionList.add(ab.build());
-        
-        // Create an Apply Action
-        ApplyActionsBuilder aab = new ApplyActionsBuilder();
-        aab.setAction(actionList);
-
-        // Wrap our Apply Action in an Instruction
-        InstructionBuilder ib = new InstructionBuilder();
-        ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
-        ib.setOrder(0);
-        ib.setKey(new InstructionKey(0));
-
-        // Put our Instruction in a list of Instructions
-        InstructionsBuilder isb = new InstructionsBuilder();
-        List<Instruction> instructions = new ArrayList<Instruction>();
-        instructions.add(ib.build());
-        isb.setInstruction(instructions);
-        
-        allToCtrlFlow.setMatch(emptyMatchBuilder.build());
-        allToCtrlFlow.setInstructions(isb.build());
-        allToCtrlFlow.setPriority(priority);
-        allToCtrlFlow.setBufferId(0L);
-        allToCtrlFlow.setHardTimeout(300);
-        allToCtrlFlow.setIdleTimeout(240);
-        allToCtrlFlow.setFlags(new FlowModFlags(false, false, false, false, false));
-        
-        return allToCtrlFlow;
-    }
-
-    /**
-     * @param flowId
-     * @param tablePathArg 
-     * @return
-     */
-    static InstanceIdentifier<Flow> assemleFlowPath(FlowId flowId, InstanceIdentifier<Table> tablePathArg) {
-        FlowKey flowKey = new FlowKey(flowId);
-        InstanceIdentifier<Flow> flowPath = InstanceIdentifier.builder(tablePathArg)
-                .child(Flow.class, flowKey )
-                .toInstance();
-        return flowPath;
-    }
-
-    /**
-     * @param payload
-     * @return
-     */
-    static byte[] extractDstMac(byte[] payload) {
-        return Arrays.copyOfRange(payload, 0, 6);
-    }
-
-    /**
-     * @param payload
-     * @return
-     */
-    static byte[] extractSrcMac(byte[] payload) {
-        return Arrays.copyOfRange(payload, 6, 12);
-    }
-
-    static MacAddress rawMacToMac(byte[] rawMac) {
-        MacAddress mac = null;
-        if (rawMac != null && rawMac.length == 6) {
-            StringBuffer sb = new StringBuffer();
-            for (byte octet : rawMac) {
-                sb.append(String.format(":%02X", octet));
-            }
-            mac = new MacAddress(sb.substring(1));
-        }
-        return mac;
-    }
-
-    static TransmitPacketInput buildPacketOut(byte[] payload, NodeConnectorRef ingress,
-            String outputPort, NodeKey nodekey) {
-        InstanceIdentifier<Node> nodePath = InstanceIdentifier.builder(Nodes.class)
-                .child(Node.class, nodekey).toInstance();
-        
-        NodeConnectorRef egressConfRef = new NodeConnectorRef(
-                createNodeConnRef(nodePath, nodekey, outputPort));
-        
-        TransmitPacketInputBuilder tPackBuilder = new TransmitPacketInputBuilder();
-        tPackBuilder.setPayload(payload);
-        tPackBuilder.setNode(new NodeRef(nodePath));
-        tPackBuilder.setCookie(null);
-        tPackBuilder.setEgress(egressConfRef);
-        tPackBuilder.setIngress(ingress);
-        return tPackBuilder.build();
-    }
-
-    private static NodeConnectorRef createNodeConnRef(InstanceIdentifier<Node> nodeInstId, 
-            NodeKey nodeKey, String port) {
-        StringBuilder sBuild = new StringBuilder(nodeKey.getId().getValue()).append(":").append(port);
-        NodeConnectorKey nConKey = new NodeConnectorKey(new NodeConnectorId(sBuild.toString()));
-        InstanceIdentifier<NodeConnector> portPath = InstanceIdentifier.builder(nodeInstId)
-                .child(NodeConnector.class, nConKey).toInstance();
-        return new NodeConnectorRef(portPath);
-    }
-
-}
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/PacketUtils.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/PacketUtils.java
new file mode 100644 (file)
index 0000000..7135d70
--- /dev/null
@@ -0,0 +1,114 @@
+/**
+ * Copyright (c) 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.openflowplugin.learningswitch;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * 
+ */
+public abstract class PacketUtils {
+
+    
+
+    /**
+     * @param payload
+     * @return destination MAC address
+     */
+    public static byte[] extractDstMac(byte[] payload) {
+        return Arrays.copyOfRange(payload, 0, 6);
+    }
+
+    /**
+     * @param payload
+     * @return source MAC address
+     */
+    public static byte[] extractSrcMac(byte[] payload) {
+        return Arrays.copyOfRange(payload, 6, 12);
+    }
+
+    /**
+     * @param payload
+     * @return source MAC address
+     */
+    public static byte[] extractEtherType(byte[] payload) {
+        return Arrays.copyOfRange(payload, 12, 14);
+    }
+
+    /**
+     * @param rawMac
+     * @return {@link MacAddress} wrapping string value, baked upon binary MAC
+     *         address
+     */
+    public static MacAddress rawMacToMac(byte[] rawMac) {
+        MacAddress mac = null;
+        if (rawMac != null && rawMac.length == 6) {
+            StringBuffer sb = new StringBuffer();
+            for (byte octet : rawMac) {
+                sb.append(String.format(":%02X", octet));
+            }
+            mac = new MacAddress(sb.substring(1));
+        }
+        return mac;
+    }
+
+    /**
+     * @param nodeInstId
+     * @param nodeKey
+     * @param port
+     * @return port wrapped into {@link NodeConnectorRef}
+     */
+    public static NodeConnectorRef createNodeConnRef(InstanceIdentifier<Node> nodeInstId, NodeKey nodeKey, String port) {
+        StringBuilder sBuild = new StringBuilder(nodeKey.getId().getValue()).append(":").append(port);
+        NodeConnectorKey nConKey = new NodeConnectorKey(new NodeConnectorId(sBuild.toString()));
+        InstanceIdentifier<NodeConnector> portPath = InstanceIdentifier.builder(nodeInstId)
+                .child(NodeConnector.class, nConKey).toInstance();
+        return new NodeConnectorRef(portPath);
+    }
+}
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/SimpleLearningSwitchHandlerImpl.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/SimpleLearningSwitchHandlerImpl.java
deleted file mode 100644 (file)
index a58536e..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-/**
- * Copyright (c) 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.openflowplugin.learningswitch;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * 
- */
-public class SimpleLearningSwitchHandlerImpl implements SimpleLearningSwitchHandler {
-    
-    private static final Logger LOG = LoggerFactory
-            .getLogger(SimpleLearningSwitchHandler.class);
-    
-    private DataChangeListenerRegistrationPublisher registrationPublisher;
-    private OFDataStoreAccessor dataStoreAccessor;
-    private PacketProcessingService packetProcessingService;
-
-    private boolean iAmLearning = false;
-
-    private NodeId nodeId;
-    private AtomicLong flowIdInc = new AtomicLong();
-    private InstanceIdentifier<Node> nodePath;
-    private InstanceIdentifier<Table> tablePath;
-
-    private Map<MacAddress, NodeConnectorRef> mac2portMapping;
-    private Set<String> coveredMacPaths;
-
-    @Override
-    public synchronized void onSwitchAppeared(InstanceIdentifier<Table> appearedTablePath) {
-        if (iAmLearning) {
-            LOG.debug("already learning a node, skipping {}", nodeId.getValue());
-            return;
-        }
-        
-        LOG.debug("expected table acquired, learning ..");
-       
-        // disable listening - simple learning handles only one node (switch)
-        try {
-            LOG.debug("closing dataChangeListenerRegistration");
-            registrationPublisher.getDataChangeListenerRegistration().close();
-        } catch (Exception e) {
-            LOG.error("closing registration upon flowCapable node update listener failed: " + e.getMessage(), e);
-        }
-        iAmLearning  = true;
-        tablePath = appearedTablePath;
-        nodePath = tablePath.firstIdentifierOf(Node.class);
-        nodeId = nodePath.firstKeyOf(Node.class, NodeKey.class).getId();
-        mac2portMapping = new HashMap<>();
-        coveredMacPaths = new HashSet<>();
-        
-        // start forwarding all packages to controller
-        FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
-        InstanceIdentifier<Flow> flowPath = OFFlowUtil.assemleFlowPath(flowId, tablePath);
-
-        // create flow in table with id = 0, priority = 4 (other params are defaulted in OFDataStoreUtil)
-        FlowBuilder allToCtrlFlow = OFFlowUtil.createFwdAllToControllerFlow(
-                appearedTablePath.firstKeyOf(Table.class, TableKey.class).getId(), 0, flowId);
-
-        LOG.debug("writing packetForwardToController flow");
-        dataStoreAccessor.writeFlowToConfig(flowPath, allToCtrlFlow.build());        
-    }
-
-    @Override
-    public void setRegistrationPublisher(
-            DataChangeListenerRegistrationPublisher registrationPublisher) {
-        this.registrationPublisher = registrationPublisher;
-    }
-    
-    @Override
-    public void setDataStoreAccessor(OFDataStoreAccessor dataStoreAccessor) {
-        this.dataStoreAccessor = dataStoreAccessor;
-    }
-    
-    @Override
-    public void setPacketProcessingService(
-            PacketProcessingService packetProcessingService) {
-        this.packetProcessingService = packetProcessingService;
-    }
-    
-    @Override
-    public void onPacketReceived(PacketReceived notification) {
-        if (!iAmLearning) {
-            // ignoring packets - this should not happen
-            return;
-        }
-        
-        LOG.debug("Received packet via match: {}", notification.getMatch());
-        
-        // detect and compare node - we support one switch
-        if (!nodePath.contains(notification.getIngress().getValue())) {
-            return;
-        }
-        
-        // read src MAC and dst MAC
-        byte[] dstMacRaw = OFFlowUtil.extractDstMac(notification.getPayload());
-        byte[] srcMacRaw = OFFlowUtil.extractSrcMac(notification.getPayload());
-        
-        MacAddress dstMac = OFFlowUtil.rawMacToMac(dstMacRaw);
-        MacAddress srcMac = OFFlowUtil.rawMacToMac(srcMacRaw);
-        
-        LOG.debug("Received packet from MAC match: {}, ingress: {}", srcMac, notification.getIngress());
-        LOG.debug("Received packet to   MAC match: {}", dstMac);
-        
-        mac2portMapping.put(srcMac, notification.getIngress());
-
-        // if dst MAC mapped: 
-        NodeConnectorRef dstNodeConnectorRef = mac2portMapping.get(dstMac);
-        if (dstNodeConnectorRef != null) {
-            synchronized (coveredMacPaths) {
-                // add flow
-                addBridgeFlow(srcMac, dstMac, dstNodeConnectorRef);
-                addBridgeFlow(dstMac, srcMac, notification.getIngress());
-            }
-        }
-        // flood
-        flood(notification.getPayload(), notification.getIngress());
-    }
-
-    /**
-     * @param srcMac
-     * @param dstMac
-     * @param dstNodeConnectorRef
-     */
-    private void addBridgeFlow(MacAddress srcMac, MacAddress dstMac,
-            NodeConnectorRef dstNodeConnectorRef) {
-        synchronized (coveredMacPaths) {
-            String macPath = srcMac.toString() + dstMac.toString();
-            if (!coveredMacPaths.contains(macPath)) {
-                LOG.debug("covering mac path: {}", macPath);
-                coveredMacPaths.add(macPath);
-                FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
-
-                // create flow in table with id = 0, priority = 2 (other params are defaulted in OFDataStoreUtil)
-                InstanceIdentifier<Flow> flowPath = OFFlowUtil.assemleFlowPath(flowId, tablePath);
-                FlowBuilder srcToDstFlow = OFFlowUtil.createDirectMacToMacFlow((short) 0, 512,
-                        srcMac, dstMac, dstNodeConnectorRef);
-
-                dataStoreAccessor.writeFlowToConfig(flowPath, srcToDstFlow.build());
-            }
-        }
-    }
-
-    private void flood(byte[] payload, NodeConnectorRef ingress) {
-        NodeKey nodeKey = new NodeKey(nodeId);
-        
-        TransmitPacketInput input = OFFlowUtil.buildPacketOut(payload, ingress, "0xfffffffb", nodeKey);
-        packetProcessingService.transmitPacket(input);
-    }
-}
index ddb7703e903c5eda06f7d8d68a37ce73df47ada3..3ce3dd0bc368332e2f72968455b2773cb10f6a89 100644 (file)
@@ -27,7 +27,7 @@ public class WakeupOnNode implements DataChangeListener {
     private static final Logger LOG = LoggerFactory
             .getLogger(WakeupOnNode.class);
     
-    private SimpleLearningSwitchHandler learningSwitchHandler;
+    private LearningSwitchHandler learningSwitchHandler;
 
     @Override
     public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
@@ -54,7 +54,7 @@ public class WakeupOnNode implements DataChangeListener {
      * @param learningSwitchHandler the learningSwitchHandler to set
      */
     public void setLearningSwitchHandler(
-            SimpleLearningSwitchHandler learningSwitchHandler) {
+            LearningSwitchHandler learningSwitchHandler) {
         this.learningSwitchHandler = learningSwitchHandler;
     }
 }
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/LearningSwitchManagerMultiImpl.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/LearningSwitchManagerMultiImpl.java
new file mode 100644 (file)
index 0000000..51c36ae
--- /dev/null
@@ -0,0 +1,130 @@
+/**
+ * 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.openflowplugin.learningswitch.multi;
+
+import org.opendaylight.controller.sal.binding.api.NotificationService;
+import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.openflowplugin.learningswitch.DataChangeListenerRegistrationHolder;
+import org.opendaylight.openflowplugin.learningswitch.LearningSwitchManager;
+import org.opendaylight.openflowplugin.learningswitch.FlowCommitWrapper;
+import org.opendaylight.openflowplugin.learningswitch.FlowCommitWrapperImpl;
+import org.opendaylight.openflowplugin.learningswitch.WakeupOnNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.NotificationListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listens to packetIn notification and 
+ * <ul>
+ * <li>in HUB mode simply floods all switch ports (except ingress port)</li>
+ * <li>in LSWITCH mode collects source MAC address of packetIn and bind it with ingress port. 
+ * If target MAC address is already bound then a flow is created (for direct communication between 
+ * corresponding MACs)</li>
+ * </ul>
+ */
+public class LearningSwitchManagerMultiImpl implements DataChangeListenerRegistrationHolder,
+        LearningSwitchManager {
+    
+    protected static final Logger LOG = LoggerFactory
+            .getLogger(LearningSwitchManagerMultiImpl.class);
+
+    private NotificationService notificationService;
+    private PacketProcessingService packetProcessingService;
+    private DataBrokerService data;
+
+    private Registration<NotificationListener> packetInRegistration;
+
+    private ListenerRegistration<DataChangeListener> dataChangeListenerRegistration; 
+    
+    /**
+     * @param notificationService the notificationService to set
+     */
+    @Override
+    public void setNotificationService(NotificationService notificationService) {
+        this.notificationService = notificationService;
+    }
+
+    /**
+     * @param packetProcessingService the packetProcessingService to set
+     */
+    @Override
+    public void setPacketProcessingService(
+            PacketProcessingService packetProcessingService) {
+        this.packetProcessingService = packetProcessingService;
+    }
+    
+    /**
+     * @param data the data to set
+     */
+    @Override
+    public void setDataBroker(DataBrokerService data) {
+        this.data = data;
+    }
+
+    /**
+     * starting learning switch
+     */
+    @Override
+    public void start() {
+        LOG.debug("start() -->");
+        FlowCommitWrapper dataStoreAccessor = new FlowCommitWrapperImpl(data);
+
+        PacketInDispatcherImpl packetInDispatcher = new PacketInDispatcherImpl();
+        MultipleLearningSwitchHandlerFacadeImpl learningSwitchHandler = new MultipleLearningSwitchHandlerFacadeImpl();
+        learningSwitchHandler.setRegistrationPublisher(this);
+        learningSwitchHandler.setDataStoreAccessor(dataStoreAccessor);
+        learningSwitchHandler.setPacketProcessingService(packetProcessingService);
+        learningSwitchHandler.setPacketInDispatcher(packetInDispatcher);
+        packetInRegistration = notificationService.registerNotificationListener(packetInDispatcher);
+        
+        WakeupOnNode wakeupListener = new WakeupOnNode();
+        wakeupListener.setLearningSwitchHandler(learningSwitchHandler);
+        dataChangeListenerRegistration = data.registerDataChangeListener(
+                InstanceIdentifier.builder(Nodes.class)
+                    .child(Node.class)
+                    .augmentation(FlowCapableNode.class)
+                    .child(Table.class).toInstance(),
+                wakeupListener);
+        LOG.debug("start() <--");
+    }
+    
+    /**
+     * stopping learning switch 
+     */
+    @Override
+    public void stop() {
+        LOG.debug("stop() -->");
+        //TODO: remove flow (created in #start())
+        try {
+            packetInRegistration.close();
+        } catch (Exception e) {
+            LOG.error(e.getMessage(), e);
+        }
+        try {
+            dataChangeListenerRegistration.close();
+        } catch (Exception e) {
+            LOG.error(e.getMessage(), e);
+        }
+        LOG.debug("stop() <--");
+    }
+    
+   
+    @Override
+    public ListenerRegistration<DataChangeListener> getDataChangeListenerRegistration() {
+        return dataChangeListenerRegistration;
+    }
+}
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/MultipleLearningSwitchHandlerFacadeImpl.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/MultipleLearningSwitchHandlerFacadeImpl.java
new file mode 100644 (file)
index 0000000..7824dce
--- /dev/null
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 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.openflowplugin.learningswitch.multi;
+
+import org.opendaylight.openflowplugin.learningswitch.DataChangeListenerRegistrationHolder;
+import org.opendaylight.openflowplugin.learningswitch.InstanceIdentifierUtils;
+import org.opendaylight.openflowplugin.learningswitch.FlowCommitWrapper;
+import org.opendaylight.openflowplugin.learningswitch.LearningSwitchHandler;
+import org.opendaylight.openflowplugin.learningswitch.LearningSwitchHandlerSimpleImpl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+ */
+public class MultipleLearningSwitchHandlerFacadeImpl implements LearningSwitchHandler {
+    
+    private static final Logger LOG = LoggerFactory
+            .getLogger(MultipleLearningSwitchHandlerFacadeImpl.class);
+    
+    private FlowCommitWrapper dataStoreAccessor;
+    private PacketProcessingService packetProcessingService;
+    private PacketInDispatcherImpl packetInDispatcher;
+
+    @Override
+    public synchronized void onSwitchAppeared(InstanceIdentifier<Table> appearedTablePath) {
+        LOG.debug("expected table acquired, learning ..");
+       
+        /**
+         * appearedTablePath is in form of /nodes/node/node-id/table/table-id
+         * so we shorten it to /nodes/node/node-id to get identifier of switch.
+         * 
+         */
+        InstanceIdentifier<Node> nodePath = InstanceIdentifierUtils.getNodePath(appearedTablePath);
+        
+        /**
+         * We check if we already initialized dispatcher for that node,
+         * if not we create new handler for switch.
+         * 
+         */
+        if (!packetInDispatcher.getHandlerMapping().containsKey(nodePath)) {
+            // delegate this node (owning appearedTable) to SimpleLearningSwitchHandler  
+            LearningSwitchHandlerSimpleImpl simpleLearningSwitch = new LearningSwitchHandlerSimpleImpl();
+            /**
+             * We set runtime dependencies
+             */
+            simpleLearningSwitch.setDataStoreAccessor(dataStoreAccessor);
+            simpleLearningSwitch.setPacketProcessingService(packetProcessingService);
+            
+            /**
+             * We propagate table event to newly instantiated instance of learning switch
+             */
+            simpleLearningSwitch.onSwitchAppeared(appearedTablePath);
+            /**
+             * We update mapping of already instantiated LearningSwitchHanlders
+             */
+            packetInDispatcher.getHandlerMapping().put(nodePath, simpleLearningSwitch);
+        }
+    }
+
+    @Override
+    public void setRegistrationPublisher(
+            DataChangeListenerRegistrationHolder registrationPublisher) {
+        //NOOP
+    }
+    
+    @Override
+    public void setDataStoreAccessor(FlowCommitWrapper dataStoreAccessor) {
+        this.dataStoreAccessor = dataStoreAccessor;
+    }
+    
+    @Override
+    public void setPacketProcessingService(
+            PacketProcessingService packetProcessingService) {
+        this.packetProcessingService = packetProcessingService;
+    }
+
+    /**
+     * @param packetInDispatcher
+     */
+    public void setPacketInDispatcher(PacketInDispatcherImpl packetInDispatcher) {
+        this.packetInDispatcher = packetInDispatcher;
+    }
+}
diff --git a/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/PacketInDispatcherImpl.java b/samples/learning-switch/src/main/java/org/opendaylight/openflowplugin/learningswitch/multi/PacketInDispatcherImpl.java
new file mode 100644 (file)
index 0000000..30abfdc
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 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.openflowplugin.learningswitch.multi;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * 
+ */
+public class PacketInDispatcherImpl implements PacketProcessingListener {
+    
+    private Map<InstanceIdentifier<Node>, PacketProcessingListener> handlerMapping;
+    
+    /**
+     * default constructor
+     */
+    public PacketInDispatcherImpl() {
+        handlerMapping = new HashMap<>();
+    }
+
+    @Override
+    public void onPacketReceived(PacketReceived notification) {
+        // find corresponding handler
+        /**
+         * Notification contains reference to ingress port
+         * in a form of path in inventory: /nodes/node/node-connector
+         * 
+         * In order to get path we shorten path to the first node reference
+         * by using firstIdentifierOf helper method provided by InstanceIdentifier,
+         * this will effectively shorten the path to /nodes/node.
+         * 
+         */
+        InstanceIdentifier<?> ingressPort = notification.getIngress().getValue();
+        InstanceIdentifier<Node> nodeOfPacket = ingressPort.firstIdentifierOf(Node.class);
+        /**
+         * We lookup up the the packet-in listener for this node.
+         * 
+         */
+        PacketProcessingListener nodeHandler = handlerMapping.get(nodeOfPacket);
+        
+        /**
+         * 
+         * If we have packet-processing listener, we delegate notification.
+         * 
+         */
+        if (nodeHandler != null) {
+            nodeHandler.onPacketReceived(notification);
+        }
+    }
+    
+    /**
+     * @return the handlerMapping
+     */
+    public Map<InstanceIdentifier<Node>, PacketProcessingListener> getHandlerMapping() {
+        return handlerMapping;
+    }
+}