Segment Routing refactor with flow objectives

Change-Id: I0b87f89bb8b18522b9d38bdf5e96f55485b6f1e3
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 0ac3728..cb295db 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -20,10 +20,11 @@
 
 import java.net.URI;
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Random;
 import java.util.Set;
 
 import org.onlab.packet.MacAddress;
@@ -35,25 +36,18 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.group.DefaultGroupBucket;
-import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.NextObjective;
 import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupBucket;
-import org.onosproject.net.group.GroupBuckets;
-import org.onosproject.net.group.GroupDescription;
-import org.onosproject.net.group.GroupEvent;
 import org.onosproject.net.group.GroupKey;
-import org.onosproject.net.group.GroupListener;
-import org.onosproject.net.group.GroupService;
 import org.onosproject.net.link.LinkService;
 import org.slf4j.Logger;
 
 /**
- * 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 whether the
- * current device is an edge device or a transit device.
+ * 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
+ * whether the current device is an edge device or a transit device.
  */
 public class DefaultGroupHandler {
     protected final Logger log = getLogger(getClass());
@@ -66,88 +60,78 @@
     protected final boolean isEdgeRouter;
     protected final MacAddress nodeMacAddr;
     protected LinkService linkService;
-    protected GroupService groupService;
+    protected FlowObjectiveService flowObjectiveService;
 
     protected HashMap<DeviceId, Set<PortNumber>> devicePortMap =
             new HashMap<DeviceId, Set<PortNumber>>();
     protected HashMap<PortNumber, DeviceId> portDeviceMap =
             new HashMap<PortNumber, DeviceId>();
+    protected HashMap<GroupKey, Integer> deviceNextObjectiveIds =
+            new HashMap<GroupKey, Integer>();
+    protected Random rand = new Random();
 
-    private GroupListener listener = new InternalGroupListener();
     protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
-    .register(URI.class)
-    .register(HashSet.class)
-    .register(DeviceId.class)
-    .register(PortNumber.class)
-    .register(NeighborSet.class)
-    .register(PolicyGroupIdentifier.class)
-    .register(PolicyGroupParams.class)
-    .register(GroupBucketIdentifier.class)
-    .register(GroupBucketIdentifier.BucketOutputType.class);
+            .register(URI.class).register(HashSet.class)
+            .register(DeviceId.class).register(PortNumber.class)
+            .register(NeighborSet.class).register(PolicyGroupIdentifier.class)
+            .register(PolicyGroupParams.class)
+            .register(GroupBucketIdentifier.class)
+            .register(GroupBucketIdentifier.BucketOutputType.class);
 
-    protected DefaultGroupHandler(DeviceId deviceId,
-                               ApplicationId appId,
-                               DeviceProperties config,
-                               LinkService linkService,
-                               GroupService groupService) {
+    protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
+                                  DeviceProperties config,
+                                  LinkService linkService,
+                                  FlowObjectiveService flowObjService) {
         this.deviceId = checkNotNull(deviceId);
         this.appId = checkNotNull(appId);
         this.deviceConfig = checkNotNull(config);
         this.linkService = checkNotNull(linkService);
-        this.groupService = checkNotNull(groupService);
         allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
         nodeSegmentId = config.getSegmentId(deviceId);
         isEdgeRouter = config.isEdgeDevice(deviceId);
         nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
-
-        this.groupService.addListener(listener);
+        this.flowObjectiveService = flowObjService;
 
         populateNeighborMaps();
     }
 
     /**
-     * Creates a group handler object based on the type of device. If
-     * device is of edge type it returns edge group handler, else it
-     * returns transit group handler.
+     * Creates a group handler object based on the type of device. If device is
+     * of edge type it returns edge group handler, else it returns transit group
+     * handler.
      *
      * @param deviceId device identifier
      * @param appId application identifier
      * @param config interface to retrieve the device properties
      * @param linkService link service object
-     * @param groupService group service object
+     * @param flowObjService flow objective service object
      * @return default group handler type
      */
     public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
-                                                  ApplicationId appId,
-                                                  DeviceProperties config,
-                                                  LinkService linkService,
-                                                  GroupService groupService) {
+                                                         ApplicationId appId,
+                                                         DeviceProperties config,
+                                                         LinkService linkService,
+                                                         FlowObjectiveService flowObjService) {
         if (config.isEdgeDevice(deviceId)) {
-            return new DefaultEdgeGroupHandler(deviceId,
-                                               appId,
-                                               config,
-                                               linkService,
-                                               groupService);
+            return new DefaultEdgeGroupHandler(deviceId, appId, config,
+                                               linkService, flowObjService);
         } else {
-            return new DefaultTransitGroupHandler(deviceId,
-                                                  appId,
-                                                  config,
-                                                  linkService,
-                                                  groupService);
+            return new DefaultTransitGroupHandler(deviceId, appId, config,
+                                                  linkService, flowObjService);
         }
     }
 
     /**
-     * Creates the auto created groups for this device based on the
-     * current snapshot of the topology.
+     * Creates the auto created groups for this device based on the current
+     * snapshot of the topology.
      */
-    //Empty implementations to be overridden by derived classes
+    // Empty implementations to be overridden by derived classes
     public void createGroups() {
     }
 
     /**
-     * Performs group creation or update procedures when a new link
-     * is discovered on this device.
+     * Performs group creation or update procedures when a new link is
+     * discovered on this device.
      *
      * @param newLink new neighbor link
      */
@@ -158,16 +142,14 @@
             return;
         }
 
-
         if (!newLink.src().deviceId().equals(deviceId)) {
             log.warn("linkUp: deviceId{} doesn't match with link src{}",
-                     deviceId,
-                     newLink.src().deviceId());
+                     deviceId, newLink.src().deviceId());
             return;
         }
 
-        log.debug("Device {} linkUp at local port {} to neighbor {}",
-                  deviceId, newLink.src().port(), newLink.dst().deviceId());
+        log.debug("Device {} linkUp at local port {} to neighbor {}", deviceId,
+                  newLink.src().port(), newLink.dst().deviceId());
         if (devicePortMap.get(newLink.dst().deviceId()) == null) {
             // New Neighbor
             newNeighbor(newLink);
@@ -178,8 +160,7 @@
     }
 
     /**
-     * Performs group recovery procedures when a port goes down
-     * on this device.
+     * Performs group recovery procedures when a port goes down on this device.
      *
      * @param port port number that has gone down
      */
@@ -188,35 +169,34 @@
             log.warn("portDown: unknown port");
             return;
         }
-        log.debug("Device {} portDown {} to neighbor {}",
-                  deviceId, port, portDeviceMap.get(port));
-        Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
-                                                     portDeviceMap.get(port),
-                                                     devicePortMap.keySet());
+        log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
+                  portDeviceMap.get(port));
+        Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(portDeviceMap
+                                                                                .get(port),
+                                                                        devicePortMap
+                                                                                .keySet());
         for (NeighborSet ns : nsSet) {
             // Create the bucket to be removed
-            TrafficTreatment.Builder tBuilder =
-                    DefaultTrafficTreatment.builder();
+            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+                    .builder();
             tBuilder.setOutput(port)
-                    .setEthDst(deviceConfig.getDeviceMac(
-                         portDeviceMap.get(port)))
-                    .setEthSrc(nodeMacAddr);
+                    .setEthDst(deviceConfig.getDeviceMac(portDeviceMap
+                                       .get(port))).setEthSrc(nodeMacAddr);
             if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
-                tBuilder.pushMpls()
-                    .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
+                tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
+                                                    .getEdgeLabel()));
             }
-            GroupBucket removeBucket = DefaultGroupBucket.
-                    createSelectGroupBucket(tBuilder.build());
-            GroupBuckets removeBuckets = new GroupBuckets(
-                                               Arrays.asList(removeBucket));
-            log.debug("portDown in device{}: "
-                    + "groupService.removeBucketsFromGroup "
-                    + "for neighborset{}", deviceId, ns);
-            groupService.removeBucketsFromGroup(deviceId,
-                                                getGroupKey(ns),
-                                                removeBuckets,
-                                                getGroupKey(ns),
-                                                appId);
+            /*
+             * GroupBucket removeBucket = DefaultGroupBucket.
+             * createSelectGroupBucket(tBuilder.build()); GroupBuckets
+             * removeBuckets = new GroupBuckets( Arrays.asList(removeBucket));
+             * log.debug("portDown in device{}: " +
+             * "groupService.removeBucketsFromGroup " + "for neighborset{}",
+             * deviceId, ns); groupService.removeBucketsFromGroup(deviceId,
+             * getGroupKey(ns), removeBuckets, getGroupKey(ns), appId);
+             */
+            //TODO: Use next objective API to update the previously created
+            //next objectives.
         }
 
         devicePortMap.get(portDeviceMap.get(port)).remove(port);
@@ -224,33 +204,44 @@
     }
 
     /**
-     * Returns a group associated with the key.
+     * Returns the next objective associated with the neighborset.
+     * If there is no next objective for this neighborset, this API
+     * would create a next objective and return.
      *
-     * @param key cookie associated with the group
-     * @return group if found or null
+     * @param ns neighborset
+     * @return int if found or -1
      */
-    public Group getGroup(GroupKey key) {
-        return groupService.getGroup(deviceId, key);
+    public int getNextObjectiveId(NeighborSet ns) {
+        Integer nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
+        if (nextId == null) {
+            createGroupsFromNeighborsets(Collections.singleton(ns));
+            nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
+            if (nextId == null) {
+                log.warn("getNextObjectiveId: unable to create next objective");
+                return -1;
+            }
+        }
+        return nextId.intValue();
     }
 
-    //Empty implementation
+    // Empty implementation
     protected void newNeighbor(Link newLink) {
     }
 
-    //Empty implementation
+    // Empty implementation
     protected void newPortToExistingNeighbor(Link newLink) {
     }
 
-    //Empty implementation
-    protected Set<NeighborSet> computeImpactedNeighborsetForPortEvent(
-                                    DeviceId impactedNeighbor,
-                                    Set<DeviceId> updatedNeighbors) {
+    // Empty implementation
+    protected Set<NeighborSet>
+        computeImpactedNeighborsetForPortEvent(DeviceId impactedNeighbor,
+                                               Set<DeviceId> updatedNeighbors) {
         return null;
     }
 
     private void populateNeighborMaps() {
         Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
-        for (Link link:outgoingLinks) {
+        for (Link link : outgoingLinks) {
             if (link.type() != Link.Type.DIRECT) {
                 continue;
             }
@@ -258,7 +249,8 @@
         }
     }
 
-    protected void addNeighborAtPort(DeviceId neighborId, PortNumber portToNeighbor) {
+    protected void addNeighborAtPort(DeviceId neighborId,
+                                     PortNumber portToNeighbor) {
         // Update DeviceToPort database
         log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
                   deviceId, neighborId, portToNeighbor);
@@ -276,8 +268,7 @@
         }
     }
 
-    protected Set<Set<DeviceId>>
-            getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
+    protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
         List<DeviceId> list = new ArrayList<DeviceId>(neighbors);
         Set<Set<DeviceId>> sets = new HashSet<Set<DeviceId>>();
         // get the number of elements in the neighbors
@@ -304,15 +295,14 @@
         return (deviceConfig.getSegmentId(deviceId) == sId);
     }
 
-    protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(
-                                   Set<DeviceId> neighbors) {
+    protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
 
         List<Integer> nsSegmentIds = new ArrayList<Integer>();
 
         // Always pair up with no edge label
-        //If (neighbors.size() == 1) {
+        // If (neighbors.size() == 1) {
         nsSegmentIds.add(-1);
-        //}
+        // }
 
         // Filter out SegmentIds matching with the
         // nodes in the combo
@@ -338,70 +328,28 @@
 
     protected void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) {
         for (NeighborSet ns : nsSet) {
-            // Create the bucket array from the neighbor set
-            List<GroupBucket> buckets = new ArrayList<GroupBucket>();
+            int nextId = flowObjectiveService.allocateNextId();
+            NextObjective.Builder nextObjBuilder = DefaultNextObjective
+                    .builder().withId(nextId)
+                    .withType(NextObjective.Type.HASHED).fromApp(appId);
             for (DeviceId d : ns.getDeviceIds()) {
                 for (PortNumber sp : devicePortMap.get(d)) {
-                    TrafficTreatment.Builder tBuilder =
-                            DefaultTrafficTreatment.builder();
+                    TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+                            .builder();
                     tBuilder.setOutput(sp)
                             .setEthDst(deviceConfig.getDeviceMac(d))
                             .setEthSrc(nodeMacAddr);
                     if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
-                        tBuilder.pushMpls()
-                                .setMpls(MplsLabel.
-                                         mplsLabel(ns.getEdgeLabel()));
+                        tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
+                                                            .getEdgeLabel()));
                     }
-                    buckets.add(DefaultGroupBucket.createSelectGroupBucket(
-                                                                tBuilder.build()));
+                    nextObjBuilder.addTreatment(tBuilder.build());
                 }
             }
-            GroupBuckets groupBuckets = new GroupBuckets(buckets);
-            GroupDescription newGroupDesc = new DefaultGroupDescription(
-                                      deviceId,
-                                      Group.Type.SELECT,
-                                      groupBuckets,
-                                      getGroupKey(ns),
-                                      appId);
-            log.debug("createGroupsFromNeighborsets: "
-                    + "groupService.addGroup for neighborset{}", ns);
-            groupService.addGroup(newGroupDesc);
-        }
-    }
 
-    protected void handleGroupEvent(GroupEvent event) {
-        switch (event.type()) {
-        case GROUP_ADDED:
-            log.debug("Received GROUP_ADDED from group service "
-                    + "for device {} with group key{} with id{}",
-                      event.subject().deviceId(),
-                      event.subject().appCookie(),
-                      event.subject().id());
-            break;
-        case GROUP_UPDATED:
-            log.trace("Received GROUP_UPDATED from group service "
-                    + "for device {} with group key{} with id{}",
-                      event.subject().deviceId(),
-                      event.subject().appCookie(),
-                      event.subject().id());
-            break;
-        case GROUP_REMOVED:
-            log.debug("Received GROUP_REMOVED from group service "
-                    + "for device {} with group key{} with id{}",
-                      event.subject().deviceId(),
-                      event.subject().appCookie(),
-                      event.subject().id());
-            break;
-        default:
-            break;
-        }
-    }
-
-    private class InternalGroupListener implements GroupListener {
-
-        @Override
-        public void event(GroupEvent event) {
-            handleGroupEvent(event);
+            NextObjective nextObj = nextObjBuilder.add();
+            flowObjectiveService.next(deviceId, nextObj);
+            deviceNextObjectiveIds.put(getGroupKey(ns), nextId);
         }
     }