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 | } | ... | ... |
-
Please register or login to post a comment