ONOS-6259: Topo2 - Implement server-side highlighting model
- (Still WIP)
- refactored trafficSummary() and related methods up into superclass
- introduced hook method for link aggregation
Change-Id: I0a993bbc5ba5f0e861214f8d06877dad3f7bc8ee
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
index 660ba72..1d5127d 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
@@ -19,7 +19,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
-import org.onosproject.incubator.net.PortStatisticsService.MetricType;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.ElementId;
@@ -41,7 +40,6 @@
import org.onosproject.net.intent.OpticalPathIntent;
import org.onosproject.net.intent.PathIntent;
import org.onosproject.net.link.LinkService;
-import org.onosproject.net.statistic.Load;
import org.onosproject.ui.impl.topo.util.IntentSelection;
import org.onosproject.ui.impl.topo.util.ServicesBundle;
import org.onosproject.ui.impl.topo.util.TopoIntentFilter;
@@ -68,8 +66,6 @@
import java.util.Set;
import java.util.stream.Collectors;
-import static org.onosproject.incubator.net.PortStatisticsService.MetricType.BYTES;
-import static org.onosproject.incubator.net.PortStatisticsService.MetricType.PACKETS;
import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.RELATED_INTENTS;
import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.SELECTED_INTENT;
@@ -107,7 +103,7 @@
// =======================================================================
// === API ===
- // monitor(Mode) is implemented in the super class
+ // monitor(Mode) is now implemented in the super class
/**
* Monitor for traffic data to be sent back to the web client, under
@@ -273,29 +269,8 @@
// =======================================================================
// === Generate messages in JSON object node format
- private Highlights trafficSummary(StatsType type) {
- Highlights highlights = new Highlights();
-
- TrafficLinkMap linkMap = new TrafficLinkMap();
- compileLinks(linkMap);
- addEdgeLinks(linkMap);
-
- for (TrafficLink tlink : linkMap.biLinks()) {
- if (type == StatsType.FLOW_STATS) {
- attachFlowLoad(tlink);
- } else if (type == StatsType.PORT_STATS) {
- attachPortLoad(tlink, BYTES);
- } else if (type == StatsType.PORT_PACKET_STATS) {
- attachPortLoad(tlink, PACKETS);
- }
-
- // we only want to report on links deemed to have traffic
- if (tlink.hasTraffic()) {
- highlights.add(tlink.highlight(type));
- }
- }
- return highlights;
- }
+ // NOTE: trafficSummary(StatsType) => Highlights
+ // has been moved to the superclass
// create highlights for links, showing flows for selected devices.
private Highlights deviceLinkFlows() {
@@ -374,49 +349,6 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- private void compileLinks(TrafficLinkMap linkMap) {
- services.link().getLinks().forEach(linkMap::add);
- }
-
- private void addEdgeLinks(TrafficLinkMap linkMap) {
- services.host().getHosts().forEach(host -> {
- linkMap.add(createEdgeLink(host, true));
- linkMap.add(createEdgeLink(host, false));
- });
- }
-
- private Load getLinkFlowLoad(Link link) {
- if (link != null && link.src().elementId() instanceof DeviceId) {
- return services.flowStats().load(link);
- }
- return null;
- }
-
- private void attachFlowLoad(TrafficLink link) {
- link.addLoad(getLinkFlowLoad(link.one()));
- link.addLoad(getLinkFlowLoad(link.two()));
- }
-
- private void attachPortLoad(TrafficLink link, MetricType metricType) {
- // For bi-directional traffic links, use
- // the max link rate of either direction
- // (we choose 'one' since we know that is never null)
- Link one = link.one();
- Load egressSrc = services.portStats().load(one.src(), metricType);
- Load egressDst = services.portStats().load(one.dst(), metricType);
- link.addLoad(maxLoad(egressSrc, egressDst), metricType == BYTES ? BPS_THRESHOLD : 0);
- }
-
- private Load maxLoad(Load a, Load b) {
- if (a == null) {
- return b;
- }
- if (b == null) {
- return a;
- }
- return a.rate() > b.rate() ? a : b;
- }
-
// Counts all flow entries that egress on the links of the given device.
private Map<Link, Integer> getLinkFlowCounts(DeviceId deviceId) {
// get the flows for the device
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitorBase.java b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitorBase.java
index dfedaf3..c59f2c5 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitorBase.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitorBase.java
@@ -17,15 +17,27 @@
package org.onosproject.ui.impl;
+import org.onosproject.incubator.net.PortStatisticsService.MetricType;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.statistic.Load;
import org.onosproject.ui.impl.topo.util.ServicesBundle;
+import org.onosproject.ui.impl.topo.util.TrafficLink;
+import org.onosproject.ui.impl.topo.util.TrafficLinkMap;
import org.onosproject.ui.topo.AbstractTopoMonitor;
+import org.onosproject.ui.topo.Highlights;
import org.onosproject.ui.topo.TopoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.HashSet;
+import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
+import static org.onosproject.incubator.net.PortStatisticsService.MetricType.BYTES;
+import static org.onosproject.incubator.net.PortStatisticsService.MetricType.PACKETS;
+import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.IDLE;
/**
@@ -36,7 +48,7 @@
private final Logger log = LoggerFactory.getLogger(getClass());
// 4 Kilo Bytes as threshold
- static final double BPS_THRESHOLD = 4 * TopoUtils.N_KILO;
+ protected static final double BPS_THRESHOLD = 4 * TopoUtils.N_KILO;
/**
@@ -221,6 +233,163 @@
// =======================================================================
+ // === Methods for computing traffic on links
+
+ /**
+ * Generates a {@link Highlights} object summarizing the traffic on the
+ * network, ready to be transmitted back to the client for display on
+ * the topology view.
+ *
+ * @param type the type of statistics to be displayed
+ * @return highlights, representing links to be labeled/colored
+ */
+ protected Highlights trafficSummary(TrafficLink.StatsType type) {
+ Highlights highlights = new Highlights();
+
+ // TODO: consider whether a map would be better...
+ Set<TrafficLink> linksWithTraffic = computeLinksWithTraffic(type);
+
+ Set<TrafficLink> aggregatedLinks = doAggregation(linksWithTraffic);
+
+ for (TrafficLink tlink : aggregatedLinks) {
+ highlights.add(tlink.highlight(type));
+ }
+ return highlights;
+ }
+
+ /**
+ * Generates a set of "traffic links" encapsulating information about the
+ * traffic on each link (that is deemed to have traffic).
+ *
+ * @param type the type of statistics to be displayed
+ * @return the set of links with traffic
+ */
+ protected Set<TrafficLink> computeLinksWithTraffic(TrafficLink.StatsType type) {
+ TrafficLinkMap linkMap = new TrafficLinkMap();
+ compileLinks(linkMap);
+ addEdgeLinks(linkMap);
+
+ Set<TrafficLink> linksWithTraffic = new HashSet<>();
+ // TODO: consider whether a map would be better...
+
+ for (TrafficLink tlink : linkMap.biLinks()) {
+ if (type == TrafficLink.StatsType.FLOW_STATS) {
+ attachFlowLoad(tlink);
+ } else if (type == TrafficLink.StatsType.PORT_STATS) {
+ attachPortLoad(tlink, BYTES);
+ } else if (type == TrafficLink.StatsType.PORT_PACKET_STATS) {
+ attachPortLoad(tlink, PACKETS);
+ }
+
+ // we only want to report on links deemed to have traffic
+ if (tlink.hasTraffic()) {
+ linksWithTraffic.add(tlink);
+ }
+ }
+ return linksWithTraffic;
+ }
+
+ /**
+ * Iterates across the set of links in the topology and generates the
+ * appropriate set of traffic links.
+ *
+ * @param linkMap link map to augment with traffic links
+ */
+ protected void compileLinks(TrafficLinkMap linkMap) {
+ services.link().getLinks().forEach(linkMap::add);
+ }
+
+ /**
+ * Iterates across the set of hosts in the topology and generates the
+ * appropriate set of traffic links for the edge links.
+ *
+ * @param linkMap link map to augment with traffic links
+ */
+ protected void addEdgeLinks(TrafficLinkMap linkMap) {
+ services.host().getHosts().forEach(host -> {
+ linkMap.add(createEdgeLink(host, true));
+ linkMap.add(createEdgeLink(host, false));
+ });
+ }
+
+ /**
+ * Processes the given traffic link to attach the "flow load" attributed
+ * to the underlying topology links.
+ *
+ * @param link the traffic link to process
+ */
+ protected void attachFlowLoad(TrafficLink link) {
+ link.addLoad(getLinkFlowLoad(link.one()));
+ link.addLoad(getLinkFlowLoad(link.two()));
+ }
+
+ /**
+ * Returns the load for the given link, as determined by the statistics
+ * service. May return null.
+ *
+ * @param link the link on which to look up the stats
+ * @return the corresponding load (or null)
+ */
+ protected Load getLinkFlowLoad(Link link) {
+ if (link != null && link.src().elementId() instanceof DeviceId) {
+ return services.flowStats().load(link);
+ }
+ return null;
+ }
+
+ /**
+ * Processes the given traffic link to attach the "port load" attributed
+ * to the underlying topology links, for the specified metric type (either
+ * bytes/sec or packets/sec).
+ *
+ * @param link the traffic link to process
+ * @param metricType the metric type (bytes or packets)
+ */
+ protected void attachPortLoad(TrafficLink link, MetricType metricType) {
+ // For bi-directional traffic links, use
+ // the max link rate of either direction
+ // (we choose 'one' since we know that is never null)
+ Link one = link.one();
+ Load egressSrc = services.portStats().load(one.src(), metricType);
+ Load egressDst = services.portStats().load(one.dst(), metricType);
+ link.addLoad(maxLoad(egressSrc, egressDst), metricType == BYTES ? BPS_THRESHOLD : 0);
+ }
+
+ /**
+ * Returns the load with the greatest rate.
+ *
+ * @param a load a
+ * @param b load b
+ * @return the larger of the two
+ */
+ protected Load maxLoad(Load a, Load b) {
+ if (a == null) {
+ return b;
+ }
+ if (b == null) {
+ return a;
+ }
+ return a.rate() > b.rate() ? a : b;
+ }
+
+
+ /**
+ * Subclasses (well, Traffic2Monitor really) can override this method and
+ * process the traffic links before generating the highlights object.
+ * In particular, links that roll up into "synthetic links" between
+ * regions should show aggregated data from the constituent links.
+ * <p>
+ * This default implementation does nothing.
+ *
+ * @param linksWithTraffic link data for all links
+ * @return transformed link data appropriate to the region display
+ */
+ protected Set<TrafficLink> doAggregation(Set<TrafficLink> linksWithTraffic) {
+ return linksWithTraffic;
+ }
+
+
+ // =======================================================================
// === Background Task
// Provides periodic update of traffic information to the client
@@ -258,5 +427,4 @@
}
}
}
-
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
index b30ff11..e577f45 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
@@ -40,11 +40,10 @@
The original topology view message handler was broken into two classes
TopologyViewMessageHandler, and TopologyViewMessageHandlerBase.
- We do not need to follow that model necessarily. Starting with a
- single class, and breaking it apart later if necessary.
- Need to figure out the connection between this message handler and the
- new way of doing things with UiTopoSession...
+ We do not need to follow that model necessarily. Instead, we have this
+ class and Topo2Jsonifier, which takes UiModel objects and renders them
+ as JSON objects.
*/
@@ -201,12 +200,12 @@
@Override
public void process(ObjectNode payload) {
// client view has gone away; so shut down server-side processing
- // TODO: implement...
log.debug("topo2Stop: {}", payload);
+ // TODO: tell traffic monitor to stop monitoring...
+ // this requires this handler to know about traffic handler!!
// OLD CODE DID THE FOLLOWING...
-// removeListeners();
// stopSummaryMonitoring();
// traffic.stopMonitoring();
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Monitor.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Monitor.java
index cb91e0d..0653832 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Monitor.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Monitor.java
@@ -19,9 +19,13 @@
import org.onosproject.ui.impl.TrafficMonitorBase;
import org.onosproject.ui.impl.topo.util.ServicesBundle;
+import org.onosproject.ui.impl.topo.util.TrafficLink;
+import org.onosproject.ui.topo.Highlights;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Set;
+
/**
* Encapsulates the behavior of monitoring specific traffic patterns in the
* Topology-2 view.
@@ -31,6 +35,7 @@
private static final Logger log =
LoggerFactory.getLogger(Traffic2Monitor.class);
+ // link back to our message handler (for outbound messages)
private final Topo2TrafficMessageHandler msgHandler;
/**
@@ -49,24 +54,32 @@
@Override
protected void sendAllFlowTraffic() {
log.debug("TOPO-2-TRAFFIC: sendAllFlowTraffic");
+ Highlights h = trafficSummary(TrafficLink.StatsType.FLOW_STATS);
+
// TODO
}
@Override
protected void sendAllPortTrafficBits() {
log.debug("TOPO-2-TRAFFIC: sendAllPortTrafficBits");
+ Highlights h = trafficSummary(TrafficLink.StatsType.PORT_STATS);
+
// TODO
}
@Override
protected void sendAllPortTrafficPackets() {
log.debug("TOPO-2-TRAFFIC: sendAllPortTrafficPackets");
+ Highlights h = trafficSummary(TrafficLink.StatsType.PORT_PACKET_STATS);
+
// TODO
}
@Override
protected void sendClearHighlights() {
log.debug("TOPO-2-TRAFFIC: sendClearHighlights");
+ Highlights h = new Highlights();
+
// TODO
}
@@ -83,4 +96,16 @@
@Override
protected void clearSelection() {
}
+
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // -- link aggregation
+
+
+ @Override
+ protected Set<TrafficLink> doAggregation(Set<TrafficLink> linksWithTraffic) {
+ // TODO: figure out how to aggregate the link data...
+ log.debug("Need to aggregate {} links", linksWithTraffic.size());
+
+ return linksWithTraffic;
+ }
}