[CORD-575] Implements VPWS transport in SR app

Changes:
- Enables the BoS==False use case;
- Adds RandomNeighborSet to emulate the hashing;

Change-Id: I5cc05eb25f5185e612061880fcdb194ed71277d8
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 48c988c..14ce83d 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.segmentrouting;
 
+import com.google.common.collect.Lists;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
@@ -54,6 +55,8 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
@@ -293,10 +296,10 @@
         // is not set.
         if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
             tbuilder.immediate().decNwTtl();
-            ns = new NeighborSet(nextHops);
+            ns = new NeighborSet(nextHops, false);
             treatment = tbuilder.build();
         } else {
-            ns = new NeighborSet(nextHops, segmentId);
+            ns = new NeighborSet(nextHops, false, segmentId);
             treatment = null;
         }
 
@@ -373,17 +376,117 @@
     }
 
     /**
+     * Deals with !MPLS Bos use case.
+     *
+     * @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
+     * @return a collection of fwdobjective
+     */
+    private Collection<ForwardingObjective> handleMpls(DeviceId targetSwId,
+                                                       DeviceId destSwId,
+                                                       Set<DeviceId> nextHops,
+                                                       int segmentId,
+                                                       IpAddress routerIp,
+                                                       boolean isMplsBos) {
+
+        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+        List<ForwardingObjective.Builder> fwdObjBuilders = Lists.newArrayList();
+        // For the transport of VPWS we can use two or three MPLS label
+        sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
+        sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
+        sbuilder.matchMplsBos(isMplsBos);
+        TrafficSelector selector = sbuilder.build();
+
+        // setup metadata to pass to nextObjective - indicate the vlan on egress
+        // if needed by the switch pipeline. Since mpls next-hops are always to
+        // other neighboring routers, there is no subnet assigned on those ports.
+        TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
+        metabuilder.matchVlanId(
+                VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
+
+        if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
+            // If the next hop is the destination router for the segment, do pop
+            log.debug("populateMplsRule: Installing MPLS forwarding objective for "
+                              + "label {} in switch {} with pop", segmentId, targetSwId);
+            // 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,
+                                                                                        true,
+                                                                                        isMplsBos,
+                                                                                        metabuilder.build(),
+                                                                                        routerIp);
+            // Error case, we cannot handle, exit.
+            if (fwdObjNoBosBuilder == null) {
+                return Collections.emptyList();
+            }
+            fwdObjBuilders.add(fwdObjNoBosBuilder);
+
+        } else {
+            // next hop is not destination, SR CONTINUE case (swap with self)
+            log.debug("Installing MPLS forwarding objective for "
+                              + "label {} in switch {} without pop", segmentId, targetSwId);
+            // 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,
+                                                                                        false,
+                                                                                        isMplsBos,
+                                                                                        metabuilder.build(),
+                                                                                        routerIp);
+            // Error case, we cannot handle, exit.
+            if (fwdObjNoBosBuilder == null) {
+                return Collections.emptyList();
+            }
+            fwdObjBuilders.add(fwdObjNoBosBuilder);
+
+        }
+
+        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())
+                    .withSelector(selector)
+                    .withPriority(SegmentRoutingService.DEFAULT_PRIORITY))
+                    .withFlag(ForwardingObjective.Flag.SPECIFIC);
+
+            ObjectiveContext context = new DefaultObjectiveContext(
+                    (objective) ->
+                            log.debug("MPLS rule {} for SID {} populated in dev:{} ",
+                                      objective.id(), segmentId, targetSwId),
+                    (objective, error) ->
+                            log.warn("Failed to populate MPLS rule {} for SID {}: {} in dev:{}",
+                                     objective.id(), segmentId, error, targetSwId));
+
+            ForwardingObjective fob = fwdObjBuilder.add(context);
+            fwdObjs.add(fob);
+
+        }
+
+        return fwdObjs;
+    }
+
+    /**
      * 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
+     * @param routerIp the router ip
      * @return true if all rules are set successfully, false otherwise
      */
     public boolean populateMplsRule(DeviceId targetSwId, DeviceId destSwId,
                                     Set<DeviceId> nextHops, IpAddress routerIp) {
+
         int segmentId;
         try {
             if (routerIp.isIp4()) {
@@ -396,93 +499,26 @@
             return false;
         }
 
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>();
-
-        // TODO Handle the case of Bos == false
-        sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
-        sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
-        sbuilder.matchMplsBos(true);
-        TrafficSelector selector = sbuilder.build();
-
-        // setup metadata to pass to nextObjective - indicate the vlan on egress
-        // if needed by the switch pipeline. Since mpls next-hops are always to
-        // other neighboring routers, there is no subnet assigned on those ports.
-        TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
-        metabuilder.matchVlanId(
-            VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
-
-        // If the next hop is the destination router for the segment, do pop
-        if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
-            log.debug("populateMplsRule: Installing MPLS forwarding objective for "
-                    + "label {} in switch {} with pop", segmentId, targetSwId);
-
-            // bos pop case (php)
-            ForwardingObjective.Builder fwdObjBosBuilder =
-                    getMplsForwardingObjective(targetSwId,
-                                               nextHops,
-                                               true,
-                                               true,
-                                               metabuilder.build(),
-                                               routerIp);
-            if (fwdObjBosBuilder == null) {
-                return false;
-            }
-            fwdObjBuilders.add(fwdObjBosBuilder);
-
-            // XXX not-bos pop case,  SR app multi-label not implemented yet
-            /*ForwardingObjective.Builder fwdObjNoBosBuilder =
-                    getMplsForwardingObjective(deviceId,
-                                               nextHops,
-                                               true,
-                                               false);*/
-
-        } else {
-            // next hop is not destination, SR CONTINUE case (swap with self)
-            log.debug("Installing MPLS forwarding objective for "
-                    + "label {} in switch {} without pop", segmentId, targetSwId);
-
-            // continue case with bos - this does get triggered in edge routers
-            // and in core routers - driver can handle depending on availability
-            // of MPLS ECMP or not
-            ForwardingObjective.Builder fwdObjBosBuilder =
-                    getMplsForwardingObjective(targetSwId,
-                                               nextHops,
-                                               false,
-                                               true,
-                                               metabuilder.build(),
-                                               routerIp);
-            if (fwdObjBosBuilder == null) {
-                return false;
-            }
-            fwdObjBuilders.add(fwdObjBosBuilder);
-
-            // XXX continue case with not-bos - SR app multi label not implemented yet
-            // also requires MPLS ECMP
-            /*ForwardingObjective.Builder fwdObjNoBosBuilder =
-                    getMplsForwardingObjective(deviceId,
-                                               nextHops,
-                                               false,
-                                               false); */
-
+        List<ForwardingObjective> fwdObjs = new ArrayList<>();
+        Collection<ForwardingObjective> fwdObjsMpls = Collections.emptyList();
+        // Generates the transit rules used by the standard "routing".
+        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, segmentId, routerIp, true);
+        if (fwdObjsMpls.isEmpty()) {
+            return false;
         }
-        // XXX when other cases above are implemented check for validity of
-        // debug messages below
-        for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
-            ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
-                    .makePermanent()).withSelector(selector)
-                    .withPriority(SegmentRoutingService.DEFAULT_PRIORITY))
-                    .withFlag(ForwardingObjective.Flag.SPECIFIC);
-            ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("MPLS rule {} for SID {} populated in dev:{} ",
-                                             objective.id(), segmentId, targetSwId),
-                    (objective, error) ->
-                            log.warn("Failed to populate MPLS rule {} for SID {}: {} in dev:{}",
-                                     objective.id(), segmentId, error, targetSwId));
-            ForwardingObjective fob = fwdObjBuilder.add(context);
+        fwdObjs.addAll(fwdObjsMpls);
+        // Generates the transit rules used by the MPLS VPWS. For now it is
+        // the only case !BoS supported.
+        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, segmentId, 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: {}",
-                      fob.id(), segmentId, fob.nextId(), targetSwId);
-            srManager.flowObjectiveService.forward(targetSwId, fob);
+                      fwdObj.id(), segmentId, fwdObj.nextId(), targetSwId);
+            srManager.flowObjectiveService.forward(targetSwId, fwdObj);
             rulePopulationCounter.incrementAndGet();
         }
 
@@ -523,16 +559,24 @@
             tbuilder.deferred().decMplsTtl();
         }
 
-        // All forwarding is via ECMP group, the metadata informs the driver
-        // that the next-Objective will be used by MPLS flows. In other words,
-        // MPLS ECMP is requested. It is up to the driver to decide if these
-        // packets will be hashed or not.
         fwdBuilder.withTreatment(tbuilder.build());
-        NeighborSet ns = new NeighborSet(nextHops);
-        log.debug("Trying to get a nextObjId for mpls rule on device:{} to ns:{}",
-                 deviceId, ns);
-
-        int nextId = srManager.getNextObjectiveId(deviceId, ns, meta);
+        // if MPLS-ECMP == True we will build a standard NeighborSet.
+        // Otherwise a RandomNeighborSet.
+        NeighborSet ns = NeighborSet.neighborSet(false, nextHops, false);
+        if (!isBos && this.srManager.getMplsEcmp()) {
+            ns = NeighborSet.neighborSet(false, nextHops, true);
+        } else if (!isBos && !this.srManager.getMplsEcmp()) {
+            ns = NeighborSet.neighborSet(true, nextHops, true);
+        }
+        log.info("Trying to get a nextObjId for mpls rule on device:{} to ns:{}",
+                  deviceId, ns);
+        // 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.
+        int nextId = srManager.getNextObjectiveId(deviceId, ns, meta, isBos);
         if (nextId <= 0) {
             log.warn("No next objective in {} for ns: {}", deviceId, ns);
             return null;
@@ -591,6 +635,7 @@
             if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
                 isSuppressed = true;
                 suppressedPorts++;
+                continue;
             }
 
             Ip4Prefix portSubnet = config.getPortIPv4Subnet(deviceId, port.number());
@@ -684,17 +729,13 @@
      */
     private TrafficSelector.Builder buildIpSelectorFromIpPrefix(IpPrefix prefixToMatch) {
         TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
-        /*
-         * If the prefix is IPv4
-         */
+        // If the prefix is IPv4
         if (prefixToMatch.isIp4()) {
             selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
             selectorBuilder.matchIPDst(prefixToMatch.getIp4Prefix());
             return selectorBuilder;
         }
-        /*
-         * If the prefix is IPv6
-         */
+        // If the prefix is IPv6
         selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
         selectorBuilder.matchIPv6Dst(prefixToMatch.getIp6Prefix());
         return selectorBuilder;
@@ -708,9 +749,7 @@
      * @param deviceId the switch dpid for the router
      */
     public void populateArpNdpPunts(DeviceId deviceId) {
-        /*
-         * We are not the master just skip.
-         */
+        // We are not the master just skip.
         if (!srManager.mastershipService.isLocalMaster(deviceId)) {
             log.debug("Not installing ARP/NDP punts - not the master for dev:{} ",
                       deviceId);
@@ -804,9 +843,8 @@
                 return;
             }
 
-            /* Driver should treat objective with MacAddress.NONE as the
-             * subnet broadcast rule
-             */
+            // Driver should treat objective with MacAddress.NONE as the
+            // subnet broadcast rule
             TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
             sbuilder.matchVlanId(vlanId);
             sbuilder.matchEthDst(MacAddress.NONE);
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index a78f093..e7cf32f 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -585,15 +585,16 @@
      * @param deviceId Device ID
      * @param ns NegighborSet
      * @param meta metadata passed into the creation of a Next Objective
+     * @param isBos indicates if it is BoS or not
      * @return next objective ID or -1 if an error was encountered during the
      *         creation of the nextObjective
      */
     public int getNextObjectiveId(DeviceId deviceId, NeighborSet ns,
-                                  TrafficSelector meta) {
+                                  TrafficSelector meta, boolean isBos) {
         if (groupHandlerMap.get(deviceId) != null) {
             log.trace("getNextObjectiveId query in device {}", deviceId);
             return groupHandlerMap
-                    .get(deviceId).getNextObjectiveId(ns, meta);
+                    .get(deviceId).getNextObjectiveId(ns, meta, isBos);
         } else {
             log.warn("getNextObjectiveId query - groupHandler for device {} "
                     + "not found", deviceId);
@@ -602,6 +603,23 @@
     }
 
     /**
+     * Returns the next objective ID for the given NeighborSet.
+     * If the nextObjective does not exist, a new one is created and
+     * its id is returned.
+     *
+     * @param deviceId Device ID
+     * @param ns NegighborSet
+     * @param meta metadata passed into the creation of a Next Objective
+     * @return next objective ID or -1 if an error was encountered during the
+     *         creation of the nextObjective
+     */
+    public int getNextObjectiveId(DeviceId deviceId,
+                                  NeighborSet ns,
+                                  TrafficSelector meta) {
+        return this.getNextObjectiveId(deviceId, ns, meta, true);
+    }
+
+    /**
      * Returns the next objective ID for the given subnet prefix. It is expected
      * that the next-objective has been pre-created from configuration.
      *
diff --git a/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java b/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
index a3ee137..cd9467a 100644
--- a/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
@@ -221,8 +221,8 @@
         } else {
             deviceIds.add(config.getDeviceId(sid));
         }
-
-        NeighborSet ns = new NeighborSet(deviceIds, tunnel.labelIds().get(2));
+        // For these NeighborSet isMpls is meaningless.
+        NeighborSet ns = new NeighborSet(deviceIds, false, tunnel.labelIds().get(2));
 
         // If the tunnel reuses any existing groups, then tunnel handler
         // should not remove the group.
@@ -232,7 +232,7 @@
             tunnel.allowToRemoveGroup(true);
         }
 
-        return groupHandlerMap.get(deviceId).getNextObjectiveId(ns, null);
+        return groupHandlerMap.get(deviceId).getNextObjectiveId(ns, null, true);
     }
 
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
index cbfe7c5..7fc9480 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
@@ -15,10 +15,6 @@
  */
 package org.onosproject.segmentrouting.grouphandler;
 
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
@@ -27,6 +23,10 @@
 import org.onosproject.segmentrouting.SegmentRoutingManager;
 import org.onosproject.segmentrouting.config.DeviceProperties;
 
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 /**
  * Default ECMP group handler creation module for an edge device.
  * This component creates a set of ECMP groups for every neighbor
@@ -76,7 +76,8 @@
             List<Integer> groupSegmentIds =
                     getSegmentIdsTobePairedWithNeighborSet(combo);
             for (Integer sId : groupSegmentIds) {
-                NeighborSet ns = new NeighborSet(combo, sId);
+                // For these NeighborSet isMpls is meaningless.
+                NeighborSet ns = new NeighborSet(combo, false, sId);
                 log.trace("createGroupsAtEdgeRouter: sw {} "
                         + "combo {} sId {} ns {}",
                         deviceId, combo, sId, ns);
@@ -163,7 +164,8 @@
             List<Integer> groupSegmentIds =
                     getSegmentIdsTobePairedWithNeighborSet(combo);
             for (Integer sId : groupSegmentIds) {
-                NeighborSet ns = new NeighborSet(combo, sId);
+                // For these NeighborSet isMpls is meaningless.
+                NeighborSet ns = new NeighborSet(combo, false, sId);
                 log.trace("computeImpactedNeighborsetForPortEvent: sw {} "
                         + "combo {} sId {} ns {}",
                         deviceId, combo, sId, ns);
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index d943710..f301a78 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -15,19 +15,8 @@
  */
 package org.onosproject.segmentrouting.grouphandler;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
+import com.google.common.collect.Iterables;
+import org.apache.commons.lang3.RandomUtils;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
@@ -57,6 +46,19 @@
 import org.onosproject.store.service.EventuallyConsistentMap;
 import org.slf4j.Logger;
 
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
 /**
  * Default ECMP group handler creation module. This component creates a set of
  * ECMP groups for every neighbor that this device is connected to based on
@@ -427,10 +429,11 @@
      *
      * @param ns neighborset
      * @param meta metadata passed into the creation of a Next Objective
+     * @param isBos if Bos is set
      * @return int if found or -1 if there are errors in the creation of the
      *          neighbor set.
      */
-    public int getNextObjectiveId(NeighborSet ns, TrafficSelector meta) {
+    public int getNextObjectiveId(NeighborSet ns, TrafficSelector meta, boolean isBos) {
         Integer nextId = nsNextObjStore.
                 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
         if (nextId == null) {
@@ -443,7 +446,7 @@
                       .filter((nsStoreEntry) ->
                       (nsStoreEntry.getKey().deviceId().equals(deviceId)))
                       .collect(Collectors.toList()));
-            createGroupsFromNeighborsets(Collections.singleton(ns), meta);
+            createGroupsFromNeighborsets(Collections.singleton(ns), meta, isBos);
             nextId = nsNextObjStore.
                     get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
             if (nextId == null) {
@@ -599,9 +602,7 @@
     private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
         int segmentId;
         try {
-            /*
-             * IPv6 sid is not inserted. this part of the code is not used for now.
-             */
+            // IPv6 sid is not inserted. this part of the code is not used for now.
             segmentId = deviceConfig.getIPv4SegmentId(deviceId);
         } catch (DeviceConfigNotFoundException e) {
             log.warn(e.getMessage() + " Aborting isSegmentIdSameAsNodeSegmentId.");
@@ -647,15 +648,28 @@
      *
      * @param nsSet a set of NeighborSet
      * @param meta metadata passed into the creation of a Next Objective
+     * @param isBos if BoS is set
      */
     public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet,
-                                             TrafficSelector meta) {
+                                             TrafficSelector meta,
+                                             boolean isBos) {
         for (NeighborSet ns : nsSet) {
             int nextId = flowObjectiveService.allocateNextId();
+            NextObjective.Type type = NextObjective.Type.HASHED;
+            Set<DeviceId> neighbors = ns.getDeviceIds();
+            // If Bos == False and MPLS-ECMP == false, we have
+            // to use simple group and we will pick a single neighbor.
+            if (!isBos && !srManager.getMplsEcmp()) {
+                type = NextObjective.Type.SIMPLE;
+                neighbors = Collections.singleton(ns.getFirstNeighbor());
+            }
             NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                    .builder().withId(nextId)
-                    .withType(NextObjective.Type.HASHED).fromApp(appId);
-            for (DeviceId neighborId : ns.getDeviceIds()) {
+                    .builder()
+                    .withId(nextId)
+                    .withType(type)
+                    .fromApp(appId);
+            // For each neighbor, we have to update the sent actions
+            for (DeviceId neighborId : neighbors) {
                 if (devicePortMap.get(neighborId) == null) {
                     log.warn("Neighbor {} is not in the port map yet for dev:{}",
                              neighborId, deviceId);
@@ -673,8 +687,17 @@
                     log.warn(e.getMessage() + " Aborting createGroupsFromNeighborsets.");
                     return;
                 }
-
-                for (PortNumber sp : devicePortMap.get(neighborId)) {
+                // For each port, we have to create a new treatment
+                Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
+                // In this case we are using a SIMPLE group. We randomly pick a port
+                if (!isBos && !srManager.getMplsEcmp()) {
+                    int size = devicePortMap.get(neighborId).size();
+                    int index = RandomUtils.nextInt(0, size);
+                    neighborPorts = Collections.singleton(
+                            Iterables.get(devicePortMap.get(neighborId), index)
+                    );
+                }
+                for (PortNumber sp : neighborPorts) {
                     TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
                             .builder();
                     tBuilder.setEthDst(neighborMac)
@@ -693,11 +716,13 @@
             }
 
             ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("createGroupsFromNeighborsets installed NextObj {} on {}",
+                    (objective) ->
+                            log.debug("createGroupsFromNeighborsets installed NextObj {} on {}",
                             nextId, deviceId),
                     (objective, error) ->
                             log.warn("createGroupsFromNeighborsets failed to install NextObj {} on {}: {}",
-                                    nextId, deviceId, error));
+                                    nextId, deviceId, error)
+            );
             NextObjective nextObj = nextObjBuilder.add(context);
             log.debug("**createGroupsFromNeighborsets: Submited "
                     + "next objective {} in device {}",
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
index 820960b..cf6975c 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
@@ -15,9 +15,6 @@
  */
 package org.onosproject.segmentrouting.grouphandler;
 
-import java.util.HashSet;
-import java.util.Set;
-
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
@@ -27,6 +24,9 @@
 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
 import org.onosproject.segmentrouting.config.DeviceProperties;
 
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Default ECMP group handler creation module for a transit device.
  * This component creates a set of ECMP groups for every neighbor
@@ -67,7 +67,8 @@
             if (combo.isEmpty()) {
                 continue;
             }
-            NeighborSet ns = new NeighborSet(combo);
+             // For these NeighborSet isMpls is meaningless.
+            NeighborSet ns = new NeighborSet(combo, false);
             log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
                       deviceId, combo, ns);
             nsSet.add(ns);
@@ -150,7 +151,8 @@
             if (combo.isEmpty()) {
                 continue;
             }
-            NeighborSet ns = new NeighborSet(combo);
+            // For these NeighborSet isMpls is meaningless.
+            NeighborSet ns = new NeighborSet(combo, false);
             log.debug("createGroupsAtTransitRouter: sw {} combo {} ns {}",
                       deviceId, combo, ns);
             nsSet.add(ns);
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
index aefcd21..06f7861 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSet.java
@@ -16,13 +16,14 @@
 
 package org.onosproject.segmentrouting.grouphandler;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import org.onosproject.net.DeviceId;
 
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
 
-import org.onosproject.net.DeviceId;
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Representation of a set of neighbor switch dpids along with edge node
@@ -34,31 +35,36 @@
     private final Set<DeviceId> neighbors;
     private final int edgeLabel;
     public static final int NO_EDGE_LABEL = -1;
+    private boolean mplsSet;
 
     /**
      * Constructor with set of neighbors. Edge label is
      * default to -1.
      *
      * @param neighbors set of neighbors to be part of neighbor set
+     * @param isMplsSet indicates if it is a mpls neighbor set
      */
-    public NeighborSet(Set<DeviceId> neighbors) {
+    public NeighborSet(Set<DeviceId> neighbors, boolean isMplsSet) {
         checkNotNull(neighbors);
         this.edgeLabel = NO_EDGE_LABEL;
         this.neighbors = new HashSet<>();
         this.neighbors.addAll(neighbors);
+        this.mplsSet = isMplsSet;
     }
 
     /**
      * Constructor with set of neighbors and edge label.
      *
      * @param neighbors set of neighbors to be part of neighbor set
+     * @param isMplsSet indicates if it is a mpls neighbor set
      * @param edgeLabel label to be pushed as part of group operation
      */
-    public NeighborSet(Set<DeviceId> neighbors, int edgeLabel) {
+    public NeighborSet(Set<DeviceId> neighbors, boolean isMplsSet, int edgeLabel) {
         checkNotNull(neighbors);
         this.edgeLabel = edgeLabel;
         this.neighbors = new HashSet<>();
         this.neighbors.addAll(neighbors);
+        this.mplsSet = isMplsSet;
     }
 
     /**
@@ -67,6 +73,46 @@
     public NeighborSet() {
         this.edgeLabel = NO_EDGE_LABEL;
         this.neighbors = new HashSet<>();
+        this.mplsSet = true;
+    }
+
+    /**
+     * Factory method for NeighborSet hierarchy.
+     *
+     * @param random the expected behavior.
+     * @param neighbors the set of neighbors to be part of neighbor set
+     * @param isMplsSet indicates if it is a mpls neighbor set
+     * @return the neighbor set object.
+     */
+    public static NeighborSet neighborSet(boolean random, Set<DeviceId> neighbors, boolean isMplsSet) {
+        return random ?
+                new RandomNeighborSet(neighbors) :
+                new NeighborSet(neighbors, isMplsSet);
+    }
+
+    /**
+     * Factory method for NeighborSet hierarchy.
+     *
+     * @param random the expected behavior.
+     * @param neighbors the set of neighbors to be part of neighbor set
+     * @param isMplsSet indicates if it is a mpls neighbor set
+     * @param edgeLabel label to be pushed as part of group operation
+     * @return the neighbor set object
+     */
+    public static NeighborSet neighborSet(boolean random, Set<DeviceId> neighbors, boolean isMplsSet, int edgeLabel) {
+        return random ?
+                new RandomNeighborSet(neighbors, edgeLabel) :
+                new NeighborSet(neighbors, isMplsSet, edgeLabel);
+    }
+
+    /**
+     * Factory method for NeighborSet hierarchy.
+     *
+     * @param random the expected behavior.
+     * @return the neighbor set object
+     */
+    public static NeighborSet neighborSet(boolean random) {
+        return random ? new RandomNeighborSet() : new NeighborSet();
     }
 
     /**
@@ -87,6 +133,26 @@
         return edgeLabel;
     }
 
+    /**
+     * Gets the first neighbor of the set. The default
+     * implementation assure the first neighbor is the
+     * first of the set. Subclasses can modify this.
+     *
+     * @return the first neighbor of the set
+     */
+    public DeviceId getFirstNeighbor() {
+        return neighbors.isEmpty() ? DeviceId.NONE : neighbors.iterator().next();
+    }
+
+    /**
+     * Gets the value of mplsSet.
+     *
+     * @return the value of mplsSet
+     */
+    public boolean mplsSet() {
+        return mplsSet;
+    }
+
     // The list of neighbor ids and label are used for comparison.
     @Override
     public boolean equals(Object o) {
@@ -99,25 +165,22 @@
         NeighborSet that = (NeighborSet) o;
         return (this.neighbors.containsAll(that.neighbors) &&
                 that.neighbors.containsAll(this.neighbors) &&
-                (this.edgeLabel == that.edgeLabel));
+                (this.edgeLabel == that.edgeLabel) &&
+                (this.mplsSet == that.mplsSet));
     }
 
     // The list of neighbor ids and label are used for comparison.
     @Override
     public int hashCode() {
-        int result = 17;
-        int combinedHash = 0;
-        for (DeviceId d : neighbors) {
-            combinedHash = combinedHash + Objects.hash(d);
-        }
-        result = 31 * result + combinedHash + Objects.hash(edgeLabel);
-
-        return result;
+        return Objects.hash(neighbors, edgeLabel, mplsSet);
     }
 
     @Override
     public String toString() {
-        return " Neighborset Sw: " + neighbors
-                + " and Label: " + edgeLabel;
+        return toStringHelper(this)
+                .add("Neighborset Sw", neighbors)
+                .add("Label", edgeLabel)
+                .add("MplsSet", mplsSet)
+                .toString();
     }
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/RandomNeighborSet.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/RandomNeighborSet.java
new file mode 100644
index 0000000..a56859f
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/RandomNeighborSet.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.grouphandler;
+
+import com.google.common.collect.Iterables;
+import org.apache.commons.lang3.RandomUtils;
+import org.onosproject.net.DeviceId;
+
+import java.util.Set;
+
+/**
+ * Extends its super classe modifying its internal behavior.
+ * Pick a neighbor will pick a random neighbor.
+ */
+public class RandomNeighborSet extends NeighborSet {
+
+    public RandomNeighborSet(Set<DeviceId> neighbors) {
+        super(neighbors, true);
+    }
+
+    public RandomNeighborSet(Set<DeviceId> neighbors, int edgeLabel) {
+        super(neighbors, true, edgeLabel);
+    }
+
+    public RandomNeighborSet() {
+        super();
+    }
+
+    @Override
+    public DeviceId getFirstNeighbor() {
+        if (getDeviceIds().isEmpty()) {
+            return DeviceId.NONE;
+        }
+        int size = getDeviceIds().size();
+        int index = RandomUtils.nextInt(0, size);
+        return Iterables.get(getDeviceIds(), index);
+    }
+
+    @Override
+    public String toString() {
+        return " RandomNeighborset Sw: " + getDeviceIds()
+                + " and Label: " + getEdgeLabel();
+    }
+}