Handling multiple layers of spines.

Also in this commit:
     - Triggering swap group creation and accounting for it in DestinationSet
     - Fixes in ofdpa2 and ofdpa3 pipeline to allow SR Continue operation
     - Renaming mplsSet in DestinationSet to notBos
     - Removing unused RandomDestinationSet
     - Bug fix in ofdpa driver for swap group chain creation
     - Bug fix in ofdpa driver for verify group operation
     - Better internal bookeeping of device ports and associated neighbors

Change-Id: I2b8f1c4c0b305ef847d57ca7a5320943e06d190d
diff --git a/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index c152b99..bd57248 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -352,32 +352,25 @@
         DestinationSet ds;
         TrafficTreatment treatment;
 
-        // If the next hop is the same as the final destination, then MPLS label
-        // is not set.
-        /*if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
-            tbuilder.immediate().decNwTtl();
-            ds = new DestinationSet(false, destSw);
-            treatment = tbuilder.build();
-        } else {
-            ds = new DestinationSet(false, segmentId, destSw);
-            treatment = null;
-        }*/
         if (destSw2 == null) {
             // single dst - create destination set based on next-hop
+            // If the next hop is the same as the final destination, then MPLS
+            // label is not set.
             Set<DeviceId> nhd1 = nextHops.get(destSw1);
             if (nhd1.size() == 1 && nhd1.iterator().next().equals(destSw1)) {
                 tbuilder.immediate().decNwTtl();
-                ds = new DestinationSet(false, destSw1);
+                ds = new DestinationSet(false, false, destSw1);
                 treatment = tbuilder.build();
             } else {
-                ds = new DestinationSet(false, segmentId1, destSw1);
+                ds = new DestinationSet(false, false, segmentId1, destSw1);
                 treatment = null;
             }
         } else {
             // dst pair - IP rules for dst-pairs are always from other edge nodes
             // the destination set needs to have both destinations, even if there
             // are no next hops to one of them
-            ds = new DestinationSet(false, segmentId1, destSw1, segmentId2, destSw2);
+            ds = new DestinationSet(false, false, segmentId1, destSw1,
+                                    segmentId2, destSw2);
             treatment = null;
         }
 
@@ -394,7 +387,7 @@
         }
 
         int nextId = grpHandler.getNextObjectiveId(ds, nextHops,
-                                                   metabuilder.build(), true);
+                                                   metabuilder.build(), false);
         if (nextId <= 0) {
             log.warn("No next objective in {} for ds: {}", targetSw, ds);
             return false;
@@ -461,13 +454,75 @@
     }
 
     /**
-     * Deals with !MPLS Bos use case.
+     * Populates MPLS flow rules in the target device to point towards the
+     * destination device.
+     *
+     * @param targetSwId target device ID of the switch to set the rules
+     * @param destSwId destination switch device ID
+     * @param nextHops next hops switch ID list
+     * @param routerIp the router ip of the destination switch
+     * @return true if all rules are set successfully, false otherwise
+     */
+    boolean populateMplsRule(DeviceId targetSwId, DeviceId destSwId,
+                             Set<DeviceId> nextHops, IpAddress routerIp) {
+        int segmentId;
+        try {
+            if (routerIp.isIp4()) {
+                segmentId = config.getIPv4SegmentId(destSwId);
+            } else {
+                segmentId = config.getIPv6SegmentId(destSwId);
+            }
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn(e.getMessage() + " Aborting populateMplsRule.");
+            return false;
+        }
+
+        List<ForwardingObjective> fwdObjs = new ArrayList<>();
+        Collection<ForwardingObjective> fwdObjsMpls;
+        // Generates the transit rules used by the standard "routing".
+        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, segmentId,
+                                 routerIp, true);
+        if (fwdObjsMpls.isEmpty()) {
+            return false;
+        }
+        fwdObjs.addAll(fwdObjsMpls);
+
+        // Generates the transit rules used by the MPLS Pwaas.
+        int pwSrLabel;
+        try {
+            pwSrLabel = config.getPWRoutingLabel(destSwId);
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn(e.getMessage()
+                    + " Aborting populateMplsRule. No label for PseudoWire traffic.");
+            return false;
+        }
+        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, pwSrLabel,
+                                 routerIp, false);
+        if (fwdObjsMpls.isEmpty()) {
+            return false;
+        }
+        fwdObjs.addAll(fwdObjsMpls);
+
+        for (ForwardingObjective fwdObj : fwdObjs) {
+            log.debug("Sending MPLS fwd obj {} for SID {}-> next {} in sw: {}",
+                      fwdObj.id(), segmentId, fwdObj.nextId(), targetSwId);
+            srManager.flowObjectiveService.forward(targetSwId, fwdObj);
+            rulePopulationCounter.incrementAndGet();
+        }
+
+        return true;
+    }
+
+    /**
+     * Differentiates between popping and swapping labels when building an MPLS
+     * forwarding objective.
      *
      * @param targetSwId the target sw
      * @param destSwId the destination sw
      * @param nextHops the set of next hops
-     * @param segmentId the segmentId to match
-     * @param routerIp the router ip
+     * @param segmentId the segmentId to match representing the destination
+     *            switch
+     * @param routerIp the router ip representing the destination switch
      * @return a collection of fwdobjective
      */
     private Collection<ForwardingObjective> handleMpls(
@@ -497,9 +552,6 @@
             log.debug("populateMplsRule: Installing MPLS forwarding objective for "
                     + "label {} in switch {} with pop to next-hops {}",
                     segmentId, targetSwId, nextHops);
-            // Not-bos pop case (php for the current label). If MPLS-ECMP
-            // has been configured, the application we will request the
-            // installation for an MPLS-ECMP group.
             ForwardingObjective.Builder fwdObjNoBosBuilder =
                     getMplsForwardingObjective(targetSwId,
                                                nextHops,
@@ -507,6 +559,7 @@
                                                isMplsBos,
                                                metabuilder.build(),
                                                routerIp,
+                                               segmentId,
                                                destSwId);
             // Error case, we cannot handle, exit.
             if (fwdObjNoBosBuilder == null) {
@@ -515,13 +568,11 @@
             fwdObjBuilders.add(fwdObjNoBosBuilder);
 
         } else {
-            // next hop is not destination, SR CONTINUE case (swap with self)
+            // next hop is not destination, irrespective of the number of next
+            // hops (1 or more) -- SR CONTINUE case (swap with self)
             log.debug("Installing MPLS forwarding objective for "
                     + "label {} in switch {} without pop to next-hops {}",
                     segmentId, targetSwId, nextHops);
-            // Not-bos pop case. If MPLS-ECMP has been configured, the
-            // application we will request the installation for an MPLS-ECMP
-            // group.
             ForwardingObjective.Builder fwdObjNoBosBuilder =
                     getMplsForwardingObjective(targetSwId,
                                                nextHops,
@@ -529,6 +580,7 @@
                                                isMplsBos,
                                                metabuilder.build(),
                                                routerIp,
+                                               segmentId,
                                                destSwId);
             // Error case, we cannot handle, exit.
             if (fwdObjNoBosBuilder == null) {
@@ -541,7 +593,6 @@
         List<ForwardingObjective> fwdObjs = Lists.newArrayList();
         // We add the final property to the fwdObjs.
         for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
-
             ((Builder) ((Builder) fwdObjBuilder
                     .fromApp(srManager.appId)
                     .makePermanent())
@@ -559,71 +610,25 @@
 
             ForwardingObjective fob = fwdObjBuilder.add(context);
             fwdObjs.add(fob);
-
         }
 
         return fwdObjs;
     }
 
     /**
-     * Populates MPLS flow rules in the target device to point towards the
-     * destination device.
+     * Returns a Forwarding Objective builder for the MPLS rule that references
+     * the desired Next Objective. Creates a DestinationSet that allows the
+     * groupHandler to create or find the required next objective.
      *
-     * @param targetSwId target device ID of the switch to set the rules
-     * @param destSwId destination switch device ID
-     * @param nextHops next hops switch ID list
-     * @param routerIp the router ip
-     * @return true if all rules are set successfully, false otherwise
+     * @param targetSw the target sw
+     * @param nextHops the set of next hops
+     * @param phpRequired true if penultimate-hop-popping is required
+     * @param isBos true if matched label is bottom-of-stack
+     * @param meta metadata for creating next objective
+     * @param routerIp the router ip representing the destination switch
+     * @param destSw the destination sw
+     * @return the mpls forwarding objective builder
      */
-    boolean populateMplsRule(DeviceId targetSwId, DeviceId destSwId,
-                                    Set<DeviceId> nextHops,
-                                    IpAddress routerIp) {
-
-        int segmentId;
-        try {
-            if (routerIp.isIp4()) {
-                segmentId = config.getIPv4SegmentId(destSwId);
-            } else {
-                segmentId = config.getIPv6SegmentId(destSwId);
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting populateMplsRule.");
-            return false;
-        }
-
-        List<ForwardingObjective> fwdObjs = new ArrayList<>();
-        Collection<ForwardingObjective> fwdObjsMpls;
-        // Generates the transit rules used by the standard "routing".
-        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, segmentId, routerIp, true);
-        if (fwdObjsMpls.isEmpty()) {
-            return false;
-        }
-        fwdObjs.addAll(fwdObjsMpls);
-
-        // Generates the transit rules used by the MPLS Pwaas.
-        int pwSrLabel;
-        try {
-            pwSrLabel = config.getPWRoutingLabel(destSwId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting populateMplsRule. No label for PseudoWire traffic.");
-            return false;
-        }
-        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, pwSrLabel, routerIp, false);
-        if (fwdObjsMpls.isEmpty()) {
-            return false;
-        }
-        fwdObjs.addAll(fwdObjsMpls);
-
-        for (ForwardingObjective fwdObj : fwdObjs) {
-            log.debug("Sending MPLS fwd obj {} for SID {}-> next {} in sw: {}",
-                      fwdObj.id(), segmentId, fwdObj.nextId(), targetSwId);
-            srManager.flowObjectiveService.forward(targetSwId, fwdObj);
-            rulePopulationCounter.incrementAndGet();
-        }
-
-        return true;
-    }
-
     private ForwardingObjective.Builder getMplsForwardingObjective(
                                              DeviceId targetSw,
                                              Set<DeviceId> nextHops,
@@ -631,13 +636,15 @@
                                              boolean isBos,
                                              TrafficSelector meta,
                                              IpAddress routerIp,
+                                             int segmentId,
                                              DeviceId destSw) {
 
         ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
                 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
 
         TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-
+        DestinationSet ds = null;
+        boolean simple = false;
         if (phpRequired) {
             // php case - pop should always be flow-action
             log.debug("getMplsForwardingObjective: php required");
@@ -649,49 +656,54 @@
                     tbuilder.deferred().popMpls(EthType.EtherType.IPV6.ethType());
                 }
                 tbuilder.decNwTtl();
+                // standard case -> BoS == True; pop results in IP packet and forwarding
+                // is via an ECMP group
+                ds = new DestinationSet(false, false, destSw);
             } else {
                 tbuilder.deferred().popMpls(EthType.EtherType.MPLS_UNICAST.ethType())
                     .decMplsTtl();
+                // double-label case -> BoS == False, pop results in MPLS packet
+                // depending on configuration we can ECMP this packet or choose one output
+                if (srManager.getMplsEcmp()) {
+                    ds = new DestinationSet(true, false, destSw);
+                } else {
+                    ds = new DestinationSet(true, false, destSw);
+                    simple = true;
+                }
             }
         } else {
             // swap with self case - SR CONTINUE
-            log.debug("getMplsForwardingObjective: php not required");
+            log.debug("getMplsForwardingObjective: swap with self");
             tbuilder.deferred().decMplsTtl();
+            // swap results in MPLS packet with same BoS bit regardless of bit value
+            // depending on configuration we can ECMP this packet or choose one output
+            if (srManager.getMplsEcmp()) {
+                ds = new DestinationSet(false, true, segmentId, destSw);
+            } else {
+                ds = new DestinationSet(false, true, segmentId, destSw);
+                simple = true;
+            }
         }
 
         fwdBuilder.withTreatment(tbuilder.build());
-        // if MPLS-ECMP == True we will build a standard NeighborSet.
-        // Otherwise a RandomNeighborSet.
-        DestinationSet ns = DestinationSet.destinationSet(false, false, destSw);
-        if (!isBos && this.srManager.getMplsEcmp()) {
-            ns = DestinationSet.destinationSet(false, true, destSw);
-        } else if (!isBos && !this.srManager.getMplsEcmp()) {
-            ns = DestinationSet.destinationSet(true, true, destSw);
-        }
-
-        log.debug("Trying to get a nextObjId for mpls rule on device:{} to ns:{}",
-                  targetSw, ns);
+        log.debug("Trying to get a nextObjId for mpls rule on device:{} to ds:{}",
+                  targetSw, ds);
         DefaultGroupHandler gh = srManager.getGroupHandler(targetSw);
         if (gh == null) {
             log.warn("getNextObjectiveId query - groupHandler for device {} "
                     + "not found", targetSw);
             return null;
         }
-        // If BoS == True, all forwarding is via L3 ECMP group.
-        // If Bos == False, the forwarding can be via MPLS-ECMP group or through
-        // MPLS-Interface group. This depends on the configuration of the option
-        // MPLS-ECMP.
-        // The metadata informs the driver that the next-Objective will be used
-        // by MPLS flows and if Bos == False the driver will use MPLS groups.
+
         Map<DeviceId, Set<DeviceId>> dstNextHops = new HashMap<>();
         dstNextHops.put(destSw, nextHops);
-        int nextId = gh.getNextObjectiveId(ns, dstNextHops, meta, isBos);
+        int nextId = gh.getNextObjectiveId(ds, dstNextHops, meta, simple);
         if (nextId <= 0) {
-            log.warn("No next objective in {} for ns: {}", targetSw, ns);
+            log.warn("No next objective in {} for ds: {}", targetSw, ds);
             return null;
         } else {
-            log.debug("nextObjId found:{} for mpls rule on device:{} to ns:{}",
-                      nextId, targetSw, ns);
+            log.debug("nextObjId found:{} for mpls rule on device:{} to ds:{}",
+                      nextId, targetSw, ds);
         }
 
         fwdBuilder.nextStep(nextId);