CORD-48 Added to support for OFDPA emulation with CPQD switch, via more table-miss-entries.
Fixed a race condition where device processing starts before config has fully loaded.
GroupHandler in SR app is now created only once, not every time a Device update happens.

Change-Id: I945c47ee9caa2f5740296f49d5d223783271bba4
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 9d60b27..b82752d 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -596,29 +596,35 @@
 
     private void processDeviceAdded(Device device) {
         log.debug("A new device with ID {} was added", device.id());
+        if (deviceConfiguration == null) {
+            log.warn("Device configuration uploading. Device {} will be "
+                    + "processed after config completes.", device.id());
+            return;
+        }
         // Irrespective of whether the local is a MASTER or not for this device,
         // we need to create a SR-group-handler instance. This is because in a
         // multi-instance setup, any instance can initiate forwarding/next-objectives
         // for any switch (even if this instance is a SLAVE or not even connected
         // to the switch). To handle this, a default-group-handler instance is necessary
         // per switch.
-        DefaultGroupHandler groupHandler = DefaultGroupHandler.
-                createGroupHandler(device.id(),
-                                   appId,
-                                   deviceConfiguration,
-                                   linkService,
-                                   flowObjectiveService,
-                                   nsNextObjStore,
-                                   subnetNextObjStore);
-        groupHandlerMap.put(device.id(), groupHandler);
-
-        // Also, in some cases, drivers may need extra
-        // information to process rules (eg. Router IP/MAC); and so, we send
-        // port addressing rules to the driver as well irrespective of whether
-        // this instance is the master or not.
-        defaultRoutingHandler.populatePortAddressingRules(device.id());
-
+        if (groupHandlerMap.get(device.id()) == null) {
+            DefaultGroupHandler groupHandler = DefaultGroupHandler.
+                    createGroupHandler(device.id(),
+                                       appId,
+                                       deviceConfiguration,
+                                       linkService,
+                                       flowObjectiveService,
+                                       nsNextObjStore,
+                                       subnetNextObjStore);
+            groupHandlerMap.put(device.id(), groupHandler);
+            // Also, in some cases, drivers may need extra
+            // information to process rules (eg. Router IP/MAC); and so, we send
+            // port addressing rules to the driver as well irrespective of whether
+            // this instance is the master or not.
+            defaultRoutingHandler.populatePortAddressingRules(device.id());
+        }
         if (mastershipService.isLocalMaster(device.id())) {
+            DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
             groupHandler.createGroupsFromSubnetConfig();
         }
     }
@@ -660,21 +666,23 @@
                 // for any switch (even if this instance is a SLAVE or not even connected
                 // to the switch). To handle this, a default-group-handler instance is necessary
                 // per switch.
-                DefaultGroupHandler groupHandler = DefaultGroupHandler
-                        .createGroupHandler(device.id(), appId,
-                                            deviceConfiguration, linkService,
-                                            flowObjectiveService,
-                                            nsNextObjStore,
-                                            subnetNextObjStore);
-                groupHandlerMap.put(device.id(), groupHandler);
+                if (groupHandlerMap.get(device.id()) == null) {
+                    DefaultGroupHandler groupHandler = DefaultGroupHandler
+                            .createGroupHandler(device.id(), appId,
+                                                deviceConfiguration, linkService,
+                                                flowObjectiveService,
+                                                nsNextObjStore,
+                                                subnetNextObjStore);
+                    groupHandlerMap.put(device.id(), groupHandler);
 
-                // Also, in some cases, drivers may need extra
-                // information to process rules (eg. Router IP/MAC); and so, we send
-                // port addressing rules to the driver as well, irrespective of whether
-                // this instance is the master or not.
-                defaultRoutingHandler.populatePortAddressingRules(device.id());
-
+                    // Also, in some cases, drivers may need extra
+                    // information to process rules (eg. Router IP/MAC); and so, we send
+                    // port addressing rules to the driver as well, irrespective of whether
+                    // this instance is the master or not.
+                    defaultRoutingHandler.populatePortAddressingRules(device.id());
+                }
                 if (mastershipService.isLocalMaster(device.id())) {
+                    DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
                     groupHandler.createGroupsFromSubnetConfig();
                 }
             }
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java b/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java
index a830ed4..8f976da 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA2Pipeline.java
@@ -17,6 +17,13 @@
 
 import static org.slf4j.LoggerFactory.getLogger;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -25,6 +32,8 @@
 import org.onosproject.net.flow.FlowRuleOperationsContext;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.slf4j.Logger;
 
 
@@ -37,16 +46,58 @@
     private final Logger log = getLogger(getClass());
 
     @Override
+    protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
+                                                 VlanIdCriterion vidCriterion,
+                                                 VlanId assignedVlan,
+                                                 ApplicationId applicationId) {
+        List<FlowRule> rules = new ArrayList<FlowRule>();
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        selector.matchVlanId(vidCriterion.vlanId());
+        if (vidCriterion.vlanId() == VlanId.NONE) {
+            // untagged packets are assigned vlans
+            treatment.pushVlan().setVlanId(assignedVlan);
+        }
+        treatment.transition(TMAC_TABLE);
+
+        // ofdpa cannot match on ALL portnumber, so we need to use separate
+        // rules for each port.
+        List<PortNumber> portnums = new ArrayList<PortNumber>();
+        if (portCriterion.port() == PortNumber.ALL) {
+            for (Port port : deviceService.getPorts(deviceId)) {
+                if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
+                    portnums.add(port.number());
+                }
+            }
+        } else {
+            portnums.add(portCriterion.port());
+        }
+        for (PortNumber pnum : portnums) {
+            selector.matchInPort(pnum);
+            FlowRule rule = DefaultFlowRule.builder()
+                    .forDevice(deviceId)
+                    .withSelector(selector.build())
+                    .withTreatment(treatment.build())
+                    .withPriority(DEFAULT_PRIORITY)
+                    .fromApp(applicationId)
+                    .makePermanent()
+                    .forTable(VLAN_TABLE).build();
+            rules.add(rule);
+        }
+        return rules;
+    }
+
+
+    @Override
     protected void initializePipeline() {
         processPortTable();
+        // vlan table processing not required, as default is to drop packets
+        // which can be accomplished without a table-miss-entry.
         processTmacTable();
         processIpTable();
+        processMplsTable();
         processBridgingTable();
         processAclTable();
-        // XXX implement table miss entries and default groups
-        //processVlanTable();
-        //processMPLSTable();
-        //processGroupTable();
     }
 
     @Override
@@ -140,6 +191,49 @@
         }));
     }
 
+    @Override
+    protected void processMplsTable() {
+        //table miss entry
+        FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+        selector = DefaultTrafficSelector.builder();
+        treatment = DefaultTrafficTreatment.builder();
+        treatment.transition(MPLS_TABLE_1);
+        FlowRule rule = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(selector.build())
+                .withTreatment(treatment.build())
+                .withPriority(LOWEST_PRIORITY)
+                .fromApp(driverId)
+                .makePermanent()
+                .forTable(MPLS_TABLE_0).build();
+        ops =  ops.add(rule);
+
+        treatment.transition(ACL_TABLE);
+        rule = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(selector.build())
+                .withTreatment(treatment.build())
+                .withPriority(LOWEST_PRIORITY)
+                .fromApp(driverId)
+                .makePermanent()
+                .forTable(MPLS_TABLE_1).build();
+        ops = ops.add(rule);
+
+        flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                log.info("Initialized MPLS tables");
+            }
+
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                log.info("Failed to initialize MPLS tables");
+            }
+        }));
+    }
+
     private void processBridgingTable() {
         //table miss entry
         FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java b/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
index b1a1256..e63a404 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/OFDPA2Pipeline.java
@@ -122,7 +122,7 @@
     protected static final long OFPP_MAX = 0xffffff00L;
 
     private static final int HIGHEST_PRIORITY = 0xffff;
-    private static final int DEFAULT_PRIORITY = 0x8000;
+    protected static final int DEFAULT_PRIORITY = 0x8000;
     protected static final int LOWEST_PRIORITY = 0x0;
 
     /*
@@ -458,8 +458,9 @@
         if (vidCriterion.vlanId() == VlanId.NONE) {
             // untagged packets are assigned vlans
             treatment.pushVlan().setVlanId(assignedVlan);
-            // XXX ofdpa may require an additional vlan match on the assigned vlan
-            // and it may not require the push.
+            // XXX ofdpa will require an additional vlan match on the assigned vlan
+            // and it may not require the push. This is not in compliance with OF
+            // standard. Waiting on what the exact flows are going to look like.
         }
         treatment.transition(TMAC_TABLE);