ONOS-1786: Updates group buckets when link status is changed.
 - Add null check for port map when creating new groups

Change-Id: I92b494d91e908011f2c08be850ccde648e647a09
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index c5bd6ba..cbad910 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -159,6 +159,7 @@
     private boolean repopulateRoutingRulesForRoutes(Set<ArrayList<DeviceId>> routes) {
         rulePopulator.resetCounter();
         for (ArrayList<DeviceId> link: routes) {
+            // When only the source device is defined, reinstall routes to all other devices
             if (link.size() == 1) {
                 ECMPShortestPathGraph ecmpSpg = new ECMPShortestPathGraph(link.get(0), srManager);
                 if (populateEcmpRoutingRules(link.get(0), ecmpSpg)) {
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 154127e..09bfc50 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -173,6 +173,11 @@
         TrafficTreatment treatment = tbuilder.build();
         TrafficSelector selector = sbuilder.build();
 
+        if (srManager.getNextObjectiveId(deviceId, ns) <= 0) {
+            log.warn("No next objective in {} for ns: {}", deviceId, ns);
+            return false;
+        }
+
         ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
                 .builder()
                 .fromApp(srManager.appId)
@@ -183,7 +188,7 @@
                 .withPriority(100)
                 .withFlag(ForwardingObjective.Flag.SPECIFIC);
         log.debug("Installing IPv4 forwarding objective "
-                + "for router IP/subnet {} in switch {}",
+                        + "for router IP/subnet {} in switch {}",
                 ipPrefix,
                 deviceId);
         srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add());
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index b7167c8..9ace6b8 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -304,6 +304,10 @@
 
     private void processLinkRemoved(Link link) {
         log.debug("A link {} was removed", link.toString());
+        DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src().deviceId());
+        if (groupHandler != null) {
+            groupHandler.portDown(link.src().port());
+        }
         defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link);
     }
 
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
index e54f51f..4159d3c 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
@@ -25,7 +25,9 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.NextObjective;
 import org.onosproject.net.link.LinkService;
 
 /**
@@ -126,18 +128,18 @@
                         .setMpls(MplsLabel.
                                  mplsLabel(ns.getEdgeLabel()));
             }
-            /*GroupBucket updatedBucket = DefaultGroupBucket.
-                    createSelectGroupBucket(tBuilder.build());
-            GroupBuckets updatedBuckets = new GroupBuckets(
-                                        Arrays.asList(updatedBucket));
-            log.debug("newPortToExistingNeighborAtEdgeRouter: "
-                    + "groupService.addBucketsToGroup for neighborset{}", ns);
-            groupService.addBucketsToGroup(deviceId,
-                                           getGroupKey(ns),
-                                           updatedBuckets,
-                                           getGroupKey(ns),
-                                           appId);*/
-            //TODO: Use nextObjective APIs to update the next objective
+
+            Integer nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
+            if (nextId != null) {
+                NextObjective.Builder nextObjBuilder = DefaultNextObjective
+                        .builder().withId(nextId)
+                        .withType(NextObjective.Type.HASHED).fromApp(appId);
+
+                nextObjBuilder.addTreatment(tBuilder.build());
+
+                NextObjective nextObjective = nextObjBuilder.add();
+                flowObjectiveService.next(deviceId, nextObjective);
+            }
         }
     }
 
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 cb295db..1d9f4ec 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
@@ -186,17 +186,19 @@
                 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);
-             */
-            //TODO: Use next objective API to update the previously created
-            //next objectives.
+
+            Integer nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
+            if (nextId != null) {
+                NextObjective.Builder nextObjBuilder = DefaultNextObjective
+                        .builder().withType(NextObjective.Type.SIMPLE).withId(nextId).fromApp(appId);
+
+                nextObjBuilder.addTreatment(tBuilder.build());
+
+                NextObjective nextObjective = nextObjBuilder.remove();
+
+                flowObjectiveService.next(deviceId, nextObjective);
+            }
+
         }
 
         devicePortMap.get(portDeviceMap.get(port)).remove(port);
@@ -333,6 +335,11 @@
                     .builder().withId(nextId)
                     .withType(NextObjective.Type.HASHED).fromApp(appId);
             for (DeviceId d : ns.getDeviceIds()) {
+                if (devicePortMap.get(d) == null) {
+                    log.warn("Device {} is not in the port map yet", d);
+                    return;
+                }
+
                 for (PortNumber sp : devicePortMap.get(d)) {
                     TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
                             .builder();
@@ -341,7 +348,7 @@
                             .setEthSrc(nodeMacAddr);
                     if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
                         tBuilder.pushMpls().setMpls(MplsLabel.mplsLabel(ns
-                                                            .getEdgeLabel()));
+                                .getEdgeLabel()));
                     }
                     nextObjBuilder.addTreatment(tBuilder.build());
                 }
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
index 67e9eef..752ee2d 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
@@ -24,7 +24,9 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.NextObjective;
 import org.onosproject.net.link.LinkService;
 
 /**
@@ -114,18 +116,19 @@
                         .setMpls(MplsLabel.
                                  mplsLabel(ns.getEdgeLabel()));
             }
-            /*GroupBucket updatedBucket = DefaultGroupBucket.
-                    createSelectGroupBucket(tBuilder.build());
-            GroupBuckets updatedBuckets = new GroupBuckets(
-                                        Arrays.asList(updatedBucket));
-            log.debug("newPortToExistingNeighborAtEdgeRouter: "
-                    + "groupService.addBucketsToGroup for neighborset{}", ns);
-            groupService.addBucketsToGroup(deviceId,
-                                           getGroupKey(ns),
-                                           updatedBuckets,
-                                           getGroupKey(ns),
-                                           appId);*/
-            //TODO: Use nextObjective APIs to update the next objective
+
+
+            Integer nextId = deviceNextObjectiveIds.get(getGroupKey(ns));
+            if (nextId != null) {
+                NextObjective.Builder nextObjBuilder = DefaultNextObjective
+                        .builder().withId(nextId)
+                        .withType(NextObjective.Type.HASHED).fromApp(appId);
+
+                nextObjBuilder.addTreatment(tBuilder.build());
+
+                NextObjective nextObjective = nextObjBuilder.add();
+                flowObjectiveService.next(deviceId, nextObjective);
+            }
         }
     }
 
diff --git a/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java b/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
index 4e8d326..8e91a99 100644
--- a/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
+++ b/drivers/src/main/java/org/onosproject/driver/pipeline/SpringOpenTTP.java
@@ -73,6 +73,7 @@
 import org.slf4j.Logger;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -207,63 +208,140 @@
 
     @Override
     public void next(NextObjective nextObjective) {
-        switch (nextObjective.type()) {
-        case SIMPLE:
-            log.debug("processing SIMPLE next objective");
-            Collection<TrafficTreatment> treatments = nextObjective.next();
-            if (treatments.size() == 1) {
-                TrafficTreatment treatment = treatments.iterator().next();
-                GroupBucket bucket = DefaultGroupBucket
-                        .createIndirectGroupBucket(treatment);
-                final GroupKey key = new DefaultGroupKey(
-                                                         appKryo.serialize(nextObjective
-                                                                 .id()));
-                GroupDescription groupDescription = new DefaultGroupDescription(
-                                                    deviceId,
-                                                    GroupDescription.Type.INDIRECT,
-                                                    new GroupBuckets(
-                                                       Collections.singletonList(bucket)),
-                                                    key,
-                                                    nextObjective.appId());
-                groupService.addGroup(groupDescription);
-                pendingGroups.put(key, nextObjective);
+
+        if (nextObjective.op() == Objective.Operation.REMOVE) {
+            if (nextObjective.next() == null) {
+                removeGroup(nextObjective);
+            } else {
+                removeBucketFromGroup(nextObjective);
             }
-            break;
-        case HASHED:
-            log.debug("processing HASHED next objective");
-            List<GroupBucket> buckets = nextObjective
-                    .next()
-                    .stream()
-                    .map((treatment) -> DefaultGroupBucket
-                                 .createSelectGroupBucket(treatment))
-                    .collect(Collectors.toList());
-            if (!buckets.isEmpty()) {
-                final GroupKey key = new DefaultGroupKey(
-                                                         appKryo.serialize(nextObjective
-                                                                 .id()));
-                GroupDescription groupDescription = new DefaultGroupDescription(
-                                                            deviceId,
-                                                            GroupDescription.Type.SELECT,
-                                                            new GroupBuckets(buckets),
-                                                            key,
-                                                            nextObjective.appId());
-                groupService.addGroup(groupDescription);
-                pendingGroups.put(key, nextObjective);
+        } else if (nextObjective.op() == Objective.Operation.ADD) {
+            NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
+            if (nextGroup != null) {
+                addBucketToGroup(nextObjective);
+            } else {
+                addGroup(nextObjective);
             }
-            break;
-        case BROADCAST:
-        case FAILOVER:
-            log.debug("BROADCAST and FAILOVER next objectives not supported");
-            fail(nextObjective, ObjectiveError.UNSUPPORTED);
-            log.warn("Unsupported next objective type {}", nextObjective.type());
-            break;
-        default:
-            fail(nextObjective, ObjectiveError.UNKNOWN);
-            log.warn("Unknown next objective type {}", nextObjective.type());
+        } else {
+            log.warn("Unsupported operation {}", nextObjective.op());
         }
 
     }
 
+    private void removeGroup(NextObjective nextObjective) {
+        final GroupKey key = new DefaultGroupKey(
+                appKryo.serialize(nextObjective.id()));
+        groupService.removeGroup(deviceId, key, appId);
+    }
+
+    private void addGroup(NextObjective nextObjective) {
+        switch (nextObjective.type()) {
+            case SIMPLE:
+                log.debug("processing SIMPLE next objective");
+                Collection<TrafficTreatment> treatments = nextObjective.next();
+                if (treatments.size() == 1) {
+                    TrafficTreatment treatment = treatments.iterator().next();
+                    GroupBucket bucket = DefaultGroupBucket
+                            .createIndirectGroupBucket(treatment);
+                    final GroupKey key = new DefaultGroupKey(
+                            appKryo.serialize(nextObjective
+                                    .id()));
+                    GroupDescription groupDescription = new DefaultGroupDescription(
+                            deviceId,
+                            GroupDescription.Type.INDIRECT,
+                            new GroupBuckets(
+                                    Collections.singletonList(bucket)),
+                            key,
+                            nextObjective.appId());
+                    groupService.addGroup(groupDescription);
+                    pendingGroups.put(key, nextObjective);
+                }
+                break;
+            case HASHED:
+                log.debug("processing HASHED next objective");
+                List<GroupBucket> buckets = nextObjective
+                        .next()
+                        .stream()
+                        .map((treatment) -> DefaultGroupBucket
+                                .createSelectGroupBucket(treatment))
+                        .collect(Collectors.toList());
+                if (!buckets.isEmpty()) {
+                    final GroupKey key = new DefaultGroupKey(
+                            appKryo.serialize(nextObjective
+                                    .id()));
+                    GroupDescription groupDescription = new DefaultGroupDescription(
+                            deviceId,
+                            GroupDescription.Type.SELECT,
+                            new GroupBuckets(buckets),
+                            key,
+                            nextObjective.appId());
+                    groupService.addGroup(groupDescription);
+                    pendingGroups.put(key, nextObjective);
+                }
+                break;
+            case BROADCAST:
+            case FAILOVER:
+                log.debug("BROADCAST and FAILOVER next objectives not supported");
+                fail(nextObjective, ObjectiveError.UNSUPPORTED);
+                log.warn("Unsupported next objective type {}", nextObjective.type());
+                break;
+            default:
+                fail(nextObjective, ObjectiveError.UNKNOWN);
+                log.warn("Unknown next objective type {}", nextObjective.type());
+        }
+    }
+
+    private void addBucketToGroup(NextObjective nextObjective) {
+        Collection<TrafficTreatment> treatments = nextObjective.next();
+        TrafficTreatment treatment = treatments.iterator().next();
+        final GroupKey key = new DefaultGroupKey(
+                appKryo.serialize(nextObjective
+                        .id()));
+        Group group = groupService.getGroup(deviceId, key);
+        if (group == null) {
+            log.warn("Group is not found in {} for {}", deviceId, key);
+            return;
+        }
+        GroupBucket bucket;
+        if (group.type() == GroupDescription.Type.INDIRECT) {
+            bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
+        } else if (group.type() == GroupDescription.Type.SELECT) {
+            bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
+        } else {
+            log.warn("Unsupported Group type {}", group.type());
+            return;
+        }
+        GroupBuckets bucketsToAdd = new GroupBuckets(Arrays.asList(bucket));
+        groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
+    }
+
+    private void removeBucketFromGroup(NextObjective nextObjective) {
+        NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
+        if (nextGroup != null) {
+            Collection<TrafficTreatment> treatments = nextObjective.next();
+            TrafficTreatment treatment = treatments.iterator().next();
+            final GroupKey key = new DefaultGroupKey(
+                    appKryo.serialize(nextObjective
+                            .id()));
+            Group group = groupService.getGroup(deviceId, key);
+            if (group == null) {
+                log.warn("Group is not found in {} for {}", deviceId, key);
+                return;
+            }
+            GroupBucket bucket;
+            if (group.type() == GroupDescription.Type.INDIRECT) {
+                bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
+            } else if (group.type() == GroupDescription.Type.SELECT) {
+                bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
+            } else {
+                log.warn("Unsupported Group type {}", group.type());
+                return;
+            }
+            GroupBuckets removeBuckets = new GroupBuckets(Arrays.asList(bucket));
+            groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
+        }
+    }
+
     private Collection<FlowRule> processForward(ForwardingObjective fwd) {
         switch (fwd.flag()) {
         case SPECIFIC:
diff --git a/drivers/src/main/resources/onos-drivers.xml b/drivers/src/main/resources/onos-drivers.xml
index d7c61c8..db3fb72 100644
--- a/drivers/src/main/resources/onos-drivers.xml
+++ b/drivers/src/main/resources/onos-drivers.xml
@@ -23,7 +23,7 @@
         <behaviour api="org.onosproject.net.behaviour.Pipeliner"
                    impl="org.onosproject.driver.pipeline.OVSCorsaPipeline"/>
     </driver>
-    <driver name="spring-open-cpqd" manufacturer="Stanford University, Ericsson Research and CPqD Research" hwVersion="OpenFlow 1.3 Reference Userspace Switch" swVersion="Apr  6 2015 16:10:53">
+    <driver name="spring-open-cpqd" manufacturer="Stanford University, Ericsson Research and CPqD Research" hwVersion="OpenFlow 1.3 Reference Userspace Switch" swVersion=".*">
         <behaviour api="org.onosproject.net.behaviour.Pipeliner"
                    impl="org.onosproject.driver.pipeline.SpringOpenTTP"/>
     </driver>