Thomas Vachuska

Enhancing the GUI traffic-related code.

Fixed a defect in reactive forwarding.

Change-Id: I1a91f6e5f57b39425ef06092c82b06d04c9b59a0
......@@ -132,8 +132,8 @@ public class ReactiveForwarding {
InboundPacket pkt = context.inPacket();
Ethernet ethPkt = pkt.parsed();
// Bail if this is deemed to be a control packet.
if (isControlPacket(ethPkt)) {
// Bail if this is deemed to be a control or IPv6 multicast packet.
if (isControlPacket(ethPkt) || isIpv6Multicast(ethPkt)) {
return;
}
......@@ -194,6 +194,11 @@ public class ReactiveForwarding {
return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
}
// Indicated whether this is an IPv6 multicast packet.
private boolean isIpv6Multicast(Ethernet eth) {
return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
}
// Selects a path from the given set that does not lead back to the
// specified port.
private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
......
/*
* Copyright 2014 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.onos.net.intent.constraint;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.intent.Constraint;
import org.onlab.onos.net.resource.LinkResourceService;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
/**
* Constraint that serves as a request for asymmetric bi-directional path.
*/
public class AsymmetricPathConstraint implements Constraint {
@Override
public double cost(Link link, LinkResourceService resourceService) {
return 1;
}
@Override
public boolean validate(Path path, LinkResourceService resourceService) {
return true;
}
@Override
public int hashCode() {
return Objects.hashCode(true);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return true;
}
@Override
public String toString() {
return toStringHelper(this).toString();
}
}
......@@ -20,15 +20,20 @@ import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.net.DefaultLink;
import org.onlab.onos.net.DefaultPath;
import org.onlab.onos.net.Host;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.Path;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.host.HostService;
import org.onlab.onos.net.intent.HostToHostIntent;
import org.onlab.onos.net.intent.Intent;
import org.onlab.onos.net.intent.PathIntent;
import org.onlab.onos.net.intent.constraint.AsymmetricPathConstraint;
import org.onlab.onos.net.resource.LinkResourceAllocations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
......@@ -58,8 +63,10 @@ public class HostToHostIntentCompiler
@Override
public List<Intent> compile(HostToHostIntent intent, List<Intent> installable,
Set<LinkResourceAllocations> resources) {
boolean isAsymmetric = intent.constraints().contains(new AsymmetricPathConstraint());
Path pathOne = getPath(intent, intent.one(), intent.two());
Path pathTwo = getPath(intent, intent.two(), intent.one());
Path pathTwo = isAsymmetric ?
getPath(intent, intent.two(), intent.one()) : invertPath(pathOne);
Host one = hostService.getHost(intent.one());
Host two = hostService.getHost(intent.two());
......@@ -68,6 +75,23 @@ public class HostToHostIntentCompiler
createPathIntent(pathTwo, two, one, intent));
}
// Inverts the specified path. This makes an assumption that each link in
// the path has a reverse link available. Under most circumstances, this
// assumption will hold.
private Path invertPath(Path path) {
List<Link> reverseLinks = new ArrayList<>(path.links().size());
for (Link link : path.links()) {
reverseLinks.add(0, reverseLink(link));
}
return new DefaultPath(path.providerId(), reverseLinks, path.cost());
}
// Produces a reverse variant of the specified link.
private Link reverseLink(Link link) {
return new DefaultLink(link.providerId(), link.dst(), link.src(),
link.type(), link.state(), link.isDurable());
}
// Creates a path intent from the specified path and original connectivity intent.
private Intent createPathIntent(Path path, Host src, Host dst,
HostToHostIntent intent) {
......
......@@ -31,14 +31,16 @@ import java.util.Map;
*/
public class Ethernet extends BasePacket {
private static final String HEXES = "0123456789ABCDEF";
public static final short TYPE_ARP = 0x0806;
public static final short TYPE_ARP = (short) 0x0806;
public static final short TYPE_RARP = (short) 0x8035;
public static final short TYPE_IPV4 = 0x0800;
public static final short TYPE_IPV4 = (short) 0x0800;
public static final short TYPE_IPV6 = (short) 0x86dd;
public static final short TYPE_LLDP = (short) 0x88cc;
public static final short TYPE_BSN = (short) 0x8942;
public static final short VLAN_UNTAGGED = (short) 0xffff;
public static final short MPLS_UNICAST = (short) 0x8847;
public static final short MPLS_MULTICAST = (short) 0x8848;
public static final short DATALAYER_ADDRESS_LENGTH = 6; // bytes
public static final Map<Short, Class<? extends IPacket>> ETHER_TYPE_CLASS_MAP =
new HashMap<>();
......
......@@ -36,6 +36,7 @@ import org.onlab.onos.net.Host;
import org.onlab.onos.net.HostId;
import org.onlab.onos.net.HostLocation;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.LinkKey;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.device.DeviceEvent;
import org.onlab.onos.net.device.DeviceService;
......@@ -66,6 +67,7 @@ import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
......@@ -82,6 +84,7 @@ import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_REMOVED;
import static org.onlab.onos.cluster.ControllerNode.State.ACTIVE;
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.HostId.hostId;
import static org.onlab.onos.net.LinkKey.linkKey;
import static org.onlab.onos.net.PortNumber.P0;
import static org.onlab.onos.net.PortNumber.portNumber;
import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_ADDED;
......@@ -110,8 +113,6 @@ public abstract class TopologyViewMessages {
private static final String KB_UNIT = "KB";
private static final String B_UNIT = "B";
private static final String ANIMATED = "animated";
protected final ServiceDirectory directory;
protected final ClusterService clusterService;
protected final DeviceService deviceService;
......@@ -560,14 +561,51 @@ public abstract class TopologyViewMessages {
ObjectNode payload = mapper.createObjectNode();
ArrayNode paths = mapper.createArrayNode();
payload.set("paths", paths);
for (Link link : linkService.getLinks()) {
Set<Link> links = new HashSet<>();
links.add(link);
addPathTraffic(paths, "plain", "secondary", links);
ObjectNode pathNodeN = mapper.createObjectNode();
ArrayNode linksNodeN = mapper.createArrayNode();
ArrayNode labelsN = mapper.createArrayNode();
pathNodeN.put("class", "plain").put("traffic", false);
pathNodeN.set("links", linksNodeN);
pathNodeN.set("labels", labelsN);
paths.add(pathNodeN);
ObjectNode pathNodeT = mapper.createObjectNode();
ArrayNode linksNodeT = mapper.createArrayNode();
ArrayNode labelsT = mapper.createArrayNode();
pathNodeT.put("class", "secondary").put("traffic", true);
pathNodeT.set("links", linksNodeT);
pathNodeT.set("labels", labelsT);
paths.add(pathNodeT);
for (BiLink link : consolidateLinks(linkService.getLinks())) {
boolean bi = link.two != null;
if (isInfrastructureEgress(link.one) ||
(bi && isInfrastructureEgress(link.two))) {
link.addLoad(statService.load(link.one));
link.addLoad(bi ? statService.load(link.two) : null);
if (link.hasTraffic) {
linksNodeT.add(compactLinkString(link.one));
labelsT.add(formatBytes(link.bytes));
} else {
linksNodeN.add(compactLinkString(link.one));
labelsN.add("");
}
}
}
return envelope("showTraffic", sid, payload);
}
private Collection<BiLink> consolidateLinks(Iterable<Link> links) {
Map<LinkKey, BiLink> biLinks = new HashMap<>();
for (Link link : links) {
addLink(biLinks, link);
}
return biLinks.values();
}
// Produces JSON message to trigger flow overview visualization
protected ObjectNode flowSummaryMessage(long sid, Set<Device> devices) {
ObjectNode payload = mapper.createObjectNode();
......@@ -603,6 +641,33 @@ public abstract class TopologyViewMessages {
ArrayNode paths = mapper.createArrayNode();
payload.set("paths", paths);
// Classify links based on their traffic traffic first...
Map<LinkKey, BiLink> biLinks = classifyLinkTraffic(trafficClasses);
// Then separate the links into their respective classes and send them out.
Map<String, ObjectNode> pathNodes = new HashMap<>();
for (BiLink biLink : biLinks.values()) {
boolean hasTraffic = biLink.hasTraffic;
String tc = (biLink.classes + (hasTraffic ? " animated" : "")).trim();
ObjectNode pathNode = pathNodes.get(tc);
if (pathNode == null) {
pathNode = mapper.createObjectNode()
.put("class", tc).put("traffic", hasTraffic);
pathNode.set("links", mapper.createArrayNode());
pathNode.set("labels", mapper.createArrayNode());
pathNodes.put(tc, pathNode);
paths.add(pathNode);
}
((ArrayNode) pathNode.path("links")).add(compactLinkString(biLink.one));
((ArrayNode) pathNode.path("labels")).add(hasTraffic ? formatBytes(biLink.bytes) : "");
}
return envelope("showTraffic", sid, payload);
}
// Classifies the link traffic according to the specified classes.
private Map<LinkKey, BiLink> classifyLinkTraffic(TrafficClass... trafficClasses) {
Map<LinkKey, BiLink> biLinks = new HashMap<>();
for (TrafficClass trafficClass : trafficClasses) {
for (Intent intent : trafficClass.intents) {
boolean isOptical = intent instanceof OpticalConnectivityIntent;
......@@ -611,24 +676,49 @@ public abstract class TopologyViewMessages {
for (Intent installable : installables) {
String cls = isOptical ? trafficClass.type + " optical" : trafficClass.type;
if (installable instanceof PathIntent) {
addPathTraffic(paths, cls, ANIMATED,
((PathIntent) installable).path().links());
classifyLinks(cls, biLinks, ((PathIntent) installable).path().links());
} else if (installable instanceof LinkCollectionIntent) {
addPathTraffic(paths, cls, ANIMATED,
((LinkCollectionIntent) installable).links());
classifyLinks(cls, biLinks, ((LinkCollectionIntent) installable).links());
} else if (installable instanceof OpticalPathIntent) {
addPathTraffic(paths, cls, ANIMATED,
((OpticalPathIntent) installable).path().links());
classifyLinks(cls, biLinks, ((OpticalPathIntent) installable).path().links());
}
}
}
}
}
return biLinks;
}
return envelope("showTraffic", sid, payload);
// Adds the link segments (path or tree) associated with the specified
// connectivity intent
private void classifyLinks(String type, Map<LinkKey, BiLink> biLinks,
Iterable<Link> links) {
if (links != null) {
for (Link link : links) {
BiLink biLink = addLink(biLinks, link);
if (isInfrastructureEgress(link)) {
biLink.addLoad(statService.load(link));
biLink.addClass(type);
}
}
}
}
private BiLink addLink(Map<LinkKey, BiLink> biLinks, Link link) {
LinkKey key = canonicalLinkKey(link);
BiLink biLink = biLinks.get(key);
if (biLink != null) {
biLink.setOther(link);
} else {
biLink = new BiLink(key, link);
biLinks.put(key, biLink);
}
return biLink;
}
// Adds the link segments (path or tree) associated with the specified
// connectivity intent
protected void addPathTraffic(ArrayNode paths, String type, String trafficType,
......@@ -646,7 +736,7 @@ public abstract class TopologyViewMessages {
String label = "";
if (load.rate() > 0) {
hasTraffic = true;
label = format(load);
label = formatBytes(load.latest());
}
labels.add(label);
}
......@@ -660,8 +750,7 @@ public abstract class TopologyViewMessages {
}
// Poor-mans formatting to get the labels with byte counts looking nice.
private String format(Load load) {
long bytes = load.latest();
private String formatBytes(long bytes) {
String unit;
double value;
if (bytes > GB) {
......@@ -713,6 +802,44 @@ public abstract class TopologyViewMessages {
return result;
}
// Produces canonical link key, i.e. one that will match link and its inverse.
private LinkKey canonicalLinkKey(Link link) {
String sn = link.src().elementId().toString();
String dn = link.dst().elementId().toString();
return sn.compareTo(dn) < 0 ?
linkKey(link.src(), link.dst()) : linkKey(link.dst(), link.src());
}
// Representation of link and its inverse and any traffic data.
private class BiLink {
public final LinkKey key;
public final Link one;
public Link two;
public boolean hasTraffic = false;
public long bytes = 0;
public String classes = "";
BiLink(LinkKey key, Link link) {
this.key = key;
this.one = link;
}
void setOther(Link link) {
this.two = link;
}
void addLoad(Load load) {
if (load != null) {
this.hasTraffic = hasTraffic || load.rate() > 0;
this.bytes += load.latest();
}
}
void addClass(String trafficClass) {
classes = classes + " " + trafficClass;
}
}
// Auxiliary key/value carrier.
private class Prop {
public final String key;
......
......@@ -558,7 +558,7 @@ public class TopologyViewWebSocket
}
}
// Accummulates events to drive methodic update of the summary pane.
// Accumulates events to drive methodic update of the summary pane.
private class InternalEventAccummulator extends AbstractEventAccumulator {
protected InternalEventAccummulator() {
super(new Timer("topo-summary"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
......