SFC data model to config OpenFlow table offsets 36/30136/5
authorBrady Johnson <[email protected]>
Tue, 24 Nov 2015 13:18:36 +0000 (14:18 +0100)
committerBrady Johnson <[email protected]>
Thu, 3 Dec 2015 13:20:27 +0000 (13:20 +0000)
- For now, the Sfc OpenFlow Renderer data model only
  has attributes for OpenFlow tables, but in the future
  more attributes can be added.
- Introduced SfcSynchronizer class to be able to synchronize
  FlowTable offset changes with Rsp modifications.
- Introduced SfcL2OfRendererDataListener, that listens for
  changes to Sfc OpenFlow Renderer data model changes.
- Not UnitTested yet, waiting for patch 29986 to be merged,
  after which SfcL2FlowProgrammerOFimpl UT will be refactored.
- TableEgress feature not implemented yet. I want to refactor
  SfcL2FlowProgrammerOFimpl first, which will make implementing
  the feature much easier.
- Patch Set 2 - rebase

Change-Id: I7c5ed97d68ea0e08beabeae601c4c24d944cf589
Signed-off-by: Brady Johnson <[email protected]>
sfc-model/src/main/yang/sfc-of-renderer.yang [new file with mode: 0644]
sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/SfcL2FlowProgrammerInterface.java
sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/SfcL2Renderer.java
sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/SfcL2RspDataListener.java
sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/SfcL2RspProcessor.java
sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/SfcSynchronizer.java [new file with mode: 0644]
sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/openflow/SfcL2FlowProgrammerOFimpl.java
sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/openflow/SfcL2OfRendererDataListener.java [new file with mode: 0644]
sfcofl2/src/test/java/org/opendaylight/sfc/l2renderer/SfcL2RspProcessorTest.java

diff --git a/sfc-model/src/main/yang/sfc-of-renderer.yang b/sfc-model/src/main/yang/sfc-of-renderer.yang
new file mode 100644 (file)
index 0000000..e3d5b6d
--- /dev/null
@@ -0,0 +1,36 @@
+module sfc-of-renderer {
+  yang-version 1;
+
+  namespace "urn:ericsson:params:xml:ns:yang:sfc-of-renderer";
+  prefix "sfcofrenderer";
+
+  organization "Ericsson, Inc.";
+  contact "Brady Johnson <[email protected]>";
+
+  revision 2015-11-23 {
+    description
+      "This module defines the SFC OpenFlow renderer configuration model.";
+  }
+
+  container sfc-of-renderer-config {
+    description
+      "Configuration values for the SFC OpenFlow Renderer that are set
+       in config subsystem";
+
+    leaf sfc-of-table-offset {
+      description
+        "Used to offset pipeline to start at offset+1. Table0 is required.
+        This allows the end-user to configure where the SFC pipeline starts.";
+      type uint8;
+    }
+
+    leaf sfc-of-app-egress-table-offset {
+      description
+        "When SFC co-exists with other applications on the same OpenFlow
+         switch, and the packets should be handed-off to the application
+         instead of egressing them, this is the OpenFlow table to send the
+         packets to for further processing.";
+      type uint8;
+    }
+  }
+}
index 539e396865bc156df378a3b1f129e20f3f603ff5..c27503ac037157c703996d634be7ec303e0ecdb6 100644 (file)
@@ -24,10 +24,18 @@ public interface SfcL2FlowProgrammerInterface {
 
     public void shutdown() throws ExecutionException, InterruptedException;
 
+    // These table methods are used for app-coexistence
+
     public short getTableBase();
 
     public void setTableBase(short tableBase);
 
+    public short getMaxTableOffset();
+
+    public short getTableEgress();
+
+    public void setTableEgress(short tableEgress);
+
     // Set the RSP Id that subsequent flow creations belong to
     public void setFlowRspId(Long rspId);
 
index 7e7ad744e11c8a0444e385b88d976f6d88eb32ce..1339c84ccbd5dbd7be6d7d709c704155392e7ff0 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.sfc.l2renderer;
 
 import org.opendaylight.sfc.l2renderer.openflow.SfcL2FlowProgrammerOFimpl;
+import org.opendaylight.sfc.l2renderer.openflow.SfcL2OfRendererDataListener;
 import org.opendaylight.sfc.l2renderer.openflow.SfcIpv4PacketInHandler;
 import org.opendaylight.sfc.l2renderer.sfg.SfcL2SfgDataListener;
 
@@ -30,19 +31,24 @@ import org.slf4j.LoggerFactory;
 public class SfcL2Renderer implements AutoCloseable {
 
     private static final Logger LOG = LoggerFactory.getLogger(SfcL2Renderer.class);
-    private SfcL2FlowProgrammerInterface sfcL2FlowProgrammer = null;
-    private Registration pktInRegistration = null;
+    private SfcL2FlowProgrammerInterface sfcL2FlowProgrammer;
+    private Registration pktInRegistration;
+    private SfcSynchronizer sfcSynchronizer;
+
     SfcL2RspDataListener openflowRspDataListener = null;
     SfcL2SfgDataListener sfcL2SfgDataListener = null;
     SfcIpv4PacketInHandler packetInHandler = null;
+    SfcL2OfRendererDataListener sfcOfRendererListener = null;
 
     public SfcL2Renderer(DataBroker dataBroker, NotificationProviderService notificationService) {
         LOG.info("SfcL2Renderer starting the SfcL2Renderer plugin...");
 
+        this.sfcSynchronizer = new SfcSynchronizer();
         this.sfcL2FlowProgrammer = new SfcL2FlowProgrammerOFimpl();
         SfcL2BaseProviderUtils sfcL2ProviderUtils = new SfcL2ProviderUtils();
-        this.openflowRspDataListener = new SfcL2RspDataListener(dataBroker, sfcL2FlowProgrammer, sfcL2ProviderUtils);
+        this.openflowRspDataListener = new SfcL2RspDataListener(dataBroker, sfcL2FlowProgrammer, sfcL2ProviderUtils, sfcSynchronizer);
         this.sfcL2SfgDataListener = new SfcL2SfgDataListener(dataBroker, sfcL2FlowProgrammer, sfcL2ProviderUtils);
+        this.sfcOfRendererListener = new SfcL2OfRendererDataListener(dataBroker, sfcL2FlowProgrammer, sfcSynchronizer);
 
         this.packetInHandler = new SfcIpv4PacketInHandler((SfcL2FlowProgrammerOFimpl) sfcL2FlowProgrammer);
         this.pktInRegistration = notificationService.registerNotificationListener(packetInHandler);
index d1faf6e0d5f0d7fbd0e0c5beb9b6dbfea9e64c32..5574f913daa04b44f46835c98660e69bf24cdfee 100644 (file)
@@ -37,11 +37,12 @@ public class SfcL2RspDataListener extends SfcL2AbstractDataListener {
     public SfcL2RspDataListener(
             DataBroker dataBroker,
             SfcL2FlowProgrammerInterface sfcL2FlowProgrammer,
-            SfcL2BaseProviderUtils sfcL2ProviderUtils) {
+            SfcL2BaseProviderUtils sfcL2ProviderUtils,
+            SfcSynchronizer sfcSynchronizer) {
         setDataBroker(dataBroker);
         setIID(OpendaylightSfc.RSP_ENTRY_IID);
         registerAsDataChangeListener(LogicalDatastoreType.OPERATIONAL);
-        this.sfcL2RspProcessor = new SfcL2RspProcessor(sfcL2FlowProgrammer, sfcL2ProviderUtils);
+        this.sfcL2RspProcessor = new SfcL2RspProcessor(sfcL2FlowProgrammer, sfcL2ProviderUtils, sfcSynchronizer);
     }
 
     @Override
index 81a5d08d32edf6b36b989b29c1aa1a15cac8900d..162a117e6c73961f0f023444fe02adf5d70cd1ec 100644 (file)
@@ -51,6 +51,7 @@ public class SfcL2RspProcessor {
     private static final Logger LOG = LoggerFactory.getLogger(SfcL2RspProcessor.class);
     private SfcL2FlowProgrammerInterface sfcL2FlowProgrammer;
     private SfcL2BaseProviderUtils sfcL2ProviderUtils;
+    private SfcSynchronizer sfcSynchronizer;
     private Map<SffName, Boolean> sffInitialized;
     private int lastMplsLabel;
     private int lastVlanId;
@@ -68,10 +69,13 @@ public class SfcL2RspProcessor {
     private static final String MPLS = "mpls";
     private static final Long SFC_FLOWS = new Long(0xdeadbeef);
 
-    public SfcL2RspProcessor(SfcL2FlowProgrammerInterface sfcL2FlowProgrammer,
-            SfcL2BaseProviderUtils sfcL2ProviderUtils) {
+    public SfcL2RspProcessor(
+            SfcL2FlowProgrammerInterface sfcL2FlowProgrammer,
+            SfcL2BaseProviderUtils sfcL2ProviderUtils,
+            SfcSynchronizer sfcSynchronizer) {
         this.sfcL2FlowProgrammer = sfcL2FlowProgrammer;
         this.sfcL2ProviderUtils = sfcL2ProviderUtils;
+        this.sfcSynchronizer = sfcSynchronizer;
         this.sffInitialized = new HashMap<SffName, Boolean>();
         this.lastMplsLabel = 0;
         this.lastVlanId = 0;
@@ -79,13 +83,16 @@ public class SfcL2RspProcessor {
 
     // if this method takes too long, consider launching it in a thread
     public void processRenderedServicePath(RenderedServicePath rsp) {
-        sfcL2ProviderUtils.addRsp(rsp.getPathId());
-
         // Setting to INGRESS for the first graph entry, which is the RSP Ingress
         SffName prevSffName = new SffName(SffGraph.INGRESS);
         SffGraph sffGraph = new SffGraph();
 
         try {
+            // This call blocks until the lock is obtained
+            sfcSynchronizer.lock();
+
+            sfcL2ProviderUtils.addRsp(rsp.getPathId());
+
             //
             // Populate the SFF Connection Graph
             //
@@ -150,9 +157,10 @@ public class SfcL2RspProcessor {
 
         } catch (RuntimeException e) {
             LOG.error("RuntimeException in processRenderedServicePath: ", e.getMessage(), e);
+        } finally {
+            sfcSynchronizer.unlock();
+            sfcL2ProviderUtils.removeRsp(rsp.getPathId());
         }
-
-        sfcL2ProviderUtils.removeRsp(rsp.getPathId());
     }
 
     public void deleteRenderedServicePath(RenderedServicePath rsp) {
diff --git a/sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/SfcSynchronizer.java b/sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/SfcSynchronizer.java
new file mode 100644 (file)
index 0000000..7008c38
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, 2015 Ericsson 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.sfc.l2renderer;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * A simple synchronization class used to synchronize different events in SFC.
+ * Encapsulates the usage of a ReentrantLock.
+ *
+ * @author ebrjohn
+ *
+ */
+public class SfcSynchronizer {
+    private ReentrantLock lock;
+
+    public SfcSynchronizer() {
+        lock = new ReentrantLock();
+    }
+
+    /**
+     * To be called by threads that need to lock.
+     * This is a blocking call.
+     */
+    public void lock() {
+        lock.lock();
+    }
+
+    /**
+     * Query if the lock is available
+     *
+     * @return true if no threads are waiting on the lock, false otherwise
+     */
+    public boolean isLocked() {
+        return lock.isLocked();
+    }
+
+    /**
+     * Release the lock
+     */
+    public void unlock() {
+        lock.unlock();
+    }
+}
index 9d6a70e76bc077ca098e10947fca5f4f29a129be..5c501f29f9e087ae0fc35fd4af956305a20a974d 100644 (file)
@@ -98,6 +98,7 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
     private static final short TABLE_INDEX_PATH_MAPPER_ACL = 3;
     private static final short TABLE_INDEX_NEXT_HOP = 4;
     private static final short TABLE_INDEX_TRANSPORT_EGRESS = 10;
+    private static final short TABLE_INDEX_MAX_OFFSET = TABLE_INDEX_TRANSPORT_EGRESS;
 
     private static final int FLOW_PRIORITY_TRANSPORT_INGRESS = 250;
     private static final int FLOW_PRIORITY_ARP_TRANSPORT_INGRESS = 300;
@@ -130,12 +131,16 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
     // Instance variables
     private short tableBase;
+    // TODO tableEgress is not implemented yet
+    // Used for app-coexistence
+    private short tableEgress;
     private ExecutorService threadPoolExecutorService;
     private Map<Long, List<FlowDetails>> rspNameToFlowsMap;
     private Long flowRspId;
 
     public SfcL2FlowProgrammerOFimpl() {
         this.tableBase = (short) 0;
+        this.tableEgress = (short) 0;
         this.rspNameToFlowsMap = new HashMap<Long, List<FlowDetails>>();
         this.flowRspId = new Long(0);
 
@@ -171,6 +176,21 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
         this.tableBase = tableBase;
     }
 
+    @Override
+    public short getTableEgress() {
+        return tableEgress;
+    }
+
+    @Override
+    public void setTableEgress(short tableEgress) {
+        this.tableEgress = tableEgress;
+    }
+
+    @Override
+    public short getMaxTableOffset() {
+        return TABLE_INDEX_MAX_OFFSET;
+    }
+
     @Override
     public void setFlowRspId(Long rspId) {
         this.flowRspId = rspId;
@@ -219,8 +239,8 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
         ConfigureTableMatchAnyThread configureTableMatchAnyThread =
                 new ConfigureTableMatchAnyThread(
                         sffNodeName,
-                        getTableId(TABLE_INDEX_CLASSIFIER_TABLE),
-                        getTableId(TABLE_INDEX_INGRESS_TRANSPORT_TABLE),
+                        TABLE_INDEX_CLASSIFIER_TABLE,
+                        TABLE_INDEX_INGRESS_TRANSPORT_TABLE,
                         doDrop);
         try {
             threadPoolExecutorService.execute(configureTableMatchAnyThread);
@@ -239,8 +259,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
     //
     @Override
     public void configureTransportIngressTableMatchAny(final String sffNodeName, final boolean doDrop) {
-        ConfigureTableMatchAnyThread configureTableMatchAnyThread = new ConfigureTableMatchAnyThread(sffNodeName,
-                getTableId(TABLE_INDEX_INGRESS_TRANSPORT_TABLE), getTableId(TABLE_INDEX_PATH_MAPPER), doDrop);
+        ConfigureTableMatchAnyThread configureTableMatchAnyThread =
+                   new ConfigureTableMatchAnyThread(
+                        sffNodeName,
+                        TABLE_INDEX_INGRESS_TRANSPORT_TABLE,
+                        TABLE_INDEX_PATH_MAPPER,
+                        doDrop);
         try {
             threadPoolExecutorService.execute(configureTableMatchAnyThread);
         } catch (Exception ex) {
@@ -250,8 +274,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
     @Override
     public void configurePathMapperTableMatchAny(final String sffNodeName, final boolean doDrop) {
-        ConfigureTableMatchAnyThread configureTableMatchAnyThread = new ConfigureTableMatchAnyThread(sffNodeName,
-                getTableId(TABLE_INDEX_PATH_MAPPER), getTableId(TABLE_INDEX_PATH_MAPPER_ACL), doDrop);
+        ConfigureTableMatchAnyThread configureTableMatchAnyThread =
+                new ConfigureTableMatchAnyThread(
+                        sffNodeName,
+                        TABLE_INDEX_PATH_MAPPER,
+                        TABLE_INDEX_PATH_MAPPER_ACL,
+                        doDrop);
         try {
             threadPoolExecutorService.execute(configureTableMatchAnyThread);
         } catch (Exception ex) {
@@ -261,8 +289,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
     @Override
     public void configurePathMapperAclTableMatchAny(final String sffNodeName, final boolean doDrop) {
-        ConfigureTableMatchAnyThread configureTableMatchAnyThread = new ConfigureTableMatchAnyThread(sffNodeName,
-                getTableId(TABLE_INDEX_PATH_MAPPER_ACL), getTableId(TABLE_INDEX_NEXT_HOP), doDrop);
+        ConfigureTableMatchAnyThread configureTableMatchAnyThread =
+                new ConfigureTableMatchAnyThread(
+                        sffNodeName,
+                        TABLE_INDEX_PATH_MAPPER_ACL,
+                        TABLE_INDEX_NEXT_HOP,
+                        doDrop);
         try {
             threadPoolExecutorService.execute(configureTableMatchAnyThread);
         } catch (Exception ex) {
@@ -272,8 +304,11 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
     @Override
     public void configureNextHopTableMatchAny(final String sffNodeName, final boolean doDrop) {
-        ConfigureTableMatchAnyThread configureTableMatchAnyThread = new ConfigureTableMatchAnyThread(sffNodeName,
-                getTableId(TABLE_INDEX_NEXT_HOP), getTableId(TABLE_INDEX_TRANSPORT_EGRESS), doDrop);
+        ConfigureTableMatchAnyThread configureTableMatchAnyThread =
+                new ConfigureTableMatchAnyThread(sffNodeName,
+                        TABLE_INDEX_NEXT_HOP,
+                        TABLE_INDEX_TRANSPORT_EGRESS,
+                        doDrop);
         try {
             threadPoolExecutorService.execute(configureTableMatchAnyThread);
         } catch (Exception ex) {
@@ -284,8 +319,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
     @Override
     public void configureTransportEgressTableMatchAny(final String sffNodeName, final boolean doDrop) {
         // This is the last table, cant set next table AND doDrop should be false
-        ConfigureTableMatchAnyThread configureTableMatchAnyThread = new ConfigureTableMatchAnyThread(sffNodeName,
-                getTableId(TABLE_INDEX_TRANSPORT_EGRESS), (short) -1, doDrop);
+        ConfigureTableMatchAnyThread configureTableMatchAnyThread =
+                new ConfigureTableMatchAnyThread(
+                        sffNodeName,
+                        TABLE_INDEX_TRANSPORT_EGRESS,
+                        (short) -1,
+                        doDrop);
         try {
             threadPoolExecutorService.execute(configureTableMatchAnyThread);
         } catch (Exception ex) {
@@ -293,19 +332,26 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
         }
     }
 
+    /**
+     * Configure MatchAny rules for the different tables used. When passing
+     * a tableIndex, it will be converted to the correct table internally.
+     *
+     * @author ebrjohn
+     *
+     */
     private class ConfigureTableMatchAnyThread implements Runnable {
 
         private String sffNodeName;
         private boolean doDrop;
-        private short tableId;
-        private short nextTableId;
+        private short tableIdIndex;
+        private short nextTableIdIndex;
         private Long rspId;
 
-        public ConfigureTableMatchAnyThread(final String sffNodeName, final short tableId, final short nextTableId,
+        public ConfigureTableMatchAnyThread(final String sffNodeName, final short tableIdIndex, final short nextTableIdIndex,
                 final boolean doDrop) {
             this.sffNodeName = sffNodeName;
-            this.tableId = tableId;
-            this.nextTableId = nextTableId;
+            this.tableIdIndex = tableIdIndex;
+            this.nextTableIdIndex = nextTableIdIndex;
             this.doDrop = doDrop;
             this.rspId = flowRspId;
         }
@@ -314,8 +360,8 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
         public void run() {
             try {
                 LOG.debug(
-                        "SfcProviderSffFlowWriter.ConfigureTableMatchAnyThread, sff [{}] tableId [{}] nextTableId [{}] doDrop {}",
-                        this.sffNodeName, this.tableId, this.nextTableId, this.doDrop);
+                        "SfcProviderSffFlowWriter.ConfigureTableMatchAnyThread, sff [{}] tableIndex [{}] nextTableIndex [{}] doDrop {}",
+                        this.sffNodeName, this.tableIdIndex, this.nextTableIdIndex, this.doDrop);
 
                 //
                 // Create the actions
@@ -341,7 +387,9 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
                 } else {
                     //
                     // Action, goto Ingress table
-                    GoToTableBuilder gotoIngress = SfcOpenflowUtils.createActionGotoTable(this.nextTableId);
+                    GoToTableBuilder gotoIngress =
+                            SfcOpenflowUtils.createActionGotoTable(
+                                    getTableId(this.nextTableIdIndex));
 
                     InstructionBuilder ib = new InstructionBuilder();
                     ib.setKey(new InstructionKey(order));
@@ -361,8 +409,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
                 //
                 // Create and configure the FlowBuilder
-                FlowBuilder transportIngressFlow = SfcOpenflowUtils.createFlowBuilder(this.tableId,
-                        FLOW_PRIORITY_MATCH_ANY, "MatchAny", match, isb);
+                FlowBuilder transportIngressFlow = SfcOpenflowUtils.createFlowBuilder(
+                        getTableId(this.tableIdIndex),
+                        FLOW_PRIORITY_MATCH_ANY,
+                        "MatchAny",
+                        match,
+                        isb);
 
                 writeFlowToConfig(rspId, sffNodeName, transportIngressFlow);
 
@@ -408,7 +460,7 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
     public void configureVxlanGpeTransportIngressFlow(final String sffNodeName) {
         ConfigureTransportIngressThread configureIngressTransportThread =
                 new ConfigureTransportIngressThread(sffNodeName, SfcOpenflowUtils.ETHERTYPE_IPV4);
-        configureIngressTransportThread.setNextTable(TABLE_INDEX_NEXT_HOP);
+        configureIngressTransportThread.setNextTableIndex(TABLE_INDEX_NEXT_HOP);
         try {
             threadPoolExecutorService.execute(configureIngressTransportThread);
         } catch (Exception ex) {
@@ -433,14 +485,14 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
         String sffNodeName;
         long etherType;
         short ipProtocol;
-        short nextTable;
+        short nextTableIndex;
         Long rspId;
 
         public ConfigureTransportIngressThread(final String sffNodeName, long etherType) {
             this.sffNodeName = sffNodeName;
             this.etherType = etherType;
             this.ipProtocol = (short) -1;
-            this.nextTable = TABLE_INDEX_PATH_MAPPER;
+            this.nextTableIndex = TABLE_INDEX_PATH_MAPPER;
             this.rspId = flowRspId;
         }
 
@@ -448,8 +500,8 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
             this.ipProtocol = ipProtocol;
         }
 
-        public void setNextTable(short nextTable) {
-            this.nextTable = nextTable;
+        public void setNextTableIndex(short nextTableIndex) {
+            this.nextTableIndex = nextTableIndex;
         }
 
         @Override
@@ -479,7 +531,7 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
                 //
                 // Action, goto the nextTable, defaults to Ingress table unless otherwise set
-                GoToTableBuilder gotoIngress = SfcOpenflowUtils.createActionGotoTable(getTableId(this.nextTable));
+                GoToTableBuilder gotoIngress = SfcOpenflowUtils.createActionGotoTable(getTableId(this.nextTableIndex));
 
                 InstructionBuilder ib = new InstructionBuilder();
                 ib.setInstruction(new GoToTableCaseBuilder().setGoToTable(gotoIngress.build()).build());
@@ -492,8 +544,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
                 //
                 // Create and configure the FlowBuilder
                 FlowBuilder transportIngressFlow =
-                        SfcOpenflowUtils.createFlowBuilder(TABLE_INDEX_INGRESS_TRANSPORT_TABLE,
-                                FLOW_PRIORITY_TRANSPORT_INGRESS, "ingress_Transport_Default_Flow", match, isb);
+                        SfcOpenflowUtils.createFlowBuilder(
+                                getTableId(TABLE_INDEX_INGRESS_TRANSPORT_TABLE),
+                                FLOW_PRIORITY_TRANSPORT_INGRESS,
+                                "ingress_Transport_Default_Flow",
+                                match,
+                                isb);
 
                 writeFlowToConfig(rspId, sffNodeName, transportIngressFlow);
 
@@ -565,8 +621,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
                 // Create and configure the FlowBuilder
                 FlowBuilder transportIngressFlow =
-                        SfcOpenflowUtils.createFlowBuilder(TABLE_INDEX_INGRESS_TRANSPORT_TABLE,
-                                FLOW_PRIORITY_ARP_TRANSPORT_INGRESS, "ingress_Transport_Default_Flow", match, isb);
+                        SfcOpenflowUtils.createFlowBuilder(
+                                getTableId(TABLE_INDEX_INGRESS_TRANSPORT_TABLE),
+                                FLOW_PRIORITY_ARP_TRANSPORT_INGRESS,
+                                "ingress_Transport_Default_Flow",
+                                match,
+                                isb);
 
                 writeFlowToConfig(rspId, sffNodeName, transportIngressFlow);
 
@@ -735,8 +795,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
                 //
                 // Create and configure the FlowBuilder
-                FlowBuilder ingressFlow = SfcOpenflowUtils.createFlowBuilder(TABLE_INDEX_PATH_MAPPER, flowPriority,
-                        "nextHop", match, isb);
+                FlowBuilder ingressFlow = SfcOpenflowUtils.createFlowBuilder(
+                        getTableId(TABLE_INDEX_PATH_MAPPER),
+                        flowPriority,
+                        "nextHop",
+                        match,
+                        isb);
 
                 writeFlowToConfig(rspId, sffNodeName, ingressFlow);
 
@@ -819,8 +883,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
                 //
                 // Create and configure the FlowBuilder
-                FlowBuilder ingressFlow = SfcOpenflowUtils.createFlowBuilder(TABLE_INDEX_PATH_MAPPER_ACL,
-                        FLOW_PRIORITY_PATH_MAPPER_ACL, "nextHop", match, isb);
+                FlowBuilder ingressFlow = SfcOpenflowUtils.createFlowBuilder(
+                        getTableId(TABLE_INDEX_PATH_MAPPER_ACL),
+                        FLOW_PRIORITY_PATH_MAPPER_ACL,
+                        "nextHop",
+                        match,
+                        isb);
                 // Set an idle timeout on this flow
                 ingressFlow.setIdleTimeout(PKTIN_IDLE_TIMEOUT);
 
@@ -957,7 +1025,8 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
                 ApplyActionsBuilder aab = new ApplyActionsBuilder();
                 aab.setAction(actionList);
 
-                GoToTableBuilder gotoTb = SfcOpenflowUtils.createActionGotoTable(TABLE_INDEX_TRANSPORT_EGRESS);
+                GoToTableBuilder gotoTb = SfcOpenflowUtils.createActionGotoTable(
+                        getTableId(TABLE_INDEX_TRANSPORT_EGRESS));
 
                 InstructionBuilder gotoTbIb = new InstructionBuilder();
                 gotoTbIb.setInstruction(new GoToTableCaseBuilder().setGoToTable(gotoTb.build()).build());
@@ -980,7 +1049,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
                 //
                 // Create and configure the FlowBuilder
                 FlowBuilder nextHopFlow =
-                        SfcOpenflowUtils.createFlowBuilder(TABLE_INDEX_NEXT_HOP, flowPriority, "nextHop", match, isb);
+                        SfcOpenflowUtils.createFlowBuilder(
+                                getTableId(TABLE_INDEX_NEXT_HOP),
+                                flowPriority,
+                                "nextHop",
+                                match,
+                                isb);
 
                 writeFlowToConfig(rspId, sffNodeName, nextHopFlow);
 
@@ -1203,8 +1277,13 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
                 // Put our Instruction in a list of Instructions
                 InstructionsBuilder isb = SfcOpenflowUtils.createInstructionsBuilder(ib);
 
-                FlowBuilder egressTransportFlow = SfcOpenflowUtils.createFlowBuilder(TABLE_INDEX_TRANSPORT_EGRESS,
-                        flowPriority, TRANSPORT_EGRESS_COOKIE, "default_egress_flow", match, isb);
+                FlowBuilder egressTransportFlow = SfcOpenflowUtils.createFlowBuilder(
+                        getTableId(TABLE_INDEX_TRANSPORT_EGRESS),
+                        flowPriority,
+                        TRANSPORT_EGRESS_COOKIE,
+                        "default_egress_flow",
+                        match,
+                        isb);
 
                 //
                 // Now write the Flow Entry
@@ -1286,8 +1365,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
 
                 //
                 // Create and configure the FlowBuilder
-                FlowBuilder transportIngressFlow = SfcOpenflowUtils.createFlowBuilder(TABLE_INDEX_TRANSPORT_EGRESS,
-                        FLOW_PRIORITY_TRANSPORT_EGRESS + 10, "MatchAny", match, isb);
+                FlowBuilder transportIngressFlow = SfcOpenflowUtils.createFlowBuilder(
+                        getTableId(TABLE_INDEX_TRANSPORT_EGRESS),
+                        FLOW_PRIORITY_TRANSPORT_EGRESS + 10,
+                        "MatchAny",
+                        match,
+                        isb);
 
                 writeFlowToConfig(rspId, sffNodeName, transportIngressFlow);
 
@@ -1595,7 +1678,12 @@ public class SfcL2FlowProgrammerOFimpl implements SfcL2FlowProgrammerInterface {
                 //
                 // Create and configure the FlowBuilder
                 FlowBuilder nextHopFlow =
-                        SfcOpenflowUtils.createFlowBuilder(TABLE_INDEX_NEXT_HOP, flowPriority, "nextHop", match, isb);
+                        SfcOpenflowUtils.createFlowBuilder(
+                                getTableId(TABLE_INDEX_NEXT_HOP),
+                                flowPriority,
+                                "nextHop",
+                                match,
+                                isb);
                 LOG.debug("writing group next hop flow: \n{}", nextHopFlow);
                 writeFlowToConfig(rspId, sffNodeName, nextHopFlow);
 
diff --git a/sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/openflow/SfcL2OfRendererDataListener.java b/sfcofl2/src/main/java/org/opendaylight/sfc/l2renderer/openflow/SfcL2OfRendererDataListener.java
new file mode 100644 (file)
index 0000000..25bc0e8
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2014, 2015 Ericsson 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.sfc.l2renderer.openflow;
+
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.of.renderer.rev151123.SfcOfRendererConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import org.opendaylight.sfc.l2renderer.SfcL2AbstractDataListener;
+import org.opendaylight.sfc.l2renderer.SfcL2FlowProgrammerInterface;
+import org.opendaylight.sfc.l2renderer.SfcSynchronizer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * DataListener to listen for SFC OpenFlow Renderer data store changes.
+ *
+ * @author ebrjohn
+ *
+ */
+public class SfcL2OfRendererDataListener extends SfcL2AbstractDataListener {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SfcL2OfRendererDataListener.class);
+    private SfcL2FlowProgrammerInterface sfcL2FlowProgrammer;
+    private SfcSynchronizer sfcSynchronizer;
+    private ExecutorService threadExecutor;
+
+    public SfcL2OfRendererDataListener(
+            DataBroker dataBroker,
+            SfcL2FlowProgrammerInterface sfcL2FlowProgrammer,
+            SfcSynchronizer sfcSynchronizer) {
+        setDataBroker(dataBroker);
+        setIID(InstanceIdentifier.builder(SfcOfRendererConfig.class).build());
+        registerAsDataChangeListener(LogicalDatastoreType.CONFIGURATION);
+        this.sfcL2FlowProgrammer = sfcL2FlowProgrammer;
+        this.sfcSynchronizer = sfcSynchronizer;
+        threadExecutor = Executors.newSingleThreadExecutor();
+    }
+
+    @Override
+    public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        // SFC OF Renderer config create
+        for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getCreatedData().entrySet()) {
+            if (entry.getValue() instanceof SfcOfRendererConfig) {
+                LOG.info("SfcL2OfRendererDataListener.onDataChanged create SFC OF Renderer config {}",
+                        ((SfcOfRendererConfig) entry.getValue()));
+                processConfig((SfcOfRendererConfig) entry.getValue());
+            }
+        }
+
+        // SFC OF Renderer config update
+        for (Entry<InstanceIdentifier<?>, DataObject> entry : change.getUpdatedData().entrySet()) {
+            if (entry.getValue() instanceof SfcOfRendererConfig) {
+                LOG.info("SfcL2OfRendererDataListener.onDataChanged update SFC OF Renderer config {}",
+                        ((SfcOfRendererConfig) entry.getValue()));
+                processConfig((SfcOfRendererConfig) entry.getValue());
+            }
+        }
+
+        // Not interested in deleted data
+    }
+
+    /**
+     * Process an OpenFlow Renderer configuration change. Only creates and
+     * updates are handled
+     * @param config the configuration details
+     */
+    private void processConfig(SfcOfRendererConfig config) {
+        if(verifyMaxTableId(config.getSfcOfTableOffset(), this.sfcL2FlowProgrammer.getMaxTableOffset()) == null) {
+            return;
+        }
+
+        if(verifyMaxTableId(config.getSfcOfAppEgressTableOffset(), (short) 0) == null) {
+            return;
+        }
+
+        UpdateOpenFlowTableOffsets updateThread =
+                new UpdateOpenFlowTableOffsets(
+                        config.getSfcOfTableOffset(),
+                        config.getSfcOfAppEgressTableOffset());
+
+        try {
+            threadExecutor.submit(updateThread);
+        } catch(Exception e) {
+            LOG.error("Error executing UpdateOpenFlowTableOffsets thread [{}]", e.toString());
+        }
+    }
+
+    /**
+     * Verify that the given tableOffset and optional maxTable is in range
+     *
+     * @param tableOffset the tableOffset to verify
+     * @param maxTable optionally the number of tables beyond tableOffset to be used
+     * @return a valid TableId or null if invalid
+     */
+    public TableId verifyMaxTableId(short tableOffset, short maxTable) {
+        try {
+            return new TableId((short) (tableOffset + maxTable));
+        } catch (IllegalArgumentException e) {
+            LOG.error("SfcL2OfRendererDataListener::verifyMaxTableId invalid table offset [{}] maxTable [{}]",
+                    tableOffset, maxTable);
+            return null;
+        }
+    }
+
+    /**
+     * A thread to update the OpenFlow table offsets. A thread is needed, since
+     * we cant just update the table offsets while an RSP is being processed,
+     * so we need to wait until RSP processing is completed.
+     *
+     * @author ebrjohn
+     *
+     */
+    private class UpdateOpenFlowTableOffsets implements Runnable {
+
+        private short sfcOffsetTable;
+        private short sfcAppEgressTable;
+
+        public UpdateOpenFlowTableOffsets(short sfcOffsetTable, short sfcAppEgressTable) {
+            this.sfcOffsetTable = sfcOffsetTable;
+            this.sfcAppEgressTable = sfcAppEgressTable;
+        }
+
+        @Override
+        public void run() {
+            try {
+                sfcSynchronizer.lock();
+                sfcL2FlowProgrammer.setTableBase(this.sfcOffsetTable);
+                sfcL2FlowProgrammer.setTableEgress(this.sfcAppEgressTable);
+
+                LOG.info("UpdateOpenFlowTableOffsets complete tableOffset [{}] egressTable [{}]",
+                        this.sfcOffsetTable, this.sfcAppEgressTable);
+            } finally {
+                sfcSynchronizer.unlock();
+            }
+        }
+    }
+}
index b7f6d2b91e56397901f061ca6e017a66e837ba41..5aaf65f08f6946772e89812677faab445076ae68 100644 (file)
@@ -63,7 +63,10 @@ public class SfcL2RspProcessorTest {
 
         this.flowProgrammerTestMoc = mock(SfcL2FlowProgrammerOFimpl.class);
         this.sfcUtilsTestMock = new SfcL2ProviderUtilsTestMock();
-        this.sfcL2RspProcessor = new SfcL2RspProcessor(this.flowProgrammerTestMoc, this.sfcUtilsTestMock);
+        this.sfcL2RspProcessor = new SfcL2RspProcessor(
+                this.flowProgrammerTestMoc,
+                this.sfcUtilsTestMock,
+                new SfcSynchronizer());
         this.rspBuilder = new RspBuilder(this.sfcUtilsTestMock);
 
         this.sfTypes = new ArrayList<SftType>();