e792bf6663dd62618405a8a4fb59db6ed244fd76
[onosfw.git] /
1 /*
2  * Copyright 2015 Open Networking Laboratory
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.onosproject.segmentrouting.grouphandler;
17
18 import static com.google.common.base.Preconditions.checkNotNull;
19 import static org.slf4j.LoggerFactory.getLogger;
20
21 import java.net.URI;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.stream.Collectors;
30
31 import org.onlab.packet.Ip4Prefix;
32 import org.onlab.packet.IpPrefix;
33 import org.onlab.packet.MacAddress;
34 import org.onlab.packet.MplsLabel;
35 import org.onlab.util.KryoNamespace;
36 import org.onosproject.core.ApplicationId;
37 import org.onosproject.net.DeviceId;
38 import org.onosproject.net.Link;
39 import org.onosproject.net.PortNumber;
40 import org.onosproject.net.flow.DefaultTrafficTreatment;
41 import org.onosproject.net.flow.TrafficSelector;
42 import org.onosproject.net.flow.TrafficTreatment;
43 import org.onosproject.net.flowobjective.DefaultNextObjective;
44 import org.onosproject.net.flowobjective.FlowObjectiveService;
45 import org.onosproject.net.flowobjective.NextObjective;
46 import org.onosproject.net.flowobjective.Objective;
47 import org.onosproject.net.flowobjective.ObjectiveContext;
48 import org.onosproject.net.flowobjective.ObjectiveError;
49 import org.onosproject.net.group.DefaultGroupKey;
50 import org.onosproject.net.group.GroupKey;
51 import org.onosproject.net.link.LinkService;
52 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
53 import org.onosproject.segmentrouting.config.DeviceProperties;
54 import org.onosproject.store.service.EventuallyConsistentMap;
55 import org.slf4j.Logger;
56
57 /**
58  * Default ECMP group handler creation module. This component creates a set of
59  * ECMP groups for every neighbor that this device is connected to based on
60  * whether the current device is an edge device or a transit device.
61  */
62 public class DefaultGroupHandler {
63     protected static final Logger log = getLogger(DefaultGroupHandler.class);
64
65     protected final DeviceId deviceId;
66     protected final ApplicationId appId;
67     protected final DeviceProperties deviceConfig;
68     protected final List<Integer> allSegmentIds;
69     protected int nodeSegmentId = -1;
70     protected boolean isEdgeRouter = false;
71     protected MacAddress nodeMacAddr = null;
72     protected LinkService linkService;
73     protected FlowObjectiveService flowObjectiveService;
74
75     protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
76             new ConcurrentHashMap<>();
77     protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
78             new ConcurrentHashMap<>();
79     protected EventuallyConsistentMap<
80         NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null;
81     protected EventuallyConsistentMap<
82             SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null;
83
84     protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
85             .register(URI.class).register(HashSet.class)
86             .register(DeviceId.class).register(PortNumber.class)
87             .register(NeighborSet.class).register(PolicyGroupIdentifier.class)
88             .register(PolicyGroupParams.class)
89             .register(GroupBucketIdentifier.class)
90             .register(GroupBucketIdentifier.BucketOutputType.class);
91
92     protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
93                                   DeviceProperties config,
94                                   LinkService linkService,
95                                   FlowObjectiveService flowObjService,
96                                   EventuallyConsistentMap<
97                                           NeighborSetNextObjectiveStoreKey,
98                                           Integer> nsNextObjStore,
99                                   EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
100                                           Integer> subnetNextObjStore) {
101         this.deviceId = checkNotNull(deviceId);
102         this.appId = checkNotNull(appId);
103         this.deviceConfig = checkNotNull(config);
104         this.linkService = checkNotNull(linkService);
105         this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
106         try {
107             this.nodeSegmentId = config.getSegmentId(deviceId);
108             this.isEdgeRouter = config.isEdgeDevice(deviceId);
109             this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
110         } catch (DeviceConfigNotFoundException e) {
111             log.warn(e.getMessage()
112                     + " Skipping value assignment in DefaultGroupHandler");
113         }
114         this.flowObjectiveService = flowObjService;
115         this.nsNextObjStore = nsNextObjStore;
116         this.subnetNextObjStore = subnetNextObjStore;
117
118         populateNeighborMaps();
119     }
120
121     /**
122      * Creates a group handler object based on the type of device. If device is
123      * of edge type it returns edge group handler, else it returns transit group
124      * handler.
125      *
126      * @param deviceId device identifier
127      * @param appId application identifier
128      * @param config interface to retrieve the device properties
129      * @param linkService link service object
130      * @param flowObjService flow objective service object
131      * @param nsNextObjStore NeighborSet next objective store map
132      * @param subnetNextObjStore subnet next objective store map
133      * @throws DeviceConfigNotFoundException if the device configuration is not found
134      * @return default group handler type
135      */
136     public static DefaultGroupHandler createGroupHandler(DeviceId deviceId,
137                                                          ApplicationId appId,
138                                                          DeviceProperties config,
139                                                          LinkService linkService,
140                                                          FlowObjectiveService flowObjService,
141                                                          EventuallyConsistentMap<
142                                                                  NeighborSetNextObjectiveStoreKey,
143                                                                  Integer> nsNextObjStore,
144                                                          EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
145                                                                  Integer> subnetNextObjStore)
146                                                          throws DeviceConfigNotFoundException {
147         // handle possible exception in the caller
148         if (config.isEdgeDevice(deviceId)) {
149             return new DefaultEdgeGroupHandler(deviceId, appId, config,
150                                                linkService,
151                                                flowObjService,
152                                                nsNextObjStore,
153                                                subnetNextObjStore);
154         } else {
155             return new DefaultTransitGroupHandler(deviceId, appId, config,
156                                                   linkService,
157                                                   flowObjService,
158                                                   nsNextObjStore,
159                                                   subnetNextObjStore);
160         }
161     }
162
163     /**
164      * Creates the auto created groups for this device based on the current
165      * snapshot of the topology.
166      */
167     // Empty implementations to be overridden by derived classes
168     public void createGroups() {
169     }
170
171     /**
172      * Performs group creation or update procedures when a new link is
173      * discovered on this device.
174      *
175      * @param newLink new neighbor link
176      */
177     public void linkUp(Link newLink, boolean isMaster) {
178
179         if (newLink.type() != Link.Type.DIRECT) {
180             log.warn("linkUp: unknown link type");
181             return;
182         }
183
184         if (!newLink.src().deviceId().equals(deviceId)) {
185             log.warn("linkUp: deviceId{} doesn't match with link src{}",
186                      deviceId, newLink.src().deviceId());
187             return;
188         }
189
190         log.info("* LinkUP: Device {} linkUp at local port {} to neighbor {}", deviceId,
191                  newLink.src().port(), newLink.dst().deviceId());
192         MacAddress dstMac;
193         try {
194             dstMac = deviceConfig.getDeviceMac(newLink.dst().deviceId());
195         } catch (DeviceConfigNotFoundException e) {
196             log.warn(e.getMessage() + " Aborting linkUp.");
197             return;
198         }
199
200         addNeighborAtPort(newLink.dst().deviceId(),
201                           newLink.src().port());
202         /*if (devicePortMap.get(newLink.dst().deviceId()) == null) {
203             // New Neighbor
204             newNeighbor(newLink);
205         } else {
206             // Old Neighbor
207             newPortToExistingNeighbor(newLink);
208         }*/
209         Set<NeighborSet> nsSet = nsNextObjStore.keySet()
210                 .stream()
211                 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
212                 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
213                 .filter((ns) -> (ns.getDeviceIds()
214                         .contains(newLink.dst().deviceId())))
215                 .collect(Collectors.toSet());
216         log.trace("linkUp: nsNextObjStore contents for device {}:",
217                 deviceId,
218                 nsSet);
219         for (NeighborSet ns : nsSet) {
220             // Create the new bucket to be updated
221             TrafficTreatment.Builder tBuilder =
222                     DefaultTrafficTreatment.builder();
223             tBuilder.setOutput(newLink.src().port())
224                     .setEthDst(dstMac)
225                     .setEthSrc(nodeMacAddr);
226             if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
227                 tBuilder.pushMpls()
228                         .copyTtlOut()
229                         .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
230             }
231
232             Integer nextId = nsNextObjStore.
233                     get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
234             if (nextId != null) {
235                 NextObjective.Builder nextObjBuilder = DefaultNextObjective
236                         .builder().withId(nextId)
237                         .withType(NextObjective.Type.HASHED).fromApp(appId);
238
239                 nextObjBuilder.addTreatment(tBuilder.build());
240
241                 log.info("**linkUp in device {}: Adding Bucket "
242                         + "with Port {} to next object id {} and amIMaster:{}",
243                         deviceId,
244                         newLink.src().port(),
245                         nextId, isMaster);
246
247                 if (isMaster) {
248                     NextObjective nextObjective = nextObjBuilder.
249                             addToExisting(new SRNextObjectiveContext(deviceId));
250                     flowObjectiveService.next(deviceId, nextObjective);
251                 }
252             } else {
253                 log.warn("linkUp in device {}, but global store has no record "
254                         + "for neighbor-set {}", deviceId, ns);
255             }
256         }
257     }
258
259     /**
260      * Performs group recovery procedures when a port goes down on this device.
261      *
262      * @param port port number that has gone down
263      */
264     public void portDown(PortNumber port) {
265         if (portDeviceMap.get(port) == null) {
266             log.warn("portDown: unknown port");
267             return;
268         }
269
270         MacAddress dstMac;
271         try {
272             dstMac = deviceConfig.getDeviceMac(portDeviceMap.get(port));
273         } catch (DeviceConfigNotFoundException e) {
274             log.warn(e.getMessage() + " Aborting portDown.");
275             return;
276         }
277
278         log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
279                   portDeviceMap.get(port));
280         /*Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(portDeviceMap
281                                                                                 .get(port),
282                                                                         devicePortMap
283                                                                                 .keySet());*/
284         Set<NeighborSet> nsSet = nsNextObjStore.keySet()
285                 .stream()
286                 .filter((nsStoreEntry) -> (nsStoreEntry.deviceId().equals(deviceId)))
287                 .map((nsStoreEntry) -> (nsStoreEntry.neighborSet()))
288                 .filter((ns) -> (ns.getDeviceIds()
289                         .contains(portDeviceMap.get(port))))
290                 .collect(Collectors.toSet());
291         log.trace("portDown: nsNextObjStore contents for device {}:",
292                   deviceId,
293                   nsSet);
294         for (NeighborSet ns : nsSet) {
295             // Create the bucket to be removed
296             TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
297                     .builder();
298             tBuilder.setOutput(port)
299                     .setEthDst(dstMac)
300                     .setEthSrc(nodeMacAddr);
301             if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
302                 tBuilder.pushMpls()
303                         .copyTtlOut()
304                         .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
305             }
306
307             Integer nextId = nsNextObjStore.
308                     get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
309             if (nextId != null) {
310                 NextObjective.Builder nextObjBuilder = DefaultNextObjective
311                         .builder().withType(NextObjective.Type.SIMPLE).withId(nextId).fromApp(appId);
312
313                 nextObjBuilder.addTreatment(tBuilder.build());
314
315                 log.info("**portDown in device {}: Removing Bucket "
316                         + "with Port {} to next object id {}",
317                         deviceId,
318                         port,
319                         nextId);
320                 // should do removefromexisting and only if master
321                 /*NextObjective nextObjective = nextObjBuilder.
322                         remove(new SRNextObjectiveContext(deviceId));
323
324                 flowObjectiveService.next(deviceId, nextObjective);*/
325             }
326
327         }
328
329         devicePortMap.get(portDeviceMap.get(port)).remove(port);
330         portDeviceMap.remove(port);
331     }
332
333     /**
334      * Returns the next objective associated with the neighborset.
335      * If there is no next objective for this neighborset, this API
336      * would create a next objective and return. Optionally metadata can be
337      * passed in for the creation of the next objective.
338      *
339      * @param ns neighborset
340      * @param meta metadata passed into the creation of a Next Objective
341      * @return int if found or -1 if there are errors in the creation of the
342      *          neighbor set.
343      */
344     public int getNextObjectiveId(NeighborSet ns, TrafficSelector meta) {
345         Integer nextId = nsNextObjStore.
346                 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
347         if (nextId == null) {
348             log.trace("getNextObjectiveId in device{}: Next objective id "
349                     + "not found for {} and creating", deviceId, ns);
350             log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
351                       deviceId,
352                       nsNextObjStore.entrySet()
353                       .stream()
354                       .filter((nsStoreEntry) ->
355                       (nsStoreEntry.getKey().deviceId().equals(deviceId)))
356                       .collect(Collectors.toList()));
357             createGroupsFromNeighborsets(Collections.singleton(ns), meta);
358             nextId = nsNextObjStore.
359                     get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
360             if (nextId == null) {
361                 log.warn("getNextObjectiveId: unable to create next objective");
362                 return -1;
363             } else {
364                 log.debug("getNextObjectiveId in device{}: Next objective id {} "
365                     + "created for {}", deviceId, nextId, ns);
366             }
367         } else {
368             log.trace("getNextObjectiveId in device{}: Next objective id {} "
369                     + "found for {}", deviceId, nextId, ns);
370         }
371         return nextId;
372     }
373
374     /**
375      * Returns the next objective associated with the subnet.
376      * If there is no next objective for this subnet, this API
377      * would create a next objective and return.
378      *
379      * @param prefix subnet information
380      * @return int if found or -1
381      */
382     public int getSubnetNextObjectiveId(IpPrefix prefix) {
383         Integer nextId = subnetNextObjStore.
384                 get(new SubnetNextObjectiveStoreKey(deviceId, prefix));
385
386         return (nextId != null) ? nextId : -1;
387     }
388
389     /**
390      * Checks if the next objective ID (group) for the neighbor set exists or not.
391      *
392      * @param ns neighbor set to check
393      * @return true if it exists, false otherwise
394      */
395     public boolean hasNextObjectiveId(NeighborSet ns) {
396         Integer nextId = nsNextObjStore.
397                 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
398         if (nextId == null) {
399             return false;
400         }
401
402         return true;
403     }
404
405     // Empty implementation
406     protected void newNeighbor(Link newLink) {
407     }
408
409     // Empty implementation
410     protected void newPortToExistingNeighbor(Link newLink) {
411     }
412
413     // Empty implementation
414     protected Set<NeighborSet>
415         computeImpactedNeighborsetForPortEvent(DeviceId impactedNeighbor,
416                                                Set<DeviceId> updatedNeighbors) {
417         return null;
418     }
419
420     private void populateNeighborMaps() {
421         Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
422         for (Link link : outgoingLinks) {
423             if (link.type() != Link.Type.DIRECT) {
424                 continue;
425             }
426             addNeighborAtPort(link.dst().deviceId(), link.src().port());
427         }
428     }
429
430     protected void addNeighborAtPort(DeviceId neighborId,
431                                      PortNumber portToNeighbor) {
432         // Update DeviceToPort database
433         log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
434                   deviceId, neighborId, portToNeighbor);
435         Set<PortNumber> ports = Collections
436                 .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
437         ports.add(portToNeighbor);
438         Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
439         if (portnums != null) {
440             portnums.add(portToNeighbor);
441         }
442
443         // Update portToDevice database
444         DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
445         if (prev != null) {
446             log.warn("Device: {} port: {} has neighbor: {}. NOT updating "
447                     + "to neighbor: {}", deviceId, portToNeighbor, prev, neighborId);
448         }
449     }
450
451     protected Set<Set<DeviceId>> getPowerSetOfNeighbors(Set<DeviceId> neighbors) {
452         List<DeviceId> list = new ArrayList<>(neighbors);
453         Set<Set<DeviceId>> sets = new HashSet<>();
454         // get the number of elements in the neighbors
455         int elements = list.size();
456         // the number of members of a power set is 2^n
457         // including the empty set
458         int powerElements = (1 << elements);
459
460         // run a binary counter for the number of power elements
461         // NOTE: Exclude empty set
462         for (long i = 1; i < powerElements; i++) {
463             Set<DeviceId> neighborSubSet = new HashSet<>();
464             for (int j = 0; j < elements; j++) {
465                 if ((i >> j) % 2 == 1) {
466                     neighborSubSet.add(list.get(j));
467                 }
468             }
469             sets.add(neighborSubSet);
470         }
471         return sets;
472     }
473
474     private boolean isSegmentIdSameAsNodeSegmentId(DeviceId deviceId, int sId) {
475         int segmentId;
476         try {
477             segmentId = deviceConfig.getSegmentId(deviceId);
478         } catch (DeviceConfigNotFoundException e) {
479             log.warn(e.getMessage() + " Aborting isSegmentIdSameAsNodeSegmentId.");
480             return false;
481         }
482
483         return segmentId == sId;
484     }
485
486     protected List<Integer> getSegmentIdsTobePairedWithNeighborSet(Set<DeviceId> neighbors) {
487
488         List<Integer> nsSegmentIds = new ArrayList<>();
489
490         // Always pair up with no edge label
491         // If (neighbors.size() == 1) {
492         nsSegmentIds.add(-1);
493         // }
494
495         // Filter out SegmentIds matching with the
496         // nodes in the combo
497         for (Integer sId : allSegmentIds) {
498             if (sId.equals(nodeSegmentId)) {
499                 continue;
500             }
501             boolean filterOut = false;
502             // Check if the edge label being set is of
503             // any node in the Neighbor set
504             for (DeviceId deviceId : neighbors) {
505                 if (isSegmentIdSameAsNodeSegmentId(deviceId, sId)) {
506                     filterOut = true;
507                     break;
508                 }
509             }
510             if (!filterOut) {
511                 nsSegmentIds.add(sId);
512             }
513         }
514         return nsSegmentIds;
515     }
516
517     /**
518      * Creates Groups from a set of NeighborSet given.
519      *
520      * @param nsSet a set of NeighborSet
521      * @param meta metadata passed into the creation of a Next Objective
522      */
523     public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet,
524                                              TrafficSelector meta) {
525         for (NeighborSet ns : nsSet) {
526             int nextId = flowObjectiveService.allocateNextId();
527             NextObjective.Builder nextObjBuilder = DefaultNextObjective
528                     .builder().withId(nextId)
529                     .withType(NextObjective.Type.HASHED).fromApp(appId);
530             for (DeviceId neighborId : ns.getDeviceIds()) {
531                 if (devicePortMap.get(neighborId) == null) {
532                     log.warn("Neighbor {} is not in the port map yet for dev:{}",
533                              neighborId, deviceId);
534                     return;
535                 } else if (devicePortMap.get(neighborId).size() == 0) {
536                     log.warn("There are no ports for "
537                             + "the Device {} in the port map yet", neighborId);
538                     return;
539                 }
540
541                 MacAddress neighborMac;
542                 try {
543                     neighborMac = deviceConfig.getDeviceMac(neighborId);
544                 } catch (DeviceConfigNotFoundException e) {
545                     log.warn(e.getMessage() + " Aborting createGroupsFromNeighborsets.");
546                     return;
547                 }
548
549                 for (PortNumber sp : devicePortMap.get(neighborId)) {
550                     TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
551                             .builder();
552                     tBuilder.setEthDst(neighborMac)
553                             .setEthSrc(nodeMacAddr);
554                     if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
555                         tBuilder.pushMpls()
556                                 .copyTtlOut()
557                                 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
558                     }
559                     tBuilder.setOutput(sp);
560                     nextObjBuilder.addTreatment(tBuilder.build());
561                 }
562             }
563             if (meta != null) {
564                 nextObjBuilder.setMeta(meta);
565             }
566             NextObjective nextObj = nextObjBuilder.
567                     add(new SRNextObjectiveContext(deviceId));
568             log.info("**createGroupsFromNeighborsets: Submited "
569                     + "next objective {} in device {}",
570                     nextId, deviceId);
571             flowObjectiveService.next(deviceId, nextObj);
572             nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, ns),
573                                nextId);
574         }
575     }
576
577
578     public void createGroupsFromSubnetConfig() {
579         Map<Ip4Prefix, List<PortNumber>> subnetPortMap =
580                 this.deviceConfig.getSubnetPortsMap(this.deviceId);
581         // Construct a broadcast group for each subnet
582         subnetPortMap.forEach((subnet, ports) -> {
583             SubnetNextObjectiveStoreKey key =
584                     new SubnetNextObjectiveStoreKey(deviceId, subnet);
585
586             if (subnetNextObjStore.containsKey(key)) {
587                 log.debug("Broadcast group for device {} and subnet {} exists",
588                           deviceId, subnet);
589                 return;
590             }
591
592             int nextId = flowObjectiveService.allocateNextId();
593
594             NextObjective.Builder nextObjBuilder = DefaultNextObjective
595                     .builder().withId(nextId)
596                     .withType(NextObjective.Type.BROADCAST).fromApp(appId);
597
598             ports.forEach(port -> {
599                 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
600                 tBuilder.popVlan();
601                 tBuilder.setOutput(port);
602                 nextObjBuilder.addTreatment(tBuilder.build());
603             });
604
605             NextObjective nextObj = nextObjBuilder.add();
606             flowObjectiveService.next(deviceId, nextObj);
607             log.debug("createGroupFromSubnetConfig: Submited "
608                               + "next objective {} in device {}",
609                       nextId, deviceId);
610
611             subnetNextObjStore.put(key, nextId);
612         });
613     }
614
615     public GroupKey getGroupKey(Object obj) {
616         return new DefaultGroupKey(kryo.build().serialize(obj));
617     }
618
619     /**
620      * Removes groups for the next objective ID given.
621      *
622      * @param objectiveId next objective ID to remove
623      * @return true if succeeds, false otherwise
624      */
625     public boolean removeGroup(int objectiveId) {
626
627         if (nsNextObjStore.containsValue(objectiveId)) {
628             NextObjective.Builder nextObjBuilder = DefaultNextObjective
629                     .builder().withId(objectiveId)
630                     .withType(NextObjective.Type.HASHED).fromApp(appId);
631             NextObjective nextObjective = nextObjBuilder.
632                     remove(new SRNextObjectiveContext(deviceId));
633             log.info("**removeGroup: Submited "
634                     + "next objective {} in device {}",
635                     objectiveId, deviceId);
636             flowObjectiveService.next(deviceId, nextObjective);
637
638             for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry: nsNextObjStore.entrySet()) {
639                 if (entry.getValue().equals(objectiveId)) {
640                     nsNextObjStore.remove(entry.getKey());
641                     break;
642                 }
643             }
644             return true;
645         }
646
647         return false;
648     }
649
650     protected static class SRNextObjectiveContext implements ObjectiveContext {
651         final DeviceId deviceId;
652
653         SRNextObjectiveContext(DeviceId deviceId) {
654             this.deviceId = deviceId;
655         }
656         @Override
657         public void onSuccess(Objective objective) {
658             log.info("Next objective {} operation successful in device {}",
659                       objective.id(), deviceId);
660         }
661
662         @Override
663         public void onError(Objective objective, ObjectiveError error) {
664             log.warn("Next objective {} operation failed with error: {} in device {}",
665                      objective.id(), error, deviceId);
666         }
667     }
668 }