Saurav Das
Committed by Gerrit Code Review

CORD-48 Implementation of hashing Next Objective in OF-DPA driver. Major changes…

… to ensure multi-ONOS-instance group-chain installation.
Also includes:
     Changes to Next Objective that adds metadata field for applications to optionally send auxillary info to drivers
     Changes to Next Objective that allows more explicit modification of the next objective
     Changes to Forwarding Objective and PendingNext to include hashCode() and equals() method
     MplsBosInstruction included in kryo serializer
     GroupKey's byte[] represented as a hex string
     Bug fix in mpls flow installation to report failure in install
     Bug fix in linkUp in SR app to disallow non-masters to modify groups
     Bug fix in ordering of actions in group

Change-Id: I3e7003f55724c2de79589e43e11d05ff4815a81d
Showing 18 changed files with 1240 additions and 457 deletions
...@@ -448,7 +448,6 @@ public class DefaultRoutingHandler { ...@@ -448,7 +448,6 @@ public class DefaultRoutingHandler {
448 if (nextHops.isEmpty()) { 448 if (nextHops.isEmpty()) {
449 nextHops.add(destSw); 449 nextHops.add(destSw);
450 } 450 }
451 -
452 // If both target switch and dest switch are edge routers, then set IP 451 // If both target switch and dest switch are edge routers, then set IP
453 // rule for both subnet and router IP. 452 // rule for both subnet and router IP.
454 boolean targetIsEdge; 453 boolean targetIsEdge;
...@@ -485,8 +484,8 @@ public class DefaultRoutingHandler { ...@@ -485,8 +484,8 @@ public class DefaultRoutingHandler {
485 return false; 484 return false;
486 } 485 }
487 486
488 - // If the target switch is an edge router, then set IP rules for the router IP.
489 } else if (targetIsEdge) { 487 } else if (targetIsEdge) {
488 + // If the target switch is an edge router, then set IP rules for the router IP.
490 Ip4Address routerIp = destRouterIp; 489 Ip4Address routerIp = destRouterIp;
491 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH); 490 IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
492 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}", 491 log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
...@@ -496,7 +495,6 @@ public class DefaultRoutingHandler { ...@@ -496,7 +495,6 @@ public class DefaultRoutingHandler {
496 return false; 495 return false;
497 } 496 }
498 } 497 }
499 -
500 // Populates MPLS rules to all routers 498 // Populates MPLS rules to all routers
501 log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules", 499 log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
502 targetSw, destSw); 500 targetSw, destSw);
...@@ -504,7 +502,6 @@ public class DefaultRoutingHandler { ...@@ -504,7 +502,6 @@ public class DefaultRoutingHandler {
504 if (!result) { 502 if (!result) {
505 return false; 503 return false;
506 } 504 }
507 -
508 return true; 505 return true;
509 } 506 }
510 507
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.segmentrouting; 16 package org.onosproject.segmentrouting;
17 17
18 +import org.onlab.packet.EthType;
18 import org.onlab.packet.Ethernet; 19 import org.onlab.packet.Ethernet;
19 import org.onlab.packet.Ip4Address; 20 import org.onlab.packet.Ip4Address;
20 import org.onlab.packet.Ip4Prefix; 21 import org.onlab.packet.Ip4Prefix;
...@@ -26,7 +27,6 @@ import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; ...@@ -26,7 +27,6 @@ import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
26 import org.onosproject.segmentrouting.config.DeviceConfiguration; 27 import org.onosproject.segmentrouting.config.DeviceConfiguration;
27 import org.onosproject.segmentrouting.grouphandler.NeighborSet; 28 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
28 import org.onosproject.net.DeviceId; 29 import org.onosproject.net.DeviceId;
29 -import org.onosproject.net.Link;
30 import org.onosproject.net.Port; 30 import org.onosproject.net.Port;
31 import org.onosproject.net.PortNumber; 31 import org.onosproject.net.PortNumber;
32 import org.onosproject.net.flow.DefaultTrafficSelector; 32 import org.onosproject.net.flow.DefaultTrafficSelector;
...@@ -227,7 +227,15 @@ public class RoutingRulePopulator { ...@@ -227,7 +227,15 @@ public class RoutingRulePopulator {
227 treatment = null; 227 treatment = null;
228 } 228 }
229 229
230 - if (srManager.getNextObjectiveId(deviceId, ns) <= 0) { 230 + // setup metadata to pass to nextObjective - indicate the vlan on egress
231 + // if needed by the switch pipeline. Since neighbor sets are always to
232 + // other neighboring routers, there is no subnet assigned on those ports.
233 + TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
234 + metabuilder.matchVlanId(
235 + VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
236 +
237 + int nextId = srManager.getNextObjectiveId(deviceId, ns, metabuilder.build());
238 + if (nextId <= 0) {
231 log.warn("No next objective in {} for ns: {}", deviceId, ns); 239 log.warn("No next objective in {} for ns: {}", deviceId, ns);
232 return false; 240 return false;
233 } 241 }
...@@ -236,7 +244,7 @@ public class RoutingRulePopulator { ...@@ -236,7 +244,7 @@ public class RoutingRulePopulator {
236 .builder() 244 .builder()
237 .fromApp(srManager.appId) 245 .fromApp(srManager.appId)
238 .makePermanent() 246 .makePermanent()
239 - .nextStep(srManager.getNextObjectiveId(deviceId, ns)) 247 + .nextStep(nextId)
240 .withSelector(selector) 248 .withSelector(selector)
241 .withPriority(100) 249 .withPriority(100)
242 .withFlag(ForwardingObjective.Flag.SPECIFIC); 250 .withFlag(ForwardingObjective.Flag.SPECIFIC);
...@@ -279,63 +287,70 @@ public class RoutingRulePopulator { ...@@ -279,63 +287,70 @@ public class RoutingRulePopulator {
279 List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>(); 287 List<ForwardingObjective.Builder> fwdObjBuilders = new ArrayList<>();
280 288
281 // TODO Handle the case of Bos == false 289 // TODO Handle the case of Bos == false
282 - sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
283 sbuilder.matchEthType(Ethernet.MPLS_UNICAST); 290 sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
291 + sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
292 + TrafficSelector selector = sbuilder.build();
293 +
294 + // setup metadata to pass to nextObjective - indicate the vlan on egress
295 + // if needed by the switch pipeline. Since mpls next-hops are always to
296 + // other neighboring routers, there is no subnet assigned on those ports.
297 + TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
298 + metabuilder.matchVlanId(
299 + VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET));
284 300
285 - // If the next hop is the destination router, do PHP 301 + // If the next hop is the destination router for the segment, do pop
286 if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) { 302 if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
287 log.debug("populateMplsRule: Installing MPLS forwarding objective for " 303 log.debug("populateMplsRule: Installing MPLS forwarding objective for "
288 - + "label {} in switch {} with PHP", 304 + + "label {} in switch {} with pop", segmentId, deviceId);
289 - segmentId,
290 - deviceId);
291 305
306 + // bos pop case (php)
292 ForwardingObjective.Builder fwdObjBosBuilder = 307 ForwardingObjective.Builder fwdObjBosBuilder =
293 getMplsForwardingObjective(deviceId, 308 getMplsForwardingObjective(deviceId,
294 - destSwId,
295 nextHops, 309 nextHops,
296 true, 310 true,
297 - true);
298 - // TODO: Check with Sangho on why we need this
299 - ForwardingObjective.Builder fwdObjNoBosBuilder =
300 - getMplsForwardingObjective(deviceId,
301 - destSwId,
302 - nextHops,
303 true, 311 true,
304 - false); 312 + metabuilder.build());
305 - if (fwdObjBosBuilder != null) { 313 + if (fwdObjBosBuilder == null) {
306 - fwdObjBuilders.add(fwdObjBosBuilder);
307 - } else {
308 - log.warn("Failed to set MPLS rules.");
309 return false; 314 return false;
310 } 315 }
316 + fwdObjBuilders.add(fwdObjBosBuilder);
317 +
318 + // XXX not-bos pop case, SR app multi-label not implemented yet
319 + /*ForwardingObjective.Builder fwdObjNoBosBuilder =
320 + getMplsForwardingObjective(deviceId,
321 + nextHops,
322 + true,
323 + false);*/
324 +
311 } else { 325 } else {
326 + // next hop is not destination, SR CONTINUE case (swap with self)
312 log.debug("Installing MPLS forwarding objective for " 327 log.debug("Installing MPLS forwarding objective for "
313 - + "label {} in switch {} without PHP", 328 + + "label {} in switch {} without pop", segmentId, deviceId);
314 - segmentId,
315 - deviceId);
316 329
330 + // continue case with bos - this does get triggered in edge routers
331 + // and in core routers - driver can handle depending on availability
332 + // of MPLS ECMP or not
317 ForwardingObjective.Builder fwdObjBosBuilder = 333 ForwardingObjective.Builder fwdObjBosBuilder =
318 getMplsForwardingObjective(deviceId, 334 getMplsForwardingObjective(deviceId,
319 - destSwId,
320 nextHops, 335 nextHops,
321 false, 336 false,
322 - true); 337 + true,
323 - // TODO: Check with Sangho on why we need this 338 + metabuilder.build());
324 - ForwardingObjective.Builder fwdObjNoBosBuilder = 339 + if (fwdObjBosBuilder == null) {
340 + return false;
341 + }
342 + fwdObjBuilders.add(fwdObjBosBuilder);
343 +
344 + // XXX continue case with not-bos - SR app multi label not implemented yet
345 + // also requires MPLS ECMP
346 + /*ForwardingObjective.Builder fwdObjNoBosBuilder =
325 getMplsForwardingObjective(deviceId, 347 getMplsForwardingObjective(deviceId,
326 - destSwId,
327 nextHops, 348 nextHops,
328 false, 349 false,
329 - false); 350 + false); */
330 - if (fwdObjBosBuilder != null) { 351 +
331 - fwdObjBuilders.add(fwdObjBosBuilder);
332 - } else {
333 - log.warn("Failed to set MPLS rules.");
334 - return false;
335 - }
336 } 352 }
337 353
338 - TrafficSelector selector = sbuilder.build();
339 for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) { 354 for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
340 ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId) 355 ((Builder) ((Builder) fwdObjBuilder.fromApp(srManager.appId)
341 .makePermanent()).withSelector(selector) 356 .makePermanent()).withSelector(selector)
...@@ -352,67 +367,52 @@ public class RoutingRulePopulator { ...@@ -352,67 +367,52 @@ public class RoutingRulePopulator {
352 return true; 367 return true;
353 } 368 }
354 369
355 - private ForwardingObjective.Builder getMplsForwardingObjective(DeviceId deviceId, 370 + private ForwardingObjective.Builder getMplsForwardingObjective(
356 - DeviceId destSw, 371 + DeviceId deviceId,
357 Set<DeviceId> nextHops, 372 Set<DeviceId> nextHops,
358 boolean phpRequired, 373 boolean phpRequired,
359 - boolean isBos) { 374 + boolean isBos,
375 + TrafficSelector meta) {
376 +
360 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective 377 ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
361 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC); 378 .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
362 - DeviceId nextHop = (DeviceId) nextHops.toArray()[0];
363 -
364 - boolean isEdge;
365 - MacAddress srcMac;
366 - MacAddress dstMac;
367 - try {
368 - isEdge = config.isEdgeDevice(deviceId);
369 - srcMac = config.getDeviceMac(deviceId);
370 - dstMac = config.getDeviceMac(nextHop);
371 - } catch (DeviceConfigNotFoundException e) {
372 - log.warn(e.getMessage() + " Aborting getMplsForwardingObjective");
373 - return null;
374 - }
375 379
376 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); 380 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
377 381
378 if (phpRequired) { 382 if (phpRequired) {
383 + // php case - pop should always be flow-action
379 log.debug("getMplsForwardingObjective: php required"); 384 log.debug("getMplsForwardingObjective: php required");
380 tbuilder.deferred().copyTtlIn(); 385 tbuilder.deferred().copyTtlIn();
381 if (isBos) { 386 if (isBos) {
382 - tbuilder.deferred().popMpls(Ethernet.TYPE_IPV4).decNwTtl(); 387 + tbuilder.deferred().popMpls(EthType.EtherType.IPV4.ethType())
388 + .decNwTtl();
383 } else { 389 } else {
384 - tbuilder.deferred().popMpls(Ethernet.MPLS_UNICAST).decMplsTtl(); 390 + tbuilder.deferred().popMpls(EthType.EtherType.MPLS_UNICAST.ethType())
391 + .decMplsTtl();
385 } 392 }
386 } else { 393 } else {
394 + // swap with self case - SR CONTINUE
387 log.debug("getMplsForwardingObjective: php not required"); 395 log.debug("getMplsForwardingObjective: php not required");
388 tbuilder.deferred().decMplsTtl(); 396 tbuilder.deferred().decMplsTtl();
389 } 397 }
390 398
391 - if (!isECMPSupportedInTransitRouter() && !isEdge) { 399 + // All forwarding is via ECMP group, the metadata informs the driver
392 - PortNumber port = selectOnePort(deviceId, nextHops); 400 + // that the next-Objective will be used by MPLS flows. In other words,
393 - if (port == null) { 401 + // MPLS ECMP is requested. It is up to the driver to decide if these
394 - log.warn("No link from {} to {}", deviceId, nextHops); 402 + // packets will be hashed or not.
395 - return null;
396 - }
397 - tbuilder.deferred()
398 - .setEthSrc(srcMac)
399 - .setEthDst(dstMac)
400 - .setOutput(port);
401 fwdBuilder.withTreatment(tbuilder.build()); 403 fwdBuilder.withTreatment(tbuilder.build());
402 - } else {
403 NeighborSet ns = new NeighborSet(nextHops); 404 NeighborSet ns = new NeighborSet(nextHops);
404 - fwdBuilder.withTreatment(tbuilder.build()); 405 + log.debug("Trying to get a nextObjid for mpls rule on device:{} to ns:{}",
405 - fwdBuilder.nextStep(srManager 406 + deviceId, ns);
406 - .getNextObjectiveId(deviceId, ns));
407 - }
408 407
409 - return fwdBuilder; 408 + int nextId = srManager.getNextObjectiveId(deviceId, ns, meta);
409 + if (nextId <= 0) {
410 + log.warn("No next objective in {} for ns: {}", deviceId, ns);
411 + return null;
410 } 412 }
411 413
412 - private boolean isECMPSupportedInTransitRouter() { 414 + fwdBuilder.nextStep(nextId);
413 - 415 + return fwdBuilder;
414 - // TODO: remove this function when objectives subsystem is supported.
415 - return false;
416 } 416 }
417 417
418 /** 418 /**
...@@ -552,22 +552,6 @@ public class RoutingRulePopulator { ...@@ -552,22 +552,6 @@ public class RoutingRulePopulator {
552 } 552 }
553 553
554 554
555 - private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) {
556 -
557 - Set<Link> links = srManager.linkService.getDeviceLinks(srcId);
558 - for (DeviceId destId: destIds) {
559 - for (Link link : links) {
560 - if (link.dst().deviceId().equals(destId)) {
561 - return link.src().port();
562 - } else if (link.src().deviceId().equals(destId)) {
563 - return link.dst().port();
564 - }
565 - }
566 - }
567 -
568 - return null;
569 - }
570 -
571 private static class SRObjectiveContext implements ObjectiveContext { 555 private static class SRObjectiveContext implements ObjectiveContext {
572 enum ObjectiveType { 556 enum ObjectiveType {
573 FILTER, 557 FILTER,
......
...@@ -424,17 +424,22 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -424,17 +424,22 @@ public class SegmentRoutingManager implements SegmentRoutingService {
424 424
425 /** 425 /**
426 * Returns the next objective ID for the given NeighborSet. 426 * Returns the next objective ID for the given NeighborSet.
427 - * If the nextObjectiveID does not exist, a new one is created and returned. 427 + * If the nextObjective does not exist, a new one is created and
428 + * it's id is returned.
429 + * TODO move the side-effect creation of a Next Objective into a new method
428 * 430 *
429 * @param deviceId Device ID 431 * @param deviceId Device ID
430 * @param ns NegighborSet 432 * @param ns NegighborSet
431 - * @return next objective ID 433 + * @param meta metadata passed into the creation of a Next Objective
434 + * @return next objective ID or -1 if an error was encountered during the
435 + * creation of the nextObjective
432 */ 436 */
433 - public int getNextObjectiveId(DeviceId deviceId, NeighborSet ns) { 437 + public int getNextObjectiveId(DeviceId deviceId, NeighborSet ns,
438 + TrafficSelector meta) {
434 if (groupHandlerMap.get(deviceId) != null) { 439 if (groupHandlerMap.get(deviceId) != null) {
435 log.trace("getNextObjectiveId query in device {}", deviceId); 440 log.trace("getNextObjectiveId query in device {}", deviceId);
436 return groupHandlerMap 441 return groupHandlerMap
437 - .get(deviceId).getNextObjectiveId(ns); 442 + .get(deviceId).getNextObjectiveId(ns, meta);
438 } else { 443 } else {
439 log.warn("getNextObjectiveId query in device {} not found", deviceId); 444 log.warn("getNextObjectiveId query in device {} not found", deviceId);
440 return -1; 445 return -1;
...@@ -586,7 +591,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -586,7 +591,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
586 DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src() 591 DefaultGroupHandler groupHandler = groupHandlerMap.get(link.src()
587 .deviceId()); 592 .deviceId());
588 if (groupHandler != null) { 593 if (groupHandler != null) {
589 - groupHandler.linkUp(link); 594 + groupHandler.linkUp(link, mastershipService.isLocalMaster(
595 + link.src().deviceId()));
590 } else { 596 } else {
591 Device device = deviceService.getDevice(link.src().deviceId()); 597 Device device = deviceService.getDevice(link.src().deviceId());
592 if (device != null) { 598 if (device != null) {
...@@ -596,7 +602,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -596,7 +602,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
596 processDeviceAdded(device); 602 processDeviceAdded(device);
597 groupHandler = groupHandlerMap.get(link.src() 603 groupHandler = groupHandlerMap.get(link.src()
598 .deviceId()); 604 .deviceId());
599 - groupHandler.linkUp(link); 605 + groupHandler.linkUp(link, mastershipService.isLocalMaster(device.id()));
600 } 606 }
601 } 607 }
602 608
......
...@@ -194,7 +194,7 @@ public class TunnelHandler { ...@@ -194,7 +194,7 @@ public class TunnelHandler {
194 tunnel.allowToRemoveGroup(true); 194 tunnel.allowToRemoveGroup(true);
195 } 195 }
196 196
197 - return groupHandlerMap.get(deviceId).getNextObjectiveId(ns); 197 + return groupHandlerMap.get(deviceId).getNextObjectiveId(ns, null);
198 } 198 }
199 199
200 } 200 }
......
...@@ -93,7 +93,7 @@ public class DefaultEdgeGroupHandler extends DefaultGroupHandler { ...@@ -93,7 +93,7 @@ public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
93 + "with label for sw {} is {}", 93 + "with label for sw {} is {}",
94 deviceId, nsSet); 94 deviceId, nsSet);
95 95
96 - createGroupsFromNeighborsets(nsSet); 96 + //createGroupsFromNeighborsets(nsSet);
97 } 97 }
98 98
99 @Override 99 @Override
...@@ -107,7 +107,7 @@ public class DefaultEdgeGroupHandler extends DefaultGroupHandler { ...@@ -107,7 +107,7 @@ public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
107 Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent( 107 Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
108 newNeighborLink.dst().deviceId(), 108 newNeighborLink.dst().deviceId(),
109 devicePortMap.keySet()); 109 devicePortMap.keySet());
110 - createGroupsFromNeighborsets(nsSet); 110 + //createGroupsFromNeighborsets(nsSet);
111 } 111 }
112 112
113 @Override 113 @Override
......
...@@ -21,11 +21,11 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -21,11 +21,11 @@ import static org.slf4j.LoggerFactory.getLogger;
21 import java.net.URI; 21 import java.net.URI;
22 import java.util.ArrayList; 22 import java.util.ArrayList;
23 import java.util.Collections; 23 import java.util.Collections;
24 -import java.util.HashMap;
25 import java.util.HashSet; 24 import java.util.HashSet;
26 import java.util.List; 25 import java.util.List;
27 import java.util.Map; 26 import java.util.Map;
28 import java.util.Set; 27 import java.util.Set;
28 +import java.util.concurrent.ConcurrentHashMap;
29 import java.util.stream.Collectors; 29 import java.util.stream.Collectors;
30 30
31 import org.onlab.packet.Ip4Prefix; 31 import org.onlab.packet.Ip4Prefix;
...@@ -38,6 +38,7 @@ import org.onosproject.net.DeviceId; ...@@ -38,6 +38,7 @@ import org.onosproject.net.DeviceId;
38 import org.onosproject.net.Link; 38 import org.onosproject.net.Link;
39 import org.onosproject.net.PortNumber; 39 import org.onosproject.net.PortNumber;
40 import org.onosproject.net.flow.DefaultTrafficTreatment; 40 import org.onosproject.net.flow.DefaultTrafficTreatment;
41 +import org.onosproject.net.flow.TrafficSelector;
41 import org.onosproject.net.flow.TrafficTreatment; 42 import org.onosproject.net.flow.TrafficTreatment;
42 import org.onosproject.net.flowobjective.DefaultNextObjective; 43 import org.onosproject.net.flowobjective.DefaultNextObjective;
43 import org.onosproject.net.flowobjective.FlowObjectiveService; 44 import org.onosproject.net.flowobjective.FlowObjectiveService;
...@@ -71,10 +72,10 @@ public class DefaultGroupHandler { ...@@ -71,10 +72,10 @@ public class DefaultGroupHandler {
71 protected LinkService linkService; 72 protected LinkService linkService;
72 protected FlowObjectiveService flowObjectiveService; 73 protected FlowObjectiveService flowObjectiveService;
73 74
74 - protected HashMap<DeviceId, Set<PortNumber>> devicePortMap = new HashMap<>(); 75 + protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
75 - protected HashMap<PortNumber, DeviceId> portDeviceMap = new HashMap<>(); 76 + new ConcurrentHashMap<>();
76 - //protected HashMap<NeighborSet, Integer> deviceNextObjectiveIds = 77 + protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
77 - // new HashMap<NeighborSet, Integer>(); 78 + new ConcurrentHashMap<>();
78 protected EventuallyConsistentMap< 79 protected EventuallyConsistentMap<
79 NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null; 80 NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null;
80 protected EventuallyConsistentMap< 81 protected EventuallyConsistentMap<
...@@ -173,7 +174,7 @@ public class DefaultGroupHandler { ...@@ -173,7 +174,7 @@ public class DefaultGroupHandler {
173 * 174 *
174 * @param newLink new neighbor link 175 * @param newLink new neighbor link
175 */ 176 */
176 - public void linkUp(Link newLink) { 177 + public void linkUp(Link newLink, boolean isMaster) {
177 178
178 if (newLink.type() != Link.Type.DIRECT) { 179 if (newLink.type() != Link.Type.DIRECT) {
179 log.warn("linkUp: unknown link type"); 180 log.warn("linkUp: unknown link type");
...@@ -186,6 +187,8 @@ public class DefaultGroupHandler { ...@@ -186,6 +187,8 @@ public class DefaultGroupHandler {
186 return; 187 return;
187 } 188 }
188 189
190 + log.info("* LinkUP: Device {} linkUp at local port {} to neighbor {}", deviceId,
191 + newLink.src().port(), newLink.dst().deviceId());
189 MacAddress dstMac; 192 MacAddress dstMac;
190 try { 193 try {
191 dstMac = deviceConfig.getDeviceMac(newLink.dst().deviceId()); 194 dstMac = deviceConfig.getDeviceMac(newLink.dst().deviceId());
...@@ -194,8 +197,6 @@ public class DefaultGroupHandler { ...@@ -194,8 +197,6 @@ public class DefaultGroupHandler {
194 return; 197 return;
195 } 198 }
196 199
197 - log.debug("Device {} linkUp at local port {} to neighbor {}", deviceId,
198 - newLink.src().port(), newLink.dst().deviceId());
199 addNeighborAtPort(newLink.dst().deviceId(), 200 addNeighborAtPort(newLink.dst().deviceId(),
200 newLink.src().port()); 201 newLink.src().port());
201 /*if (devicePortMap.get(newLink.dst().deviceId()) == null) { 202 /*if (devicePortMap.get(newLink.dst().deviceId()) == null) {
...@@ -237,15 +238,21 @@ public class DefaultGroupHandler { ...@@ -237,15 +238,21 @@ public class DefaultGroupHandler {
237 238
238 nextObjBuilder.addTreatment(tBuilder.build()); 239 nextObjBuilder.addTreatment(tBuilder.build());
239 240
240 - log.debug("linkUp in device {}: Adding Bucket " 241 + log.info("**linkUp in device {}: Adding Bucket "
241 - + "with Port {} to next object id {}", 242 + + "with Port {} to next object id {} and amIMaster:{}",
242 deviceId, 243 deviceId,
243 newLink.src().port(), 244 newLink.src().port(),
244 - nextId); 245 + nextId, isMaster);
246 +
247 + if (isMaster) {
245 NextObjective nextObjective = nextObjBuilder. 248 NextObjective nextObjective = nextObjBuilder.
246 - add(new SRNextObjectiveContext(deviceId)); 249 + addToExisting(new SRNextObjectiveContext(deviceId));
247 flowObjectiveService.next(deviceId, nextObjective); 250 flowObjectiveService.next(deviceId, nextObjective);
248 } 251 }
252 + } else {
253 + log.warn("linkUp in device {}, but global store has no record "
254 + + "for neighbor-set {}", deviceId, ns);
255 + }
249 } 256 }
250 } 257 }
251 258
...@@ -305,15 +312,16 @@ public class DefaultGroupHandler { ...@@ -305,15 +312,16 @@ public class DefaultGroupHandler {
305 312
306 nextObjBuilder.addTreatment(tBuilder.build()); 313 nextObjBuilder.addTreatment(tBuilder.build());
307 314
308 - log.debug("portDown in device {}: Removing Bucket " 315 + log.info("**portDown in device {}: Removing Bucket "
309 + "with Port {} to next object id {}", 316 + "with Port {} to next object id {}",
310 deviceId, 317 deviceId,
311 port, 318 port,
312 nextId); 319 nextId);
313 - NextObjective nextObjective = nextObjBuilder. 320 + // should do removefromexisting and only if master
321 + /*NextObjective nextObjective = nextObjBuilder.
314 remove(new SRNextObjectiveContext(deviceId)); 322 remove(new SRNextObjectiveContext(deviceId));
315 323
316 - flowObjectiveService.next(deviceId, nextObjective); 324 + flowObjectiveService.next(deviceId, nextObjective);*/
317 } 325 }
318 326
319 } 327 }
...@@ -325,12 +333,15 @@ public class DefaultGroupHandler { ...@@ -325,12 +333,15 @@ public class DefaultGroupHandler {
325 /** 333 /**
326 * Returns the next objective associated with the neighborset. 334 * Returns the next objective associated with the neighborset.
327 * If there is no next objective for this neighborset, this API 335 * If there is no next objective for this neighborset, this API
328 - * would create a next objective and return. 336 + * would create a next objective and return. Optionally metadata can be
337 + * passed in for the creation of the next objective.
329 * 338 *
330 * @param ns neighborset 339 * @param ns neighborset
331 - * @return int if found or -1 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.
332 */ 343 */
333 - public int getNextObjectiveId(NeighborSet ns) { 344 + public int getNextObjectiveId(NeighborSet ns, TrafficSelector meta) {
334 Integer nextId = nsNextObjStore. 345 Integer nextId = nsNextObjStore.
335 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns)); 346 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
336 if (nextId == null) { 347 if (nextId == null) {
...@@ -343,7 +354,7 @@ public class DefaultGroupHandler { ...@@ -343,7 +354,7 @@ public class DefaultGroupHandler {
343 .filter((nsStoreEntry) -> 354 .filter((nsStoreEntry) ->
344 (nsStoreEntry.getKey().deviceId().equals(deviceId))) 355 (nsStoreEntry.getKey().deviceId().equals(deviceId)))
345 .collect(Collectors.toList())); 356 .collect(Collectors.toList()));
346 - createGroupsFromNeighborsets(Collections.singleton(ns)); 357 + createGroupsFromNeighborsets(Collections.singleton(ns), meta);
347 nextId = nsNextObjStore. 358 nextId = nsNextObjStore.
348 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns)); 359 get(new NeighborSetNextObjectiveStoreKey(deviceId, ns));
349 if (nextId == null) { 360 if (nextId == null) {
...@@ -421,17 +432,19 @@ public class DefaultGroupHandler { ...@@ -421,17 +432,19 @@ public class DefaultGroupHandler {
421 // Update DeviceToPort database 432 // Update DeviceToPort database
422 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}", 433 log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
423 deviceId, neighborId, portToNeighbor); 434 deviceId, neighborId, portToNeighbor);
424 - if (devicePortMap.get(neighborId) != null) { 435 + Set<PortNumber> ports = Collections
425 - devicePortMap.get(neighborId).add(portToNeighbor); 436 + .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
426 - } else {
427 - Set<PortNumber> ports = new HashSet<>();
428 ports.add(portToNeighbor); 437 ports.add(portToNeighbor);
429 - devicePortMap.put(neighborId, ports); 438 + Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
439 + if (portnums != null) {
440 + portnums.add(portToNeighbor);
430 } 441 }
431 442
432 // Update portToDevice database 443 // Update portToDevice database
433 - if (portDeviceMap.get(portToNeighbor) == null) { 444 + DeviceId prev = portDeviceMap.putIfAbsent(portToNeighbor, neighborId);
434 - portDeviceMap.put(portToNeighbor, neighborId); 445 + if (prev != null) {
446 + log.warn("Device: {} port: {} has neighbor: {}. NOT updating "
447 + + "to neighbor: {}", deviceId, portToNeighbor, prev, neighborId);
435 } 448 }
436 } 449 }
437 450
...@@ -505,61 +518,66 @@ public class DefaultGroupHandler { ...@@ -505,61 +518,66 @@ public class DefaultGroupHandler {
505 * Creates Groups from a set of NeighborSet given. 518 * Creates Groups from a set of NeighborSet given.
506 * 519 *
507 * @param nsSet a set of NeighborSet 520 * @param nsSet a set of NeighborSet
521 + * @param meta metadata passed into the creation of a Next Objective
508 */ 522 */
509 - public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet) { 523 + public void createGroupsFromNeighborsets(Set<NeighborSet> nsSet,
524 + TrafficSelector meta) {
510 for (NeighborSet ns : nsSet) { 525 for (NeighborSet ns : nsSet) {
511 int nextId = flowObjectiveService.allocateNextId(); 526 int nextId = flowObjectiveService.allocateNextId();
512 NextObjective.Builder nextObjBuilder = DefaultNextObjective 527 NextObjective.Builder nextObjBuilder = DefaultNextObjective
513 .builder().withId(nextId) 528 .builder().withId(nextId)
514 .withType(NextObjective.Type.HASHED).fromApp(appId); 529 .withType(NextObjective.Type.HASHED).fromApp(appId);
515 - for (DeviceId d : ns.getDeviceIds()) { 530 + for (DeviceId neighborId : ns.getDeviceIds()) {
516 - if (devicePortMap.get(d) == null) { 531 + if (devicePortMap.get(neighborId) == null) {
517 - log.warn("Device {} is not in the port map yet", d); 532 + log.warn("Neighbor {} is not in the port map yet for dev:{}",
533 + neighborId, deviceId);
518 return; 534 return;
519 - } else if (devicePortMap.get(d).size() == 0) { 535 + } else if (devicePortMap.get(neighborId).size() == 0) {
520 log.warn("There are no ports for " 536 log.warn("There are no ports for "
521 - + "the Device {} in the port map yet", d); 537 + + "the Device {} in the port map yet", neighborId);
522 return; 538 return;
523 } 539 }
524 540
525 - MacAddress deviceMac; 541 + MacAddress neighborMac;
526 try { 542 try {
527 - deviceMac = deviceConfig.getDeviceMac(d); 543 + neighborMac = deviceConfig.getDeviceMac(neighborId);
528 } catch (DeviceConfigNotFoundException e) { 544 } catch (DeviceConfigNotFoundException e) {
529 log.warn(e.getMessage() + " Aborting createGroupsFromNeighborsets."); 545 log.warn(e.getMessage() + " Aborting createGroupsFromNeighborsets.");
530 return; 546 return;
531 } 547 }
532 548
533 - for (PortNumber sp : devicePortMap.get(d)) { 549 + for (PortNumber sp : devicePortMap.get(neighborId)) {
534 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment 550 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
535 .builder(); 551 .builder();
536 - tBuilder.setOutput(sp) 552 + tBuilder.setEthDst(neighborMac)
537 - .setEthDst(deviceMac)
538 .setEthSrc(nodeMacAddr); 553 .setEthSrc(nodeMacAddr);
539 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) { 554 if (ns.getEdgeLabel() != NeighborSet.NO_EDGE_LABEL) {
540 tBuilder.pushMpls() 555 tBuilder.pushMpls()
541 .copyTtlOut() 556 .copyTtlOut()
542 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel())); 557 .setMpls(MplsLabel.mplsLabel(ns.getEdgeLabel()));
543 } 558 }
559 + tBuilder.setOutput(sp);
544 nextObjBuilder.addTreatment(tBuilder.build()); 560 nextObjBuilder.addTreatment(tBuilder.build());
545 } 561 }
546 } 562 }
547 - 563 + if (meta != null) {
564 + nextObjBuilder.setMeta(meta);
565 + }
548 NextObjective nextObj = nextObjBuilder. 566 NextObjective nextObj = nextObjBuilder.
549 add(new SRNextObjectiveContext(deviceId)); 567 add(new SRNextObjectiveContext(deviceId));
550 - flowObjectiveService.next(deviceId, nextObj); 568 + log.info("**createGroupsFromNeighborsets: Submited "
551 - log.debug("createGroupsFromNeighborsets: Submited "
552 + "next objective {} in device {}", 569 + "next objective {} in device {}",
553 nextId, deviceId); 570 nextId, deviceId);
571 + flowObjectiveService.next(deviceId, nextObj);
554 nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, ns), 572 nsNextObjStore.put(new NeighborSetNextObjectiveStoreKey(deviceId, ns),
555 nextId); 573 nextId);
556 } 574 }
557 } 575 }
558 576
577 +
559 public void createGroupsFromSubnetConfig() { 578 public void createGroupsFromSubnetConfig() {
560 Map<Ip4Prefix, List<PortNumber>> subnetPortMap = 579 Map<Ip4Prefix, List<PortNumber>> subnetPortMap =
561 this.deviceConfig.getSubnetPortsMap(this.deviceId); 580 this.deviceConfig.getSubnetPortsMap(this.deviceId);
562 -
563 // Construct a broadcast group for each subnet 581 // Construct a broadcast group for each subnet
564 subnetPortMap.forEach((subnet, ports) -> { 582 subnetPortMap.forEach((subnet, ports) -> {
565 SubnetNextObjectiveStoreKey key = 583 SubnetNextObjectiveStoreKey key =
...@@ -612,6 +630,9 @@ public class DefaultGroupHandler { ...@@ -612,6 +630,9 @@ public class DefaultGroupHandler {
612 .withType(NextObjective.Type.HASHED).fromApp(appId); 630 .withType(NextObjective.Type.HASHED).fromApp(appId);
613 NextObjective nextObjective = nextObjBuilder. 631 NextObjective nextObjective = nextObjBuilder.
614 remove(new SRNextObjectiveContext(deviceId)); 632 remove(new SRNextObjectiveContext(deviceId));
633 + log.info("**removeGroup: Submited "
634 + + "next objective {} in device {}",
635 + objectiveId, deviceId);
615 flowObjectiveService.next(deviceId, nextObjective); 636 flowObjectiveService.next(deviceId, nextObjective);
616 637
617 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry: nsNextObjStore.entrySet()) { 638 for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry: nsNextObjStore.entrySet()) {
...@@ -634,14 +655,14 @@ public class DefaultGroupHandler { ...@@ -634,14 +655,14 @@ public class DefaultGroupHandler {
634 } 655 }
635 @Override 656 @Override
636 public void onSuccess(Objective objective) { 657 public void onSuccess(Objective objective) {
637 - log.debug("Next objective operation successful in device {}", 658 + log.info("Next objective {} operation successful in device {}",
638 - deviceId); 659 + objective.id(), deviceId);
639 } 660 }
640 661
641 @Override 662 @Override
642 public void onError(Objective objective, ObjectiveError error) { 663 public void onError(Objective objective, ObjectiveError error) {
643 log.warn("Next objective {} operation failed with error: {} in device {}", 664 log.warn("Next objective {} operation failed with error: {} in device {}",
644 - objective, error, deviceId); 665 + objective.id(), error, deviceId);
645 } 666 }
646 } 667 }
647 } 668 }
......
...@@ -81,7 +81,7 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler { ...@@ -81,7 +81,7 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler {
81 log.debug("createGroupsAtTransitRouter: The neighborset with label " 81 log.debug("createGroupsAtTransitRouter: The neighborset with label "
82 + "for sw {} is {}", deviceId, nsSet); 82 + "for sw {} is {}", deviceId, nsSet);
83 83
84 - createGroupsFromNeighborsets(nsSet); 84 + //createGroupsFromNeighborsets(nsSet);
85 } 85 }
86 86
87 @Override 87 @Override
...@@ -95,7 +95,7 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler { ...@@ -95,7 +95,7 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler {
95 Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent( 95 Set<NeighborSet> nsSet = computeImpactedNeighborsetForPortEvent(
96 newNeighborLink.dst().deviceId(), 96 newNeighborLink.dst().deviceId(),
97 devicePortMap.keySet()); 97 devicePortMap.keySet());
98 - createGroupsFromNeighborsets(nsSet); 98 + //createGroupsFromNeighborsets(nsSet);
99 } 99 }
100 100
101 @Override 101 @Override
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 package org.onosproject.net.flowobjective; 16 package org.onosproject.net.flowobjective;
17 17
18 import com.google.common.annotations.Beta; 18 import com.google.common.annotations.Beta;
19 +
19 import org.onosproject.core.ApplicationId; 20 import org.onosproject.core.ApplicationId;
20 import org.onosproject.net.flow.TrafficSelector; 21 import org.onosproject.net.flow.TrafficSelector;
21 import org.onosproject.net.flow.TrafficTreatment; 22 import org.onosproject.net.flow.TrafficTreatment;
...@@ -119,6 +120,53 @@ public final class DefaultForwardingObjective implements ForwardingObjective { ...@@ -119,6 +120,53 @@ public final class DefaultForwardingObjective implements ForwardingObjective {
119 return context; 120 return context;
120 } 121 }
121 122
123 + /*
124 + * (non-Javadoc)
125 + *
126 + * @see java.lang.Object#hashCode()
127 + */
128 + @Override
129 + public int hashCode() {
130 + return Objects.hash(selector, flag, permanent,
131 + timeout, appId, priority, nextId,
132 + treatment, op);
133 + }
134 +
135 + /*
136 + * (non-Javadoc)
137 + *
138 + * @see java.lang.Object#equals(java.lang.Object)
139 + */
140 + @Override
141 + public boolean equals(final Object obj) {
142 + if (this == obj) {
143 + return true;
144 + }
145 + if (!(obj instanceof DefaultForwardingObjective)) {
146 + return false;
147 + }
148 + final DefaultForwardingObjective other = (DefaultForwardingObjective) obj;
149 + boolean nextEq = false, treatmentEq = false;
150 + if (this.selector.equals(other.selector) &&
151 + this.flag == other.flag &&
152 + this.permanent == other.permanent &&
153 + this.timeout == other.timeout &&
154 + this.appId.equals(other.appId) &&
155 + this.priority == other.priority &&
156 + this.op == other.op) {
157 + if (this.nextId != null && other.nextId != null) {
158 + nextEq = this.nextId == other.nextId;
159 + }
160 + if (this.treatment != null && other.treatment != null) {
161 + treatmentEq = this.treatment.equals(other.treatment);
162 + }
163 + if (nextEq && treatmentEq) {
164 + return true;
165 + }
166 + }
167 + return false;
168 + }
169 +
122 /** 170 /**
123 * Returns a new builder. 171 * Returns a new builder.
124 * 172 *
......
...@@ -18,6 +18,7 @@ package org.onosproject.net.flowobjective; ...@@ -18,6 +18,7 @@ package org.onosproject.net.flowobjective;
18 import com.google.common.annotations.Beta; 18 import com.google.common.annotations.Beta;
19 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.ImmutableList;
20 import org.onosproject.core.ApplicationId; 20 import org.onosproject.core.ApplicationId;
21 +import org.onosproject.net.flow.TrafficSelector;
21 import org.onosproject.net.flow.TrafficTreatment; 22 import org.onosproject.net.flow.TrafficTreatment;
22 23
23 import java.util.Collection; 24 import java.util.Collection;
...@@ -39,6 +40,7 @@ public final class DefaultNextObjective implements NextObjective { ...@@ -39,6 +40,7 @@ public final class DefaultNextObjective implements NextObjective {
39 private final Integer id; 40 private final Integer id;
40 private final Operation op; 41 private final Operation op;
41 private final Optional<ObjectiveContext> context; 42 private final Optional<ObjectiveContext> context;
43 + private final TrafficSelector meta;
42 44
43 private DefaultNextObjective(Builder builder) { 45 private DefaultNextObjective(Builder builder) {
44 this.treatments = builder.treatments; 46 this.treatments = builder.treatments;
...@@ -47,6 +49,7 @@ public final class DefaultNextObjective implements NextObjective { ...@@ -47,6 +49,7 @@ public final class DefaultNextObjective implements NextObjective {
47 this.id = builder.id; 49 this.id = builder.id;
48 this.op = builder.op; 50 this.op = builder.op;
49 this.context = Optional.ofNullable(builder.context); 51 this.context = Optional.ofNullable(builder.context);
52 + this.meta = builder.meta;
50 } 53 }
51 54
52 @Override 55 @Override
...@@ -94,6 +97,11 @@ public final class DefaultNextObjective implements NextObjective { ...@@ -94,6 +97,11 @@ public final class DefaultNextObjective implements NextObjective {
94 return context; 97 return context;
95 } 98 }
96 99
100 + @Override
101 + public TrafficSelector meta() {
102 + return meta;
103 + }
104 +
97 /** 105 /**
98 * Returns a new builder. 106 * Returns a new builder.
99 * 107 *
...@@ -111,6 +119,7 @@ public final class DefaultNextObjective implements NextObjective { ...@@ -111,6 +119,7 @@ public final class DefaultNextObjective implements NextObjective {
111 private List<TrafficTreatment> treatments; 119 private List<TrafficTreatment> treatments;
112 private Operation op; 120 private Operation op;
113 private ObjectiveContext context; 121 private ObjectiveContext context;
122 + private TrafficSelector meta;
114 123
115 private final ImmutableList.Builder<TrafficTreatment> listBuilder 124 private final ImmutableList.Builder<TrafficTreatment> listBuilder
116 = ImmutableList.builder(); 125 = ImmutableList.builder();
...@@ -172,6 +181,12 @@ public final class DefaultNextObjective implements NextObjective { ...@@ -172,6 +181,12 @@ public final class DefaultNextObjective implements NextObjective {
172 } 181 }
173 182
174 @Override 183 @Override
184 + public Builder setMeta(TrafficSelector meta) {
185 + this.meta = meta;
186 + return this;
187 + }
188 +
189 + @Override
175 public NextObjective add() { 190 public NextObjective add() {
176 treatments = listBuilder.build(); 191 treatments = listBuilder.build();
177 op = Operation.ADD; 192 op = Operation.ADD;
...@@ -218,5 +233,55 @@ public final class DefaultNextObjective implements NextObjective { ...@@ -218,5 +233,55 @@ public final class DefaultNextObjective implements NextObjective {
218 233
219 return new DefaultNextObjective(this); 234 return new DefaultNextObjective(this);
220 } 235 }
236 +
237 + @Override
238 + public NextObjective addToExisting() {
239 + treatments = listBuilder.build();
240 + op = Operation.ADD_TO_EXISTING;
241 + checkNotNull(appId, "Must supply an application id");
242 + checkNotNull(id, "id cannot be null");
243 + checkNotNull(type, "The type cannot be null");
244 + checkArgument(!treatments.isEmpty(), "Must have at least one treatment");
245 +
246 + return new DefaultNextObjective(this);
247 + }
248 +
249 + @Override
250 + public NextObjective removeFromExisting() {
251 + treatments = listBuilder.build();
252 + op = Operation.REMOVE_FROM_EXISTING;
253 + checkNotNull(appId, "Must supply an application id");
254 + checkNotNull(id, "id cannot be null");
255 + checkNotNull(type, "The type cannot be null");
256 +
257 + return new DefaultNextObjective(this);
221 } 258 }
259 +
260 + @Override
261 + public NextObjective addToExisting(ObjectiveContext context) {
262 + treatments = listBuilder.build();
263 + op = Operation.ADD_TO_EXISTING;
264 + this.context = context;
265 + checkNotNull(appId, "Must supply an application id");
266 + checkNotNull(id, "id cannot be null");
267 + checkNotNull(type, "The type cannot be null");
268 + checkArgument(!treatments.isEmpty(), "Must have at least one treatment");
269 +
270 + return new DefaultNextObjective(this);
271 + }
272 +
273 + @Override
274 + public NextObjective removeFromExisting(ObjectiveContext context) {
275 + treatments = listBuilder.build();
276 + op = Operation.REMOVE_FROM_EXISTING;
277 + this.context = context;
278 + checkNotNull(appId, "Must supply an application id");
279 + checkNotNull(id, "id cannot be null");
280 + checkNotNull(type, "The type cannot be null");
281 +
282 + return new DefaultNextObjective(this);
283 + }
284 +
285 + }
286 +
222 } 287 }
......
...@@ -17,6 +17,7 @@ package org.onosproject.net.flowobjective; ...@@ -17,6 +17,7 @@ package org.onosproject.net.flowobjective;
17 17
18 import com.google.common.annotations.Beta; 18 import com.google.common.annotations.Beta;
19 import org.onosproject.core.ApplicationId; 19 import org.onosproject.core.ApplicationId;
20 +import org.onosproject.net.flow.TrafficSelector;
20 import org.onosproject.net.flow.TrafficTreatment; 21 import org.onosproject.net.flow.TrafficTreatment;
21 22
22 import java.util.Collection; 23 import java.util.Collection;
...@@ -34,7 +35,7 @@ import java.util.Collection; ...@@ -34,7 +35,7 @@ import java.util.Collection;
34 * - Failover 35 * - Failover
35 * - Simple 36 * - Simple
36 * 37 *
37 - * These types will indicate to the driver what the intended behaviour is. 38 + * These types will indicate to the driver what the intended behavior is.
38 * For example, a broadcast next objective with a collection of output 39 * For example, a broadcast next objective with a collection of output
39 * treatments will indicate to a driver that all output actions are expected 40 * treatments will indicate to a driver that all output actions are expected
40 * to be executed simultaneously. The driver is then free to implement this 41 * to be executed simultaneously. The driver is then free to implement this
...@@ -84,6 +85,16 @@ public interface NextObjective extends Objective { ...@@ -84,6 +85,16 @@ public interface NextObjective extends Objective {
84 Type type(); 85 Type type();
85 86
86 /** 87 /**
88 + * Auxiliary optional information provided to the device-driver.Typically
89 + * conveys information about selectors (matches) that are intended to
90 + * use this Next Objective.
91 + *
92 + * @return a selector intended to pass meta information to the device driver.
93 + * Value may be null if no meta information is provided.
94 + */
95 + TrafficSelector meta();
96 +
97 + /**
87 * A next step builder. 98 * A next step builder.
88 */ 99 */
89 interface Builder extends Objective.Builder { 100 interface Builder extends Objective.Builder {
...@@ -131,6 +142,14 @@ public interface NextObjective extends Objective { ...@@ -131,6 +142,14 @@ public interface NextObjective extends Objective {
131 Builder withPriority(int priority); 142 Builder withPriority(int priority);
132 143
133 /** 144 /**
145 + * Set meta information related to this next objective.
146 + *
147 + * @param selector match conditions
148 + * @return an objective builder
149 + */
150 + Builder setMeta(TrafficSelector selector);
151 +
152 + /**
134 * Builds the next objective that will be added. 153 * Builds the next objective that will be added.
135 * 154 *
136 * @return a next objective 155 * @return a next objective
...@@ -162,6 +181,40 @@ public interface NextObjective extends Objective { ...@@ -162,6 +181,40 @@ public interface NextObjective extends Objective {
162 */ 181 */
163 NextObjective remove(ObjectiveContext context); 182 NextObjective remove(ObjectiveContext context);
164 183
184 + /**
185 + * Build the next objective that will be added, with {@link Operation}
186 + * ADD_TO_EXISTING.
187 + *
188 + * @return a next objective
189 + */
190 + NextObjective addToExisting();
191 +
192 + /**
193 + * Build the next objective that will be removed, with {@link Operation}
194 + * REMOVE_FROM_EXISTING.
195 + *
196 + * @return a next objective
197 + */
198 + NextObjective removeFromExisting();
199 +
200 + /**
201 + * Builds the next objective that will be added, with {@link Operation}
202 + * ADD_TO_EXISTING. The context will be used to notify the calling application.
203 + *
204 + * @param context an objective context
205 + * @return a next objective
206 + */
207 + NextObjective addToExisting(ObjectiveContext context);
208 +
209 + /**
210 + * Builds the next objective that will be removed, with {@link Operation}
211 + * REMOVE_FROM_EXISTING. The context will be used to notify the calling application.
212 + *
213 + * @param context an objective context
214 + * @return a next objective
215 + */
216 + NextObjective removeFromExisting(ObjectiveContext context);
217 +
165 } 218 }
166 219
167 } 220 }
......
...@@ -21,7 +21,7 @@ import org.onosproject.core.ApplicationId; ...@@ -21,7 +21,7 @@ import org.onosproject.core.ApplicationId;
21 import java.util.Optional; 21 import java.util.Optional;
22 22
23 /** 23 /**
24 - * Base representation of an flow description. 24 + * Base representation of a flow-objective description.
25 */ 25 */
26 @Beta 26 @Beta
27 public interface Objective { 27 public interface Objective {
...@@ -35,14 +35,30 @@ public interface Objective { ...@@ -35,14 +35,30 @@ public interface Objective {
35 */ 35 */
36 enum Operation { 36 enum Operation {
37 /** 37 /**
38 - * Adds the objective. 38 + * Adds the objective. Can be used for any flow objective. For forwarding
39 + * and filtering objectives, existing objectives with identical selector
40 + * and priority fields (but different treatments or next) will be replaced.
41 + * For next objectives, if modification is desired, ADD will not
42 + * do anything - use ADD_TO_EXISTING.
39 */ 43 */
40 ADD, 44 ADD,
41 45
42 /** 46 /**
43 - * Removes the objective. 47 + * Removes the objective. Can be used for any flow objective.
44 */ 48 */
45 - REMOVE 49 + REMOVE,
50 +
51 + /**
52 + * Add to an existing Next Objective. Should not be used for any other
53 + * objective.
54 + */
55 + ADD_TO_EXISTING,
56 +
57 + /**
58 + * Remove from an existing Next Objective. Should not be used for any
59 + * other objective.
60 + */
61 + REMOVE_FROM_EXISTING
46 } 62 }
47 63
48 /** 64 /**
...@@ -129,6 +145,7 @@ public interface Objective { ...@@ -129,6 +145,7 @@ public interface Objective {
129 * @return an objective builder 145 * @return an objective builder
130 */ 146 */
131 Builder withPriority(int priority); 147 Builder withPriority(int priority);
148 +
132 } 149 }
133 150
134 } 151 }
......
...@@ -25,6 +25,7 @@ import java.util.Arrays; ...@@ -25,6 +25,7 @@ import java.util.Arrays;
25 public class DefaultGroupKey implements GroupKey { 25 public class DefaultGroupKey implements GroupKey {
26 26
27 private final byte[] key; 27 private final byte[] key;
28 + protected static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
28 29
29 public DefaultGroupKey(byte[] key) { 30 public DefaultGroupKey(byte[] key) {
30 this.key = checkNotNull(key); 31 this.key = checkNotNull(key);
...@@ -52,4 +53,20 @@ public class DefaultGroupKey implements GroupKey { ...@@ -52,4 +53,20 @@ public class DefaultGroupKey implements GroupKey {
52 return Arrays.hashCode(this.key); 53 return Arrays.hashCode(this.key);
53 } 54 }
54 55
56 + /**
57 + * Returns a hex string representation of the byte array that is used
58 + * as a group key. This solution was adapted from
59 + * http://stackoverflow.com/questions/9655181/
60 + */
61 + @Override
62 + public String toString() {
63 + char[] hexChars = new char[key.length * 2];
64 + for (int j = 0; j < key.length; j++) {
65 + int v = key[j] & 0xFF;
66 + hexChars[j * 2] = HEX_ARRAY[v >>> 4];
67 + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
68 + }
69 + return "GroupKey:0x" + new String(hexChars);
70 + }
71 +
55 } 72 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -54,6 +54,7 @@ import org.slf4j.Logger; ...@@ -54,6 +54,7 @@ import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory; 54 import org.slf4j.LoggerFactory;
55 55
56 import java.util.Map; 56 import java.util.Map;
57 +import java.util.Objects;
57 import java.util.Set; 58 import java.util.Set;
58 import java.util.concurrent.ExecutorService; 59 import java.util.concurrent.ExecutorService;
59 60
...@@ -226,10 +227,11 @@ public class FlowObjectiveManager implements FlowObjectiveService { ...@@ -226,10 +227,11 @@ public class FlowObjectiveManager implements FlowObjectiveService {
226 if (fwd.nextId() != null && 227 if (fwd.nextId() != null &&
227 flowObjectiveStore.getNextGroup(fwd.nextId()) == null) { 228 flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
228 log.trace("Queuing forwarding objective for nextId {}", fwd.nextId()); 229 log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
229 - if (pendingForwards.putIfAbsent(fwd.nextId(), 230 + // TODO: change to computeIfAbsent
230 - Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) { 231 + Set<PendingNext> pnext = pendingForwards.putIfAbsent(fwd.nextId(),
231 - Set<PendingNext> pending = pendingForwards.get(fwd.nextId()); 232 + Sets.newHashSet(new PendingNext(deviceId, fwd)));
232 - pending.add(new PendingNext(deviceId, fwd)); 233 + if (pnext != null) {
234 + pnext.add(new PendingNext(deviceId, fwd));
233 } 235 }
234 return true; 236 return true;
235 } 237 }
...@@ -412,5 +414,26 @@ public class FlowObjectiveManager implements FlowObjectiveService { ...@@ -412,5 +414,26 @@ public class FlowObjectiveManager implements FlowObjectiveService {
412 public ForwardingObjective forwardingObjective() { 414 public ForwardingObjective forwardingObjective() {
413 return fwd; 415 return fwd;
414 } 416 }
417 +
418 + @Override
419 + public int hashCode() {
420 + return Objects.hash(deviceId, fwd);
421 + }
422 +
423 + @Override
424 + public boolean equals(final Object obj) {
425 + if (this == obj) {
426 + return true;
427 + }
428 + if (!(obj instanceof PendingNext)) {
429 + return false;
430 + }
431 + final PendingNext other = (PendingNext) obj;
432 + if (this.deviceId.equals(other.deviceId) &&
433 + this.fwd.equals(other.fwd)) {
434 + return true;
435 + }
436 + return false;
437 + }
415 } 438 }
416 } 439 }
......
...@@ -348,9 +348,11 @@ public class DistributedGroupStore ...@@ -348,9 +348,11 @@ public class DistributedGroupStore
348 public void storeGroupDescription(GroupDescription groupDesc) { 348 public void storeGroupDescription(GroupDescription groupDesc) {
349 log.debug("In storeGroupDescription"); 349 log.debug("In storeGroupDescription");
350 // Check if a group is existing with the same key 350 // Check if a group is existing with the same key
351 - if (getGroup(groupDesc.deviceId(), groupDesc.appCookie()) != null) { 351 + Group existingGroup = getGroup(groupDesc.deviceId(), groupDesc.appCookie());
352 - log.warn("Group already exists with the same key {}", 352 + if (existingGroup != null) {
353 - groupDesc.appCookie()); 353 + log.warn("Group already exists with the same key {} in dev:{} with id:{}",
354 + groupDesc.appCookie(), groupDesc.deviceId(),
355 + Integer.toHexString(existingGroup.id().id()));
354 return; 356 return;
355 } 357 }
356 358
......
...@@ -368,6 +368,7 @@ public final class KryoNamespaces { ...@@ -368,6 +368,7 @@ public final class KryoNamespaces {
368 L2ModificationInstruction.ModVlanPcpInstruction.class, 368 L2ModificationInstruction.ModVlanPcpInstruction.class,
369 L2ModificationInstruction.PopVlanInstruction.class, 369 L2ModificationInstruction.PopVlanInstruction.class,
370 L2ModificationInstruction.ModMplsLabelInstruction.class, 370 L2ModificationInstruction.ModMplsLabelInstruction.class,
371 + L2ModificationInstruction.ModMplsBosInstruction.class,
371 L2ModificationInstruction.ModMplsTtlInstruction.class, 372 L2ModificationInstruction.ModMplsTtlInstruction.class,
372 L2ModificationInstruction.ModTunnelIdInstruction.class, 373 L2ModificationInstruction.ModTunnelIdInstruction.class,
373 L3ModificationInstruction.class, 374 L3ModificationInstruction.class,
......
...@@ -18,15 +18,19 @@ package org.onosproject.driver.pipeline; ...@@ -18,15 +18,19 @@ package org.onosproject.driver.pipeline;
18 import static org.slf4j.LoggerFactory.getLogger; 18 import static org.slf4j.LoggerFactory.getLogger;
19 19
20 import java.util.ArrayList; 20 import java.util.ArrayList;
21 +import java.util.Collection;
21 import java.util.Collections; 22 import java.util.Collections;
23 +import java.util.Deque;
22 import java.util.List; 24 import java.util.List;
23 import java.util.Set; 25 import java.util.Set;
24 import java.util.concurrent.ConcurrentHashMap; 26 import java.util.concurrent.ConcurrentHashMap;
25 27
28 +import org.onlab.packet.Ethernet;
26 import org.onlab.packet.VlanId; 29 import org.onlab.packet.VlanId;
27 import org.onosproject.core.ApplicationId; 30 import org.onosproject.core.ApplicationId;
28 import org.onosproject.net.Port; 31 import org.onosproject.net.Port;
29 import org.onosproject.net.PortNumber; 32 import org.onosproject.net.PortNumber;
33 +import org.onosproject.net.behaviour.NextGroup;
30 import org.onosproject.net.flow.DefaultFlowRule; 34 import org.onosproject.net.flow.DefaultFlowRule;
31 import org.onosproject.net.flow.DefaultTrafficSelector; 35 import org.onosproject.net.flow.DefaultTrafficSelector;
32 import org.onosproject.net.flow.DefaultTrafficTreatment; 36 import org.onosproject.net.flow.DefaultTrafficTreatment;
...@@ -35,8 +39,18 @@ import org.onosproject.net.flow.FlowRuleOperations; ...@@ -35,8 +39,18 @@ import org.onosproject.net.flow.FlowRuleOperations;
35 import org.onosproject.net.flow.FlowRuleOperationsContext; 39 import org.onosproject.net.flow.FlowRuleOperationsContext;
36 import org.onosproject.net.flow.TrafficSelector; 40 import org.onosproject.net.flow.TrafficSelector;
37 import org.onosproject.net.flow.TrafficTreatment; 41 import org.onosproject.net.flow.TrafficTreatment;
42 +import org.onosproject.net.flow.criteria.Criterion;
43 +import org.onosproject.net.flow.criteria.EthTypeCriterion;
44 +import org.onosproject.net.flow.criteria.IPCriterion;
45 +import org.onosproject.net.flow.criteria.MplsBosCriterion;
46 +import org.onosproject.net.flow.criteria.MplsCriterion;
38 import org.onosproject.net.flow.criteria.PortCriterion; 47 import org.onosproject.net.flow.criteria.PortCriterion;
39 import org.onosproject.net.flow.criteria.VlanIdCriterion; 48 import org.onosproject.net.flow.criteria.VlanIdCriterion;
49 +import org.onosproject.net.flow.instructions.Instruction;
50 +import org.onosproject.net.flowobjective.ForwardingObjective;
51 +import org.onosproject.net.flowobjective.ObjectiveError;
52 +import org.onosproject.net.group.Group;
53 +import org.onosproject.net.group.GroupKey;
40 import org.slf4j.Logger; 54 import org.slf4j.Logger;
41 55
42 56
...@@ -108,6 +122,81 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline { ...@@ -108,6 +122,81 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline {
108 return rules; 122 return rules;
109 } 123 }
110 124
125 + @Override
126 + protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
127 + TrafficSelector selector = fwd.selector();
128 + EthTypeCriterion ethType =
129 + (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
130 + if ((ethType == null) ||
131 + (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
132 + (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)) {
133 + log.warn("processSpecific: Unsupported "
134 + + "forwarding objective criteraia");
135 + fail(fwd, ObjectiveError.UNSUPPORTED);
136 + return Collections.emptySet();
137 + }
138 +
139 + int forTableId = -1;
140 + TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
141 + if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
142 + filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
143 + .matchIPDst(((IPCriterion)
144 + selector.getCriterion(Criterion.Type.IPV4_DST)).ip());
145 + forTableId = UNICAST_ROUTING_TABLE;
146 + log.debug("processing IPv4 specific forwarding objective {} hash{} in dev:{}",
147 + fwd.id(), fwd.hashCode(), deviceId);
148 + } else {
149 + filteredSelector
150 + .matchEthType(Ethernet.MPLS_UNICAST)
151 + .matchMplsLabel(((MplsCriterion)
152 + selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
153 + MplsBosCriterion bos = (MplsBosCriterion) selector
154 + .getCriterion(Criterion.Type.MPLS_BOS);
155 + if (bos != null) {
156 + filteredSelector.matchMplsBos(bos.mplsBos());
157 + }
158 + forTableId = MPLS_TABLE_1;
159 + log.debug("processing MPLS specific forwarding objective {} hash:{} in dev {}",
160 + fwd.id(), fwd.hashCode(), deviceId);
161 + }
162 +
163 + TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
164 + if (fwd.treatment() != null) {
165 + for (Instruction i : fwd.treatment().allInstructions()) {
166 + tb.add(i);
167 + }
168 + }
169 +
170 + if (fwd.nextId() != null) {
171 + NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
172 + List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
173 + // we only need the top level group's key to point the flow to it
174 + Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
175 + if (group == null) {
176 + log.warn("The group left!");
177 + fail(fwd, ObjectiveError.GROUPMISSING);
178 + return Collections.emptySet();
179 + }
180 + tb.deferred().group(group.id());
181 + }
182 + tb.transition(ACL_TABLE);
183 + FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
184 + .fromApp(fwd.appId())
185 + .withPriority(fwd.priority())
186 + .forDevice(deviceId)
187 + .withSelector(filteredSelector.build())
188 + .withTreatment(tb.build())
189 + .forTable(forTableId);
190 +
191 + if (fwd.permanent()) {
192 + ruleBuilder.makePermanent();
193 + } else {
194 + ruleBuilder.makeTemporary(fwd.timeout());
195 + }
196 +
197 + return Collections.singletonList(ruleBuilder.build());
198 + }
199 +
111 200
112 @Override 201 @Override
113 protected void initializePipeline() { 202 protected void initializePipeline() {
......
...@@ -19,9 +19,11 @@ import static org.onlab.util.Tools.groupedThreads; ...@@ -19,9 +19,11 @@ import static org.onlab.util.Tools.groupedThreads;
19 import static org.slf4j.LoggerFactory.getLogger; 19 import static org.slf4j.LoggerFactory.getLogger;
20 20
21 import java.nio.ByteBuffer; 21 import java.nio.ByteBuffer;
22 +import java.util.ArrayDeque;
22 import java.util.ArrayList; 23 import java.util.ArrayList;
23 import java.util.Collection; 24 import java.util.Collection;
24 import java.util.Collections; 25 import java.util.Collections;
26 +import java.util.Deque;
25 import java.util.List; 27 import java.util.List;
26 import java.util.Map; 28 import java.util.Map;
27 import java.util.Set; 29 import java.util.Set;
...@@ -65,15 +67,20 @@ import org.onosproject.net.flow.TrafficSelector; ...@@ -65,15 +67,20 @@ import org.onosproject.net.flow.TrafficSelector;
65 import org.onosproject.net.flow.TrafficTreatment; 67 import org.onosproject.net.flow.TrafficTreatment;
66 import org.onosproject.net.flow.criteria.Criteria; 68 import org.onosproject.net.flow.criteria.Criteria;
67 import org.onosproject.net.flow.criteria.Criterion; 69 import org.onosproject.net.flow.criteria.Criterion;
70 +import org.onosproject.net.flow.criteria.Criterion.Type;
68 import org.onosproject.net.flow.criteria.EthCriterion; 71 import org.onosproject.net.flow.criteria.EthCriterion;
69 import org.onosproject.net.flow.criteria.EthTypeCriterion; 72 import org.onosproject.net.flow.criteria.EthTypeCriterion;
70 import org.onosproject.net.flow.criteria.IPCriterion; 73 import org.onosproject.net.flow.criteria.IPCriterion;
74 +import org.onosproject.net.flow.criteria.MplsBosCriterion;
75 +import org.onosproject.net.flow.criteria.MplsCriterion;
71 import org.onosproject.net.flow.criteria.PortCriterion; 76 import org.onosproject.net.flow.criteria.PortCriterion;
72 import org.onosproject.net.flow.criteria.VlanIdCriterion; 77 import org.onosproject.net.flow.criteria.VlanIdCriterion;
73 import org.onosproject.net.flow.instructions.Instruction; 78 import org.onosproject.net.flow.instructions.Instruction;
74 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; 79 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
75 import org.onosproject.net.flow.instructions.L2ModificationInstruction; 80 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
81 +import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
76 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction; 82 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
83 +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
77 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction; 84 import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
78 import org.onosproject.net.flowobjective.FilteringObjective; 85 import org.onosproject.net.flowobjective.FilteringObjective;
79 import org.onosproject.net.flowobjective.FlowObjectiveStore; 86 import org.onosproject.net.flowobjective.FlowObjectiveStore;
...@@ -128,50 +135,43 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -128,50 +135,43 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
128 protected static final int LOWEST_PRIORITY = 0x0; 135 protected static final int LOWEST_PRIORITY = 0x0;
129 136
130 /* 137 /*
131 - * Group keys are normally generated by using the next Objective id. In the
132 - * case of a next objective resulting in a group chain, each group derives a
133 - * group key from the next objective id in the following way:
134 - * The upper 4 bits of the group-key are used to denote the position of the
135 - * group in the group chain. For example, in the chain
136 - * group0 --> group1 --> group2 --> port
137 - * group0's group key would have the upper 4 bits as 0, group1's upper four
138 - * bits would be 1, and so on
139 - */
140 - private static final int GROUP0MASK = 0x0;
141 - private static final int GROUP1MASK = 0x10000000;
142 -
143 - /*
144 * OFDPA requires group-id's to have a certain form. 138 * OFDPA requires group-id's to have a certain form.
145 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid> 139 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
146 * L3 Unicast Groups have <4bits-2><28bits-index> 140 * L3 Unicast Groups have <4bits-2><28bits-index>
141 + * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
142 + * L3 ECMP Groups have <4bits-7><28bits-index>
143 + * L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index>
144 + * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
147 */ 145 */
148 private static final int L2INTERFACEMASK = 0x0; 146 private static final int L2INTERFACEMASK = 0x0;
149 private static final int L3UNICASTMASK = 0x20000000; 147 private static final int L3UNICASTMASK = 0x20000000;
150 - //private static final int MPLSINTERFACEMASK = 0x90000000; 148 + private static final int MPLSINTERFACEMASK = 0x90000000;
151 private static final int L3ECMPMASK = 0x70000000; 149 private static final int L3ECMPMASK = 0x70000000;
152 private static final int L2FLOODMASK = 0x40000000; 150 private static final int L2FLOODMASK = 0x40000000;
151 + private static final int L3VPNMASK = 0x92000000;
153 152
154 private final Logger log = getLogger(getClass()); 153 private final Logger log = getLogger(getClass());
155 private ServiceDirectory serviceDirectory; 154 private ServiceDirectory serviceDirectory;
156 protected FlowRuleService flowRuleService; 155 protected FlowRuleService flowRuleService;
157 private CoreService coreService; 156 private CoreService coreService;
158 - private GroupService groupService; 157 + protected GroupService groupService;
159 - private FlowObjectiveStore flowObjectiveStore; 158 + protected FlowObjectiveStore flowObjectiveStore;
160 protected DeviceId deviceId; 159 protected DeviceId deviceId;
161 protected ApplicationId driverId; 160 protected ApplicationId driverId;
162 protected PacketService packetService; 161 protected PacketService packetService;
163 protected DeviceService deviceService; 162 protected DeviceService deviceService;
164 private InternalPacketProcessor processor = new InternalPacketProcessor(); 163 private InternalPacketProcessor processor = new InternalPacketProcessor();
165 - private KryoNamespace appKryo = new KryoNamespace.Builder() 164 + protected KryoNamespace appKryo = new KryoNamespace.Builder()
166 .register(KryoNamespaces.API) 165 .register(KryoNamespaces.API)
167 .register(GroupKey.class) 166 .register(GroupKey.class)
168 .register(DefaultGroupKey.class) 167 .register(DefaultGroupKey.class)
169 - .register(OfdpaGroupChain.class) 168 + .register(OfdpaNextGroup.class)
170 .register(byte[].class) 169 .register(byte[].class)
170 + .register(ArrayDeque.class)
171 .build(); 171 .build();
172 172
173 - private Cache<GroupKey, OfdpaGroupChain> pendingNextObjectives; 173 + private Cache<GroupKey, OfdpaNextGroup> pendingNextObjectives;
174 - private ConcurrentHashMap<GroupKey, GroupChainElem> pendingGroups; 174 + private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups;
175 175
176 private ScheduledExecutorService groupChecker = 176 private ScheduledExecutorService groupChecker =
177 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner", 177 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner",
...@@ -184,6 +184,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -184,6 +184,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
184 Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId, 184 Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId,
185 Set<PortNumber>>(); 185 Set<PortNumber>>();
186 186
187 + // index number for group creation
188 + AtomicInteger l3vpnindex = new AtomicInteger(0);
187 189
188 190
189 @Override 191 @Override
...@@ -193,7 +195,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -193,7 +195,8 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
193 195
194 pendingNextObjectives = CacheBuilder.newBuilder() 196 pendingNextObjectives = CacheBuilder.newBuilder()
195 .expireAfterWrite(20, TimeUnit.SECONDS) 197 .expireAfterWrite(20, TimeUnit.SECONDS)
196 - .removalListener((RemovalNotification<GroupKey, OfdpaGroupChain> notification) -> { 198 + .removalListener((
199 + RemovalNotification<GroupKey, OfdpaNextGroup> notification) -> {
197 if (notification.getCause() == RemovalCause.EXPIRED) { 200 if (notification.getCause() == RemovalCause.EXPIRED) {
198 fail(notification.getValue().nextObjective(), 201 fail(notification.getValue().nextObjective(),
199 ObjectiveError.GROUPINSTALLATIONFAILED); 202 ObjectiveError.GROUPINSTALLATIONFAILED);
...@@ -201,7 +204,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -201,7 +204,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
201 }).build(); 204 }).build();
202 205
203 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS); 206 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
204 - pendingGroups = new ConcurrentHashMap<GroupKey, GroupChainElem>(); 207 + pendingGroups = new ConcurrentHashMap<GroupKey, Set<GroupChainElem>>();
205 208
206 coreService = serviceDirectory.get(CoreService.class); 209 coreService = serviceDirectory.get(CoreService.class);
207 flowRuleService = serviceDirectory.get(FlowRuleService.class); 210 flowRuleService = serviceDirectory.get(FlowRuleService.class);
...@@ -285,22 +288,49 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -285,22 +288,49 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
285 288
286 @Override 289 @Override
287 public void next(NextObjective nextObjective) { 290 public void next(NextObjective nextObjective) {
288 - log.debug("Processing NextObjective id{} op{}", nextObjective.id(),
289 - nextObjective.op());
290 - if (nextObjective.op() == Objective.Operation.REMOVE) {
291 - if (nextObjective.next().isEmpty()) {
292 - removeGroup(nextObjective);
293 - } else {
294 - removeBucketFromGroup(nextObjective);
295 - }
296 - } else if (nextObjective.op() == Objective.Operation.ADD) {
297 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id()); 291 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
292 + switch (nextObjective.op()) {
293 + case ADD:
294 + if (nextGroup != null) {
295 + log.warn("Cannot add next {} that already exists in device {}",
296 + nextObjective.id(), deviceId);
297 + return;
298 + }
299 + log.debug("Processing NextObjective id{} in dev{} - add group",
300 + nextObjective.id(), deviceId);
301 + addGroup(nextObjective);
302 + break;
303 + case ADD_TO_EXISTING:
298 if (nextGroup != null) { 304 if (nextGroup != null) {
305 + log.debug("Processing NextObjective id{} in dev{} - add bucket",
306 + nextObjective.id(), deviceId);
299 addBucketToGroup(nextObjective); 307 addBucketToGroup(nextObjective);
300 } else { 308 } else {
301 - addGroup(nextObjective); 309 + // it is possible that group-chain has not been fully created yet
310 + waitToAddBucketToGroup(nextObjective);
302 } 311 }
303 - } else { 312 + break;
313 + case REMOVE:
314 + if (nextGroup == null) {
315 + log.warn("Cannot remove next {} that does not exist in device {}",
316 + nextObjective.id(), deviceId);
317 + return;
318 + }
319 + log.debug("Processing NextObjective id{} in dev{} - remove group",
320 + nextObjective.id(), deviceId);
321 + removeGroup(nextObjective);
322 + break;
323 + case REMOVE_FROM_EXISTING:
324 + if (nextGroup == null) {
325 + log.warn("Cannot remove from next {} that does not exist in device {}",
326 + nextObjective.id(), deviceId);
327 + return;
328 + }
329 + log.debug("Processing NextObjective id{} in dev{} - remove bucket",
330 + nextObjective.id(), deviceId);
331 + removeBucketFromGroup(nextObjective);
332 + break;
333 + default:
304 log.warn("Unsupported operation {}", nextObjective.op()); 334 log.warn("Unsupported operation {}", nextObjective.op());
305 } 335 }
306 } 336 }
...@@ -309,7 +339,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -309,7 +339,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
309 // Flow handling 339 // Flow handling
310 ////////////////////////////////////// 340 //////////////////////////////////////
311 341
312 -
313 /** 342 /**
314 * As per OFDPA 2.0 TTP, filtering of VLAN ids, MAC addresses (for routing) 343 * As per OFDPA 2.0 TTP, filtering of VLAN ids, MAC addresses (for routing)
315 * and IP addresses configured on switch ports happen in different tables. 344 * and IP addresses configured on switch ports happen in different tables.
...@@ -520,7 +549,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -520,7 +549,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
520 /** 549 /**
521 * Allows routed packets with correct destination MAC to be directed 550 * Allows routed packets with correct destination MAC to be directed
522 * to unicast-IP routing table or MPLS forwarding table. 551 * to unicast-IP routing table or MPLS forwarding table.
523 - * XXX need to add rule for multicast routing.
524 * 552 *
525 * @param portCriterion port on device for which this filter is programmed 553 * @param portCriterion port on device for which this filter is programmed
526 * @param ethCriterion dstMac of device for which is filter is programmed 554 * @param ethCriterion dstMac of device for which is filter is programmed
...@@ -661,38 +689,78 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -661,38 +689,78 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
661 689
662 /** 690 /**
663 * In the OF-DPA 2.0 pipeline, specific forwarding refers to the IP table 691 * In the OF-DPA 2.0 pipeline, specific forwarding refers to the IP table
664 - * (unicast or multicast) or the L2 table (mac + vlan). 692 + * (unicast or multicast) or the L2 table (mac + vlan) or the MPLS table.
665 * 693 *
666 * @param fwd the forwarding objective of type 'specific' 694 * @param fwd the forwarding objective of type 'specific'
667 * @return a collection of flow rules. Typically there will be only one 695 * @return a collection of flow rules. Typically there will be only one
668 * for this type of forwarding objective. An empty set may be 696 * for this type of forwarding objective. An empty set may be
669 * returned if there is an issue in processing the objective. 697 * returned if there is an issue in processing the objective.
670 */ 698 */
671 - private Collection<FlowRule> processSpecific(ForwardingObjective fwd) { 699 + protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
672 - log.debug("Processing specific forwarding objective");
673 TrafficSelector selector = fwd.selector(); 700 TrafficSelector selector = fwd.selector();
674 EthTypeCriterion ethType = 701 EthTypeCriterion ethType =
675 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE); 702 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
676 - // XXX currently supporting only the L3 unicast table 703 + if ((ethType == null) ||
677 - if (ethType == null || ethType.ethType().toShort() != Ethernet.TYPE_IPV4) { 704 + (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
705 + (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)) {
706 + log.warn("processSpecific: Unsupported "
707 + + "forwarding objective criteraia");
678 fail(fwd, ObjectiveError.UNSUPPORTED); 708 fail(fwd, ObjectiveError.UNSUPPORTED);
679 return Collections.emptySet(); 709 return Collections.emptySet();
680 } 710 }
681 711
682 - TrafficSelector filteredSelector = 712 + int forTableId = -1;
683 - DefaultTrafficSelector.builder() 713 + TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
684 - .matchEthType(Ethernet.TYPE_IPV4) 714 + if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
685 - .matchIPDst( 715 + filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
686 - ((IPCriterion) 716 + .matchIPDst(((IPCriterion)
687 - selector.getCriterion(Criterion.Type.IPV4_DST)).ip()) 717 + selector.getCriterion(Criterion.Type.IPV4_DST)).ip());
688 - .build(); 718 + forTableId = UNICAST_ROUTING_TABLE;
719 + log.debug("processing IPv4 specific forwarding objective {} in dev:{}",
720 + fwd.id(), deviceId);
721 + } else {
722 + filteredSelector
723 + .matchEthType(Ethernet.MPLS_UNICAST)
724 + .matchMplsLabel(((MplsCriterion)
725 + selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
726 + MplsBosCriterion bos = (MplsBosCriterion) selector
727 + .getCriterion(Criterion.Type.MPLS_BOS);
728 + if (bos != null) {
729 + filteredSelector.matchMplsBos(bos.mplsBos());
730 + }
731 + forTableId = MPLS_TABLE_1;
732 + log.debug("processing MPLS specific forwarding objective {} in dev {}",
733 + fwd.id(), deviceId);
734 + }
689 735
690 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder(); 736 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
737 + boolean popMpls = false;
738 + if (fwd.treatment() != null) {
739 + for (Instruction i : fwd.treatment().allInstructions()) {
740 + tb.add(i);
741 + if (i instanceof L2ModificationInstruction &&
742 + ((L2ModificationInstruction) i).subtype() == L2SubType.MPLS_POP) {
743 + popMpls = true;
744 + }
745 + }
746 + }
691 747
692 if (fwd.nextId() != null) { 748 if (fwd.nextId() != null) {
749 + if (forTableId == MPLS_TABLE_1 && !popMpls) {
750 + log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
751 + + "is not implemented in OF-DPA yet. Aborting this flow "
752 + + "in this device {}", deviceId);
753 + // XXX We could convert to forwarding to a single-port, via a
754 + // MPLS interface, or a MPLS SWAP (with-same) but that would
755 + // have to be handled in the next-objective. Also the pop-mpls
756 + // logic used here won't work in non-BoS case.
757 + return Collections.emptySet();
758 + }
759 +
693 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId()); 760 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
694 - List<GroupKey> gkeys = appKryo.deserialize(next.data()); 761 + List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
695 - Group group = groupService.getGroup(deviceId, gkeys.get(0)); 762 + // we only need the top level group's key to point the flow to it
763 + Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
696 if (group == null) { 764 if (group == null) {
697 log.warn("The group left!"); 765 log.warn("The group left!");
698 fail(fwd, ObjectiveError.GROUPMISSING); 766 fail(fwd, ObjectiveError.GROUPMISSING);
...@@ -705,8 +773,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -705,8 +773,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
705 .fromApp(fwd.appId()) 773 .fromApp(fwd.appId())
706 .withPriority(fwd.priority()) 774 .withPriority(fwd.priority())
707 .forDevice(deviceId) 775 .forDevice(deviceId)
708 - .withSelector(filteredSelector) 776 + .withSelector(filteredSelector.build())
709 - .withTreatment(tb.build()); 777 + .withTreatment(tb.build())
778 + .forTable(forTableId);
710 779
711 if (fwd.permanent()) { 780 if (fwd.permanent()) {
712 ruleBuilder.makePermanent(); 781 ruleBuilder.makePermanent();
...@@ -714,7 +783,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -714,7 +783,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
714 ruleBuilder.makeTemporary(fwd.timeout()); 783 ruleBuilder.makeTemporary(fwd.timeout());
715 } 784 }
716 785
717 - ruleBuilder.forTable(UNICAST_ROUTING_TABLE);
718 return Collections.singletonList(ruleBuilder.build()); 786 return Collections.singletonList(ruleBuilder.build());
719 } 787 }
720 788
...@@ -724,7 +792,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -724,7 +792,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
724 } 792 }
725 } 793 }
726 794
727 - private void fail(Objective obj, ObjectiveError error) { 795 + protected void fail(Objective obj, ObjectiveError error) {
728 if (obj.context().isPresent()) { 796 if (obj.context().isPresent()) {
729 obj.context().get().onError(obj, error); 797 obj.context().get().onError(obj, error);
730 } 798 }
...@@ -765,20 +833,66 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -765,20 +833,66 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
765 833
766 /** 834 /**
767 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using 835 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
768 - * a chain of groups, namely an L3 Unicast Group that points to an L2 Interface 836 + * a chain of groups. The simple Next Objective passed
769 - * Group which in-turn points to an output port. The Next Objective passed
770 * in by the application has to be broken up into a group chain 837 * in by the application has to be broken up into a group chain
771 - * to satisfy this TTP. 838 + * comprising of an L3 Unicast Group that points to an L2 Interface
839 + * Group which in-turn points to an output port. In some cases, the simple
840 + * next Objective can just be an L2 interface without the need for chaining.
772 * 841 *
773 * @param nextObj the nextObjective of type SIMPLE 842 * @param nextObj the nextObjective of type SIMPLE
774 */ 843 */
775 private void processSimpleNextObjective(NextObjective nextObj) { 844 private void processSimpleNextObjective(NextObjective nextObj) {
776 // break up simple next objective to GroupChain objects 845 // break up simple next objective to GroupChain objects
777 TrafficTreatment treatment = nextObj.next().iterator().next(); 846 TrafficTreatment treatment = nextObj.next().iterator().next();
847 +
848 + GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
849 + nextObj.appId(), false,
850 + nextObj.meta());
851 + if (groupInfo == null) {
852 + log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
853 + return;
854 + }
855 + // create object for local and distributed storage
856 + Deque<GroupKey> gkeyChain = new ArrayDeque<>();
857 + gkeyChain.addFirst(groupInfo.innerGrpDesc.appCookie());
858 + gkeyChain.addFirst(groupInfo.outerGrpDesc.appCookie());
859 + OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(
860 + Collections.singletonList(gkeyChain),
861 + nextObj);
862 +
863 + // store l3groupkey with the ofdpaGroupChain for the nextObjective that depends on it
864 + pendingNextObjectives.put(groupInfo.outerGrpDesc.appCookie(), ofdpaGrp);
865 +
866 + // now we are ready to send the l2 groupDescription (inner), as all the stores
867 + // that will get async replies have been updated. By waiting to update
868 + // the stores, we prevent nasty race conditions.
869 + groupService.addGroup(groupInfo.innerGrpDesc);
870 + }
871 +
872 + /**
873 + * Creates one of two possible group-chains from the treatment
874 + * passed in. Depending on the MPLS boolean, this method either creates
875 + * an L3Unicast Group --> L2Interface Group, if mpls is false;
876 + * or MPLSInterface Group --> L2Interface Group, if mpls is true;
877 + * The returned 'inner' group description is always the L2 Interface group.
878 + *
879 + * @param treatment that needs to be broken up to create the group chain
880 + * @param nextId of the next objective that needs this group chain
881 + * @param appId of the application that sent this next objective
882 + * @param mpls determines if L3Unicast or MPLSInterface group is created
883 + * @param meta metadata passed in by the application as part of the nextObjective
884 + * @return GroupInfo containing the GroupDescription of the
885 + * L2Interface group(inner) and the GroupDescription of the (outer)
886 + * L3Unicast/MPLSInterface group. May return null if there is an
887 + * error in processing the chain
888 + */
889 + private GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
890 + ApplicationId appId, boolean mpls,
891 + TrafficSelector meta) {
778 // for the l2interface group, get vlan and port info 892 // for the l2interface group, get vlan and port info
779 - // for the l3unicast group, get the src/dst mac and vlan info 893 + // for the outer group, get the src/dst mac, and vlan info
780 - TrafficTreatment.Builder l3utt = DefaultTrafficTreatment.builder(); 894 + TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
781 - TrafficTreatment.Builder l2itt = DefaultTrafficTreatment.builder(); 895 + TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
782 VlanId vlanid = null; 896 VlanId vlanid = null;
783 long portNum = 0; 897 long portNum = 0;
784 for (Instruction ins : treatment.allInstructions()) { 898 for (Instruction ins : treatment.allInstructions()) {
...@@ -786,76 +900,144 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -786,76 +900,144 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
786 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; 900 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
787 switch (l2ins.subtype()) { 901 switch (l2ins.subtype()) {
788 case ETH_DST: 902 case ETH_DST:
789 - l3utt.setEthDst(((ModEtherInstruction) l2ins).mac()); 903 + outerTtb.setEthDst(((ModEtherInstruction) l2ins).mac());
790 break; 904 break;
791 case ETH_SRC: 905 case ETH_SRC:
792 - l3utt.setEthSrc(((ModEtherInstruction) l2ins).mac()); 906 + outerTtb.setEthSrc(((ModEtherInstruction) l2ins).mac());
793 break; 907 break;
794 case VLAN_ID: 908 case VLAN_ID:
795 vlanid = ((ModVlanIdInstruction) l2ins).vlanId(); 909 vlanid = ((ModVlanIdInstruction) l2ins).vlanId();
796 - l3utt.setVlanId(vlanid); 910 + outerTtb.setVlanId(vlanid);
911 + break;
912 + case VLAN_POP:
913 + innerTtb.popVlan();
797 break; 914 break;
798 case DEC_MPLS_TTL: 915 case DEC_MPLS_TTL:
799 case MPLS_LABEL: 916 case MPLS_LABEL:
800 case MPLS_POP: 917 case MPLS_POP:
801 case MPLS_PUSH: 918 case MPLS_PUSH:
802 case VLAN_PCP: 919 case VLAN_PCP:
803 - case VLAN_POP:
804 case VLAN_PUSH: 920 case VLAN_PUSH:
805 default: 921 default:
806 break; 922 break;
807 } 923 }
808 } else if (ins.type() == Instruction.Type.OUTPUT) { 924 } else if (ins.type() == Instruction.Type.OUTPUT) {
809 portNum = ((OutputInstruction) ins).port().toLong(); 925 portNum = ((OutputInstruction) ins).port().toLong();
810 - l2itt.add(ins); 926 + innerTtb.add(ins);
811 } else { 927 } else {
812 log.warn("Driver does not handle this type of TrafficTreatment" 928 log.warn("Driver does not handle this type of TrafficTreatment"
813 + " instruction in nextObjectives: {}", ins.type()); 929 + " instruction in nextObjectives: {}", ins.type());
814 } 930 }
815 } 931 }
816 932
933 + if (vlanid == null) {
934 + //use the vlanid associated with the port
935 + vlanid = port2Vlan.get(PortNumber.portNumber(portNum));
936 + }
937 +
938 + if (vlanid == null) {
939 + // use metadata
940 + for (Criterion metaCriterion : meta.criteria()) {
941 + if (metaCriterion.type() == Type.VLAN_VID) {
942 + vlanid = ((VlanIdCriterion) metaCriterion).vlanId();
943 + }
944 + }
945 + }
946 +
947 + if (vlanid == null) {
948 + log.error("Driver cannot process an L2/L3 group chain without "
949 + + "egress vlan information for dev: {} port:{}",
950 + deviceId, portNum);
951 + return null;
952 + }
953 +
817 // assemble information for ofdpa l2interface group 954 // assemble information for ofdpa l2interface group
818 - int l2gk = nextObj.id() | GROUP1MASK;
819 - final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
820 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum; 955 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum;
956 + // a globally unique groupkey that is different for ports in the same devices
957 + // but different for the same portnumber on different devices. Also different
958 + // for the various group-types created out of the same next objective.
959 + int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum);
960 + final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
821 961
822 - // assemble information for ofdpa l3unicast group 962 + // assemble information for outer group
823 - int l3gk = nextObj.id() | GROUP0MASK; 963 + GroupDescription outerGrpDesc = null;
824 - final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk)); 964 + if (mpls) {
965 + // outer group is MPLSInteface
966 + Integer mplsgroupId = MPLSINTERFACEMASK | (int) portNum;
967 + // using mplsinterfacemask in groupkey to differentiate from l2interface
968 + int mplsgk = MPLSINTERFACEMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
969 + final GroupKey mplsgroupkey = new DefaultGroupKey(appKryo.serialize(mplsgk));
970 + outerTtb.group(new DefaultGroupId(l2groupId));
971 + // create the mpls-interface group description to wait for the
972 + // l2 interface group to be processed
973 + GroupBucket mplsinterfaceGroupBucket =
974 + DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
975 + outerGrpDesc = new DefaultGroupDescription(
976 + deviceId,
977 + GroupDescription.Type.INDIRECT,
978 + new GroupBuckets(Collections.singletonList(
979 + mplsinterfaceGroupBucket)),
980 + mplsgroupkey,
981 + mplsgroupId,
982 + appId);
983 + log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
984 + deviceId, Integer.toHexString(mplsgroupId),
985 + mplsgroupkey, nextId);
986 + } else {
987 + // outer group is L3Unicast
825 Integer l3groupId = L3UNICASTMASK | (int) portNum; 988 Integer l3groupId = L3UNICASTMASK | (int) portNum;
826 - l3utt.group(new DefaultGroupId(l2groupId)); 989 + int l3gk = L3UNICASTMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum));
827 - GroupChainElem gce = new GroupChainElem(l3groupkey, l3groupId, 990 + final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk));
991 + outerTtb.group(new DefaultGroupId(l2groupId));
992 + // create the l3unicast group description to wait for the
993 + // l2 interface group to be processed
994 + GroupBucket l3unicastGroupBucket =
995 + DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
996 + outerGrpDesc = new DefaultGroupDescription(
997 + deviceId,
828 GroupDescription.Type.INDIRECT, 998 GroupDescription.Type.INDIRECT,
829 - Collections.singletonList(l3utt.build()), 999 + new GroupBuckets(Collections.singletonList(
830 - nextObj.appId(), 1); 1000 + l3unicastGroupBucket)),
831 - 1001 + l3groupkey,
832 - // create object for local and distributed storage 1002 + l3groupId,
833 - List<GroupKey> gkeys = new ArrayList<GroupKey>(); 1003 + appId);
834 - gkeys.add(l3groupkey); // group0 in chain 1004 + log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
835 - gkeys.add(l2groupkey); // group1 in chain 1005 + deviceId, Integer.toHexString(l3groupId),
836 - OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj); 1006 + l3groupkey, nextId);
837 - 1007 + }
838 - // store l2groupkey with the groupChainElem for the l3group that depends on it 1008 +
839 - pendingGroups.put(l2groupkey, gce); 1009 + // store l2groupkey with the groupChainElem for the outer-group that depends on it
840 - 1010 + GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1);
841 - // store l3groupkey with the ofdpaGroupChain for the nextObjective that depends on it 1011 + Set<GroupChainElem> gceSet = Collections.newSetFromMap(
842 - pendingNextObjectives.put(l3groupkey, ofdpaGrp); 1012 + new ConcurrentHashMap<GroupChainElem, Boolean>());
843 - 1013 + gceSet.add(gce);
844 - // create group description for the ofdpa l2interfacegroup and send to groupservice 1014 + Set<GroupChainElem> retval = pendingGroups.putIfAbsent(l2groupkey, gceSet);
845 - GroupBucket bucket = 1015 + if (retval != null) {
846 - DefaultGroupBucket.createIndirectGroupBucket(l2itt.build()); 1016 + retval.add(gce);
847 - GroupDescription groupDescription = new DefaultGroupDescription(deviceId, 1017 + }
1018 +
1019 + // create group description for the inner l2interfacegroup
1020 + GroupBucket l2interfaceGroupBucket =
1021 + DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
1022 + GroupDescription l2groupDescription =
1023 + new DefaultGroupDescription(
1024 + deviceId,
848 GroupDescription.Type.INDIRECT, 1025 GroupDescription.Type.INDIRECT,
849 - new GroupBuckets(Collections.singletonList(bucket)), 1026 + new GroupBuckets(Collections.singletonList(
1027 + l2interfaceGroupBucket)),
850 l2groupkey, 1028 l2groupkey,
851 l2groupId, 1029 l2groupId,
852 - nextObj.appId()); 1030 + appId);
853 - groupService.addGroup(groupDescription); 1031 + log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
1032 + deviceId, Integer.toHexString(l2groupId),
1033 + l2groupkey, nextId);
1034 + return new GroupInfo(l2groupDescription, outerGrpDesc);
1035 +
854 } 1036 }
855 1037
856 /** 1038 /**
857 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using 1039 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
858 - * a chain of groups. The Next Objective passed in by the application 1040 + * a chain of groups. The broadcast Next Objective passed in by the application
859 * has to be broken up into a group chain comprising of an 1041 * has to be broken up into a group chain comprising of an
860 * L2 Flood group whose buckets point to L2 Interface groups. 1042 * L2 Flood group whose buckets point to L2 Interface groups.
861 * 1043 *
...@@ -866,9 +1048,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -866,9 +1048,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
866 Collection<TrafficTreatment> buckets = nextObj.next(); 1048 Collection<TrafficTreatment> buckets = nextObj.next();
867 1049
868 // each treatment is converted to an L2 interface group 1050 // each treatment is converted to an L2 interface group
869 - int indicator = 0;
870 VlanId vlanid = null; 1051 VlanId vlanid = null;
871 - List<GroupInfo> groupInfoCollection = new ArrayList<>(); 1052 + List<GroupDescription> l2interfaceGroupDescs = new ArrayList<>();
1053 + List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
872 for (TrafficTreatment treatment : buckets) { 1054 for (TrafficTreatment treatment : buckets) {
873 TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder(); 1055 TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
874 PortNumber portNum = null; 1056 PortNumber portNum = null;
...@@ -907,83 +1089,284 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -907,83 +1089,284 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
907 } 1089 }
908 } 1090 }
909 1091
910 - // assemble info for all l2 interface groups 1092 + // assemble info for l2 interface group
911 - indicator += GROUP1MASK; 1093 + int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong());
912 - int l2gk = nextObj.id() | indicator;
913 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk)); 1094 final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk));
914 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | 1095 Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) |
915 (int) portNum.toLong(); 1096 (int) portNum.toLong();
916 - GroupBucket newbucket = 1097 + GroupBucket l2interfaceGroupBucket =
917 DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build()); 1098 DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
1099 + GroupDescription l2interfaceGroupDescription =
1100 + new DefaultGroupDescription(
1101 + deviceId,
1102 + GroupDescription.Type.INDIRECT,
1103 + new GroupBuckets(Collections.singletonList(
1104 + l2interfaceGroupBucket)),
1105 + l2groupkey,
1106 + l2groupId,
1107 + nextObj.appId());
1108 + log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
1109 + deviceId, Integer.toHexString(l2groupId),
1110 + l2groupkey, nextObj.id());
1111 +
1112 + Deque<GroupKey> gkeyChain = new ArrayDeque<>();
1113 + gkeyChain.addFirst(l2groupkey);
918 1114
919 // store the info needed to create this group 1115 // store the info needed to create this group
920 - groupInfoCollection.add(new GroupInfo(l2groupId, l2groupkey, newbucket)); 1116 + l2interfaceGroupDescs.add(l2interfaceGroupDescription);
1117 + allGroupKeys.add(gkeyChain);
921 } 1118 }
922 1119
923 // assemble info for l2 flood group 1120 // assemble info for l2 flood group
924 - int l2floodgk = nextObj.id() | GROUP0MASK;
925 - final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk));
926 Integer l2floodgroupId = L2FLOODMASK | (vlanid.toShort() << 16) | nextObj.id(); 1121 Integer l2floodgroupId = L2FLOODMASK | (vlanid.toShort() << 16) | nextObj.id();
927 - // collection of treatment with groupids of l2 interface groups 1122 + int l2floodgk = L2FLOODMASK | nextObj.id() << 12;
928 - List<TrafficTreatment> floodtt = new ArrayList<>(); 1123 + final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk));
929 - for (GroupInfo gi : groupInfoCollection) { 1124 + // collection of group buckets pointing to all the l2 interface groups
1125 + List<GroupBucket> l2floodBuckets = new ArrayList<>();
1126 + for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
930 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); 1127 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
931 - ttb.group(new DefaultGroupId(gi.groupId)); 1128 + ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId()));
932 - floodtt.add(ttb.build()); 1129 + GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
1130 + l2floodBuckets.add(abucket);
933 } 1131 }
934 - GroupChainElem gce = new GroupChainElem(l2floodgroupkey, l2floodgroupId, 1132 + // create the l2flood group-description to wait for all the
1133 + // l2interface groups to be processed
1134 + GroupDescription l2floodGroupDescription =
1135 + new DefaultGroupDescription(
1136 + deviceId,
935 GroupDescription.Type.ALL, 1137 GroupDescription.Type.ALL,
936 - floodtt, 1138 + new GroupBuckets(l2floodBuckets),
937 - nextObj.appId(), 1139 + l2floodgroupkey,
938 - groupInfoCollection.size()); 1140 + l2floodgroupId,
1141 + nextObj.appId());
1142 + GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
1143 + l2interfaceGroupDescs.size());
1144 + log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
1145 + deviceId, Integer.toHexString(l2floodgroupId),
1146 + l2floodgroupkey, nextObj.id());
939 1147
940 // create objects for local and distributed storage 1148 // create objects for local and distributed storage
941 - List<GroupKey> gkeys = new ArrayList<GroupKey>(); 1149 + allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l2floodgroupkey));
942 - gkeys.add(l2floodgroupkey); // group0 in chain 1150 + OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
943 - OfdpaGroupChain ofdpaGrp = new OfdpaGroupChain(gkeys, nextObj);
944 1151
945 // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective 1152 // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective
946 // that depends on it 1153 // that depends on it
947 pendingNextObjectives.put(l2floodgroupkey, ofdpaGrp); 1154 pendingNextObjectives.put(l2floodgroupkey, ofdpaGrp);
948 1155
949 - for (GroupInfo gi : groupInfoCollection) { 1156 + for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) {
950 // store all l2groupkeys with the groupChainElem for the l2floodgroup 1157 // store all l2groupkeys with the groupChainElem for the l2floodgroup
951 // that depends on it 1158 // that depends on it
952 - pendingGroups.put(gi.groupKey, gce); 1159 + Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1160 + new ConcurrentHashMap<GroupChainElem, Boolean>());
1161 + gceSet.add(gce);
1162 + Set<GroupChainElem> retval = pendingGroups.putIfAbsent(
1163 + l2intGrpDesc.appCookie(), gceSet);
1164 + if (retval != null) {
1165 + retval.add(gce);
1166 + }
953 1167
954 // create and send groups for all l2 interface groups 1168 // create and send groups for all l2 interface groups
955 - GroupDescription groupDescription = 1169 + groupService.addGroup(l2intGrpDesc);
1170 + }
1171 + }
1172 +
1173 + /**
1174 + * Utility class for moving group information around.
1175 + *
1176 + */
1177 + private class GroupInfo {
1178 + private GroupDescription innerGrpDesc;
1179 + private GroupDescription outerGrpDesc;
1180 +
1181 + GroupInfo(GroupDescription innerGrpDesc, GroupDescription outerGrpDesc) {
1182 + this.innerGrpDesc = innerGrpDesc;
1183 + this.outerGrpDesc = outerGrpDesc;
1184 + }
1185 + }
1186 +
1187 + /**
1188 + * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
1189 + * a chain of groups. The hashed Next Objective passed in by the application
1190 + * has to be broken up into a group chain comprising of an
1191 + * L3 ECMP group as the top level group. Buckets of this group can point
1192 + * to a variety of groups in a group chain, depending on the whether
1193 + * MPLS labels are being pushed or not.
1194 + * <p>
1195 + * NOTE: We do not create MPLS ECMP groups as they are unimplemented in
1196 + * OF-DPA 2.0 (even though it is in the spec). Therefore we do not
1197 + * check the nextObjective meta.
1198 + *
1199 + * @param nextObj the nextObjective of type HASHED
1200 + */
1201 + private void processHashedNextObjective(NextObjective nextObj) {
1202 + // break up hashed next objective to multiple groups
1203 + Collection<TrafficTreatment> buckets = nextObj.next();
1204 +
1205 + // storage for all group keys in the chain of groups created
1206 + List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1207 + List<GroupInfo> unsentGroups = new ArrayList<>();
1208 + for (TrafficTreatment bucket : buckets) {
1209 + //figure out how many labels are pushed in each bucket
1210 + int labelsPushed = 0;
1211 + MplsLabel innermostLabel = null;
1212 + for (Instruction ins : bucket.allInstructions()) {
1213 + if (ins.type() == Instruction.Type.L2MODIFICATION) {
1214 + L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
1215 + if (l2ins.subtype() == L2SubType.MPLS_PUSH) {
1216 + labelsPushed++;
1217 + }
1218 + if (l2ins.subtype() == L2SubType.MPLS_LABEL) {
1219 + if (innermostLabel == null) {
1220 + innermostLabel = ((ModMplsLabelInstruction) l2ins).mplsLabel();
1221 + }
1222 + }
1223 + }
1224 + }
1225 +
1226 + Deque<GroupKey> gkeyChain = new ArrayDeque<>();
1227 + // XXX we only deal with 0 and 1 label push right now
1228 + if (labelsPushed == 0) {
1229 + GroupInfo nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1230 + nextObj.appId(), false,
1231 + nextObj.meta());
1232 + if (nolabelGroupInfo == null) {
1233 + log.error("Could not process nextObj={} in dev:{}",
1234 + nextObj.id(), deviceId);
1235 + return;
1236 + }
1237 + gkeyChain.addFirst(nolabelGroupInfo.innerGrpDesc.appCookie());
1238 + gkeyChain.addFirst(nolabelGroupInfo.outerGrpDesc.appCookie());
1239 +
1240 + // we can't send the inner group description yet, as we have to
1241 + // create the dependent ECMP group first. So we store..
1242 + unsentGroups.add(nolabelGroupInfo);
1243 +
1244 + } else if (labelsPushed == 1) {
1245 + GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
1246 + nextObj.appId(), true,
1247 + nextObj.meta());
1248 + if (onelabelGroupInfo == null) {
1249 + log.error("Could not process nextObj={} in dev:{}",
1250 + nextObj.id(), deviceId);
1251 + return;
1252 + }
1253 + // we need to add another group to this chain - the L3VPN group
1254 + TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder();
1255 + l3vpnTtb.pushMpls()
1256 + .setMpls(innermostLabel)
1257 + .setMplsBos(true)
1258 + .copyTtlOut()
1259 + .group(new DefaultGroupId(
1260 + onelabelGroupInfo.outerGrpDesc.givenGroupId()));
1261 + GroupBucket l3vpnGrpBkt =
1262 + DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
1263 + int l3vpngroupId = L3VPNMASK | l3vpnindex.incrementAndGet();
1264 + int l3vpngk = L3VPNMASK | nextObj.id() << 12 | l3vpnindex.get();
1265 + GroupKey l3vpngroupkey = new DefaultGroupKey(appKryo.serialize(l3vpngk));
1266 + GroupDescription l3vpnGroupDesc =
956 new DefaultGroupDescription( 1267 new DefaultGroupDescription(
957 deviceId, 1268 deviceId,
958 GroupDescription.Type.INDIRECT, 1269 GroupDescription.Type.INDIRECT,
959 - new GroupBuckets(Collections.singletonList(gi.groupBucket)), 1270 + new GroupBuckets(Collections.singletonList(
960 - gi.groupKey, 1271 + l3vpnGrpBkt)),
961 - gi.groupId, 1272 + l3vpngroupkey,
1273 + l3vpngroupId,
962 nextObj.appId()); 1274 nextObj.appId());
963 - groupService.addGroup(groupDescription); 1275 + GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1);
1276 + Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1277 + new ConcurrentHashMap<GroupChainElem, Boolean>());
1278 + gceSet.add(l3vpnGce);
1279 + Set<GroupChainElem> retval = pendingGroups
1280 + .putIfAbsent(onelabelGroupInfo.outerGrpDesc.appCookie(), gceSet);
1281 + if (retval != null) {
1282 + retval.add(l3vpnGce);
964 } 1283 }
1284 +
1285 + gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie());
1286 + gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie());
1287 + gkeyChain.addFirst(l3vpngroupkey);
1288 +
1289 + //now we can replace the outerGrpDesc with the one we just created
1290 + onelabelGroupInfo.outerGrpDesc = l3vpnGroupDesc;
1291 +
1292 + // we can't send the innermost group yet, as we have to create
1293 + // the dependent ECMP group first. So we store ...
1294 + unsentGroups.add(onelabelGroupInfo);
1295 +
1296 + log.debug("Trying L3VPN: device:{} gid:{} gkey:{} nextId:{}",
1297 + deviceId, Integer.toHexString(l3vpngroupId),
1298 + l3vpngroupkey, nextObj.id());
1299 +
1300 + } else {
1301 + log.warn("Driver currently does not handle more than 1 MPLS "
1302 + + "labels. Not processing nextObjective {}", nextObj);
1303 + return;
965 } 1304 }
966 1305
967 - private class GroupInfo { 1306 + // all groups in this chain
968 - private Integer groupId; 1307 + allGroupKeys.add(gkeyChain);
969 - private GroupKey groupKey; 1308 + }
970 - private GroupBucket groupBucket;
971 1309
972 - GroupInfo(Integer groupId, GroupKey groupKey, GroupBucket groupBucket) { 1310 + // now we can create the outermost L3 ECMP group
973 - this.groupBucket = groupBucket; 1311 + List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>();
974 - this.groupId = groupId; 1312 + for (GroupInfo gi : unsentGroups) {
975 - this.groupKey = groupKey; 1313 + // create ECMP bucket to point to the outer group
1314 + TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1315 + ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId()));
1316 + GroupBucket sbucket = DefaultGroupBucket
1317 + .createSelectGroupBucket(ttb.build());
1318 + l3ecmpGroupBuckets.add(sbucket);
976 } 1319 }
1320 + int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12;
1321 + GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId));
1322 + GroupDescription l3ecmpGroupDesc =
1323 + new DefaultGroupDescription(
1324 + deviceId,
1325 + GroupDescription.Type.SELECT,
1326 + new GroupBuckets(l3ecmpGroupBuckets),
1327 + l3ecmpGroupKey,
1328 + l3ecmpGroupId,
1329 + nextObj.appId());
1330 + GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
1331 + l3ecmpGroupBuckets.size());
1332 +
1333 + // create objects for local and distributed storage
1334 + allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey));
1335 + OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
1336 +
1337 + // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
1338 + // that depends on it
1339 + pendingNextObjectives.put(l3ecmpGroupKey, ofdpaGrp);
1340 +
1341 + log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
1342 + deviceId, Integer.toHexString(l3ecmpGroupId),
1343 + l3ecmpGroupKey, nextObj.id());
1344 + // finally we are ready to send the innermost groups
1345 + for (GroupInfo gi : unsentGroups) {
1346 + log.debug("Sending innermost group {} in group chain on device {} ",
1347 + Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId);
1348 + Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1349 + new ConcurrentHashMap<GroupChainElem, Boolean>());
1350 + gceSet.add(l3ecmpGce);
1351 + Set<GroupChainElem> retval = pendingGroups
1352 + .putIfAbsent(gi.outerGrpDesc.appCookie(), gceSet);
1353 + if (retval != null) {
1354 + retval.add(l3ecmpGce);
1355 + }
1356 +
1357 + groupService.addGroup(gi.innerGrpDesc);
977 } 1358 }
978 1359
979 - private void processHashedNextObjective(NextObjective nextObj) {
980 - // TODO Auto-generated method stub
981 } 1360 }
982 1361
983 private void addBucketToGroup(NextObjective nextObjective) { 1362 private void addBucketToGroup(NextObjective nextObjective) {
984 // TODO Auto-generated method stub 1363 // TODO Auto-generated method stub
985 } 1364 }
986 1365
1366 + private void waitToAddBucketToGroup(NextObjective nextObjective) {
1367 + // TODO Auto-generated method stub
1368 + }
1369 +
987 private void removeBucketFromGroup(NextObjective nextObjective) { 1370 private void removeBucketFromGroup(NextObjective nextObjective) {
988 // TODO Auto-generated method stub 1371 // TODO Auto-generated method stub
989 } 1372 }
...@@ -1009,45 +1392,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1009,45 +1392,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1009 private void processGroupChain(GroupChainElem gce) { 1392 private void processGroupChain(GroupChainElem gce) {
1010 int waitOnGroups = gce.decrementAndGetGroupsWaitedOn(); 1393 int waitOnGroups = gce.decrementAndGetGroupsWaitedOn();
1011 if (waitOnGroups != 0) { 1394 if (waitOnGroups != 0) {
1012 - log.debug("GCE: {} waiting on {} groups. Not processing yet", 1395 + log.debug("GCE: {} not ready to be processed", gce);
1013 - gce, waitOnGroups);
1014 return; 1396 return;
1015 } 1397 }
1016 - List<GroupBucket> buckets = new ArrayList<>(); 1398 + log.debug("GCE: {} ready to be processed", gce);
1017 - switch (gce.groupType) { 1399 + groupService.addGroup(gce.groupDescription);
1018 - case INDIRECT:
1019 - GroupBucket ibucket = DefaultGroupBucket
1020 - .createIndirectGroupBucket(gce.bucketActions.iterator().next());
1021 - buckets.add(ibucket);
1022 - break;
1023 - case ALL:
1024 - for (TrafficTreatment tt : gce.bucketActions) {
1025 - GroupBucket abucket = DefaultGroupBucket
1026 - .createAllGroupBucket(tt);
1027 - buckets.add(abucket);
1028 - }
1029 - break;
1030 - case SELECT:
1031 - for (TrafficTreatment tt : gce.bucketActions) {
1032 - GroupBucket sbucket = DefaultGroupBucket
1033 - .createSelectGroupBucket(tt);
1034 - buckets.add(sbucket);
1035 - }
1036 - break;
1037 - case FAILOVER:
1038 - default:
1039 - log.error("Unknown or unimplemented GroupChainElem {}", gce);
1040 - }
1041 -
1042 - if (buckets.size() > 0) {
1043 - GroupDescription groupDesc = new DefaultGroupDescription(
1044 - deviceId, gce.groupType,
1045 - new GroupBuckets(buckets),
1046 - gce.gkey,
1047 - gce.givenGroupId,
1048 - gce.appId);
1049 - groupService.addGroup(groupDesc);
1050 - }
1051 } 1400 }
1052 1401
1053 private class GroupChecker implements Runnable { 1402 private class GroupChecker implements Runnable {
...@@ -1063,19 +1412,23 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1063,19 +1412,23 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1063 1412
1064 keys.stream().forEach(key -> { 1413 keys.stream().forEach(key -> {
1065 //first check for group chain 1414 //first check for group chain
1066 - GroupChainElem gce = pendingGroups.remove(key); 1415 + Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1067 - if (gce != null) { 1416 + if (gceSet != null) {
1068 - log.info("Group service processed group key {}. Processing next " 1417 + for (GroupChainElem gce : gceSet) {
1069 - + "group in group chain with group key {}", 1418 + log.info("Group service processed group key {} in device {}. "
1070 - appKryo.deserialize(key.key()), 1419 + + "Processing next group in group chain with group id {}",
1071 - appKryo.deserialize(gce.gkey.key())); 1420 + key, deviceId,
1421 + Integer.toHexString(gce.groupDescription.givenGroupId()));
1072 processGroupChain(gce); 1422 processGroupChain(gce);
1423 + }
1073 } else { 1424 } else {
1074 - OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key); 1425 + OfdpaNextGroup obj = pendingNextObjectives.getIfPresent(key);
1075 - log.info("Group service processed group key {}. Done implementing "
1076 - + "next objective: {}", appKryo.deserialize(key.key()),
1077 - obj.nextObjective().id());
1078 if (obj != null) { 1426 if (obj != null) {
1427 + log.info("Group service processed group key {} in device:{}. "
1428 + + "Done implementing next objective: {} <<-->> gid:{}",
1429 + key, deviceId, obj.nextObjective().id(),
1430 + Integer.toHexString(groupService.getGroup(deviceId, key)
1431 + .givenGroupId()));
1079 pass(obj.nextObjective()); 1432 pass(obj.nextObjective());
1080 pendingNextObjectives.invalidate(key); 1433 pendingNextObjectives.invalidate(key);
1081 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj); 1434 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
...@@ -1088,23 +1441,27 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1088,23 +1441,27 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1088 private class InnerGroupListener implements GroupListener { 1441 private class InnerGroupListener implements GroupListener {
1089 @Override 1442 @Override
1090 public void event(GroupEvent event) { 1443 public void event(GroupEvent event) {
1091 - log.debug("received group event of type {}", event.type()); 1444 + log.trace("received group event of type {}", event.type());
1092 if (event.type() == GroupEvent.Type.GROUP_ADDED) { 1445 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
1093 GroupKey key = event.subject().appCookie(); 1446 GroupKey key = event.subject().appCookie();
1094 // first check for group chain 1447 // first check for group chain
1095 - GroupChainElem gce = pendingGroups.remove(key); 1448 + Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1096 - if (gce != null) { 1449 + if (gceSet != null) {
1450 + for (GroupChainElem gce : gceSet) {
1097 log.info("group ADDED with group key {} .. " 1451 log.info("group ADDED with group key {} .. "
1098 + "Processing next group in group chain with group key {}", 1452 + "Processing next group in group chain with group key {}",
1099 - appKryo.deserialize(key.key()), 1453 + key,
1100 - appKryo.deserialize(gce.gkey.key())); 1454 + gce.groupDescription.appCookie());
1101 processGroupChain(gce); 1455 processGroupChain(gce);
1456 + }
1102 } else { 1457 } else {
1103 - OfdpaGroupChain obj = pendingNextObjectives.getIfPresent(key); 1458 + OfdpaNextGroup obj = pendingNextObjectives.getIfPresent(key);
1104 if (obj != null) { 1459 if (obj != null) {
1105 - log.info("group ADDED with key {}.. Done implementing next " 1460 + log.info("group ADDED with key {} in dev {}.. Done implementing next "
1106 - + "objective: {}", 1461 + + "objective: {} <<-->> gid:{}",
1107 - appKryo.deserialize(key.key()), obj.nextObjective().id()); 1462 + key, deviceId, obj.nextObjective().id(),
1463 + Integer.toHexString(groupService.getGroup(deviceId, key)
1464 + .givenGroupId()));
1108 pass(obj.nextObjective()); 1465 pass(obj.nextObjective());
1109 pendingNextObjectives.invalidate(key); 1466 pendingNextObjectives.invalidate(key);
1110 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj); 1467 flowObjectiveStore.putNextGroup(obj.nextObjective().id(), obj);
...@@ -1115,30 +1472,35 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1115,30 +1472,35 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1115 } 1472 }
1116 1473
1117 /** 1474 /**
1118 - * Represents a group-chain that implements a Next-Objective from 1475 + * Represents an entire group-chain that implements a Next-Objective from
1119 - * the application. Includes information about the next objective Id, and the 1476 + * the application. The objective is represented as a list of deques, where
1120 - * group keys for the groups in the group chain. The chain is expected to 1477 + * each deque can is a separate chain of groups.
1121 - * look like group0 --> group 1 --> outPort. Information about the groups 1478 + * <p>
1122 - * themselves can be fetched from the Group Service using the group keys from 1479 + * For example, an ECMP group with 3 buckets, where each bucket points to
1123 - * objects instantiating this class. 1480 + * a group chain of L3 Unicast and L2 interface groups will look like this:
1481 + * <ul>
1482 + * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1483 + * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1484 + * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1485 + * </ul>
1486 + * where the first element of each deque is the same, representing the
1487 + * top level ECMP group, while every other element represents a unique groupKey.
1488 + * <p>
1489 + * Also includes information about the next objective that
1490 + * resulted in this group-chain.
1124 * 1491 *
1125 - * XXX Revisit this - since the forwarding objective only ever needs the
1126 - * groupkey of the top-level group in the group chain, why store a series
1127 - * of groupkeys. Also the group-chain list only works for 1-to-1 chaining,
1128 - * not for 1-to-many chaining.
1129 */ 1492 */
1130 - private class OfdpaGroupChain implements NextGroup { 1493 + private class OfdpaNextGroup implements NextGroup {
1131 private final NextObjective nextObj; 1494 private final NextObjective nextObj;
1132 - private final List<GroupKey> gkeys; 1495 + private final List<Deque<GroupKey>> gkeys;
1133 1496
1134 - /** expected group chain: group0 --> group1 --> port. */ 1497 + public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
1135 - public OfdpaGroupChain(List<GroupKey> gkeys, NextObjective nextObj) {
1136 this.gkeys = gkeys; 1498 this.gkeys = gkeys;
1137 this.nextObj = nextObj; 1499 this.nextObj = nextObj;
1138 } 1500 }
1139 1501
1140 @SuppressWarnings("unused") 1502 @SuppressWarnings("unused")
1141 - public List<GroupKey> groupKeys() { 1503 + public List<Deque<GroupKey>> groupKey() {
1142 return gkeys; 1504 return gkeys;
1143 } 1505 }
1144 1506
...@@ -1161,22 +1523,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1161,22 +1523,11 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1161 * preceding groups in the group chain to be created. 1523 * preceding groups in the group chain to be created.
1162 */ 1524 */
1163 private class GroupChainElem { 1525 private class GroupChainElem {
1164 - private Collection<TrafficTreatment> bucketActions; 1526 + private GroupDescription groupDescription;
1165 - private Integer givenGroupId;
1166 - private GroupDescription.Type groupType;
1167 - private GroupKey gkey;
1168 - private ApplicationId appId;
1169 private AtomicInteger waitOnGroups; 1527 private AtomicInteger waitOnGroups;
1170 1528
1171 - GroupChainElem(GroupKey gkey, Integer givenGroupId, 1529 + GroupChainElem(GroupDescription groupDescription, int waitOnGroups) {
1172 - GroupDescription.Type groupType, 1530 + this.groupDescription = groupDescription;
1173 - Collection<TrafficTreatment> tr, ApplicationId appId,
1174 - int waitOnGroups) {
1175 - this.bucketActions = tr;
1176 - this.givenGroupId = givenGroupId;
1177 - this.groupType = groupType;
1178 - this.gkey = gkey;
1179 - this.appId = appId;
1180 this.waitOnGroups = new AtomicInteger(waitOnGroups); 1531 this.waitOnGroups = new AtomicInteger(waitOnGroups);
1181 } 1532 }
1182 1533
...@@ -1194,7 +1545,10 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1194,7 +1545,10 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1194 1545
1195 @Override 1546 @Override
1196 public String toString() { 1547 public String toString() {
1197 - return Integer.toHexString(givenGroupId); 1548 + return (Integer.toHexString(groupDescription.givenGroupId()) +
1549 + " groupKey: " + groupDescription.appCookie() +
1550 + " waiting-on-groups: " + waitOnGroups.get() +
1551 + " device: " + deviceId);
1198 } 1552 }
1199 1553
1200 } 1554 }
......
...@@ -47,6 +47,7 @@ import org.onosproject.net.flow.TrafficSelector; ...@@ -47,6 +47,7 @@ import org.onosproject.net.flow.TrafficSelector;
47 import org.onosproject.net.flow.TrafficTreatment; 47 import org.onosproject.net.flow.TrafficTreatment;
48 import org.onosproject.net.flow.criteria.Criteria; 48 import org.onosproject.net.flow.criteria.Criteria;
49 import org.onosproject.net.flow.criteria.Criterion; 49 import org.onosproject.net.flow.criteria.Criterion;
50 +import org.onosproject.net.flow.criteria.Criterion.Type;
50 import org.onosproject.net.flow.criteria.EthCriterion; 51 import org.onosproject.net.flow.criteria.EthCriterion;
51 import org.onosproject.net.flow.criteria.EthTypeCriterion; 52 import org.onosproject.net.flow.criteria.EthTypeCriterion;
52 import org.onosproject.net.flow.criteria.IPCriterion; 53 import org.onosproject.net.flow.criteria.IPCriterion;
...@@ -73,6 +74,7 @@ import org.onosproject.net.group.GroupEvent; ...@@ -73,6 +74,7 @@ import org.onosproject.net.group.GroupEvent;
73 import org.onosproject.net.group.GroupKey; 74 import org.onosproject.net.group.GroupKey;
74 import org.onosproject.net.group.GroupListener; 75 import org.onosproject.net.group.GroupListener;
75 import org.onosproject.net.group.GroupService; 76 import org.onosproject.net.group.GroupService;
77 +import org.onosproject.store.serializers.KryoNamespaces;
76 import org.slf4j.Logger; 78 import org.slf4j.Logger;
77 79
78 import java.util.ArrayList; 80 import java.util.ArrayList;
...@@ -129,8 +131,13 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -129,8 +131,13 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
129 groupedThreads("onos/pipeliner", 131 groupedThreads("onos/pipeliner",
130 "spring-open-%d")); 132 "spring-open-%d"));
131 protected KryoNamespace appKryo = new KryoNamespace.Builder() 133 protected KryoNamespace appKryo = new KryoNamespace.Builder()
132 - .register(GroupKey.class).register(DefaultGroupKey.class) 134 + .register(KryoNamespaces.API)
133 - .register(SegmentRoutingGroup.class).register(byte[].class).build(); 135 + .register(GroupKey.class)
136 + .register(DefaultGroupKey.class)
137 + .register(TrafficTreatment.class)
138 + .register(SpringOpenGroup.class)
139 + .register(byte[].class)
140 + .build();
134 141
135 @Override 142 @Override
136 public void init(DeviceId deviceId, PipelinerContext context) { 143 public void init(DeviceId deviceId, PipelinerContext context) {
...@@ -202,17 +209,15 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -202,17 +209,15 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
202 @Override 209 @Override
203 public void onSuccess(FlowRuleOperations ops) { 210 public void onSuccess(FlowRuleOperations ops) {
204 pass(fwd); 211 pass(fwd);
205 - log.debug("Provisioned tables in {} with " 212 + log.debug("Provisioned tables in {} successfully with "
206 - + "forwarding rules for segment " 213 + + "forwarding rules", deviceId);
207 - + "router", deviceId);
208 } 214 }
209 215
210 @Override 216 @Override
211 public void onError(FlowRuleOperations ops) { 217 public void onError(FlowRuleOperations ops) {
212 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED); 218 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
213 log.warn("Failed to provision tables in {} with " 219 log.warn("Failed to provision tables in {} with "
214 - + "forwarding rules for segment router", 220 + + "forwarding rules", deviceId);
215 - deviceId);
216 } 221 }
217 })); 222 }));
218 223
...@@ -220,26 +225,50 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -220,26 +225,50 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
220 225
221 @Override 226 @Override
222 public void next(NextObjective nextObjective) { 227 public void next(NextObjective nextObjective) {
223 -
224 - log.debug("Processing NextObjective id{} op{}", nextObjective.id(),
225 - nextObjective.op());
226 - if (nextObjective.op() == Objective.Operation.REMOVE) {
227 - if (nextObjective.next().isEmpty()) {
228 - removeGroup(nextObjective);
229 - } else {
230 - removeBucketFromGroup(nextObjective);
231 - }
232 - } else if (nextObjective.op() == Objective.Operation.ADD) {
233 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id()); 228 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
229 + switch (nextObjective.op()) {
230 + case ADD:
234 if (nextGroup != null) { 231 if (nextGroup != null) {
232 + log.warn("Cannot add next {} that already exists in device {}",
233 + nextObjective.id(), deviceId);
234 + return;
235 + }
236 + log.debug("Processing NextObjective id{} in dev{} - add group",
237 + nextObjective.id(), deviceId);
238 + addGroup(nextObjective);
239 + break;
240 + case ADD_TO_EXISTING:
241 + if (nextGroup != null) {
242 + log.debug("Processing NextObjective id{} in dev{} - add bucket",
243 + nextObjective.id(), deviceId);
235 addBucketToGroup(nextObjective); 244 addBucketToGroup(nextObjective);
236 } else { 245 } else {
237 - addGroup(nextObjective); 246 + log.warn("Cannot add to group that does not exist");
238 } 247 }
239 - } else { 248 + break;
249 + case REMOVE:
250 + if (nextGroup == null) {
251 + log.warn("Cannot remove next {} that does not exist in device {}",
252 + nextObjective.id(), deviceId);
253 + return;
254 + }
255 + log.debug("Processing NextObjective id{} in dev{} - remove group",
256 + nextObjective.id(), deviceId);
257 + removeGroup(nextObjective);
258 + break;
259 + case REMOVE_FROM_EXISTING:
260 + if (nextGroup == null) {
261 + log.warn("Cannot remove from next {} that does not exist in device {}",
262 + nextObjective.id(), deviceId);
263 + return;
264 + }
265 + log.debug("Processing NextObjective id{} in dev{} - remove bucket",
266 + nextObjective.id(), deviceId);
267 + removeBucketFromGroup(nextObjective);
268 + break;
269 + default:
240 log.warn("Unsupported operation {}", nextObjective.op()); 270 log.warn("Unsupported operation {}", nextObjective.op());
241 } 271 }
242 -
243 } 272 }
244 273
245 private void removeGroup(NextObjective nextObjective) { 274 private void removeGroup(NextObjective nextObjective) {
...@@ -256,7 +285,6 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -256,7 +285,6 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
256 List<GroupBucket> buckets; 285 List<GroupBucket> buckets;
257 switch (nextObjective.type()) { 286 switch (nextObjective.type()) {
258 case SIMPLE: 287 case SIMPLE:
259 - log.debug("processing SIMPLE next objective");
260 Collection<TrafficTreatment> treatments = nextObjective.next(); 288 Collection<TrafficTreatment> treatments = nextObjective.next();
261 if (treatments.size() == 1) { 289 if (treatments.size() == 1) {
262 TrafficTreatment treatment = treatments.iterator().next(); 290 TrafficTreatment treatment = treatments.iterator().next();
...@@ -273,14 +301,33 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -273,14 +301,33 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
273 key, 301 key,
274 null, 302 null,
275 nextObjective.appId()); 303 nextObjective.appId());
276 - log.debug("Creating SIMPLE group for next objective id {}", 304 + log.debug("Creating SIMPLE group for next objective id {} "
277 - nextObjective.id()); 305 + + "in dev:{}", nextObjective.id(), deviceId);
278 - groupService.addGroup(groupDescription);
279 pendingGroups.put(key, nextObjective); 306 pendingGroups.put(key, nextObjective);
307 + groupService.addGroup(groupDescription);
280 } 308 }
281 break; 309 break;
282 case HASHED: 310 case HASHED:
283 - log.debug("processing HASHED next objective"); 311 + // we convert MPLS ECMP groups to flow-actions for a single
312 + // bucket(output port).
313 + boolean mplsEcmp = false;
314 + if (nextObjective.meta() != null) {
315 + for (Criterion c : nextObjective.meta().criteria()) {
316 + if (c.type() == Type.MPLS_LABEL) {
317 + mplsEcmp = true;
318 + }
319 + }
320 + }
321 + if (mplsEcmp) {
322 + // covert to flow-actions in a dummy group by choosing the first bucket
323 + log.debug("Converting HASHED group for next objective id {} " +
324 + "to flow-actions in device:{}", nextObjective.id(),
325 + deviceId);
326 + TrafficTreatment treatment = nextObjective.next().iterator().next();
327 + flowObjectiveStore.putNextGroup(nextObjective.id(),
328 + new SpringOpenGroup(null, treatment));
329 + } else {
330 + // process as ECMP group
284 buckets = nextObjective 331 buckets = nextObjective
285 .next() 332 .next()
286 .stream() 333 .stream()
...@@ -289,8 +336,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -289,8 +336,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
289 .collect(Collectors.toList()); 336 .collect(Collectors.toList());
290 if (!buckets.isEmpty()) { 337 if (!buckets.isEmpty()) {
291 final GroupKey key = new DefaultGroupKey( 338 final GroupKey key = new DefaultGroupKey(
292 - appKryo.serialize(nextObjective 339 + appKryo.serialize(nextObjective.id()));
293 - .id()));
294 GroupDescription groupDescription = new DefaultGroupDescription( 340 GroupDescription groupDescription = new DefaultGroupDescription(
295 deviceId, 341 deviceId,
296 GroupDescription.Type.SELECT, 342 GroupDescription.Type.SELECT,
...@@ -298,14 +344,14 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -298,14 +344,14 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
298 key, 344 key,
299 null, 345 null,
300 nextObjective.appId()); 346 nextObjective.appId());
301 - log.debug("Creating HASHED group for next objective id {}", 347 + log.debug("Creating HASHED group for next objective id {}"
302 - nextObjective.id()); 348 + + " in dev:{}", nextObjective.id(), deviceId);
303 - groupService.addGroup(groupDescription);
304 pendingGroups.put(key, nextObjective); 349 pendingGroups.put(key, nextObjective);
350 + groupService.addGroup(groupDescription);
351 + }
305 } 352 }
306 break; 353 break;
307 case BROADCAST: 354 case BROADCAST:
308 - log.debug("processing BROADCAST next objective");
309 buckets = nextObjective 355 buckets = nextObjective
310 .next() 356 .next()
311 .stream() 357 .stream()
...@@ -323,10 +369,10 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -323,10 +369,10 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
323 key, 369 key,
324 null, 370 null,
325 nextObjective.appId()); 371 nextObjective.appId());
326 - log.debug("Creating BROADCAST group for next objective id {}", 372 + log.debug("Creating BROADCAST group for next objective id {} "
327 - nextObjective.id()); 373 + + "in device {}", nextObjective.id(), deviceId);
328 - groupService.addGroup(groupDescription);
329 pendingGroups.put(key, nextObjective); 374 pendingGroups.put(key, nextObjective);
375 + groupService.addGroup(groupDescription);
330 } 376 }
331 break; 377 break;
332 case FAILOVER: 378 case FAILOVER:
...@@ -417,9 +463,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -417,9 +463,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
417 } 463 }
418 464
419 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) { 465 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
420 - log.debug("Processing versatile forwarding objective"); 466 + log.debug("Processing versatile forwarding objective in dev:{}", deviceId);
421 TrafficSelector selector = fwd.selector(); 467 TrafficSelector selector = fwd.selector();
422 - TrafficTreatment treatment = null;
423 EthTypeCriterion ethType = 468 EthTypeCriterion ethType =
424 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE); 469 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
425 if (ethType == null) { 470 if (ethType == null) {
...@@ -428,50 +473,60 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -428,50 +473,60 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
428 return Collections.emptySet(); 473 return Collections.emptySet();
429 } 474 }
430 475
476 + if (fwd.treatment() == null && fwd.nextId() == null) {
477 + log.error("VERSATILE forwarding objective needs next objective ID "
478 + + "or treatment.");
479 + return Collections.emptySet();
480 + }
481 + // emulation of ACL table (for versatile fwd objective) requires
482 + // overriding any previous instructions
431 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment 483 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
432 .builder(); 484 .builder();
433 treatmentBuilder.wipeDeferred(); 485 treatmentBuilder.wipeDeferred();
434 486
435 if (fwd.nextId() != null) { 487 if (fwd.nextId() != null) {
436 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId()); 488 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
437 -
438 if (next != null) { 489 if (next != null) {
439 - GroupKey key = appKryo.deserialize(next.data()); 490 + SpringOpenGroup soGroup = appKryo.deserialize(next.data());
440 - 491 + if (soGroup.dummy) {
492 + // need to convert to flow-actions
493 + for (Instruction ins : soGroup.treatment.allInstructions()) {
494 + treatmentBuilder.add(ins);
495 + }
496 + } else {
497 + GroupKey key = soGroup.key;
441 Group group = groupService.getGroup(deviceId, key); 498 Group group = groupService.getGroup(deviceId, key);
442 -
443 if (group == null) { 499 if (group == null) {
444 log.warn("The group left!"); 500 log.warn("The group left!");
445 fail(fwd, ObjectiveError.GROUPMISSING); 501 fail(fwd, ObjectiveError.GROUPMISSING);
446 return Collections.emptySet(); 502 return Collections.emptySet();
447 } 503 }
448 treatmentBuilder.deferred().group(group.id()); 504 treatmentBuilder.deferred().group(group.id());
449 - treatment = treatmentBuilder.build();
450 log.debug("Adding OUTGROUP action"); 505 log.debug("Adding OUTGROUP action");
451 } 506 }
452 - } else if (fwd.treatment() != null) { 507 + }
508 + }
509 +
510 + if (fwd.treatment() != null) {
453 if (fwd.treatment().allInstructions().size() == 1 && 511 if (fwd.treatment().allInstructions().size() == 1 &&
454 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) { 512 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
455 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0); 513 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
456 if (o.port() == PortNumber.CONTROLLER) { 514 if (o.port() == PortNumber.CONTROLLER) {
457 treatmentBuilder.punt(); 515 treatmentBuilder.punt();
458 - treatment = treatmentBuilder.build();
459 } else { 516 } else {
460 - treatment = fwd.treatment(); 517 + treatmentBuilder.add(o);
461 } 518 }
462 } else { 519 } else {
463 - treatment = fwd.treatment(); 520 + for (Instruction ins : fwd.treatment().allInstructions()) {
521 + treatmentBuilder.add(ins);
522 + }
464 } 523 }
465 - } else {
466 - log.warn("VERSATILE forwarding objective needs next objective ID "
467 - + "or treatment.");
468 - return Collections.emptySet();
469 } 524 }
470 525
471 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() 526 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
472 .fromApp(fwd.appId()).withPriority(fwd.priority()) 527 .fromApp(fwd.appId()).withPriority(fwd.priority())
473 .forDevice(deviceId).withSelector(fwd.selector()) 528 .forDevice(deviceId).withSelector(fwd.selector())
474 - .withTreatment(treatment); 529 + .withTreatment(treatmentBuilder.build());
475 530
476 if (fwd.permanent()) { 531 if (fwd.permanent()) {
477 ruleBuilder.makePermanent(); 532 ruleBuilder.makePermanent();
...@@ -508,7 +563,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -508,7 +563,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
508 } 563 }
509 564
510 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) { 565 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
511 - log.debug("Processing specific"); 566 + log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
567 + fwd.id(), deviceId, fwd.nextId());
512 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd); 568 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
513 boolean isEthDstObj = isSupportedEthDstObjective(fwd); 569 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
514 570
...@@ -518,7 +574,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -518,7 +574,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
518 return processEthDstSpecificObjective(fwd); 574 return processEthDstSpecificObjective(fwd);
519 } else { 575 } else {
520 log.warn("processSpecific: Unsupported " 576 log.warn("processSpecific: Unsupported "
521 - + "forwarding objective criteraia"); 577 + + "forwarding objective criteria");
522 fail(fwd, ObjectiveError.UNSUPPORTED); 578 fail(fwd, ObjectiveError.UNSUPPORTED);
523 return Collections.emptySet(); 579 return Collections.emptySet();
524 } 580 }
...@@ -540,7 +596,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -540,7 +596,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
540 .getCriterion(Criterion.Type.IPV4_DST)) 596 .getCriterion(Criterion.Type.IPV4_DST))
541 .ip()); 597 .ip());
542 forTableId = ipv4UnicastTableId; 598 forTableId = ipv4UnicastTableId;
543 - log.debug("processing IPv4 specific forwarding objective"); 599 + log.debug("processing IPv4 specific forwarding objective:{} in dev:{}",
600 + fwd.id(), deviceId);
544 } else { 601 } else {
545 filteredSelectorBuilder = filteredSelectorBuilder 602 filteredSelectorBuilder = filteredSelectorBuilder
546 .matchEthType(Ethernet.MPLS_UNICAST) 603 .matchEthType(Ethernet.MPLS_UNICAST)
...@@ -550,7 +607,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -550,7 +607,8 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
550 //if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) { 607 //if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
551 //} 608 //}
552 forTableId = mplsTableId; 609 forTableId = mplsTableId;
553 - log.debug("processing MPLS specific forwarding objective"); 610 + log.debug("processing MPLS specific forwarding objective:{} in dev:{}",
611 + fwd.id(), deviceId);
554 } 612 }
555 613
556 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment 614 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
...@@ -561,24 +619,28 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -561,24 +619,28 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
561 } 619 }
562 } 620 }
563 621
564 - //TODO: Analyze the forwarding objective here to make
565 - //device specific decision such as no ECMP groups in Dell
566 - //switches.
567 if (fwd.nextId() != null) { 622 if (fwd.nextId() != null) {
568 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId()); 623 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
569 -
570 if (next != null) { 624 if (next != null) {
571 - GroupKey key = appKryo.deserialize(next.data()); 625 + SpringOpenGroup soGroup = appKryo.deserialize(next.data());
572 - 626 + if (soGroup.dummy) {
627 + log.debug("Adding flow-actions for fwd. obj. {} "
628 + + "in dev: {}", fwd.id(), deviceId);
629 + for (Instruction ins : soGroup.treatment.allInstructions()) {
630 + treatmentBuilder.add(ins);
631 + }
632 + } else {
633 + GroupKey key = soGroup.key;
573 Group group = groupService.getGroup(deviceId, key); 634 Group group = groupService.getGroup(deviceId, key);
574 -
575 if (group == null) { 635 if (group == null) {
576 log.warn("The group left!"); 636 log.warn("The group left!");
577 fail(fwd, ObjectiveError.GROUPMISSING); 637 fail(fwd, ObjectiveError.GROUPMISSING);
578 return Collections.emptySet(); 638 return Collections.emptySet();
579 } 639 }
580 treatmentBuilder.deferred().group(group.id()); 640 treatmentBuilder.deferred().group(group.id());
581 - log.debug("Adding OUTGROUP action"); 641 + log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
642 + + "in dev: {}", group.id(), fwd.id(), deviceId);
643 + }
582 } else { 644 } else {
583 log.warn("processSpecific: No associated next objective object"); 645 log.warn("processSpecific: No associated next objective object");
584 fail(fwd, ObjectiveError.GROUPMISSING); 646 fail(fwd, ObjectiveError.GROUPMISSING);
...@@ -621,6 +683,12 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -621,6 +683,12 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
621 // Do not match MacAddress for subnet broadcast entry 683 // Do not match MacAddress for subnet broadcast entry
622 if (!ethCriterion.mac().equals(MacAddress.NONE)) { 684 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
623 filteredSelectorBuilder.matchEthDst(ethCriterion.mac()); 685 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
686 + log.debug("processing L2 forwarding objective:{} in dev:{}",
687 + fwd.id(), deviceId);
688 + } else {
689 + log.debug("processing L2 Broadcast forwarding objective:{} "
690 + + "in dev:{} for vlan:{}",
691 + fwd.id(), deviceId, vlanIdCriterion.vlanId());
624 } 692 }
625 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId()); 693 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
626 TrafficSelector filteredSelector = filteredSelectorBuilder.build(); 694 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
...@@ -635,15 +703,25 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -635,15 +703,25 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
635 if (fwd.nextId() != null) { 703 if (fwd.nextId() != null) {
636 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId()); 704 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
637 if (next != null) { 705 if (next != null) {
638 - GroupKey key = appKryo.deserialize(next.data()); 706 + SpringOpenGroup soGrp = appKryo.deserialize(next.data());
639 - Group group = groupService.getGroup(deviceId, key); 707 + if (soGrp.dummy) {
640 - if (group != null) { 708 + log.debug("Adding flow-actions for fwd. obj. {} "
641 - treatmentBuilder.deferred().group(group.id()); 709 + + "in dev: {}", fwd.id(), deviceId);
710 + for (Instruction ins : soGrp.treatment.allInstructions()) {
711 + treatmentBuilder.add(ins);
712 + }
642 } else { 713 } else {
643 - log.warn("Group Missing"); 714 + GroupKey key = soGrp.key;
715 + Group group = groupService.getGroup(deviceId, key);
716 + if (group == null) {
717 + log.warn("The group left!");
644 fail(fwd, ObjectiveError.GROUPMISSING); 718 fail(fwd, ObjectiveError.GROUPMISSING);
645 return Collections.emptySet(); 719 return Collections.emptySet();
646 } 720 }
721 + treatmentBuilder.deferred().group(group.id());
722 + log.debug("Adding OUTGROUP action to group:{} for fwd. obj. {} "
723 + + "in dev: {}", group.id(), fwd.id(), deviceId);
724 + }
647 } 725 }
648 } 726 }
649 treatmentBuilder.immediate().transition(aclTableId); 727 treatmentBuilder.immediate().transition(aclTableId);
...@@ -869,14 +947,14 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -869,14 +947,14 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
869 public void onSuccess(FlowRuleOperations ops) { 947 public void onSuccess(FlowRuleOperations ops) {
870 pass(filt); 948 pass(filt);
871 log.debug("Provisioned tables in {} with fitering " 949 log.debug("Provisioned tables in {} with fitering "
872 - + "rules for segment router", deviceId); 950 + + "rules", deviceId);
873 } 951 }
874 952
875 @Override 953 @Override
876 public void onError(FlowRuleOperations ops) { 954 public void onError(FlowRuleOperations ops) {
877 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED); 955 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
878 log.warn("Failed to provision tables in {} with " 956 log.warn("Failed to provision tables in {} with "
879 - + "fitering rules for segment router", deviceId); 957 + + "fitering rules", deviceId);
880 } 958 }
881 })); 959 }));
882 } 960 }
...@@ -934,15 +1012,17 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -934,15 +1012,17 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
934 @Override 1012 @Override
935 public void event(GroupEvent event) { 1013 public void event(GroupEvent event) {
936 if (event.type() == GroupEvent.Type.GROUP_ADDED) { 1014 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
937 - log.debug("InnerGroupListener: Group ADDED " 1015 + log.trace("InnerGroupListener: Group ADDED "
938 + "event received in device {}", deviceId); 1016 + "event received in device {}", deviceId);
939 GroupKey key = event.subject().appCookie(); 1017 GroupKey key = event.subject().appCookie();
940 1018
941 NextObjective obj = pendingGroups.getIfPresent(key); 1019 NextObjective obj = pendingGroups.getIfPresent(key);
942 if (obj != null) { 1020 if (obj != null) {
1021 + log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1022 + deviceId, event.subject().id(), obj.id());
943 flowObjectiveStore 1023 flowObjectiveStore
944 .putNextGroup(obj.id(), 1024 .putNextGroup(obj.id(),
945 - new SegmentRoutingGroup(key)); 1025 + new SpringOpenGroup(key, null));
946 pass(obj); 1026 pass(obj);
947 pendingGroups.invalidate(key); 1027 pendingGroups.invalidate(key);
948 } 1028 }
...@@ -971,21 +1051,47 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -971,21 +1051,47 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
971 if (obj == null) { 1051 if (obj == null) {
972 return; 1052 return;
973 } 1053 }
1054 + log.debug("Group verified: dev:{} gid:{} <<->> nextId:{}",
1055 + deviceId,
1056 + groupService.getGroup(deviceId, key).id(),
1057 + obj.id());
974 pass(obj); 1058 pass(obj);
975 pendingGroups.invalidate(key); 1059 pendingGroups.invalidate(key);
976 - flowObjectiveStore.putNextGroup(obj.id(), 1060 + flowObjectiveStore.putNextGroup(
977 - new SegmentRoutingGroup( 1061 + obj.id(),
978 - key)); 1062 + new SpringOpenGroup(key, null));
979 }); 1063 });
980 } 1064 }
981 } 1065 }
982 1066
983 - private class SegmentRoutingGroup implements NextGroup { 1067 + /**
984 - 1068 + * SpringOpenGroup can either serve as storage for a GroupKey which can be
1069 + * used to fetch the group from the Group Service, or it can be serve as storage
1070 + * for Traffic Treatments which can be used as flow actions. In the latter
1071 + * case, we refer to this as a dummy group.
1072 + *
1073 + */
1074 + private class SpringOpenGroup implements NextGroup {
1075 + private final boolean dummy;
985 private final GroupKey key; 1076 private final GroupKey key;
1077 + private final TrafficTreatment treatment;
986 1078
987 - public SegmentRoutingGroup(GroupKey key) { 1079 + /**
1080 + * Storage for a GroupKey or a TrafficTreatment. One of the params
1081 + * to this constructor must be null.
1082 + * @param key represents a GroupKey
1083 + * @param treatment represents flow actions in a dummy group
1084 + */
1085 + public SpringOpenGroup(GroupKey key, TrafficTreatment treatment) {
1086 + if (key == null) {
1087 + this.key = new DefaultGroupKey(new byte[]{0});
1088 + this.treatment = treatment;
1089 + this.dummy = true;
1090 + } else {
988 this.key = key; 1091 this.key = key;
1092 + this.treatment = DefaultTrafficTreatment.builder().build();
1093 + this.dummy = false;
1094 + }
989 } 1095 }
990 1096
991 @SuppressWarnings("unused") 1097 @SuppressWarnings("unused")
...@@ -995,7 +1101,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour ...@@ -995,7 +1101,7 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour
995 1101
996 @Override 1102 @Override
997 public byte[] data() { 1103 public byte[] data() {
998 - return appKryo.serialize(key); 1104 + return appKryo.serialize(this);
999 } 1105 }
1000 1106
1001 } 1107 }
......