Enhancing the GUI traffic-related code.
Fixed a defect in reactive forwarding. Change-Id: I1a91f6e5f57b39425ef06092c82b06d04c9b59a0
Showing
6 changed files
with
243 additions
and
23 deletions
| ... | @@ -132,8 +132,8 @@ public class ReactiveForwarding { | ... | @@ -132,8 +132,8 @@ public class ReactiveForwarding { |
| 132 | InboundPacket pkt = context.inPacket(); | 132 | InboundPacket pkt = context.inPacket(); |
| 133 | Ethernet ethPkt = pkt.parsed(); | 133 | Ethernet ethPkt = pkt.parsed(); |
| 134 | 134 | ||
| 135 | - // Bail if this is deemed to be a control packet. | 135 | + // Bail if this is deemed to be a control or IPv6 multicast packet. |
| 136 | - if (isControlPacket(ethPkt)) { | 136 | + if (isControlPacket(ethPkt) || isIpv6Multicast(ethPkt)) { |
| 137 | return; | 137 | return; |
| 138 | } | 138 | } |
| 139 | 139 | ||
| ... | @@ -194,6 +194,11 @@ public class ReactiveForwarding { | ... | @@ -194,6 +194,11 @@ public class ReactiveForwarding { |
| 194 | return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN; | 194 | return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN; |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | + // Indicated whether this is an IPv6 multicast packet. | ||
| 198 | + private boolean isIpv6Multicast(Ethernet eth) { | ||
| 199 | + return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast(); | ||
| 200 | + } | ||
| 201 | + | ||
| 197 | // Selects a path from the given set that does not lead back to the | 202 | // Selects a path from the given set that does not lead back to the |
| 198 | // specified port. | 203 | // specified port. |
| 199 | private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) { | 204 | private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) { | ... | ... |
core/api/src/main/java/org/onlab/onos/net/intent/constraint/AsymmetricPathConstraint.java
0 → 100644
| 1 | +/* | ||
| 2 | + * Copyright 2014 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | +package org.onlab.onos.net.intent.constraint; | ||
| 17 | + | ||
| 18 | +import org.onlab.onos.net.Link; | ||
| 19 | +import org.onlab.onos.net.Path; | ||
| 20 | +import org.onlab.onos.net.intent.Constraint; | ||
| 21 | +import org.onlab.onos.net.resource.LinkResourceService; | ||
| 22 | + | ||
| 23 | +import java.util.Objects; | ||
| 24 | + | ||
| 25 | +import static com.google.common.base.MoreObjects.toStringHelper; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * Constraint that serves as a request for asymmetric bi-directional path. | ||
| 29 | + */ | ||
| 30 | +public class AsymmetricPathConstraint implements Constraint { | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public double cost(Link link, LinkResourceService resourceService) { | ||
| 34 | + return 1; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Override | ||
| 38 | + public boolean validate(Path path, LinkResourceService resourceService) { | ||
| 39 | + return true; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + @Override | ||
| 43 | + public int hashCode() { | ||
| 44 | + return Objects.hashCode(true); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + @Override | ||
| 48 | + public boolean equals(Object obj) { | ||
| 49 | + if (this == obj) { | ||
| 50 | + return true; | ||
| 51 | + } | ||
| 52 | + if (obj == null || getClass() != obj.getClass()) { | ||
| 53 | + return false; | ||
| 54 | + } | ||
| 55 | + return true; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @Override | ||
| 59 | + public String toString() { | ||
| 60 | + return toStringHelper(this).toString(); | ||
| 61 | + } | ||
| 62 | +} |
| ... | @@ -20,15 +20,20 @@ import org.apache.felix.scr.annotations.Component; | ... | @@ -20,15 +20,20 @@ import org.apache.felix.scr.annotations.Component; |
| 20 | import org.apache.felix.scr.annotations.Deactivate; | 20 | import org.apache.felix.scr.annotations.Deactivate; |
| 21 | import org.apache.felix.scr.annotations.Reference; | 21 | import org.apache.felix.scr.annotations.Reference; |
| 22 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 22 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 23 | +import org.onlab.onos.net.DefaultLink; | ||
| 24 | +import org.onlab.onos.net.DefaultPath; | ||
| 23 | import org.onlab.onos.net.Host; | 25 | import org.onlab.onos.net.Host; |
| 26 | +import org.onlab.onos.net.Link; | ||
| 24 | import org.onlab.onos.net.Path; | 27 | import org.onlab.onos.net.Path; |
| 25 | import org.onlab.onos.net.flow.TrafficSelector; | 28 | import org.onlab.onos.net.flow.TrafficSelector; |
| 26 | import org.onlab.onos.net.host.HostService; | 29 | import org.onlab.onos.net.host.HostService; |
| 27 | import org.onlab.onos.net.intent.HostToHostIntent; | 30 | import org.onlab.onos.net.intent.HostToHostIntent; |
| 28 | import org.onlab.onos.net.intent.Intent; | 31 | import org.onlab.onos.net.intent.Intent; |
| 29 | import org.onlab.onos.net.intent.PathIntent; | 32 | import org.onlab.onos.net.intent.PathIntent; |
| 33 | +import org.onlab.onos.net.intent.constraint.AsymmetricPathConstraint; | ||
| 30 | import org.onlab.onos.net.resource.LinkResourceAllocations; | 34 | import org.onlab.onos.net.resource.LinkResourceAllocations; |
| 31 | 35 | ||
| 36 | +import java.util.ArrayList; | ||
| 32 | import java.util.Arrays; | 37 | import java.util.Arrays; |
| 33 | import java.util.List; | 38 | import java.util.List; |
| 34 | import java.util.Set; | 39 | import java.util.Set; |
| ... | @@ -58,8 +63,10 @@ public class HostToHostIntentCompiler | ... | @@ -58,8 +63,10 @@ public class HostToHostIntentCompiler |
| 58 | @Override | 63 | @Override |
| 59 | public List<Intent> compile(HostToHostIntent intent, List<Intent> installable, | 64 | public List<Intent> compile(HostToHostIntent intent, List<Intent> installable, |
| 60 | Set<LinkResourceAllocations> resources) { | 65 | Set<LinkResourceAllocations> resources) { |
| 66 | + boolean isAsymmetric = intent.constraints().contains(new AsymmetricPathConstraint()); | ||
| 61 | Path pathOne = getPath(intent, intent.one(), intent.two()); | 67 | Path pathOne = getPath(intent, intent.one(), intent.two()); |
| 62 | - Path pathTwo = getPath(intent, intent.two(), intent.one()); | 68 | + Path pathTwo = isAsymmetric ? |
| 69 | + getPath(intent, intent.two(), intent.one()) : invertPath(pathOne); | ||
| 63 | 70 | ||
| 64 | Host one = hostService.getHost(intent.one()); | 71 | Host one = hostService.getHost(intent.one()); |
| 65 | Host two = hostService.getHost(intent.two()); | 72 | Host two = hostService.getHost(intent.two()); |
| ... | @@ -68,6 +75,23 @@ public class HostToHostIntentCompiler | ... | @@ -68,6 +75,23 @@ public class HostToHostIntentCompiler |
| 68 | createPathIntent(pathTwo, two, one, intent)); | 75 | createPathIntent(pathTwo, two, one, intent)); |
| 69 | } | 76 | } |
| 70 | 77 | ||
| 78 | + // Inverts the specified path. This makes an assumption that each link in | ||
| 79 | + // the path has a reverse link available. Under most circumstances, this | ||
| 80 | + // assumption will hold. | ||
| 81 | + private Path invertPath(Path path) { | ||
| 82 | + List<Link> reverseLinks = new ArrayList<>(path.links().size()); | ||
| 83 | + for (Link link : path.links()) { | ||
| 84 | + reverseLinks.add(0, reverseLink(link)); | ||
| 85 | + } | ||
| 86 | + return new DefaultPath(path.providerId(), reverseLinks, path.cost()); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + // Produces a reverse variant of the specified link. | ||
| 90 | + private Link reverseLink(Link link) { | ||
| 91 | + return new DefaultLink(link.providerId(), link.dst(), link.src(), | ||
| 92 | + link.type(), link.state(), link.isDurable()); | ||
| 93 | + } | ||
| 94 | + | ||
| 71 | // Creates a path intent from the specified path and original connectivity intent. | 95 | // Creates a path intent from the specified path and original connectivity intent. |
| 72 | private Intent createPathIntent(Path path, Host src, Host dst, | 96 | private Intent createPathIntent(Path path, Host src, Host dst, |
| 73 | HostToHostIntent intent) { | 97 | HostToHostIntent intent) { | ... | ... |
| ... | @@ -31,14 +31,16 @@ import java.util.Map; | ... | @@ -31,14 +31,16 @@ import java.util.Map; |
| 31 | */ | 31 | */ |
| 32 | public class Ethernet extends BasePacket { | 32 | public class Ethernet extends BasePacket { |
| 33 | private static final String HEXES = "0123456789ABCDEF"; | 33 | private static final String HEXES = "0123456789ABCDEF"; |
| 34 | - public static final short TYPE_ARP = 0x0806; | 34 | + public static final short TYPE_ARP = (short) 0x0806; |
| 35 | public static final short TYPE_RARP = (short) 0x8035; | 35 | public static final short TYPE_RARP = (short) 0x8035; |
| 36 | - public static final short TYPE_IPV4 = 0x0800; | 36 | + public static final short TYPE_IPV4 = (short) 0x0800; |
| 37 | + public static final short TYPE_IPV6 = (short) 0x86dd; | ||
| 37 | public static final short TYPE_LLDP = (short) 0x88cc; | 38 | public static final short TYPE_LLDP = (short) 0x88cc; |
| 38 | public static final short TYPE_BSN = (short) 0x8942; | 39 | public static final short TYPE_BSN = (short) 0x8942; |
| 39 | public static final short VLAN_UNTAGGED = (short) 0xffff; | 40 | public static final short VLAN_UNTAGGED = (short) 0xffff; |
| 40 | public static final short MPLS_UNICAST = (short) 0x8847; | 41 | public static final short MPLS_UNICAST = (short) 0x8847; |
| 41 | public static final short MPLS_MULTICAST = (short) 0x8848; | 42 | public static final short MPLS_MULTICAST = (short) 0x8848; |
| 43 | + | ||
| 42 | public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes | 44 | public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes |
| 43 | public static final Map<Short, Class<? extends IPacket>> ETHER_TYPE_CLASS_MAP = | 45 | public static final Map<Short, Class<? extends IPacket>> ETHER_TYPE_CLASS_MAP = |
| 44 | new HashMap<>(); | 46 | new HashMap<>(); | ... | ... |
| ... | @@ -36,6 +36,7 @@ import org.onlab.onos.net.Host; | ... | @@ -36,6 +36,7 @@ import org.onlab.onos.net.Host; |
| 36 | import org.onlab.onos.net.HostId; | 36 | import org.onlab.onos.net.HostId; |
| 37 | import org.onlab.onos.net.HostLocation; | 37 | import org.onlab.onos.net.HostLocation; |
| 38 | import org.onlab.onos.net.Link; | 38 | import org.onlab.onos.net.Link; |
| 39 | +import org.onlab.onos.net.LinkKey; | ||
| 39 | import org.onlab.onos.net.PortNumber; | 40 | import org.onlab.onos.net.PortNumber; |
| 40 | import org.onlab.onos.net.device.DeviceEvent; | 41 | import org.onlab.onos.net.device.DeviceEvent; |
| 41 | import org.onlab.onos.net.device.DeviceService; | 42 | import org.onlab.onos.net.device.DeviceService; |
| ... | @@ -66,6 +67,7 @@ import org.slf4j.LoggerFactory; | ... | @@ -66,6 +67,7 @@ import org.slf4j.LoggerFactory; |
| 66 | 67 | ||
| 67 | import java.text.DecimalFormat; | 68 | import java.text.DecimalFormat; |
| 68 | import java.util.ArrayList; | 69 | import java.util.ArrayList; |
| 70 | +import java.util.Collection; | ||
| 69 | import java.util.Collections; | 71 | import java.util.Collections; |
| 70 | import java.util.HashMap; | 72 | import java.util.HashMap; |
| 71 | import java.util.HashSet; | 73 | import java.util.HashSet; |
| ... | @@ -82,6 +84,7 @@ import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_REMOVED; | ... | @@ -82,6 +84,7 @@ import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_REMOVED; |
| 82 | import static org.onlab.onos.cluster.ControllerNode.State.ACTIVE; | 84 | import static org.onlab.onos.cluster.ControllerNode.State.ACTIVE; |
| 83 | import static org.onlab.onos.net.DeviceId.deviceId; | 85 | import static org.onlab.onos.net.DeviceId.deviceId; |
| 84 | import static org.onlab.onos.net.HostId.hostId; | 86 | import static org.onlab.onos.net.HostId.hostId; |
| 87 | +import static org.onlab.onos.net.LinkKey.linkKey; | ||
| 85 | import static org.onlab.onos.net.PortNumber.P0; | 88 | import static org.onlab.onos.net.PortNumber.P0; |
| 86 | import static org.onlab.onos.net.PortNumber.portNumber; | 89 | import static org.onlab.onos.net.PortNumber.portNumber; |
| 87 | import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; | 90 | import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED; |
| ... | @@ -110,8 +113,6 @@ public abstract class TopologyViewMessages { | ... | @@ -110,8 +113,6 @@ public abstract class TopologyViewMessages { |
| 110 | private static final String KB_UNIT = "KB"; | 113 | private static final String KB_UNIT = "KB"; |
| 111 | private static final String B_UNIT = "B"; | 114 | private static final String B_UNIT = "B"; |
| 112 | 115 | ||
| 113 | - private static final String ANIMATED = "animated"; | ||
| 114 | - | ||
| 115 | protected final ServiceDirectory directory; | 116 | protected final ServiceDirectory directory; |
| 116 | protected final ClusterService clusterService; | 117 | protected final ClusterService clusterService; |
| 117 | protected final DeviceService deviceService; | 118 | protected final DeviceService deviceService; |
| ... | @@ -560,14 +561,51 @@ public abstract class TopologyViewMessages { | ... | @@ -560,14 +561,51 @@ public abstract class TopologyViewMessages { |
| 560 | ObjectNode payload = mapper.createObjectNode(); | 561 | ObjectNode payload = mapper.createObjectNode(); |
| 561 | ArrayNode paths = mapper.createArrayNode(); | 562 | ArrayNode paths = mapper.createArrayNode(); |
| 562 | payload.set("paths", paths); | 563 | payload.set("paths", paths); |
| 563 | - for (Link link : linkService.getLinks()) { | 564 | + |
| 564 | - Set<Link> links = new HashSet<>(); | 565 | + ObjectNode pathNodeN = mapper.createObjectNode(); |
| 565 | - links.add(link); | 566 | + ArrayNode linksNodeN = mapper.createArrayNode(); |
| 566 | - addPathTraffic(paths, "plain", "secondary", links); | 567 | + ArrayNode labelsN = mapper.createArrayNode(); |
| 568 | + | ||
| 569 | + pathNodeN.put("class", "plain").put("traffic", false); | ||
| 570 | + pathNodeN.set("links", linksNodeN); | ||
| 571 | + pathNodeN.set("labels", labelsN); | ||
| 572 | + paths.add(pathNodeN); | ||
| 573 | + | ||
| 574 | + ObjectNode pathNodeT = mapper.createObjectNode(); | ||
| 575 | + ArrayNode linksNodeT = mapper.createArrayNode(); | ||
| 576 | + ArrayNode labelsT = mapper.createArrayNode(); | ||
| 577 | + | ||
| 578 | + pathNodeT.put("class", "secondary").put("traffic", true); | ||
| 579 | + pathNodeT.set("links", linksNodeT); | ||
| 580 | + pathNodeT.set("labels", labelsT); | ||
| 581 | + paths.add(pathNodeT); | ||
| 582 | + | ||
| 583 | + for (BiLink link : consolidateLinks(linkService.getLinks())) { | ||
| 584 | + boolean bi = link.two != null; | ||
| 585 | + if (isInfrastructureEgress(link.one) || | ||
| 586 | + (bi && isInfrastructureEgress(link.two))) { | ||
| 587 | + link.addLoad(statService.load(link.one)); | ||
| 588 | + link.addLoad(bi ? statService.load(link.two) : null); | ||
| 589 | + if (link.hasTraffic) { | ||
| 590 | + linksNodeT.add(compactLinkString(link.one)); | ||
| 591 | + labelsT.add(formatBytes(link.bytes)); | ||
| 592 | + } else { | ||
| 593 | + linksNodeN.add(compactLinkString(link.one)); | ||
| 594 | + labelsN.add(""); | ||
| 595 | + } | ||
| 596 | + } | ||
| 567 | } | 597 | } |
| 568 | return envelope("showTraffic", sid, payload); | 598 | return envelope("showTraffic", sid, payload); |
| 569 | } | 599 | } |
| 570 | 600 | ||
| 601 | + private Collection<BiLink> consolidateLinks(Iterable<Link> links) { | ||
| 602 | + Map<LinkKey, BiLink> biLinks = new HashMap<>(); | ||
| 603 | + for (Link link : links) { | ||
| 604 | + addLink(biLinks, link); | ||
| 605 | + } | ||
| 606 | + return biLinks.values(); | ||
| 607 | + } | ||
| 608 | + | ||
| 571 | // Produces JSON message to trigger flow overview visualization | 609 | // Produces JSON message to trigger flow overview visualization |
| 572 | protected ObjectNode flowSummaryMessage(long sid, Set<Device> devices) { | 610 | protected ObjectNode flowSummaryMessage(long sid, Set<Device> devices) { |
| 573 | ObjectNode payload = mapper.createObjectNode(); | 611 | ObjectNode payload = mapper.createObjectNode(); |
| ... | @@ -603,6 +641,33 @@ public abstract class TopologyViewMessages { | ... | @@ -603,6 +641,33 @@ public abstract class TopologyViewMessages { |
| 603 | ArrayNode paths = mapper.createArrayNode(); | 641 | ArrayNode paths = mapper.createArrayNode(); |
| 604 | payload.set("paths", paths); | 642 | payload.set("paths", paths); |
| 605 | 643 | ||
| 644 | + // Classify links based on their traffic traffic first... | ||
| 645 | + Map<LinkKey, BiLink> biLinks = classifyLinkTraffic(trafficClasses); | ||
| 646 | + | ||
| 647 | + // Then separate the links into their respective classes and send them out. | ||
| 648 | + Map<String, ObjectNode> pathNodes = new HashMap<>(); | ||
| 649 | + for (BiLink biLink : biLinks.values()) { | ||
| 650 | + boolean hasTraffic = biLink.hasTraffic; | ||
| 651 | + String tc = (biLink.classes + (hasTraffic ? " animated" : "")).trim(); | ||
| 652 | + ObjectNode pathNode = pathNodes.get(tc); | ||
| 653 | + if (pathNode == null) { | ||
| 654 | + pathNode = mapper.createObjectNode() | ||
| 655 | + .put("class", tc).put("traffic", hasTraffic); | ||
| 656 | + pathNode.set("links", mapper.createArrayNode()); | ||
| 657 | + pathNode.set("labels", mapper.createArrayNode()); | ||
| 658 | + pathNodes.put(tc, pathNode); | ||
| 659 | + paths.add(pathNode); | ||
| 660 | + } | ||
| 661 | + ((ArrayNode) pathNode.path("links")).add(compactLinkString(biLink.one)); | ||
| 662 | + ((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : ""); | ||
| 663 | + } | ||
| 664 | + | ||
| 665 | + return envelope("showTraffic", sid, payload); | ||
| 666 | + } | ||
| 667 | + | ||
| 668 | + // Classifies the link traffic according to the specified classes. | ||
| 669 | + private Map<LinkKey, BiLink> classifyLinkTraffic(TrafficClass... trafficClasses) { | ||
| 670 | + Map<LinkKey, BiLink> biLinks = new HashMap<>(); | ||
| 606 | for (TrafficClass trafficClass : trafficClasses) { | 671 | for (TrafficClass trafficClass : trafficClasses) { |
| 607 | for (Intent intent : trafficClass.intents) { | 672 | for (Intent intent : trafficClass.intents) { |
| 608 | boolean isOptical = intent instanceof OpticalConnectivityIntent; | 673 | boolean isOptical = intent instanceof OpticalConnectivityIntent; |
| ... | @@ -611,24 +676,49 @@ public abstract class TopologyViewMessages { | ... | @@ -611,24 +676,49 @@ public abstract class TopologyViewMessages { |
| 611 | for (Intent installable : installables) { | 676 | for (Intent installable : installables) { |
| 612 | String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type; | 677 | String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type; |
| 613 | if (installable instanceof PathIntent) { | 678 | if (installable instanceof PathIntent) { |
| 614 | - addPathTraffic(paths, cls, ANIMATED, | 679 | + classifyLinks(cls, biLinks, ((PathIntent) installable).path().links()); |
| 615 | - ((PathIntent) installable).path().links()); | ||
| 616 | } else if (installable instanceof LinkCollectionIntent) { | 680 | } else if (installable instanceof LinkCollectionIntent) { |
| 617 | - addPathTraffic(paths, cls, ANIMATED, | 681 | + classifyLinks(cls, biLinks, ((LinkCollectionIntent) installable).links()); |
| 618 | - ((LinkCollectionIntent) installable).links()); | ||
| 619 | } else if (installable instanceof OpticalPathIntent) { | 682 | } else if (installable instanceof OpticalPathIntent) { |
| 620 | - addPathTraffic(paths, cls, ANIMATED, | 683 | + classifyLinks(cls, biLinks, ((OpticalPathIntent) installable).path().links()); |
| 621 | - ((OpticalPathIntent) installable).path().links()); | ||
| 622 | } | 684 | } |
| 623 | - | ||
| 624 | } | 685 | } |
| 625 | } | 686 | } |
| 626 | } | 687 | } |
| 627 | } | 688 | } |
| 689 | + return biLinks; | ||
| 690 | + } | ||
| 628 | 691 | ||
| 629 | - return envelope("showTraffic", sid, payload); | 692 | + |
| 693 | + // Adds the link segments (path or tree) associated with the specified | ||
| 694 | + // connectivity intent | ||
| 695 | + private void classifyLinks(String type, Map<LinkKey, BiLink> biLinks, | ||
| 696 | + Iterable<Link> links) { | ||
| 697 | + if (links != null) { | ||
| 698 | + for (Link link : links) { | ||
| 699 | + BiLink biLink = addLink(biLinks, link); | ||
| 700 | + if (isInfrastructureEgress(link)) { | ||
| 701 | + biLink.addLoad(statService.load(link)); | ||
| 702 | + biLink.addClass(type); | ||
| 703 | + } | ||
| 704 | + } | ||
| 705 | + } | ||
| 706 | + } | ||
| 707 | + | ||
| 708 | + | ||
| 709 | + private BiLink addLink(Map<LinkKey, BiLink> biLinks, Link link) { | ||
| 710 | + LinkKey key = canonicalLinkKey(link); | ||
| 711 | + BiLink biLink = biLinks.get(key); | ||
| 712 | + if (biLink != null) { | ||
| 713 | + biLink.setOther(link); | ||
| 714 | + } else { | ||
| 715 | + biLink = new BiLink(key, link); | ||
| 716 | + biLinks.put(key, biLink); | ||
| 717 | + } | ||
| 718 | + return biLink; | ||
| 630 | } | 719 | } |
| 631 | 720 | ||
| 721 | + | ||
| 632 | // Adds the link segments (path or tree) associated with the specified | 722 | // Adds the link segments (path or tree) associated with the specified |
| 633 | // connectivity intent | 723 | // connectivity intent |
| 634 | protected void addPathTraffic(ArrayNode paths, String type, String trafficType, | 724 | protected void addPathTraffic(ArrayNode paths, String type, String trafficType, |
| ... | @@ -646,7 +736,7 @@ public abstract class TopologyViewMessages { | ... | @@ -646,7 +736,7 @@ public abstract class TopologyViewMessages { |
| 646 | String label = ""; | 736 | String label = ""; |
| 647 | if (load.rate() > 0) { | 737 | if (load.rate() > 0) { |
| 648 | hasTraffic = true; | 738 | hasTraffic = true; |
| 649 | - label = format(load); | 739 | + label = formatBytes(load.latest()); |
| 650 | } | 740 | } |
| 651 | labels.add(label); | 741 | labels.add(label); |
| 652 | } | 742 | } |
| ... | @@ -660,8 +750,7 @@ public abstract class TopologyViewMessages { | ... | @@ -660,8 +750,7 @@ public abstract class TopologyViewMessages { |
| 660 | } | 750 | } |
| 661 | 751 | ||
| 662 | // Poor-mans formatting to get the labels with byte counts looking nice. | 752 | // Poor-mans formatting to get the labels with byte counts looking nice. |
| 663 | - private String format(Load load) { | 753 | + private String formatBytes(long bytes) { |
| 664 | - long bytes = load.latest(); | ||
| 665 | String unit; | 754 | String unit; |
| 666 | double value; | 755 | double value; |
| 667 | if (bytes > GB) { | 756 | if (bytes > GB) { |
| ... | @@ -713,6 +802,44 @@ public abstract class TopologyViewMessages { | ... | @@ -713,6 +802,44 @@ public abstract class TopologyViewMessages { |
| 713 | return result; | 802 | return result; |
| 714 | } | 803 | } |
| 715 | 804 | ||
| 805 | + // Produces canonical link key, i.e. one that will match link and its inverse. | ||
| 806 | + private LinkKey canonicalLinkKey(Link link) { | ||
| 807 | + String sn = link.src().elementId().toString(); | ||
| 808 | + String dn = link.dst().elementId().toString(); | ||
| 809 | + return sn.compareTo(dn) < 0 ? | ||
| 810 | + linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src()); | ||
| 811 | + } | ||
| 812 | + | ||
| 813 | + // Representation of link and its inverse and any traffic data. | ||
| 814 | + private class BiLink { | ||
| 815 | + public final LinkKey key; | ||
| 816 | + public final Link one; | ||
| 817 | + public Link two; | ||
| 818 | + public boolean hasTraffic = false; | ||
| 819 | + public long bytes = 0; | ||
| 820 | + public String classes = ""; | ||
| 821 | + | ||
| 822 | + BiLink(LinkKey key, Link link) { | ||
| 823 | + this.key = key; | ||
| 824 | + this.one = link; | ||
| 825 | + } | ||
| 826 | + | ||
| 827 | + void setOther(Link link) { | ||
| 828 | + this.two = link; | ||
| 829 | + } | ||
| 830 | + | ||
| 831 | + void addLoad(Load load) { | ||
| 832 | + if (load != null) { | ||
| 833 | + this.hasTraffic = hasTraffic || load.rate() > 0; | ||
| 834 | + this.bytes += load.latest(); | ||
| 835 | + } | ||
| 836 | + } | ||
| 837 | + | ||
| 838 | + void addClass(String trafficClass) { | ||
| 839 | + classes = classes + " " + trafficClass; | ||
| 840 | + } | ||
| 841 | + } | ||
| 842 | + | ||
| 716 | // Auxiliary key/value carrier. | 843 | // Auxiliary key/value carrier. |
| 717 | private class Prop { | 844 | private class Prop { |
| 718 | public final String key; | 845 | public final String key; | ... | ... |
| ... | @@ -558,7 +558,7 @@ public class TopologyViewWebSocket | ... | @@ -558,7 +558,7 @@ public class TopologyViewWebSocket |
| 558 | } | 558 | } |
| 559 | } | 559 | } |
| 560 | 560 | ||
| 561 | - // Accummulates events to drive methodic update of the summary pane. | 561 | + // Accumulates events to drive methodic update of the summary pane. |
| 562 | private class InternalEventAccummulator extends AbstractEventAccumulator { | 562 | private class InternalEventAccummulator extends AbstractEventAccumulator { |
| 563 | protected InternalEventAccummulator() { | 563 | protected InternalEventAccummulator() { |
| 564 | super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS); | 564 | super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS); | ... | ... |
-
Please register or login to post a comment