Saurav Das
Committed by Gerrit Code Review

CORD-354 OF-DPA support for link-failures.

Bug fix in flowObjectives store. Adding a removeNextGroup API to the store.

Change-Id: I5890411e5b4eabdc057402687ada26e539500f8f
...@@ -597,11 +597,20 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -597,11 +597,20 @@ public class SegmentRoutingManager implements SegmentRoutingService {
597 } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED || 597 } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED ||
598 event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED || 598 event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
599 event.type() == DeviceEvent.Type.DEVICE_UPDATED) { 599 event.type() == DeviceEvent.Type.DEVICE_UPDATED) {
600 - if (deviceService.isAvailable(((Device) event.subject()).id())) { 600 + DeviceId deviceId = ((Device) event.subject()).id();
601 + if (deviceService.isAvailable(deviceId)) {
601 log.info("Processing device event {} for available device {}", 602 log.info("Processing device event {} for available device {}",
602 event.type(), ((Device) event.subject()).id()); 603 event.type(), ((Device) event.subject()).id());
603 processDeviceAdded((Device) event.subject()); 604 processDeviceAdded((Device) event.subject());
604 - } 605 + } /* else {
606 + if (event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED) {
607 + // availability changed and not available - dev gone
608 + DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
609 + if (groupHandler != null) {
610 + groupHandler.removeAllGroups();
611 + }
612 + }
613 + }*/
605 } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) { 614 } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
606 processPortRemoved((Device) event.subject(), 615 processPortRemoved((Device) event.subject(),
607 ((DeviceEvent) event).port()); 616 ((DeviceEvent) event).port());
...@@ -655,7 +664,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -655,7 +664,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
655 log.debug("A link {} was removed", link.toString()); 664 log.debug("A link {} was removed", link.toString());
656 DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src().deviceId()); 665 DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src().deviceId());
657 if (groupHandler != null) { 666 if (groupHandler != null) {
658 - groupHandler.portDown(link.src().port()); 667 + groupHandler.portDown(link.src().port(),
668 + mastershipService.isLocalMaster(link.src().deviceId()));
659 } 669 }
660 log.trace("Starting optimized route population process"); 670 log.trace("Starting optimized route population process");
661 defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link); 671 defaultRoutingHandler.populateRoutingRulesForLinkStatusChange(link);
...@@ -711,7 +721,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -711,7 +721,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
711 log.debug("Port {} was removed", port.toString()); 721 log.debug("Port {} was removed", port.toString());
712 DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id()); 722 DefaultGroupHandler groupHandler = groupHandlerMap.get(device.id());
713 if (groupHandler != null) { 723 if (groupHandler != null) {
714 - groupHandler.portDown(port.number()); 724 + groupHandler.portDown(port.number(),
725 + mastershipService.isLocalMaster(device.id()));
715 } 726 }
716 } 727 }
717 728
......
...@@ -32,11 +32,13 @@ import org.onlab.packet.Ip4Prefix; ...@@ -32,11 +32,13 @@ import org.onlab.packet.Ip4Prefix;
32 import org.onlab.packet.IpPrefix; 32 import org.onlab.packet.IpPrefix;
33 import org.onlab.packet.MacAddress; 33 import org.onlab.packet.MacAddress;
34 import org.onlab.packet.MplsLabel; 34 import org.onlab.packet.MplsLabel;
35 +import org.onlab.packet.VlanId;
35 import org.onlab.util.KryoNamespace; 36 import org.onlab.util.KryoNamespace;
36 import org.onosproject.core.ApplicationId; 37 import org.onosproject.core.ApplicationId;
37 import org.onosproject.net.DeviceId; 38 import org.onosproject.net.DeviceId;
38 import org.onosproject.net.Link; 39 import org.onosproject.net.Link;
39 import org.onosproject.net.PortNumber; 40 import org.onosproject.net.PortNumber;
41 +import org.onosproject.net.flow.DefaultTrafficSelector;
40 import org.onosproject.net.flow.DefaultTrafficTreatment; 42 import org.onosproject.net.flow.DefaultTrafficTreatment;
41 import org.onosproject.net.flow.TrafficSelector; 43 import org.onosproject.net.flow.TrafficSelector;
42 import org.onosproject.net.flow.TrafficTreatment; 44 import org.onosproject.net.flow.TrafficTreatment;
...@@ -49,6 +51,7 @@ import org.onosproject.net.flowobjective.ObjectiveError; ...@@ -49,6 +51,7 @@ import org.onosproject.net.flowobjective.ObjectiveError;
49 import org.onosproject.net.group.DefaultGroupKey; 51 import org.onosproject.net.group.DefaultGroupKey;
50 import org.onosproject.net.group.GroupKey; 52 import org.onosproject.net.group.GroupKey;
51 import org.onosproject.net.link.LinkService; 53 import org.onosproject.net.link.LinkService;
54 +import org.onosproject.segmentrouting.SegmentRoutingManager;
52 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; 55 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
53 import org.onosproject.segmentrouting.config.DeviceProperties; 56 import org.onosproject.segmentrouting.config.DeviceProperties;
54 import org.onosproject.store.service.EventuallyConsistentMap; 57 import org.onosproject.store.service.EventuallyConsistentMap;
...@@ -71,9 +74,11 @@ public class DefaultGroupHandler { ...@@ -71,9 +74,11 @@ public class DefaultGroupHandler {
71 protected MacAddress nodeMacAddr = null; 74 protected MacAddress nodeMacAddr = null;
72 protected LinkService linkService; 75 protected LinkService linkService;
73 protected FlowObjectiveService flowObjectiveService; 76 protected FlowObjectiveService flowObjectiveService;
74 - 77 + // local store for neighbor-device-ids and the set of ports on this device
78 + // that connect to the same neighbor
75 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap = 79 protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
76 new ConcurrentHashMap<>(); 80 new ConcurrentHashMap<>();
81 + //local store for ports on this device connected to neighbor-device-id
77 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap = 82 protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
78 new ConcurrentHashMap<>(); 83 new ConcurrentHashMap<>();
79 protected EventuallyConsistentMap< 84 protected EventuallyConsistentMap<
...@@ -225,26 +230,33 @@ public class DefaultGroupHandler { ...@@ -225,26 +230,33 @@ public class DefaultGroupHandler {
225 deviceId, 230 deviceId,
226 nsSet); 231 nsSet);
227 for (NeighborSet ns : nsSet) { 232 for (NeighborSet ns : nsSet) {
228 - // Create the new bucket to be updated 233 + Integer nextId = nsNextObjStore.
229 - TrafficTreatment.Builder tBuilder = 234 + get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
230 - DefaultTrafficTreatment.builder(); 235 + if (nextId != null && isMaster) {
231 - tBuilder.setOutput(newLink.src().port()) 236 + // Create the new bucket to be updated
237 + TrafficTreatment.Builder tBuilder =
238 + DefaultTrafficTreatment.builder();
239 + tBuilder.setOutput(newLink.src().port())
232 .setEthDst(dstMac) 240 .setEthDst(dstMac)
233 .setEthSrc(nodeMacAddr); 241 .setEthSrc(nodeMacAddr);
234 - if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) { 242 + if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
235 - tBuilder.pushMpls() 243 + tBuilder.pushMpls()
236 .copyTtlOut() 244 .copyTtlOut()
237 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel())); 245 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
238 - } 246 + }
239 - 247 + // setup metadata to pass to nextObjective - indicate the vlan on egress
240 - Integer nextId = nsNextObjStore. 248 + // if needed by the switch pipeline. Since hashed next-hops are always to
241 - get(new NeighborSetNextObjectiveStoreKey(deviceId, ns)); 249 + // other neighboring routers, there is no subnet assigned on those ports.
242 - if (nextId != null && isMaster) { 250 + TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
243 - NextObjective.Builder nextObjBuilder = DefaultNextObjective 251 + metabuilder.matchVlanId(
244 - .builder().withId(nextId) 252 + VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
245 - .withType(NextObjective.Type.HASHED).fromApp(appId); 253 +
246 - 254 + NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
247 - nextObjBuilder.addTreatment(tBuilder.build()); 255 + .withId(nextId)
256 + .withType(NextObjective.Type.HASHED)
257 + .addTreatment(tBuilder.build())
258 + .withMeta(metabuilder.build())
259 + .fromApp(appId);
248 log.info("**linkUp in device {}: Adding Bucket " 260 log.info("**linkUp in device {}: Adding Bucket "
249 + "with Port {} to next object id {}", 261 + "with Port {} to next object id {}",
250 deviceId, 262 deviceId,
...@@ -253,6 +265,18 @@ public class DefaultGroupHandler { ...@@ -253,6 +265,18 @@ public class DefaultGroupHandler {
253 NextObjective nextObjective = nextObjBuilder. 265 NextObjective nextObjective = nextObjBuilder.
254 addToExisting(new SRNextObjectiveContext(deviceId)); 266 addToExisting(new SRNextObjectiveContext(deviceId));
255 flowObjectiveService.next(deviceId, nextObjective); 267 flowObjectiveService.next(deviceId, nextObjective);
268 +
269 + // the addition of a bucket may actually change the neighborset
270 + // update the global store
271 + /*
272 + Set<DeviceId> neighbors = new HashSet<DeviceId>(ns.getDeviceIds());
273 + boolean newadd = neighbors.add(newLink.dst().deviceId());
274 + if (newadd) {
275 + NeighborSet nsnew = new NeighborSet(neighbors, ns.getEdgeLabel());
276 + nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, nsnew),
277 + nextId);
278 + nsNextObjStore.remove(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
279 + }*/
256 } else if (isMaster) { 280 } else if (isMaster) {
257 log.warn("linkUp in device {}, but global store has no record " 281 log.warn("linkUp in device {}, but global store has no record "
258 + "for neighbor-set {}", deviceId, ns); 282 + "for neighbor-set {}", deviceId, ns);
...@@ -265,7 +289,7 @@ public class DefaultGroupHandler { ...@@ -265,7 +289,7 @@ public class DefaultGroupHandler {
265 * 289 *
266 * @param port port number that has gone down 290 * @param port port number that has gone down
267 */ 291 */
268 - public void portDown(PortNumber port) { 292 + public void portDown(PortNumber port, boolean isMaster) {
269 if (portDeviceMap.get(port) == null) { 293 if (portDeviceMap.get(port) == null) {
270 log.warn("portDown: unknown port"); 294 log.warn("portDown: unknown port");
271 return; 295 return;
...@@ -292,40 +316,50 @@ public class DefaultGroupHandler { ...@@ -292,40 +316,50 @@ public class DefaultGroupHandler {
292 .filter((ns) -> (ns.getDeviceIds() 316 .filter((ns) -> (ns.getDeviceIds()
293 .contains(portDeviceMap.get(port)))) 317 .contains(portDeviceMap.get(port))))
294 .collect(Collectors.toSet()); 318 .collect(Collectors.toSet());
295 - log.trace("portDown: nsNextObjStore contents for device {}:", 319 + log.debug("portDown: nsNextObjStore contents for device {}:{}",
296 - deviceId, 320 + deviceId, nsSet);
297 - nsSet);
298 for (NeighborSet ns : nsSet) { 321 for (NeighborSet ns : nsSet) {
299 - // Create the bucket to be removed
300 - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
301 - .builder();
302 - tBuilder.setOutput(port)
303 - .setEthDst(dstMac)
304 - .setEthSrc(nodeMacAddr);
305 - if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
306 - tBuilder.pushMpls()
307 - .copyTtlOut()
308 - .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
309 - }
310 -
311 Integer nextId = nsNextObjStore. 322 Integer nextId = nsNextObjStore.
312 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns)); 323 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
313 - if (nextId != null) { 324 + if (nextId != null && isMaster) {
314 - NextObjective.Builder nextObjBuilder = DefaultNextObjective
315 - .builder().withType(NextObjective.Type.SIMPLE).withId(nextId).fromApp(appId);
316 -
317 - nextObjBuilder.addTreatment(tBuilder.build());
318 -
319 log.info("**portDown in device {}: Removing Bucket " 325 log.info("**portDown in device {}: Removing Bucket "
320 + "with Port {} to next object id {}", 326 + "with Port {} to next object id {}",
321 deviceId, 327 deviceId,
322 port, 328 port,
323 nextId); 329 nextId);
324 - // should do removefromexisting and only if master 330 + // Create the bucket to be removed
325 - /*NextObjective nextObjective = nextObjBuilder. 331 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
326 - remove(new SRNextObjectiveContext(deviceId)); 332 + .builder();
333 + tBuilder.setOutput(port)
334 + .setEthDst(dstMac)
335 + .setEthSrc(nodeMacAddr);
336 + if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
337 + tBuilder.pushMpls()
338 + .copyTtlOut()
339 + .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
340 + }
341 + NextObjective.Builder nextObjBuilder = DefaultNextObjective
342 + .builder()
343 + .withType(NextObjective.Type.HASHED) //same as original
344 + .withId(nextId)
345 + .fromApp(appId)
346 + .addTreatment(tBuilder.build());
347 + NextObjective nextObjective = nextObjBuilder.
348 + removeFromExisting(new SRNextObjectiveContext(deviceId));
327 349
328 - flowObjectiveService.next(deviceId, nextObjective);*/ 350 + flowObjectiveService.next(deviceId, nextObjective);
351 +
352 + // the removal of a bucket may actually change the neighborset
353 + // update the global store
354 + /*
355 + Set<DeviceId> neighbors = new HashSet<DeviceId>(ns.getDeviceIds());
356 + boolean removed = neighbors.remove(portDeviceMap.get(port));
357 + if (removed) {
358 + NeighborSet nsnew = new NeighborSet(neighbors, ns.getEdgeLabel());
359 + nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, nsnew),
360 + nextId);
361 + nsNextObjStore.remove(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
362 + }*/
329 } 363 }
330 364
331 } 365 }
...@@ -718,6 +752,22 @@ public class DefaultGroupHandler { ...@@ -718,6 +752,22 @@ public class DefaultGroupHandler {
718 return false; 752 return false;
719 } 753 }
720 754
755 + public void removeAllGroups() {
756 + for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry:
757 + nsNextObjStore.entrySet()) {
758 + removeGroup(entry.getValue());
759 + }
760 + for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
761 + portNextObjStore.entrySet()) {
762 + removeGroup(entry.getValue());
763 + }
764 + for (Map.Entry<SubnetNextObjectiveStoreKey, Integer> entry:
765 + subnetNextObjStore.entrySet()) {
766 + removeGroup(entry.getValue());
767 + }
768 + // should probably clean local stores port-neighbor
769 + }
770 +
721 protected static class SRNextObjectiveContext implements ObjectiveContext { 771 protected static class SRNextObjectiveContext implements ObjectiveContext {
722 final DeviceId deviceId; 772 final DeviceId deviceId;
723 773
......
...@@ -27,7 +27,8 @@ public interface FlowObjectiveStore ...@@ -27,7 +27,8 @@ public interface FlowObjectiveStore
27 extends Store<ObjectiveEvent, FlowObjectiveStoreDelegate> { 27 extends Store<ObjectiveEvent, FlowObjectiveStoreDelegate> {
28 28
29 /** 29 /**
30 - * Adds a NextGroup to the store. 30 + * Adds a NextGroup to the store, by mapping it to the nextId as key,
31 + * and replacing any previous mapping.
31 * 32 *
32 * @param nextId an integer 33 * @param nextId an integer
33 * @param group a next group opaque object 34 * @param group a next group opaque object
...@@ -36,12 +37,22 @@ public interface FlowObjectiveStore ...@@ -36,12 +37,22 @@ public interface FlowObjectiveStore
36 37
37 /** 38 /**
38 * Fetch a next group from the store. 39 * Fetch a next group from the store.
39 - * @param nextId an integer 40 + *
40 - * @return a next group 41 + * @param nextId an integer used as key
42 + * @return a next group, or null if group was not found
41 */ 43 */
42 NextGroup getNextGroup(Integer nextId); 44 NextGroup getNextGroup(Integer nextId);
43 45
44 /** 46 /**
47 + * Remove a next group mapping from the store.
48 + *
49 + * @param nextId the key to remove from the store.
50 + * @return the next group which mapped to the nextId and is now removed, or
51 + * null if no group mapping existed in the store
52 + */
53 + NextGroup removeNextGroup(Integer nextId);
54 +
55 + /**
45 * Allocates a next objective id. This id is globally unique 56 * Allocates a next objective id. This id is globally unique
46 * 57 *
47 * @return an integer 58 * @return an integer
......
...@@ -48,6 +48,7 @@ import org.onosproject.net.flowobjective.NextObjective; ...@@ -48,6 +48,7 @@ import org.onosproject.net.flowobjective.NextObjective;
48 import org.onosproject.net.flowobjective.Objective; 48 import org.onosproject.net.flowobjective.Objective;
49 import org.onosproject.net.flowobjective.ObjectiveError; 49 import org.onosproject.net.flowobjective.ObjectiveError;
50 import org.onosproject.net.flowobjective.ObjectiveEvent; 50 import org.onosproject.net.flowobjective.ObjectiveEvent;
51 +import org.onosproject.net.flowobjective.ObjectiveEvent.Type;
51 import org.onosproject.net.group.GroupService; 52 import org.onosproject.net.group.GroupService;
52 import org.slf4j.Logger; 53 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory; 54 import org.slf4j.LoggerFactory;
...@@ -381,19 +382,19 @@ public class FlowObjectiveManager implements FlowObjectiveService { ...@@ -381,19 +382,19 @@ public class FlowObjectiveManager implements FlowObjectiveService {
381 private class InternalStoreDelegate implements FlowObjectiveStoreDelegate { 382 private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
382 @Override 383 @Override
383 public void notify(ObjectiveEvent event) { 384 public void notify(ObjectiveEvent event) {
384 - log.debug("Received notification of obj event {}", event); 385 + if (event.type() == Type.ADD) {
385 - Set<PendingNext> pending = pendingForwards.remove(event.subject()); 386 + log.debug("Received notification of obj event {}", event);
387 + Set<PendingNext> pending = pendingForwards.remove(event.subject());
386 388
387 - if (pending == null) { 389 + if (pending == null) {
388 - log.debug("Nothing pending for this obj event"); 390 + log.debug("Nothing pending for this obj event");
389 - return; 391 + return;
390 - } 392 + }
391 -
392 - log.debug("Processing pending forwarding objectives {}", pending.size());
393 -
394 - pending.forEach(p -> getDevicePipeliner(p.deviceId())
395 - .forward(p.forwardingObjective()));
396 393
394 + log.debug("Processing pending forwarding objectives {}", pending.size());
395 + pending.forEach(p -> getDevicePipeliner(p.deviceId())
396 + .forward(p.forwardingObjective()));
397 + }
397 } 398 }
398 } 399 }
399 400
......
...@@ -79,10 +79,9 @@ public class DistributedFlowObjectiveStore ...@@ -79,10 +79,9 @@ public class DistributedFlowObjectiveStore
79 log.info("Stopped"); 79 log.info("Stopped");
80 } 80 }
81 81
82 -
83 @Override 82 @Override
84 public void putNextGroup(Integer nextId, NextGroup group) { 83 public void putNextGroup(Integer nextId, NextGroup group) {
85 - nextGroups.putIfAbsent(nextId, group.data()); 84 + nextGroups.put(nextId, group.data());
86 notifyDelegate(new ObjectiveEvent(ObjectiveEvent.Type.ADD, nextId)); 85 notifyDelegate(new ObjectiveEvent(ObjectiveEvent.Type.ADD, nextId));
87 } 86 }
88 87
...@@ -96,6 +95,16 @@ public class DistributedFlowObjectiveStore ...@@ -96,6 +95,16 @@ public class DistributedFlowObjectiveStore
96 } 95 }
97 96
98 @Override 97 @Override
98 + public NextGroup removeNextGroup(Integer nextId) {
99 + Versioned<byte[]> versionGroup = nextGroups.remove(nextId);
100 + if (versionGroup != null) {
101 + notifyDelegate(new ObjectiveEvent(ObjectiveEvent.Type.REMOVE, nextId));
102 + return new DefaultNextGroup(versionGroup.value());
103 + }
104 + return null;
105 + }
106 +
107 + @Override
99 public int allocateNextId() { 108 public int allocateNextId() {
100 return (int) nextIds.incrementAndGet(); 109 return (int) nextIds.incrementAndGet();
101 } 110 }
......
...@@ -71,7 +71,6 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline { ...@@ -71,7 +71,6 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
71 * (non-Javadoc) 71 * (non-Javadoc)
72 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter 72 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
73 */ 73 */
74 -
75 @Override 74 @Override
76 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion, 75 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
77 VlanIdCriterion vidCriterion, 76 VlanIdCriterion vidCriterion,
...@@ -267,16 +266,18 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline { ...@@ -267,16 +266,18 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
267 } 266 }
268 267
269 if (fwd.nextId() != null) { 268 if (fwd.nextId() != null) {
270 - NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId()); 269 + NextGroup next = getGroupForNextObjective(fwd.nextId());
271 - List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data()); 270 + if (next != null) {
272 - // we only need the top level group's key to point the flow to it 271 + List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
273 - Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst()); 272 + // we only need the top level group's key to point the flow to it
274 - if (group == null) { 273 + Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
275 - log.warn("The group left!"); 274 + if (group == null) {
276 - fail(fwd, ObjectiveError.GROUPMISSING); 275 + log.warn("The group left!");
277 - return Collections.emptySet(); 276 + fail(fwd, ObjectiveError.GROUPMISSING);
277 + return Collections.emptySet();
278 + }
279 + tb.deferred().group(group.id());
278 } 280 }
279 - tb.deferred().group(group.id());
280 } 281 }
281 tb.transition(ACL_TABLE); 282 tb.transition(ACL_TABLE);
282 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() 283 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
......
...@@ -94,7 +94,6 @@ import org.onosproject.net.group.GroupEvent; ...@@ -94,7 +94,6 @@ import org.onosproject.net.group.GroupEvent;
94 import org.onosproject.net.group.GroupKey; 94 import org.onosproject.net.group.GroupKey;
95 import org.onosproject.net.group.GroupListener; 95 import org.onosproject.net.group.GroupListener;
96 import org.onosproject.net.group.GroupService; 96 import org.onosproject.net.group.GroupService;
97 -import org.onosproject.net.packet.PacketService;
98 import org.onosproject.store.serializers.KryoNamespaces; 97 import org.onosproject.store.serializers.KryoNamespaces;
99 import org.slf4j.Logger; 98 import org.slf4j.Logger;
100 99
...@@ -149,7 +148,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -149,7 +148,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
149 protected FlowObjectiveStore flowObjectiveStore; 148 protected FlowObjectiveStore flowObjectiveStore;
150 protected DeviceId deviceId; 149 protected DeviceId deviceId;
151 protected ApplicationId driverId; 150 protected ApplicationId driverId;
152 - protected PacketService packetService;
153 protected DeviceService deviceService; 151 protected DeviceService deviceService;
154 protected KryoNamespace appKryo = new KryoNamespace.Builder() 152 protected KryoNamespace appKryo = new KryoNamespace.Builder()
155 .register(KryoNamespaces.API) 153 .register(KryoNamespaces.API)
...@@ -174,6 +172,10 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -174,6 +172,10 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
174 Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId, 172 Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId,
175 Set<PortNumber>>(); 173 Set<PortNumber>>();
176 174
175 + // local store for pending bucketAdds - by design there can only be one
176 + // pending bucket for a group
177 + ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>();
178 +
177 // index number for group creation 179 // index number for group creation
178 AtomicInteger l3vpnindex = new AtomicInteger(0); 180 AtomicInteger l3vpnindex = new AtomicInteger(0);
179 181
...@@ -202,7 +204,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -202,7 +204,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
202 flowRuleService = serviceDirectory.get(FlowRuleService.class); 204 flowRuleService = serviceDirectory.get(FlowRuleService.class);
203 groupService = serviceDirectory.get(GroupService.class); 205 groupService = serviceDirectory.get(GroupService.class);
204 flowObjectiveStore = context.store(); 206 flowObjectiveStore = context.store();
205 - packetService = serviceDirectory.get(PacketService.class);
206 deviceService = serviceDirectory.get(DeviceService.class); 207 deviceService = serviceDirectory.get(DeviceService.class);
207 groupService.addListener(new InnerGroupListener()); 208 groupService.addListener(new InnerGroupListener());
208 209
...@@ -293,10 +294,13 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -293,10 +294,13 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
293 if (nextGroup != null) { 294 if (nextGroup != null) {
294 log.debug("Processing NextObjective id{} in dev{} - add bucket", 295 log.debug("Processing NextObjective id{} in dev{} - add bucket",
295 nextObjective.id(), deviceId); 296 nextObjective.id(), deviceId);
296 - addBucketToGroup(nextObjective); 297 + addBucketToGroup(nextObjective, nextGroup);
297 } else { 298 } else {
298 // it is possible that group-chain has not been fully created yet 299 // it is possible that group-chain has not been fully created yet
299 - waitToAddBucketToGroup(nextObjective); 300 + log.debug("Waiting to add bucket to group for next-id:{} in dev:{}",
301 + nextObjective.id(), deviceId);
302 + // by design only one pending bucket is allowed for the group
303 + pendingBuckets.put(nextObjective.id(), nextObjective);
300 } 304 }
301 break; 305 break;
302 case REMOVE: 306 case REMOVE:
...@@ -307,7 +311,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -307,7 +311,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
307 } 311 }
308 log.debug("Processing NextObjective id{} in dev{} - remove group", 312 log.debug("Processing NextObjective id{} in dev{} - remove group",
309 nextObjective.id(), deviceId); 313 nextObjective.id(), deviceId);
310 - removeGroup(nextObjective); 314 + removeGroup(nextObjective, nextGroup);
311 break; 315 break;
312 case REMOVE_FROM_EXISTING: 316 case REMOVE_FROM_EXISTING:
313 if (nextGroup == null) { 317 if (nextGroup == null) {
...@@ -317,7 +321,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -317,7 +321,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
317 } 321 }
318 log.debug("Processing NextObjective id{} in dev{} - remove bucket", 322 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
319 nextObjective.id(), deviceId); 323 nextObjective.id(), deviceId);
320 - removeBucketFromGroup(nextObjective); 324 + removeBucketFromGroup(nextObjective, nextGroup);
321 break; 325 break;
322 default: 326 default:
323 log.warn("Unsupported operation {}", nextObjective.op()); 327 log.warn("Unsupported operation {}", nextObjective.op());
...@@ -791,17 +795,19 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -791,17 +795,19 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
791 return Collections.emptySet(); 795 return Collections.emptySet();
792 } 796 }
793 797
794 - NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId()); 798 + NextGroup next = getGroupForNextObjective(fwd.nextId());
795 - List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data()); 799 + if (next != null) {
796 - // we only need the top level group's key to point the flow to it 800 + List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
797 - Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst()); 801 + // we only need the top level group's key to point the flow to it
798 - if (group == null) { 802 + Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
799 - log.warn("Group with key:{} for next-id:{} not found in dev:{}", 803 + if (group == null) {
800 - gkeys.get(0).peekFirst(), fwd.nextId(), deviceId); 804 + log.warn("Group with key:{} for next-id:{} not found in dev:{}",
801 - fail(fwd, ObjectiveError.GROUPMISSING); 805 + gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
802 - return Collections.emptySet(); 806 + fail(fwd, ObjectiveError.GROUPMISSING);
807 + return Collections.emptySet();
808 + }
809 + tb.deferred().group(group.id());
803 } 810 }
804 - tb.deferred().group(group.id());
805 } 811 }
806 tb.transition(ACL_TABLE); 812 tb.transition(ACL_TABLE);
807 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() 813 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
...@@ -868,7 +874,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -868,7 +874,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
868 874
869 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder(); 875 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
870 if (fwd.nextId() != null) { 876 if (fwd.nextId() != null) {
871 - NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId()); 877 + NextGroup next = getGroupForNextObjective(fwd.nextId());
872 if (next != null) { 878 if (next != null) {
873 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data()); 879 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
874 // we only need the top level group's key to point the flow to it 880 // we only need the top level group's key to point the flow to it
...@@ -903,6 +909,23 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -903,6 +909,23 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
903 return rules; 909 return rules;
904 } 910 }
905 911
912 + protected NextGroup getGroupForNextObjective(Integer nextId) {
913 + NextGroup next = flowObjectiveStore.getNextGroup(nextId);
914 + if (next != null) {
915 + List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
916 + if (gkeys != null && !gkeys.isEmpty()) {
917 + return next;
918 + } else {
919 + log.warn("Empty next group found in FlowObjective store for "
920 + + "next-id:{} in dev:{}", nextId, deviceId);
921 + }
922 + } else {
923 + log.warn("next-id {} not found in Flow objective store for dev:{}",
924 + nextId, deviceId);
925 + }
926 + return null;
927 + }
928 +
906 private void pass(Objective obj) { 929 private void pass(Objective obj) {
907 if (obj.context().isPresent()) { 930 if (obj.context().isPresent()) {
908 obj.context().get().onSuccess(obj); 931 obj.context().get().onSuccess(obj);
...@@ -1013,6 +1036,16 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1013,6 +1036,16 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1013 } 1036 }
1014 } 1037 }
1015 1038
1039 + private void updatePendingGroups(GroupKey gkey, GroupChainElem gce) {
1040 + Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1041 + new ConcurrentHashMap<GroupChainElem, Boolean>());
1042 + gceSet.add(gce);
1043 + Set<GroupChainElem> retval = pendingGroups.putIfAbsent(gkey, gceSet);
1044 + if (retval != null) {
1045 + retval.add(gce);
1046 + }
1047 + }
1048 +
1016 /** 1049 /**
1017 * Creates a simple L2 Interface Group. 1050 * Creates a simple L2 Interface Group.
1018 * 1051 *
...@@ -1242,14 +1275,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1242,14 +1275,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1242 } 1275 }
1243 1276
1244 // store l2groupkey with the groupChainElem for the outer-group that depends on it 1277 // store l2groupkey with the groupChainElem for the outer-group that depends on it
1245 - GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1); 1278 + GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1, false);
1246 - Set<GroupChainElem> gceSet = Collections.newSetFromMap( 1279 + updatePendingGroups(l2groupkey, gce);
1247 - new ConcurrentHashMap<GroupChainElem, Boolean>());
1248 - gceSet.add(gce);
1249 - Set<GroupChainElem> retval = pendingGroups.putIfAbsent(l2groupkey, gceSet);
1250 - if (retval != null) {
1251 - retval.add(gce);
1252 - }
1253 1280
1254 // create group description for the inner l2interfacegroup 1281 // create group description for the inner l2interfacegroup
1255 GroupBucket l2interfaceGroupBucket = 1282 GroupBucket l2interfaceGroupBucket =
...@@ -1376,7 +1403,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1376,7 +1403,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1376 l2floodgroupId, 1403 l2floodgroupId,
1377 nextObj.appId()); 1404 nextObj.appId());
1378 GroupChainElem gce = new GroupChainElem(l2floodGroupDescription, 1405 GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
1379 - l2interfaceGroupDescs.size()); 1406 + l2interfaceGroupDescs.size(),
1407 + false);
1380 log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}", 1408 log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
1381 deviceId, Integer.toHexString(l2floodgroupId), 1409 deviceId, Integer.toHexString(l2floodgroupId),
1382 l2floodgroupkey, nextObj.id()); 1410 l2floodgroupkey, nextObj.id());
...@@ -1392,16 +1420,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1392,16 +1420,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1392 for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) { 1420 for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
1393 // store all l2groupkeys with the groupChainElem for the l2floodgroup 1421 // store all l2groupkeys with the groupChainElem for the l2floodgroup
1394 // that depends on it 1422 // that depends on it
1395 - Set<GroupChainElem> gceSet = Collections.newSetFromMap( 1423 + updatePendingGroups(l2intGrpDesc.appCookie(), gce);
1396 - new ConcurrentHashMap<GroupChainElem, Boolean>()); 1424 + // send groups for all l2 interface groups
1397 - gceSet.add(gce);
1398 - Set<GroupChainElem> retval = pendingGroups.putIfAbsent(
1399 - l2intGrpDesc.appCookie(), gceSet);
1400 - if (retval != null) {
1401 - retval.add(gce);
1402 - }
1403 -
1404 - // create and send groups for all l2 interface groups
1405 groupService.addGroup(l2intGrpDesc); 1425 groupService.addGroup(l2intGrpDesc);
1406 } 1426 }
1407 } 1427 }
...@@ -1430,17 +1450,81 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1430,17 +1450,81 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1430 * <p> 1450 * <p>
1431 * NOTE: We do not create MPLS ECMP groups as they are unimplemented in 1451 * NOTE: We do not create MPLS ECMP groups as they are unimplemented in
1432 * OF-DPA 2.0 (even though it is in the spec). Therefore we do not 1452 * OF-DPA 2.0 (even though it is in the spec). Therefore we do not
1433 - * check the nextObjective meta. 1453 + * check the nextObjective meta to see what is matching before being
1454 + * sent to this nextObjective.
1434 * 1455 *
1435 * @param nextObj the nextObjective of type HASHED 1456 * @param nextObj the nextObjective of type HASHED
1436 */ 1457 */
1437 private void processHashedNextObjective(NextObjective nextObj) { 1458 private void processHashedNextObjective(NextObjective nextObj) {
1438 - // break up hashed next objective to multiple groups
1439 - Collection<TrafficTreatment> buckets = nextObj.next();
1440 -
1441 // storage for all group keys in the chain of groups created 1459 // storage for all group keys in the chain of groups created
1442 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>(); 1460 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1443 List<GroupInfo> unsentGroups = new ArrayList<>(); 1461 List<GroupInfo> unsentGroups = new ArrayList<>();
1462 + createHashBucketChains(nextObj, allGroupKeys, unsentGroups);
1463 +
1464 + // now we can create the outermost L3 ECMP group
1465 + List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>();
1466 + for (GroupInfo gi : unsentGroups) {
1467 + // create ECMP bucket to point to the outer group
1468 + TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1469 + ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
1470 + GroupBucket sbucket = DefaultGroupBucket
1471 + .createSelectGroupBucket(ttb.build());
1472 + l3ecmpGroupBuckets.add(sbucket);
1473 + }
1474 + int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12;
1475 + GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId));
1476 + GroupDescription l3ecmpGroupDesc =
1477 + new DefaultGroupDescription(
1478 + deviceId,
1479 + GroupDescription.Type.SELECT,
1480 + new GroupBuckets(l3ecmpGroupBuckets),
1481 + l3ecmpGroupKey,
1482 + l3ecmpGroupId,
1483 + nextObj.appId());
1484 + GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
1485 + l3ecmpGroupBuckets.size(),
1486 + false);
1487 +
1488 + // create objects for local and distributed storage
1489 + allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey));
1490 + OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
1491 +
1492 + // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
1493 + // that depends on it
1494 + updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp);
1495 +
1496 + log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
1497 + deviceId, Integer.toHexString(l3ecmpGroupId),
1498 + l3ecmpGroupKey, nextObj.id());
1499 + // finally we are ready to send the innermost groups
1500 + for (GroupInfo gi : unsentGroups) {
1501 + log.debug("Sending innermost group {} in group chain on device {} ",
1502 + Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId);
1503 + updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce);
1504 + groupService.addGroup(gi.innerGrpDesc);
1505 + }
1506 +
1507 + }
1508 +
1509 + /**
1510 + * Creates group chains for all buckets in a hashed group, and stores the
1511 + * GroupInfos and GroupKeys for all the groups in the lists passed in, which
1512 + * should be empty.
1513 + * <p>
1514 + * Does not create the top level ECMP group. Does not actually send the
1515 + * groups to the groupService.
1516 + *
1517 + * @param nextObj the Next Objective with buckets that need to be converted
1518 + * to group chains
1519 + * @param allGroupKeys a list to store groupKey for each bucket-group-chain
1520 + * @param unsentGroups a list to store GroupInfo for each bucket-group-chain
1521 + */
1522 + private void createHashBucketChains(NextObjective nextObj,
1523 + List<Deque<GroupKey>> allGroupKeys,
1524 + List<GroupInfo> unsentGroups) {
1525 + // break up hashed next objective to multiple groups
1526 + Collection<TrafficTreatment> buckets = nextObj.next();
1527 +
1444 for (TrafficTreatment bucket : buckets) { 1528 for (TrafficTreatment bucket : buckets) {
1445 //figure out how many labels are pushed in each bucket 1529 //figure out how many labels are pushed in each bucket
1446 int labelsPushed = 0; 1530 int labelsPushed = 0;
...@@ -1508,15 +1592,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1508,15 +1592,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1508 l3vpngroupkey, 1592 l3vpngroupkey,
1509 l3vpngroupId, 1593 l3vpngroupId,
1510 nextObj.appId()); 1594 nextObj.appId());
1511 - GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1); 1595 + GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1, false);
1512 - Set<GroupChainElem> gceSet = Collections.newSetFromMap( 1596 + updatePendingGroups(onelabelGroupInfo.outerGrpDesc.appCookie(), l3vpnGce);
1513 - new ConcurrentHashMap<GroupChainElem, Boolean>());
1514 - gceSet.add(l3vpnGce);
1515 - Set<GroupChainElem> retval = pendingGroups
1516 - .putIfAbsent(onelabelGroupInfo.outerGrpDesc.appCookie(), gceSet);
1517 - if (retval != null) {
1518 - retval.add(l3vpnGce);
1519 - }
1520 1597
1521 gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie()); 1598 gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie());
1522 gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie()); 1599 gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie());
...@@ -1535,80 +1612,186 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1535,80 +1612,186 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1535 1612
1536 } else { 1613 } else {
1537 log.warn("Driver currently does not handle more than 1 MPLS " 1614 log.warn("Driver currently does not handle more than 1 MPLS "
1538 - + "labels. Not processing nextObjective {}", nextObj); 1615 + + "labels. Not processing nextObjective {}", nextObj.id());
1539 return; 1616 return;
1540 } 1617 }
1541 1618
1542 // all groups in this chain 1619 // all groups in this chain
1543 allGroupKeys.add(gkeyChain); 1620 allGroupKeys.add(gkeyChain);
1544 } 1621 }
1622 + }
1545 1623
1546 - // now we can create the outermost L3 ECMP group 1624 + /**
1547 - List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>(); 1625 + * Adds a bucket to the top level group of a group-chain, and creates the chain.
1548 - for (GroupInfo gi : unsentGroups) { 1626 + *
1549 - // create ECMP bucket to point to the outer group 1627 + * @param nextObjective the next group to add a bucket to
1550 - TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); 1628 + * @param next the representation of the existing group-chain for this next objective
1551 - ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId())); 1629 + */
1552 - GroupBucket sbucket = DefaultGroupBucket 1630 + private void addBucketToGroup(NextObjective nextObjective, NextGroup next) {
1553 - .createSelectGroupBucket(ttb.build()); 1631 + if (nextObjective.type() != NextObjective.Type.HASHED) {
1554 - l3ecmpGroupBuckets.add(sbucket); 1632 + log.warn("AddBuckets not applied to nextType:{} in dev:{} for next:{}",
1633 + nextObjective.type(), deviceId, nextObjective.id());
1634 + return;
1555 } 1635 }
1556 - int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12; 1636 + if (nextObjective.next().size() > 1) {
1637 + log.warn("Only one bucket can be added at a time");
1638 + return;
1639 + }
1640 + // storage for all group keys in the chain of groups created
1641 + List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1642 + List<GroupInfo> unsentGroups = new ArrayList<>();
1643 + createHashBucketChains(nextObjective, allGroupKeys, unsentGroups);
1644 +
1645 + // now we can create the outermost L3 ECMP group bucket to add
1646 + GroupInfo gi = unsentGroups.get(0); // only one bucket, so only one group-chain
1647 + TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1648 + ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
1649 + GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build());
1650 +
1651 + // recreate the original L3 ECMP group id and description
1652 + int l3ecmpGroupId = L3ECMPMASK | nextObjective.id() << 12;
1557 GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId)); 1653 GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId));
1654 +
1655 + // Although GroupDescriptions are not necessary for adding buckets to
1656 + // existing groups, we use one in the GroupChainElem. When the latter is
1657 + // processed, the info will be extracted for the bucketAdd call to groupService
1558 GroupDescription l3ecmpGroupDesc = 1658 GroupDescription l3ecmpGroupDesc =
1559 new DefaultGroupDescription( 1659 new DefaultGroupDescription(
1560 deviceId, 1660 deviceId,
1561 GroupDescription.Type.SELECT, 1661 GroupDescription.Type.SELECT,
1562 - new GroupBuckets(l3ecmpGroupBuckets), 1662 + new GroupBuckets(Collections.singletonList(sbucket)),
1563 l3ecmpGroupKey, 1663 l3ecmpGroupKey,
1564 l3ecmpGroupId, 1664 l3ecmpGroupId,
1565 - nextObj.appId()); 1665 + nextObjective.appId());
1566 - GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, 1666 + GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, 1, true);
1567 - l3ecmpGroupBuckets.size()); 1667 +
1568 - 1668 + // update original NextGroup with new bucket-chain
1569 - // create objects for local and distributed storage 1669 + // don't need to update pendingNextObjectives -- group already exists
1570 - allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey)); 1670 + Deque<GroupKey> newBucketChain = allGroupKeys.get(0);
1571 - OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj); 1671 + newBucketChain.addFirst(l3ecmpGroupKey);
1572 - 1672 + List<Deque<GroupKey>> allOriginalKeys = appKryo.deserialize(next.data());
1573 - // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective 1673 + allOriginalKeys.add(newBucketChain);
1574 - // that depends on it 1674 + flowObjectiveStore.putNextGroup(nextObjective.id(),
1575 - updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp); 1675 + new OfdpaNextGroup(allOriginalKeys, nextObjective));
1576 - 1676 +
1577 - log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}", 1677 + log.debug("Adding to L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
1578 deviceId, Integer.toHexString(l3ecmpGroupId), 1678 deviceId, Integer.toHexString(l3ecmpGroupId),
1579 - l3ecmpGroupKey, nextObj.id()); 1679 + l3ecmpGroupKey, nextObjective.id());
1580 - // finally we are ready to send the innermost groups 1680 + // send the innermost group
1581 - for (GroupInfo gi : unsentGroups) { 1681 + log.debug("Sending innermost group {} in group chain on device {} ",
1582 - log.debug("Sending innermost group {} in group chain on device {} ", 1682 + Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId);
1583 - Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId); 1683 + updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce);
1584 - Set<GroupChainElem> gceSet = Collections.newSetFromMap( 1684 + groupService.addGroup(gi.innerGrpDesc);
1585 - new ConcurrentHashMap<GroupChainElem, Boolean>());
1586 - gceSet.add(l3ecmpGce);
1587 - Set<GroupChainElem> retval = pendingGroups
1588 - .putIfAbsent(gi.outerGrpDesc.appCookie(), gceSet);
1589 - if (retval != null) {
1590 - retval.add(l3ecmpGce);
1591 - }
1592 -
1593 - groupService.addGroup(gi.innerGrpDesc);
1594 - }
1595 1685
1596 } 1686 }
1597 1687
1598 - private void addBucketToGroup(NextObjective nextObjective) { 1688 + /**
1599 - // TODO Auto-generated method stub 1689 + * Removes the bucket in the top level group of a possible group-chain. Does
1600 - } 1690 + * not remove the groups in a group-chain pointed to by this bucket, as they
1601 - 1691 + * may be in use (referenced by other groups) elsewhere.
1602 - private void waitToAddBucketToGroup(NextObjective nextObjective) { 1692 + *
1603 - // TODO Auto-generated method stub 1693 + * @param nextObjective the next group to remove a bucket from
1604 - } 1694 + * @param next the representation of the existing group-chain for this next objective
1695 + */
1696 + private void removeBucketFromGroup(NextObjective nextObjective, NextGroup next) {
1697 + if (nextObjective.type() != NextObjective.Type.HASHED) {
1698 + log.warn("RemoveBuckets not applied to nextType:{} in dev:{} for next:{}",
1699 + nextObjective.type(), deviceId, nextObjective.id());
1700 + return;
1701 + }
1702 + Collection<TrafficTreatment> treatments = nextObjective.next();
1703 + TrafficTreatment treatment = treatments.iterator().next();
1704 + // find the bucket to remove by noting the outport, and figuring out the
1705 + // top-level group in the group-chain that indirectly references the port
1706 + PortNumber outport = null;
1707 + for (Instruction ins : treatment.allInstructions()) {
1708 + if (ins instanceof OutputInstruction) {
1709 + outport = ((OutputInstruction) ins).port();
1710 + break;
1711 + }
1712 + }
1713 + if (outport == null) {
1714 + log.error("next objective {} has no outport", nextObjective.id());
1715 + return;
1716 + }
1605 1717
1606 - private void removeBucketFromGroup(NextObjective nextObjective) { 1718 + List<Deque<GroupKey>> allgkeys = appKryo.deserialize(next.data());
1607 - // TODO Auto-generated method stub 1719 + Deque<GroupKey> foundChain = null;
1720 + int index = 0;
1721 + for (Deque<GroupKey> gkeys : allgkeys) {
1722 + GroupKey groupWithPort = gkeys.peekLast();
1723 + Group group = groupService.getGroup(deviceId, groupWithPort);
1724 + if (group == null) {
1725 + log.warn("Inconsistent group chain");
1726 + continue;
1727 + }
1728 + // last group in group chain should have a single bucket pointing to port
1729 + List<Instruction> lastIns = group.buckets().buckets().iterator()
1730 + .next().treatment().allInstructions();
1731 + for (Instruction i : lastIns) {
1732 + if (i instanceof OutputInstruction) {
1733 + PortNumber lastport = ((OutputInstruction) i).port();
1734 + if (lastport.equals(outport)) {
1735 + foundChain = gkeys;
1736 + break;
1737 + }
1738 + }
1739 + }
1740 + if (foundChain != null) {
1741 + break;
1742 + }
1743 + index++;
1744 + }
1745 + if (foundChain != null) {
1746 + //first groupkey is the one we want to modify
1747 + GroupKey modGroupKey = foundChain.peekFirst();
1748 + Group modGroup = groupService.getGroup(deviceId, modGroupKey);
1749 + //second groupkey is the one we wish to remove the reference to
1750 + GroupKey pointedGroupKey = null;
1751 + int i = 0;
1752 + for (GroupKey gk : foundChain) {
1753 + if (i++ == 1) {
1754 + pointedGroupKey = gk;
1755 + break;
1756 + }
1757 + }
1758 + Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey);
1759 + GroupBucket bucket = DefaultGroupBucket.createSelectGroupBucket(
1760 + DefaultTrafficTreatment.builder()
1761 + .group(pointedGroup.id())
1762 + .build());
1763 + GroupBuckets removeBuckets = new GroupBuckets(Collections
1764 + .singletonList(bucket));
1765 + log.debug("Removing buckets from group id {} for next id {} in device {}",
1766 + modGroup.id(), nextObjective.id(), deviceId);
1767 + groupService.removeBucketsFromGroup(deviceId, modGroupKey,
1768 + removeBuckets, modGroupKey,
1769 + nextObjective.appId());
1770 + //update store
1771 + allgkeys.remove(index);
1772 + flowObjectiveStore.putNextGroup(nextObjective.id(),
1773 + new OfdpaNextGroup(allgkeys, nextObjective));
1774 + } else {
1775 + log.warn("Could not find appropriate group-chain for removing bucket"
1776 + + " for next id {} in dev:{}", nextObjective.id(), deviceId);
1777 + }
1608 } 1778 }
1609 1779
1610 - private void removeGroup(NextObjective nextObjective) { 1780 + /**
1611 - // TODO Auto-generated method stub 1781 + * Removes all groups in multiple possible group-chains that represent the next
1782 + * objective.
1783 + *
1784 + * @param nextObjective the next objective to remove
1785 + * @param next the NextGroup that represents the existing group-chain for
1786 + * this next objective
1787 + */
1788 + private void removeGroup(NextObjective nextObjective, NextGroup next) {
1789 + List<Deque<GroupKey>> allgkeys = appKryo.deserialize(next.data());
1790 + allgkeys.forEach(groupChain -> {
1791 + groupChain.forEach(groupKey ->
1792 + groupService.removeGroup(deviceId, groupKey, nextObjective.appId()));
1793 + });
1794 + flowObjectiveStore.removeNextGroup(nextObjective.id());
1612 } 1795 }
1613 1796
1614 /** 1797 /**
...@@ -1617,7 +1800,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1617,7 +1800,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1617 * and this driver has received notification for it. A second assumption is 1800 * and this driver has received notification for it. A second assumption is
1618 * that if there is another group waiting for this group then the appropriate 1801 * that if there is another group waiting for this group then the appropriate
1619 * stores already have the information to act upon the notification for the 1802 * stores already have the information to act upon the notification for the
1620 - * creating of this group. 1803 + * creation of this group.
1621 * <p> 1804 * <p>
1622 * The processing of the GroupChainElement depends on the number of groups 1805 * The processing of the GroupChainElement depends on the number of groups
1623 * this element is waiting on. For all group types other than SIMPLE, a 1806 * this element is waiting on. For all group types other than SIMPLE, a
...@@ -1632,7 +1815,15 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1632,7 +1815,15 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1632 return; 1815 return;
1633 } 1816 }
1634 log.debug("GCE: {} ready to be processed", gce); 1817 log.debug("GCE: {} ready to be processed", gce);
1635 - groupService.addGroup(gce.groupDescription); 1818 + if (gce.addBucketToGroup) {
1819 + groupService.addBucketsToGroup(gce.groupDescription.deviceId(),
1820 + gce.groupDescription.appCookie(),
1821 + gce.groupDescription.buckets(),
1822 + gce.groupDescription.appCookie(),
1823 + gce.groupDescription.appId());
1824 + } else {
1825 + groupService.addGroup(gce.groupDescription);
1826 + }
1636 } 1827 }
1637 1828
1638 private class GroupChecker implements Runnable { 1829 private class GroupChecker implements Runnable {
...@@ -1646,33 +1837,45 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1646,33 +1837,45 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1646 .collect(Collectors.toSet()); 1837 .collect(Collectors.toSet());
1647 keys.addAll(otherkeys); 1838 keys.addAll(otherkeys);
1648 1839
1649 - keys.stream().forEach(key -> { 1840 + keys.stream().forEach(key ->
1650 - //first check for group chain 1841 + processPendingGroupsOrNextObjectives(key, false));
1651 - Set<GroupChainElem> gceSet = pendingGroups.remove(key); 1842 + }
1652 - if (gceSet != null) { 1843 + }
1653 - for (GroupChainElem gce : gceSet) { 1844 +
1654 - log.info("Group service processed group key {} in device {}. " 1845 + private void processPendingGroupsOrNextObjectives(GroupKey key, boolean added) {
1655 - + "Processing next group in group chain with group id {}", 1846 + //first check for group chain
1656 - key, deviceId, 1847 + Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1657 - Integer.toHexString(gce.groupDescription.givenGroupId())); 1848 + if (gceSet != null) {
1658 - processGroupChain(gce); 1849 + for (GroupChainElem gce : gceSet) {
1659 - } 1850 + log.info("Group service {} group key {} in device {}. "
1660 - } else { 1851 + + "Processing next group in group chain with group id {}",
1661 - List<OfdpaNextGroup> objList = pendingNextObjectives.getIfPresent(key); 1852 + (added) ? "ADDED" : "processed",
1662 - if (objList != null) { 1853 + key, deviceId,
1663 - pendingNextObjectives.invalidate(key); 1854 + Integer.toHexString(gce.groupDescription.givenGroupId()));
1664 - objList.forEach(obj -> { 1855 + processGroupChain(gce);
1665 - log.info("Group service processed group key {} in device:{}. " 1856 + }
1666 - + "Done implementing next objective: {} <<-->> gid:{}", 1857 + } else {
1667 - key, deviceId, obj.nextObjective().id(), 1858 + // otherwise chain complete - check for waiting nextObjectives
1668 - Integer.toHexString(groupService.getGroup(deviceId, key) 1859 + List<OfdpaNextGroup> nextGrpList = pendingNextObjectives.getIfPresent(key);
1669 - .givenGroupId())); 1860 + if (nextGrpList != null) {
1670 - pass(obj.nextObjective()); 1861 + pendingNextObjectives.invalidate(key);
1671 - flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj); 1862 + nextGrpList.forEach(nextGrp -> {
1672 - }); 1863 + log.info("Group service {} group key {} in device:{}. "
1864 + + "Done implementing next objective: {} <<-->> gid:{}",
1865 + (added) ? "ADDED" : "processed",
1866 + key, deviceId, nextGrp.nextObjective().id(),
1867 + Integer.toHexString(groupService.getGroup(deviceId, key)
1868 + .givenGroupId()));
1869 + pass(nextGrp.nextObjective());
1870 + flowObjectiveStore.putNextGroup(nextGrp.nextObjective().id(), nextGrp);
1871 + // check if addBuckets waiting for this completion
1872 + NextObjective pendBkt = pendingBuckets
1873 + .remove(nextGrp.nextObjective().id());
1874 + if (pendBkt != null) {
1875 + addBucketToGroup(pendBkt, nextGrp);
1673 } 1876 }
1674 - } 1877 + });
1675 - }); 1878 + }
1676 } 1879 }
1677 } 1880 }
1678 1881
...@@ -1682,31 +1885,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1682,31 +1885,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1682 log.trace("received group event of type {}", event.type()); 1885 log.trace("received group event of type {}", event.type());
1683 if (event.type() == GroupEvent.Type.GROUP_ADDED) { 1886 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
1684 GroupKey key = event.subject().appCookie(); 1887 GroupKey key = event.subject().appCookie();
1685 - // first check for group chain 1888 + processPendingGroupsOrNextObjectives(key, true);
1686 - Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1687 - if (gceSet != null) {
1688 - for (GroupChainElem gce : gceSet) {
1689 - log.info("group ADDED with group key {} .. "
1690 - + "Processing next group in group chain with group key {}",
1691 - key,
1692 - gce.groupDescription.appCookie());
1693 - processGroupChain(gce);
1694 - }
1695 - } else {
1696 - List<OfdpaNextGroup> objList = pendingNextObjectives.getIfPresent(key);
1697 - if (objList != null) {
1698 - pendingNextObjectives.invalidate(key);
1699 - objList.forEach(obj -> {
1700 - log.info("group ADDED with key {} in dev {}.. Done implementing next "
1701 - + "objective: {} <<-->> gid:{}",
1702 - key, deviceId, obj.nextObjective().id(),
1703 - Integer.toHexString(groupService.getGroup(deviceId, key)
1704 - .givenGroupId()));
1705 - pass(obj.nextObjective());
1706 - flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
1707 - });
1708 - }
1709 - }
1710 } 1889 }
1711 } 1890 }
1712 } 1891 }
...@@ -1714,7 +1893,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1714,7 +1893,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1714 /** 1893 /**
1715 * Represents an entire group-chain that implements a Next-Objective from 1894 * Represents an entire group-chain that implements a Next-Objective from
1716 * the application. The objective is represented as a list of deques, where 1895 * the application. The objective is represented as a list of deques, where
1717 - * each deque can is a separate chain of groups. 1896 + * each deque is a separate chain of groups.
1718 * <p> 1897 * <p>
1719 * For example, an ECMP group with 3 buckets, where each bucket points to 1898 * For example, an ECMP group with 3 buckets, where each bucket points to
1720 * a group chain of L3 Unicast and L2 interface groups will look like this: 1899 * a group chain of L3 Unicast and L2 interface groups will look like this:
...@@ -1765,10 +1944,13 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1765,10 +1944,13 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1765 private class GroupChainElem { 1944 private class GroupChainElem {
1766 private GroupDescription groupDescription; 1945 private GroupDescription groupDescription;
1767 private AtomicInteger waitOnGroups; 1946 private AtomicInteger waitOnGroups;
1947 + private boolean addBucketToGroup;
1768 1948
1769 - GroupChainElem(GroupDescription groupDescription, int waitOnGroups) { 1949 + GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
1950 + boolean addBucketToGroup) {
1770 this.groupDescription = groupDescription; 1951 this.groupDescription = groupDescription;
1771 this.waitOnGroups = new AtomicInteger(waitOnGroups); 1952 this.waitOnGroups = new AtomicInteger(waitOnGroups);
1953 + this.addBucketToGroup = addBucketToGroup;
1772 } 1954 }
1773 1955
1774 /** 1956 /**
...@@ -1788,6 +1970,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1788,6 +1970,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1788 return (Integer.toHexString(groupDescription.givenGroupId()) + 1970 return (Integer.toHexString(groupDescription.givenGroupId()) +
1789 " groupKey: " + groupDescription.appCookie() + 1971 " groupKey: " + groupDescription.appCookie() +
1790 " waiting-on-groups: " + waitOnGroups.get() + 1972 " waiting-on-groups: " + waitOnGroups.get() +
1973 + " addBucketToGroup: " + addBucketToGroup +
1791 " device: " + deviceId); 1974 " device: " + deviceId);
1792 } 1975 }
1793 } 1976 }
......