Refactored DestinationSet and nextobj for pws.

- Removed the booleans in destination test set and refactored
  it to use a proper enumeration.
- Create a special next objective for pseudowire transport that
  does not pop the tag.
- Install special filtering rules for receiving trafffic for
  pseudowires in all devices.

Change-Id: I665a00d9a766e026d35799fe2f12d56419cb55cd
diff --git a/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 90da4b4..77103f1 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -80,6 +80,8 @@
 import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
 import static org.onosproject.segmentrouting.SegmentRoutingService.DEFAULT_PRIORITY;
 
+import static org.onosproject.segmentrouting.SegmentRoutingManager.PSEUDOWIRE_VLAN;
+
 /**
  * Populator of segment routing flow rules.
  */
@@ -539,6 +541,7 @@
         TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
         DestinationSet ds;
         TrafficTreatment treatment;
+        DestinationSet.DestinationSetType dsType;
 
         if (destSw2 == null) {
             // single dst - create destination set based on next-hop
@@ -547,18 +550,17 @@
             Set<DeviceId> nhd1 = nextHops.get(destSw1);
             if (nhd1.size() == 1 && nhd1.iterator().next().equals(destSw1)) {
                 tbuilder.immediate().decNwTtl();
-                ds = new DestinationSet(false, false, destSw1);
+                ds = DestinationSet.createTypePushNone(destSw1);
                 treatment = tbuilder.build();
             } else {
-                ds = new DestinationSet(false, false, segmentId1, destSw1);
+                ds = DestinationSet.createTypePushBos(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, false, segmentId1, destSw1,
-                                    segmentId2, destSw2);
+            ds = DestinationSet.createTypePushBos(segmentId1, destSw1, segmentId2, destSw2);
             treatment = null;
         }
 
@@ -827,6 +829,7 @@
 
         TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
         DestinationSet ds = null;
+        DestinationSet.DestinationSetType dstType = null;
         boolean simple = false;
         if (phpRequired) {
             // php case - pop should always be flow-action
@@ -841,17 +844,15 @@
                 tbuilder.decNwTtl();
                 // standard case -> BoS == True; pop results in IP packet and forwarding
                 // is via an ECMP group
-                ds = new DestinationSet(false, false, destSw);
+                ds = DestinationSet.createTypePopBos(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;
+                ds = DestinationSet.createTypePopNotBos(destSw);
+                if (!srManager.getMplsEcmp()) {
+                   simple = true;
                 }
             }
         } else {
@@ -860,11 +861,10 @@
             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
-            // XXX reconsider types
-            if (srManager.getMplsEcmp()) {
-                ds = new DestinationSet(false, true, segmentId, destSw);
-            } else {
-                ds = new DestinationSet(false, true, segmentId, destSw);
+            // differentiate here between swap with not bos or swap with bos
+            ds = isBos ? DestinationSet.createTypeSwapBos(segmentId, destSw) :
+                    DestinationSet.createTypeSwapNotBos(segmentId, destSw);
+            if (!srManager.getMplsEcmp()) {
                 simple = true;
             }
         }
@@ -975,6 +975,10 @@
             if (!processSinglePortFiltersInternal(deviceId, portnum, true, INTERNAL_VLAN, install)) {
                 return false;
             }
+            // Filter for receiveing pseudowire traffic
+            if (!processSinglePortFiltersInternal(deviceId, portnum, false, PSEUDOWIRE_VLAN, install)) {
+                return false;
+            }
         }
         return true;
     }
diff --git a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 360afdd..33c71f5 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -357,11 +357,14 @@
      * Segment Routing App ID.
      */
     public static final String APP_NAME = "org.onosproject.segmentrouting";
-
     /**
      * The default VLAN ID assigned to the interfaces without subnet config.
      */
     public static final VlanId INTERNAL_VLAN = VlanId.vlanId((short) 4094);
+    /**
+     * The Vlan id used to transport pseudowire traffic across the network.
+     */
+    public static final VlanId PSEUDOWIRE_VLAN = VlanId.vlanId((short) 4093);
 
     /**
      * Minumum and maximum value of dummy VLAN ID to be allocated.
diff --git a/app/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java b/app/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
index 1dd845b..7031507 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
@@ -222,9 +222,10 @@
             deviceIds.add(config.getDeviceId(sid));
         }
         // For these NeighborSet isMpls is meaningless.
-        DestinationSet ns = new DestinationSet(false, false,
-                                         tunnel.labelIds().get(2),
-                                         DeviceId.NONE);
+        // TODO : Revisit this, the code and also the type
+        DestinationSet ns = DestinationSet.createTypePushBos(
+                                               tunnel.labelIds().get(2),
+                                               DeviceId.NONE);
 
         // If the tunnel reuses any existing groups, then tunnel handler
         // should not remove the group.
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 39ac03b..9e28149 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -984,6 +984,10 @@
                                     .setMpls(MplsLabel.mplsLabel(edgeLabel));
                         }
                     }
+                    if ((ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.SWAP_NOT_BOS) ||
+                         (ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.POP_NOT_BOS)) {
+                        tBuilder.setVlanId(srManager.PSEUDOWIRE_VLAN);
+                    }
                     tBuilder.setOutput(sp);
                     nextObjBuilder.addTreatment(tBuilder.build());
                     treatmentAdded = true;
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java
index e70ec04..3ecdd61 100644
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java
+++ b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java
@@ -34,59 +34,52 @@
  * paired-destination switches. May also be used to represent cases where the
  * forwarding does not use ECMP groups (ie SIMPLE next objectives)
  */
-public class DestinationSet {
+public final class DestinationSet {
     public static final int NO_EDGE_LABEL = -1;
     private static final int NOT_ASSIGNED = 0;
-    private boolean notBos; // XXX replace with enum
-    private boolean swap; // XXX replace with enum
     private final DeviceId dstSw1;
     private final int edgeLabel1;
     private final DeviceId dstSw2;
     private final int edgeLabel2;
-
+    private final DestinationSetType typeOfDstSet;
 
     private static final Logger log = getLogger(DestinationSet.class);
 
     /**
      * Constructor for a single destination with no Edge label.
      *
-     * @param isNotBos indicates if it is meant for a non bottom-of-stack label
-     * @param isSwap indicates if it is meant for a swap action
+     * @param dsType type of next objective
      * @param dstSw the destination switch
      */
-    public DestinationSet(boolean isNotBos, boolean isSwap, DeviceId dstSw) {
+    private DestinationSet(DestinationSetType dsType, DeviceId dstSw) {
         this.edgeLabel1 = NO_EDGE_LABEL;
-        this.notBos = isNotBos;
-        this.swap = isSwap;
         this.dstSw1 = dstSw;
         this.edgeLabel2 = NOT_ASSIGNED;
         this.dstSw2 = null;
+        this.typeOfDstSet = dsType;
     }
 
     /**
      * Constructor for a single destination with Edge label.
      *
-     * @param isNotBos indicates if it is meant for a non bottom-of-stack label
-     * @param isSwap indicates if it is meant for a swap action
+     * @param dsType type of next objective
      * @param edgeLabel label to be pushed as part of group operation
      * @param dstSw the destination switch
      */
-    public DestinationSet(boolean isNotBos, boolean isSwap,
-                       int edgeLabel, DeviceId dstSw) {
-        this.notBos = isNotBos;
-        this.swap = isSwap;
+    private DestinationSet(DestinationSetType dsType,
+                          int edgeLabel, DeviceId dstSw) {
         this.edgeLabel1 = edgeLabel;
         this.dstSw1 = dstSw;
         this.edgeLabel2 = NOT_ASSIGNED;
         this.dstSw2 = null;
+        this.typeOfDstSet = dsType;
     }
 
     /**
      * Constructor for paired destination switches and their associated edge
      * labels.
      *
-     * @param isNotBos indicates if it is meant for a non bottom-of-stack label
-     * @param isSwap indicates if it is meant for a swap action
+     * @param dsType type of next objective
      * @param edgeLabel1 label to be pushed as part of group operation for
      *            dstSw1
      * @param dstSw1 one of the paired destination switches
@@ -94,11 +87,9 @@
      *            dstSw2
      * @param dstSw2 the other paired destination switch
      */
-    public DestinationSet(boolean isNotBos, boolean isSwap,
+    private DestinationSet(DestinationSetType dsType,
                           int edgeLabel1, DeviceId dstSw1,
                           int edgeLabel2, DeviceId dstSw2) {
-        this.notBos = isNotBos;
-        this.swap = isSwap;
         if (dstSw1.toString().compareTo(dstSw2.toString()) <= 0) {
             this.edgeLabel1 = edgeLabel1;
             this.dstSw1 = dstSw1;
@@ -110,18 +101,17 @@
             this.edgeLabel2 = edgeLabel1;
             this.dstSw2 = dstSw1;
         }
+        this.typeOfDstSet = dsType;
     }
-
     /**
      * Default constructor for kryo serialization.
      */
-    public DestinationSet() {
+    private DestinationSet() {
         this.edgeLabel1 = NOT_ASSIGNED;
         this.edgeLabel2 = NOT_ASSIGNED;
-        this.notBos = true;
-        this.swap = true;
         this.dstSw1 = DeviceId.NONE;
         this.dstSw2 = DeviceId.NONE;
+        this.typeOfDstSet = null;
     }
 
     /**
@@ -154,21 +144,38 @@
     }
 
     /**
-     * Gets the value of notBos.
+     * Returns the type of this ds.
+     *
+     * @return the type of the destination set
+     */
+    public DestinationSetType getTypeOfDstSet() {
+        return typeOfDstSet;
+    }
+
+    /**
+     * Returns true if the next objective represented by this destination set
+     * is of type SWAP_NOT_BOS or POP_NOT_BOS.
      *
      * @return the value of notBos
      */
     public boolean notBos() {
-        return notBos;
+        if ((typeOfDstSet == DestinationSetType.SWAP_NOT_BOS) || (typeOfDstSet == DestinationSetType.POP_NOT_BOS)) {
+            return true;
+        }
+        return false;
     }
 
     /**
-     * Gets the value of swap.
+     * Returns true if the next objective represented by this destination set
+     * is of type SWAP_NOT_BOS or SWAP_BOS.
      *
      * @return the value of swap
      */
     public boolean swap() {
-        return swap;
+        if ((typeOfDstSet == DestinationSetType.SWAP_BOS) || (typeOfDstSet == DestinationSetType.SWAP_NOT_BOS)) {
+            return true;
+        }
+        return false;
     }
 
     // The list of destination ids and label are used for comparison.
@@ -181,9 +188,10 @@
             return false;
         }
         DestinationSet that = (DestinationSet) o;
+        if (this.typeOfDstSet != that.typeOfDstSet) {
+            return false;
+        }
         boolean equal = (this.edgeLabel1 == that.edgeLabel1 &&
-                            this.notBos == that.notBos &&
-                            this.swap == that.swap &&
                             this.dstSw1.equals(that.dstSw1));
         if (this.dstSw2 != null && that.dstSw2 == null ||
                 this.dstSw2 == null && that.dstSw2 != null) {
@@ -200,26 +208,16 @@
     @Override
     public int hashCode() {
         if (dstSw2 == null) {
-            return Objects.hash(notBos, swap, edgeLabel1, dstSw1);
+            return Objects.hash(typeOfDstSet, edgeLabel1, dstSw1);
         }
-        return Objects.hash(notBos, swap, edgeLabel1, dstSw1, edgeLabel2,
+        return Objects.hash(typeOfDstSet, edgeLabel1, dstSw1, edgeLabel2,
                             dstSw2);
     }
 
     @Override
     public String toString() {
-        String type;
-        if (!notBos && !swap) {
-            type = "default";
-        } else if (!notBos) {
-            type = "swapbos";
-        } else if (!swap) {
-            type = "not-bos";
-        } else {
-            type = "swap-nb";
-        }
         ToStringHelper h = toStringHelper(this)
-                                .add("Type", type)
+                                .add("Type", typeOfDstSet.getType())
                                 .add("DstSw1", dstSw1)
                                 .add("Label1", edgeLabel1);
         if (dstSw2 != null) {
@@ -228,4 +226,129 @@
         }
         return h.toString();
     }
+
+    public enum DestinationSetType {
+        /**
+         * Used to represent DestinationSetType where the next hop
+         * is the same as the final destination.
+         */
+        PUSH_NONE("pushnon"),
+        /**
+         * Used to represent DestinationSetType where we need to
+         * push a single mpls label, that of the destination.
+         */
+        PUSH_BOS("pushbos"),
+        /**
+         * Used to represent DestinationSetType where we need to pop
+         * an mpls label which has the bos bit set.
+         */
+        POP_BOS("pop-bos"),
+        /**
+         * Used to represent DestinationSetType where we swap the outer
+         * mpls label with a new one, and where the outer label has the
+         * bos bit set.
+         */
+        SWAP_BOS("swapbos"),
+        /**
+         * Used to represent DestinationSetType where we need to pop
+         * an mpls label which does not have the bos bit set.
+         */
+        POP_NOT_BOS("popnbos"),
+        /**
+         * Used to represent DestinationSetType where we swap the outer
+         * mpls label with a new one, and where the outer label does not
+         * have the bos bit set.
+         */
+        SWAP_NOT_BOS("swap-nb");
+
+        private final String typeOfDstDest;
+        DestinationSetType(String s) {
+            typeOfDstDest = s;
+        }
+
+        public String getType() {
+            return typeOfDstDest;
+        }
+    }
+
+    /*
+     * Static methods for creating DestinationSet objects in
+     * order to remove ambiquity with multiple constructors.
+     */
+
+    /**
+     * Returns a DestinationSet with type PUSH_NONE.
+     *
+     * @param destSw The deviceId for this next objective.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePushNone(DeviceId destSw) {
+        return new DestinationSet(DestinationSetType.PUSH_NONE, destSw);
+    }
+
+    /**
+     * Returns a DestinationSet with type PUSH_BOS.
+     *
+     * @param edgeLabel1 The mpls label to push.
+     * @param destSw1 The device on which the label is assigned.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePushBos(int edgeLabel1, DeviceId destSw1) {
+        return new DestinationSet(DestinationSetType.PUSH_BOS, edgeLabel1, destSw1);
+    }
+
+    /**
+     * Returns a DestinationSet with type PUSH_BOS used for paired leafs.
+     *
+     * @param edgeLabel1 The label of first paired leaf.
+     * @param destSw1 The device id of first paired leaf.
+     * @param edgeLabel2 The label of the second leaf.
+     * @param destSw2 The device id of the second leaf.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePushBos(int edgeLabel1, DeviceId destSw1, int edgeLabel2, DeviceId destSw2) {
+        return new DestinationSet(DestinationSetType.PUSH_BOS, edgeLabel1, destSw1, edgeLabel2, destSw2);
+    }
+
+    /**
+     * Returns a DestinationSet with type POP_BOS.
+     *
+     * @param deviceId The deviceId for this next objective.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePopBos(DeviceId deviceId) {
+        return new DestinationSet(DestinationSetType.POP_BOS, deviceId);
+    }
+
+    /**
+     * Returns a DestinationSet with type SWAP_BOS.
+     *
+     * @param edgeLabel The edge label to swap with.
+     * @param deviceId The deviceId for this next objective.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypeSwapBos(int edgeLabel, DeviceId deviceId) {
+        return new DestinationSet(DestinationSetType.SWAP_BOS, edgeLabel, deviceId);
+    }
+
+    /**
+     * Returns a DestinationSet with type POP_NOT_BOS.
+     *
+     * @param deviceId The device-id this next objective should be installed.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypePopNotBos(DeviceId deviceId) {
+        return new DestinationSet(DestinationSetType.POP_NOT_BOS, deviceId);
+    }
+
+    /**
+     * Returns a DestinationSet with type SWAP_NOT_BOS.
+     *
+     * @param edgeLabel The edge label to swap with.
+     * @param deviceId The deviceId for this next objective.
+     * @return The DestinationSet of this type.
+     */
+    public static DestinationSet createTypeSwapNotBos(int edgeLabel, DeviceId deviceId) {
+        return new DestinationSet(DestinationSetType.SWAP_NOT_BOS, edgeLabel, deviceId);
+    }
 }
diff --git a/app/src/test/java/org/onosproject/segmentrouting/grouphandler/DestinationSetTest.java b/app/src/test/java/org/onosproject/segmentrouting/grouphandler/DestinationSetTest.java
index 6abf6e3..b41fe07 100644
--- a/app/src/test/java/org/onosproject/segmentrouting/grouphandler/DestinationSetTest.java
+++ b/app/src/test/java/org/onosproject/segmentrouting/grouphandler/DestinationSetTest.java
@@ -33,14 +33,13 @@
         d202 = DeviceId.deviceId("of:0000000000000202");
         el201 = 201;
         el202 = 202;
-        ds1 = new DestinationSet(false, false, d201);
-        ds2 = new DestinationSet(false, false, el201, d201);
-        ds3 = new DestinationSet(false, false, el201, d201, el202, d202);
-        ds4 = new DestinationSet(false, false,
-                                 DestinationSet.NO_EDGE_LABEL, d201,
-                                 DestinationSet.NO_EDGE_LABEL, d202);
-        ds5 = new DestinationSet(true, false, d201); // not-bos
-        ds6 = new DestinationSet(false, true, el201, d201); // swap group
+        ds1 = DestinationSet.createTypePushNone(d201);
+        ds2 = DestinationSet.createTypePushBos(el201, d201);
+        ds3 = DestinationSet.createTypePushBos(el201, d201, el202, d202);
+        ds4 = DestinationSet.createTypePushBos(DestinationSet.NO_EDGE_LABEL, d201,
+                                               DestinationSet.NO_EDGE_LABEL, d202);
+        ds5 = DestinationSet.createTypePopNotBos(d201);
+        ds6 = DestinationSet.createTypeSwapBos(el201, d201);
     }
 
     @Test
@@ -80,33 +79,27 @@
 
     @Test
     public void testOneDestinationWithoutLabel() {
-        DestinationSet testds = new DestinationSet(false, false, d201);
+        DestinationSet testds = DestinationSet.createTypePushNone(d201);
         assertTrue(testds.equals(ds1)); // match
 
-        testds = new DestinationSet(true, false, d201);
+        testds = DestinationSet.createTypePopNotBos(d201);
         assertFalse(testds.equals(ds1)); // wrong notBos
         assertTrue(testds.equals(ds5)); // correct notBos
 
-        testds = new DestinationSet(false, false, d202);
+        testds = DestinationSet.createTypePushNone(d202);
         assertFalse(testds.equals(ds1)); //wrong device
 
-        testds = new DestinationSet(false, false, el201, d201);
+        testds = DestinationSet.createTypePushBos(el201, d201);
         assertFalse(testds.equals(ds1)); // wrong label
 
-        testds = new DestinationSet(false, false, -1, d201, -1, d202);
+        testds = DestinationSet.createTypePushBos(-1, d201, -1, d202);
         assertFalse(testds.equals(ds1)); // 2-devs should not match
 
-        testds = new DestinationSet(false, true, d201);
-        assertFalse(testds.equals(ds1)); // wrong swap
-        assertFalse(testds.equals(ds6)); // wrong label
-
-        testds = new DestinationSet(false, true, el201, d201);
+        testds = DestinationSet.createTypeSwapBos(el201, d201);
+        assertFalse(testds.equals(ds1)); // wrong type and label
         assertTrue(testds.equals(ds6)); // correct swap
 
-        testds = new DestinationSet(false, true, DestinationSet.NO_EDGE_LABEL, d201);
-        assertFalse(testds.equals(ds6)); // wrong label
-
-        testds = new DestinationSet(true, true, el201, d201);
+        testds = DestinationSet.createTypeSwapNotBos(el201, d201);
         assertFalse(testds.equals(ds6)); // wrong notbos
     }
 
@@ -114,61 +107,53 @@
 
     @Test
     public void testOneDestinationWithLabel() {
-        DestinationSet testds = new DestinationSet(false, false, 203, d202);
+        DestinationSet testds = DestinationSet.createTypePushBos(203, d202);
         assertFalse(testds.equals(ds2)); //wrong label
 
-        testds = new DestinationSet(true, false, 201, d201);
-        assertFalse(testds.equals(ds2)); // wrong notBos
-
-        testds = new DestinationSet(false, false, 201, d202);
+        testds = DestinationSet.createTypePushBos(201, d202);
         assertFalse(testds.equals(ds2)); //wrong device
 
-        testds = new DestinationSet(false, false, 201,
-                                    DeviceId.deviceId("of:0000000000000201"));
+        testds = DestinationSet.createTypePushBos(201,  DeviceId.deviceId("of:0000000000000201"));
         assertTrue(testds.equals(ds2)); // match
 
-        testds = new DestinationSet(false, false, d201);
+        testds = DestinationSet.createTypePushNone(d201);
         assertFalse(testds.equals(ds2)); // wrong label
 
-        testds = new DestinationSet(false, false, el201, d201, el202, d202);
+        testds = DestinationSet.createTypePushBos(el201, d201, el202, d202);
         assertFalse(testds.equals(ds1)); // 2-devs should not match
     }
 
     @Test
     public void testDestPairWithLabel() {
-        DestinationSet testds = new DestinationSet(false, false, el201, d201, el202, d202);
+        DestinationSet testds = DestinationSet.createTypePushBos(el201, d201, el202, d202);
         assertTrue(testds.equals(ds3)); // match same switches, same order
         assertTrue(testds.hashCode() == ds3.hashCode());
 
-        testds = new DestinationSet(false, false, el202, d202, el201, d201);
+        testds = DestinationSet.createTypePushBos(el202, d202, el201, d201);
         assertTrue(testds.equals(ds3)); // match same switches, order reversed
         assertTrue(testds.hashCode() == ds3.hashCode());
 
-        testds = new DestinationSet(false, false, el202, d202);
+        testds = DestinationSet.createTypePushBos(el202, d202);
         assertFalse(testds.equals(ds3)); // one less switch should not match
         assertFalse(testds.hashCode() == ds3.hashCode());
 
-        testds = new DestinationSet(false, false, el201, d201);
+        testds = DestinationSet.createTypePushBos(el201, d201);
         assertFalse(testds.equals(ds3)); // one less switch should not match
         assertFalse(testds.hashCode() == ds3.hashCode());
 
-        testds = new DestinationSet(false, false, el201, d201, 0, DeviceId.NONE);
+        testds = DestinationSet.createTypePushBos(el201, d201, 0, DeviceId.NONE);
         assertFalse(testds.equals(ds3)); // one less switch should not match
         assertFalse(testds.hashCode() == ds3.hashCode());
 
-        testds = new DestinationSet(false, false, el201, d202, el201, d201);
+        testds = DestinationSet.createTypePushBos(el201, d202, el201, d201);
         assertFalse(testds.equals(ds3)); // wrong labels
         assertFalse(testds.hashCode() == ds3.hashCode());
 
-        testds = new DestinationSet(true, false, el202, d202, el201, d201);
-        assertFalse(testds.equals(ds3)); // wrong not bos
-        assertFalse(testds.hashCode() == ds3.hashCode());
-
-        testds = new DestinationSet(false, false, el202, d202, el201, d202);
+        testds = DestinationSet.createTypePushBos(el202, d202, el201, d202);
         assertFalse(testds.equals(ds3)); // wrong device
         assertFalse(testds.hashCode() == ds3.hashCode());
 
-        testds = new DestinationSet(false, false,
+        testds = DestinationSet.createTypePushBos(
                                     el202, DeviceId.deviceId("of:0000000000000205"),
                                     el201, d201);
         assertFalse(testds.equals(ds3)); // wrong device
@@ -177,39 +162,35 @@
 
     @Test
     public void testDestPairWithoutLabel() {
-        DestinationSet testds = new DestinationSet(false, false, -1, d201, -1, d202);
+        DestinationSet testds = DestinationSet.createTypePushBos(-1, d201, -1, d202);
         assertTrue(testds.equals(ds4)); // match same switches, same order
         assertTrue(testds.hashCode() == ds4.hashCode());
 
-        testds = new DestinationSet(false, false, -1, d202, -1, d201);
+        testds = DestinationSet.createTypePushBos(-1, d202, -1, d201);
         assertTrue(testds.equals(ds4)); // match same switches, order reversed
         assertTrue(testds.hashCode() == ds4.hashCode());
 
-        testds = new DestinationSet(false, false, -1, d202);
+        testds = DestinationSet.createTypePushBos(-1, d202);
         assertFalse(testds.equals(ds4)); // one less switch should not match
         assertFalse(testds.hashCode() == ds4.hashCode());
 
-        testds = new DestinationSet(false, false, -1, d201);
+        testds = DestinationSet.createTypePushBos(-1, d201);
         assertFalse(testds.equals(ds4)); // one less switch should not match
         assertFalse(testds.hashCode() == ds4.hashCode());
 
-        testds = new DestinationSet(false, false, -1, d201, 0, DeviceId.NONE);
+        testds = DestinationSet.createTypePushBos(-1, d201, 0, DeviceId.NONE);
         assertFalse(testds.equals(ds4)); // one less switch should not match
         assertFalse(testds.hashCode() == ds4.hashCode());
 
-        testds = new DestinationSet(false, false, el201, d201, -1, d202);
+        testds = DestinationSet.createTypePushBos(el201, d201, -1, d202);
         assertFalse(testds.equals(ds4)); // wrong labels
         assertFalse(testds.hashCode() == ds4.hashCode());
 
-        testds = new DestinationSet(true, false, -1, d202, -1, d201);
-        assertFalse(testds.equals(ds4)); // wrong mpls set
-        assertFalse(testds.hashCode() == ds4.hashCode());
-
-        testds = new DestinationSet(false, false, -1, d202, -1, d202);
+        testds = DestinationSet.createTypePushBos(-1, d202, -1, d202);
         assertFalse(testds.equals(ds4)); // wrong device
         assertFalse(testds.hashCode() == ds4.hashCode());
 
-        testds = new DestinationSet(false, false,
+        testds = DestinationSet.createTypePushBos(
                                     -1, DeviceId.deviceId("of:0000000000000205"),
                                     -1, d201);
         assertFalse(testds.equals(ds4)); // wrong device