Thomas Vachuska

Enhancing the GUI traffic-related code.

Fixed a defect in reactive forwarding.

Change-Id: I1a91f6e5f57b39425ef06092c82b06d04c9b59a0
...@@ -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) {
......
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);
......