ONOS-293 Added summary pane and related keyboard shortcuts; also tweaked key help sizes and dropped instances toggle from mast. Fixed ONOS-295 bug.
Change-Id: I694901957451cf88df06e6fca3a8d71de144f68e
diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java
index 6c12400..91a820c 100644
--- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java
+++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewMessages.java
@@ -23,6 +23,7 @@
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.core.CoreService;
import org.onlab.onos.mastership.MastershipService;
import org.onlab.onos.net.Annotated;
import org.onlab.onos.net.Annotations;
@@ -56,6 +57,8 @@
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.net.statistic.Load;
import org.onlab.onos.net.statistic.StatisticService;
+import org.onlab.onos.net.topology.Topology;
+import org.onlab.onos.net.topology.TopologyService;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.packet.IpAddress;
import org.slf4j.Logger;
@@ -117,8 +120,10 @@
protected final IntentService intentService;
protected final FlowRuleService flowService;
protected final StatisticService statService;
+ protected final TopologyService topologyService;
protected final ObjectMapper mapper = new ObjectMapper();
+ private final String version;
// TODO: extract into an external & durable state; good enough for now and demo
private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>();
@@ -138,6 +143,9 @@
intentService = directory.get(IntentService.class);
flowService = directory.get(FlowRuleService.class);
statService = directory.get(StatisticService.class);
+ topologyService = directory.get(TopologyService.class);
+
+ version = directory.get(CoreService.class).version().toString();
}
// Retrieves the payload from the specified event.
@@ -419,6 +427,22 @@
metaUi.put(string(payload, "id"), (ObjectNode) payload.path("memento"));
}
+ // Returns summary response.
+ protected ObjectNode summmaryMessage(long sid) {
+ Topology topology = topologyService.currentTopology();
+ return envelope("showSummary", sid,
+ json("ONOS Summary", "node",
+ new Prop("Devices", format(topology.deviceCount())),
+ new Prop("Links", format(topology.linkCount())),
+ new Prop("Hosts", format(hostService.getHostCount())),
+ new Prop("Topology SCCs", format(topology.clusterCount())),
+ new Prop("Paths", format(topology.pathCount())),
+ new Separator(),
+ new Prop("Intents", format(intentService.getIntentCount())),
+ new Prop("Flows", format(flowService.getFlowRuleCount())),
+ new Prop("Version", version.replace(".SNAPSHOT", "*"))));
+ }
+
// Returns device details response.
protected ObjectNode deviceDetails(DeviceId deviceId, long sid) {
Device device = deviceService.getDevice(deviceId);
@@ -435,12 +459,12 @@
new Prop("S/W Version", device.swVersion()),
new Prop("Serial Number", device.serialNumber()),
new Separator(),
+ new Prop("Master", master(deviceId)),
new Prop("Latitude", annot.value("latitude")),
new Prop("Longitude", annot.value("longitude")),
- new Prop("Ports", Integer.toString(portCount)),
- new Prop("Flows", Integer.toString(flowCount)),
new Separator(),
- new Prop("Master", master(deviceId))));
+ new Prop("Ports", Integer.toString(portCount)),
+ new Prop("Flows", Integer.toString(flowCount))));
}
protected int getFlowCount(DeviceId deviceId) {
@@ -641,6 +665,12 @@
return format.format(value) + " " + unit;
}
+ // Formats the given number into a string.
+ private String format(Number number) {
+ DecimalFormat format = new DecimalFormat("#,###");
+ return format.format(number);
+ }
+
private boolean isInfrastructureEgress(Link link) {
return link.src().elementId() instanceof DeviceId;
}
diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewWebSocket.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewWebSocket.java
index 104fa95..9562040 100644
--- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewWebSocket.java
+++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyViewWebSocket.java
@@ -42,7 +42,11 @@
import org.onlab.osgi.ServiceDirectory;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
@@ -70,8 +74,17 @@
private static final String APP_ID = "org.onlab.onos.gui";
+ private static final long SUMMARY_FREQUENCY_SEC = 2000;
private static final long TRAFFIC_FREQUENCY_SEC = 1000;
+ private static final Comparator<? super ControllerNode> NODE_COMPARATOR =
+ new Comparator<ControllerNode>() {
+ @Override
+ public int compare(ControllerNode o1, ControllerNode o2) {
+ return o1.id().toString().compareTo(o2.id().toString());
+ }
+ };
+
private final ApplicationId appId;
private Connection connection;
@@ -83,10 +96,14 @@
private final HostListener hostListener = new InternalHostListener();
private final IntentListener intentListener = new InternalIntentListener();
- // Intents that are being monitored for the GUI
- private ObjectNode monitorRequest;
- private final Timer timer = new Timer("intent-traffic-monitor");
- private final TimerTask timerTask = new IntentTrafficMonitor();
+ // Timers and objects being monitored
+ private final Timer timer = new Timer("topology-view");
+
+ private TimerTask trafficTask;
+ private ObjectNode trafficEvent;
+
+ private TimerTask summaryTask;
+ private ObjectNode summaryEvent;
private long lastActive = System.currentTimeMillis();
private boolean listenersRemoved = false;
@@ -140,7 +157,6 @@
this.connection = connection;
this.control = (FrameConnection) connection;
addListeners();
- timer.schedule(timerTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC);
sendAllInstances();
sendAllDevices();
@@ -181,6 +197,7 @@
updateMetaUi(event);
} else if (type.equals("addHostIntent")) {
createHostIntent(event);
+
} else if (type.equals("requestTraffic")) {
requestTraffic(event);
} else if (type.equals("requestAllTraffic")) {
@@ -189,6 +206,11 @@
requestDeviceLinkFlows(event);
} else if (type.equals("cancelTraffic")) {
cancelTraffic(event);
+
+ } else if (type.equals("requestSummary")) {
+ requestSummary(event);
+ } else if (type.equals("cancelSummary")) {
+ cancelSummary(event);
}
}
@@ -205,7 +227,9 @@
// Sends all controller nodes to the client as node-added messages.
private void sendAllInstances() {
- for (ControllerNode node : clusterService.getNodes()) {
+ List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes());
+ Collections.sort(nodes, NODE_COMPARATOR);
+ for (ControllerNode node : nodes) {
sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node)));
}
}
@@ -255,22 +279,37 @@
HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two,
DefaultTrafficSelector.builder().build(),
DefaultTrafficTreatment.builder().build());
- monitorRequest = event;
+ trafficEvent = event;
intentService.submit(hostIntent);
}
+ private synchronized long startMonitoring(ObjectNode event) {
+ if (trafficTask == null) {
+ trafficEvent = event;
+ trafficTask = new TrafficMonitor();
+ timer.schedule(trafficTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC);
+ }
+ return number(event, "sid");
+ }
+
+ private synchronized void stopMonitoring() {
+ if (trafficTask != null) {
+ trafficTask.cancel();
+ trafficTask = null;
+ trafficEvent = null;
+ }
+ }
+
// Subscribes for host traffic messages.
private synchronized void requestAllTraffic(ObjectNode event) {
ObjectNode payload = payload(event);
- long sid = number(event, "sid");
- monitorRequest = event;
+ long sid = startMonitoring(event);
sendMessage(trafficSummaryMessage(sid));
}
private void requestDeviceLinkFlows(ObjectNode event) {
ObjectNode payload = payload(event);
- long sid = number(event, "sid");
- monitorRequest = event;
+ long sid = startMonitoring(event);
// Get the set of selected hosts and their intents.
ArrayNode ids = (ArrayNode) payload.path("ids");
@@ -294,8 +333,7 @@
return;
}
- long sid = number(event, "sid");
- monitorRequest = event;
+ long sid = startMonitoring(event);
// Get the set of selected hosts and their intents.
ArrayNode ids = (ArrayNode) payload.path("ids");
@@ -325,9 +363,30 @@
// Cancels sending traffic messages.
private void cancelTraffic(ObjectNode event) {
sendMessage(trafficMessage(number(event, "sid")));
- monitorRequest = null;
+ stopMonitoring();
}
+
+ // Subscribes for summary messages.
+ private synchronized void requestSummary(ObjectNode event) {
+ if (summaryTask == null) {
+ summaryEvent = event;
+ summaryTask = new SummaryMonitor();
+ timer.schedule(summaryTask, SUMMARY_FREQUENCY_SEC, SUMMARY_FREQUENCY_SEC);
+ }
+ sendMessage(summmaryMessage(number(event, "sid")));
+ }
+
+ // Cancels sending summary messages.
+ private synchronized void cancelSummary(ObjectNode event) {
+ if (summaryTask != null) {
+ summaryTask.cancel();
+ summaryTask = null;
+ summaryEvent = null;
+ }
+ }
+
+
// Adds all internal listeners.
private void addListeners() {
clusterService.addListener(clusterListener);
@@ -385,26 +444,36 @@
private class InternalIntentListener implements IntentListener {
@Override
public void event(IntentEvent event) {
- if (monitorRequest != null) {
- requestTraffic(monitorRequest);
+ if (trafficEvent != null) {
+ requestTraffic(trafficEvent);
}
}
}
- private class IntentTrafficMonitor extends TimerTask {
+ private class TrafficMonitor extends TimerTask {
@Override
public void run() {
- if (monitorRequest != null) {
- String type = string(monitorRequest, "event", "unknown");
+ if (trafficEvent != null) {
+ String type = string(trafficEvent, "event", "unknown");
if (type.equals("requestAllTraffic")) {
- requestAllTraffic(monitorRequest);
+ requestAllTraffic(trafficEvent);
} else if (type.equals("requestDeviceLinkFlows")) {
- requestDeviceLinkFlows(monitorRequest);
+ requestDeviceLinkFlows(trafficEvent);
} else {
- requestTraffic(monitorRequest);
+ requestTraffic(trafficEvent);
}
}
}
}
+
+ private class SummaryMonitor extends TimerTask {
+ @Override
+ public void run() {
+ if (summaryEvent != null) {
+ requestSummary(summaryEvent);
+ }
+ }
+ }
+
}