ONOS-293 Added summary pane and related keyboard shortcuts; also tweaked key hel…
…p sizes and dropped instances toggle from mast. Fixed ONOS-295 bug. Change-Id: I694901957451cf88df06e6fca3a8d71de144f68e
Showing
12 changed files
with
324 additions
and
70 deletions
... | @@ -43,10 +43,10 @@ public class SummaryCommand extends AbstractShellCommand { | ... | @@ -43,10 +43,10 @@ public class SummaryCommand extends AbstractShellCommand { |
43 | .put("node", get(ClusterService.class).getLocalNode().ip().toString()) | 43 | .put("node", get(ClusterService.class).getLocalNode().ip().toString()) |
44 | .put("version", get(CoreService.class).version().toString()) | 44 | .put("version", get(CoreService.class).version().toString()) |
45 | .put("nodes", get(ClusterService.class).getNodes().size()) | 45 | .put("nodes", get(ClusterService.class).getNodes().size()) |
46 | - .put("devices", get(DeviceService.class).getDeviceCount()) | 46 | + .put("devices", topology.deviceCount()) |
47 | - .put("links", get(LinkService.class).getLinkCount()) | 47 | + .put("links", topology.linkCount()) |
48 | .put("hosts", get(HostService.class).getHostCount()) | 48 | .put("hosts", get(HostService.class).getHostCount()) |
49 | - .put("clusters", topologyService.getClusters(topology).size()) | 49 | + .put("clusters", topology.clusterCount()) |
50 | .put("paths", topology.pathCount()) | 50 | .put("paths", topology.pathCount()) |
51 | .put("flows", get(FlowRuleService.class).getFlowRuleCount()) | 51 | .put("flows", get(FlowRuleService.class).getFlowRuleCount()) |
52 | .put("intents", get(IntentService.class).getIntentCount())); | 52 | .put("intents", get(IntentService.class).getIntentCount())); | ... | ... |
... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
4 | # ----------------------------------------------------------------------------- | 4 | # ----------------------------------------------------------------------------- |
5 | 5 | ||
6 | #export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} | 6 | #export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} |
7 | -export JAVA_OPTS="${JAVA_OPTS:--Xms256M -Xmx2048M}" | 7 | +export JAVA_OPTS="${JAVA_OPTS:--Xms256m -Xmx2048m}" |
8 | 8 | ||
9 | cd /opt/onos | 9 | cd /opt/onos |
10 | /opt/onos/apache-karaf-$KARAF_VERSION/bin/karaf "$@" | 10 | /opt/onos/apache-karaf-$KARAF_VERSION/bin/karaf "$@" | ... | ... |
... | @@ -23,6 +23,7 @@ import org.onlab.onos.cluster.ClusterEvent; | ... | @@ -23,6 +23,7 @@ import org.onlab.onos.cluster.ClusterEvent; |
23 | import org.onlab.onos.cluster.ClusterService; | 23 | import org.onlab.onos.cluster.ClusterService; |
24 | import org.onlab.onos.cluster.ControllerNode; | 24 | import org.onlab.onos.cluster.ControllerNode; |
25 | import org.onlab.onos.cluster.NodeId; | 25 | import org.onlab.onos.cluster.NodeId; |
26 | +import org.onlab.onos.core.CoreService; | ||
26 | import org.onlab.onos.mastership.MastershipService; | 27 | import org.onlab.onos.mastership.MastershipService; |
27 | import org.onlab.onos.net.Annotated; | 28 | import org.onlab.onos.net.Annotated; |
28 | import org.onlab.onos.net.Annotations; | 29 | import org.onlab.onos.net.Annotations; |
... | @@ -56,6 +57,8 @@ import org.onlab.onos.net.link.LinkService; | ... | @@ -56,6 +57,8 @@ import org.onlab.onos.net.link.LinkService; |
56 | import org.onlab.onos.net.provider.ProviderId; | 57 | import org.onlab.onos.net.provider.ProviderId; |
57 | import org.onlab.onos.net.statistic.Load; | 58 | import org.onlab.onos.net.statistic.Load; |
58 | import org.onlab.onos.net.statistic.StatisticService; | 59 | import org.onlab.onos.net.statistic.StatisticService; |
60 | +import org.onlab.onos.net.topology.Topology; | ||
61 | +import org.onlab.onos.net.topology.TopologyService; | ||
59 | import org.onlab.osgi.ServiceDirectory; | 62 | import org.onlab.osgi.ServiceDirectory; |
60 | import org.onlab.packet.IpAddress; | 63 | import org.onlab.packet.IpAddress; |
61 | import org.slf4j.Logger; | 64 | import org.slf4j.Logger; |
... | @@ -117,8 +120,10 @@ public abstract class TopologyViewMessages { | ... | @@ -117,8 +120,10 @@ public abstract class TopologyViewMessages { |
117 | protected final IntentService intentService; | 120 | protected final IntentService intentService; |
118 | protected final FlowRuleService flowService; | 121 | protected final FlowRuleService flowService; |
119 | protected final StatisticService statService; | 122 | protected final StatisticService statService; |
123 | + protected final TopologyService topologyService; | ||
120 | 124 | ||
121 | protected final ObjectMapper mapper = new ObjectMapper(); | 125 | protected final ObjectMapper mapper = new ObjectMapper(); |
126 | + private final String version; | ||
122 | 127 | ||
123 | // TODO: extract into an external & durable state; good enough for now and demo | 128 | // TODO: extract into an external & durable state; good enough for now and demo |
124 | private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>(); | 129 | private static Map<String, ObjectNode> metaUi = new ConcurrentHashMap<>(); |
... | @@ -138,6 +143,9 @@ public abstract class TopologyViewMessages { | ... | @@ -138,6 +143,9 @@ public abstract class TopologyViewMessages { |
138 | intentService = directory.get(IntentService.class); | 143 | intentService = directory.get(IntentService.class); |
139 | flowService = directory.get(FlowRuleService.class); | 144 | flowService = directory.get(FlowRuleService.class); |
140 | statService = directory.get(StatisticService.class); | 145 | statService = directory.get(StatisticService.class); |
146 | + topologyService = directory.get(TopologyService.class); | ||
147 | + | ||
148 | + version = directory.get(CoreService.class).version().toString(); | ||
141 | } | 149 | } |
142 | 150 | ||
143 | // Retrieves the payload from the specified event. | 151 | // Retrieves the payload from the specified event. |
... | @@ -419,6 +427,22 @@ public abstract class TopologyViewMessages { | ... | @@ -419,6 +427,22 @@ public abstract class TopologyViewMessages { |
419 | metaUi.put(string(payload, "id"), (ObjectNode) payload.path("memento")); | 427 | metaUi.put(string(payload, "id"), (ObjectNode) payload.path("memento")); |
420 | } | 428 | } |
421 | 429 | ||
430 | + // Returns summary response. | ||
431 | + protected ObjectNode summmaryMessage(long sid) { | ||
432 | + Topology topology = topologyService.currentTopology(); | ||
433 | + return envelope("showSummary", sid, | ||
434 | + json("ONOS Summary", "node", | ||
435 | + new Prop("Devices", format(topology.deviceCount())), | ||
436 | + new Prop("Links", format(topology.linkCount())), | ||
437 | + new Prop("Hosts", format(hostService.getHostCount())), | ||
438 | + new Prop("Topology SCCs", format(topology.clusterCount())), | ||
439 | + new Prop("Paths", format(topology.pathCount())), | ||
440 | + new Separator(), | ||
441 | + new Prop("Intents", format(intentService.getIntentCount())), | ||
442 | + new Prop("Flows", format(flowService.getFlowRuleCount())), | ||
443 | + new Prop("Version", version.replace(".SNAPSHOT", "*")))); | ||
444 | + } | ||
445 | + | ||
422 | // Returns device details response. | 446 | // Returns device details response. |
423 | protected ObjectNode deviceDetails(DeviceId deviceId, long sid) { | 447 | protected ObjectNode deviceDetails(DeviceId deviceId, long sid) { |
424 | Device device = deviceService.getDevice(deviceId); | 448 | Device device = deviceService.getDevice(deviceId); |
... | @@ -435,12 +459,12 @@ public abstract class TopologyViewMessages { | ... | @@ -435,12 +459,12 @@ public abstract class TopologyViewMessages { |
435 | new Prop("S/W Version", device.swVersion()), | 459 | new Prop("S/W Version", device.swVersion()), |
436 | new Prop("Serial Number", device.serialNumber()), | 460 | new Prop("Serial Number", device.serialNumber()), |
437 | new Separator(), | 461 | new Separator(), |
462 | + new Prop("Master", master(deviceId)), | ||
438 | new Prop("Latitude", annot.value("latitude")), | 463 | new Prop("Latitude", annot.value("latitude")), |
439 | new Prop("Longitude", annot.value("longitude")), | 464 | new Prop("Longitude", annot.value("longitude")), |
440 | - new Prop("Ports", Integer.toString(portCount)), | ||
441 | - new Prop("Flows", Integer.toString(flowCount)), | ||
442 | new Separator(), | 465 | new Separator(), |
443 | - new Prop("Master", master(deviceId)))); | 466 | + new Prop("Ports", Integer.toString(portCount)), |
467 | + new Prop("Flows", Integer.toString(flowCount)))); | ||
444 | } | 468 | } |
445 | 469 | ||
446 | protected int getFlowCount(DeviceId deviceId) { | 470 | protected int getFlowCount(DeviceId deviceId) { |
... | @@ -641,6 +665,12 @@ public abstract class TopologyViewMessages { | ... | @@ -641,6 +665,12 @@ public abstract class TopologyViewMessages { |
641 | return format.format(value) + " " + unit; | 665 | return format.format(value) + " " + unit; |
642 | } | 666 | } |
643 | 667 | ||
668 | + // Formats the given number into a string. | ||
669 | + private String format(Number number) { | ||
670 | + DecimalFormat format = new DecimalFormat("#,###"); | ||
671 | + return format.format(number); | ||
672 | + } | ||
673 | + | ||
644 | private boolean isInfrastructureEgress(Link link) { | 674 | private boolean isInfrastructureEgress(Link link) { |
645 | return link.src().elementId() instanceof DeviceId; | 675 | return link.src().elementId() instanceof DeviceId; |
646 | } | 676 | } | ... | ... |
... | @@ -42,7 +42,11 @@ import org.onlab.onos.net.link.LinkListener; | ... | @@ -42,7 +42,11 @@ import org.onlab.onos.net.link.LinkListener; |
42 | import org.onlab.osgi.ServiceDirectory; | 42 | import org.onlab.osgi.ServiceDirectory; |
43 | 43 | ||
44 | import java.io.IOException; | 44 | import java.io.IOException; |
45 | +import java.util.ArrayList; | ||
46 | +import java.util.Collections; | ||
47 | +import java.util.Comparator; | ||
45 | import java.util.HashSet; | 48 | import java.util.HashSet; |
49 | +import java.util.List; | ||
46 | import java.util.Set; | 50 | import java.util.Set; |
47 | import java.util.Timer; | 51 | import java.util.Timer; |
48 | import java.util.TimerTask; | 52 | import java.util.TimerTask; |
... | @@ -70,8 +74,17 @@ public class TopologyViewWebSocket | ... | @@ -70,8 +74,17 @@ public class TopologyViewWebSocket |
70 | 74 | ||
71 | private static final String APP_ID = "org.onlab.onos.gui"; | 75 | private static final String APP_ID = "org.onlab.onos.gui"; |
72 | 76 | ||
77 | + private static final long SUMMARY_FREQUENCY_SEC = 2000; | ||
73 | private static final long TRAFFIC_FREQUENCY_SEC = 1000; | 78 | private static final long TRAFFIC_FREQUENCY_SEC = 1000; |
74 | 79 | ||
80 | + private static final Comparator<? super ControllerNode> NODE_COMPARATOR = | ||
81 | + new Comparator<ControllerNode>() { | ||
82 | + @Override | ||
83 | + public int compare(ControllerNode o1, ControllerNode o2) { | ||
84 | + return o1.id().toString().compareTo(o2.id().toString()); | ||
85 | + } | ||
86 | + }; | ||
87 | + | ||
75 | private final ApplicationId appId; | 88 | private final ApplicationId appId; |
76 | 89 | ||
77 | private Connection connection; | 90 | private Connection connection; |
... | @@ -83,10 +96,14 @@ public class TopologyViewWebSocket | ... | @@ -83,10 +96,14 @@ public class TopologyViewWebSocket |
83 | private final HostListener hostListener = new InternalHostListener(); | 96 | private final HostListener hostListener = new InternalHostListener(); |
84 | private final IntentListener intentListener = new InternalIntentListener(); | 97 | private final IntentListener intentListener = new InternalIntentListener(); |
85 | 98 | ||
86 | - // Intents that are being monitored for the GUI | 99 | + // Timers and objects being monitored |
87 | - private ObjectNode monitorRequest; | 100 | + private final Timer timer = new Timer("topology-view"); |
88 | - private final Timer timer = new Timer("intent-traffic-monitor"); | 101 | + |
89 | - private final TimerTask timerTask = new IntentTrafficMonitor(); | 102 | + private TimerTask trafficTask; |
103 | + private ObjectNode trafficEvent; | ||
104 | + | ||
105 | + private TimerTask summaryTask; | ||
106 | + private ObjectNode summaryEvent; | ||
90 | 107 | ||
91 | private long lastActive = System.currentTimeMillis(); | 108 | private long lastActive = System.currentTimeMillis(); |
92 | private boolean listenersRemoved = false; | 109 | private boolean listenersRemoved = false; |
... | @@ -140,7 +157,6 @@ public class TopologyViewWebSocket | ... | @@ -140,7 +157,6 @@ public class TopologyViewWebSocket |
140 | this.connection = connection; | 157 | this.connection = connection; |
141 | this.control = (FrameConnection) connection; | 158 | this.control = (FrameConnection) connection; |
142 | addListeners(); | 159 | addListeners(); |
143 | - timer.schedule(timerTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC); | ||
144 | 160 | ||
145 | sendAllInstances(); | 161 | sendAllInstances(); |
146 | sendAllDevices(); | 162 | sendAllDevices(); |
... | @@ -181,6 +197,7 @@ public class TopologyViewWebSocket | ... | @@ -181,6 +197,7 @@ public class TopologyViewWebSocket |
181 | updateMetaUi(event); | 197 | updateMetaUi(event); |
182 | } else if (type.equals("addHostIntent")) { | 198 | } else if (type.equals("addHostIntent")) { |
183 | createHostIntent(event); | 199 | createHostIntent(event); |
200 | + | ||
184 | } else if (type.equals("requestTraffic")) { | 201 | } else if (type.equals("requestTraffic")) { |
185 | requestTraffic(event); | 202 | requestTraffic(event); |
186 | } else if (type.equals("requestAllTraffic")) { | 203 | } else if (type.equals("requestAllTraffic")) { |
... | @@ -189,6 +206,11 @@ public class TopologyViewWebSocket | ... | @@ -189,6 +206,11 @@ public class TopologyViewWebSocket |
189 | requestDeviceLinkFlows(event); | 206 | requestDeviceLinkFlows(event); |
190 | } else if (type.equals("cancelTraffic")) { | 207 | } else if (type.equals("cancelTraffic")) { |
191 | cancelTraffic(event); | 208 | cancelTraffic(event); |
209 | + | ||
210 | + } else if (type.equals("requestSummary")) { | ||
211 | + requestSummary(event); | ||
212 | + } else if (type.equals("cancelSummary")) { | ||
213 | + cancelSummary(event); | ||
192 | } | 214 | } |
193 | } | 215 | } |
194 | 216 | ||
... | @@ -205,7 +227,9 @@ public class TopologyViewWebSocket | ... | @@ -205,7 +227,9 @@ public class TopologyViewWebSocket |
205 | 227 | ||
206 | // Sends all controller nodes to the client as node-added messages. | 228 | // Sends all controller nodes to the client as node-added messages. |
207 | private void sendAllInstances() { | 229 | private void sendAllInstances() { |
208 | - for (ControllerNode node : clusterService.getNodes()) { | 230 | + List<ControllerNode> nodes = new ArrayList<>(clusterService.getNodes()); |
231 | + Collections.sort(nodes, NODE_COMPARATOR); | ||
232 | + for (ControllerNode node : nodes) { | ||
209 | sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node))); | 233 | sendMessage(instanceMessage(new ClusterEvent(INSTANCE_ADDED, node))); |
210 | } | 234 | } |
211 | } | 235 | } |
... | @@ -255,22 +279,37 @@ public class TopologyViewWebSocket | ... | @@ -255,22 +279,37 @@ public class TopologyViewWebSocket |
255 | HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two, | 279 | HostToHostIntent hostIntent = new HostToHostIntent(appId, one, two, |
256 | DefaultTrafficSelector.builder().build(), | 280 | DefaultTrafficSelector.builder().build(), |
257 | DefaultTrafficTreatment.builder().build()); | 281 | DefaultTrafficTreatment.builder().build()); |
258 | - monitorRequest = event; | 282 | + trafficEvent = event; |
259 | intentService.submit(hostIntent); | 283 | intentService.submit(hostIntent); |
260 | } | 284 | } |
261 | 285 | ||
286 | + private synchronized long startMonitoring(ObjectNode event) { | ||
287 | + if (trafficTask == null) { | ||
288 | + trafficEvent = event; | ||
289 | + trafficTask = new TrafficMonitor(); | ||
290 | + timer.schedule(trafficTask, TRAFFIC_FREQUENCY_SEC, TRAFFIC_FREQUENCY_SEC); | ||
291 | + } | ||
292 | + return number(event, "sid"); | ||
293 | + } | ||
294 | + | ||
295 | + private synchronized void stopMonitoring() { | ||
296 | + if (trafficTask != null) { | ||
297 | + trafficTask.cancel(); | ||
298 | + trafficTask = null; | ||
299 | + trafficEvent = null; | ||
300 | + } | ||
301 | + } | ||
302 | + | ||
262 | // Subscribes for host traffic messages. | 303 | // Subscribes for host traffic messages. |
263 | private synchronized void requestAllTraffic(ObjectNode event) { | 304 | private synchronized void requestAllTraffic(ObjectNode event) { |
264 | ObjectNode payload = payload(event); | 305 | ObjectNode payload = payload(event); |
265 | - long sid = number(event, "sid"); | 306 | + long sid = startMonitoring(event); |
266 | - monitorRequest = event; | ||
267 | sendMessage(trafficSummaryMessage(sid)); | 307 | sendMessage(trafficSummaryMessage(sid)); |
268 | } | 308 | } |
269 | 309 | ||
270 | private void requestDeviceLinkFlows(ObjectNode event) { | 310 | private void requestDeviceLinkFlows(ObjectNode event) { |
271 | ObjectNode payload = payload(event); | 311 | ObjectNode payload = payload(event); |
272 | - long sid = number(event, "sid"); | 312 | + long sid = startMonitoring(event); |
273 | - monitorRequest = event; | ||
274 | 313 | ||
275 | // Get the set of selected hosts and their intents. | 314 | // Get the set of selected hosts and their intents. |
276 | ArrayNode ids = (ArrayNode) payload.path("ids"); | 315 | ArrayNode ids = (ArrayNode) payload.path("ids"); |
... | @@ -294,8 +333,7 @@ public class TopologyViewWebSocket | ... | @@ -294,8 +333,7 @@ public class TopologyViewWebSocket |
294 | return; | 333 | return; |
295 | } | 334 | } |
296 | 335 | ||
297 | - long sid = number(event, "sid"); | 336 | + long sid = startMonitoring(event); |
298 | - monitorRequest = event; | ||
299 | 337 | ||
300 | // Get the set of selected hosts and their intents. | 338 | // Get the set of selected hosts and their intents. |
301 | ArrayNode ids = (ArrayNode) payload.path("ids"); | 339 | ArrayNode ids = (ArrayNode) payload.path("ids"); |
... | @@ -325,9 +363,30 @@ public class TopologyViewWebSocket | ... | @@ -325,9 +363,30 @@ public class TopologyViewWebSocket |
325 | // Cancels sending traffic messages. | 363 | // Cancels sending traffic messages. |
326 | private void cancelTraffic(ObjectNode event) { | 364 | private void cancelTraffic(ObjectNode event) { |
327 | sendMessage(trafficMessage(number(event, "sid"))); | 365 | sendMessage(trafficMessage(number(event, "sid"))); |
328 | - monitorRequest = null; | 366 | + stopMonitoring(); |
329 | } | 367 | } |
330 | 368 | ||
369 | + | ||
370 | + // Subscribes for summary messages. | ||
371 | + private synchronized void requestSummary(ObjectNode event) { | ||
372 | + if (summaryTask == null) { | ||
373 | + summaryEvent = event; | ||
374 | + summaryTask = new SummaryMonitor(); | ||
375 | + timer.schedule(summaryTask, SUMMARY_FREQUENCY_SEC, SUMMARY_FREQUENCY_SEC); | ||
376 | + } | ||
377 | + sendMessage(summmaryMessage(number(event, "sid"))); | ||
378 | + } | ||
379 | + | ||
380 | + // Cancels sending summary messages. | ||
381 | + private synchronized void cancelSummary(ObjectNode event) { | ||
382 | + if (summaryTask != null) { | ||
383 | + summaryTask.cancel(); | ||
384 | + summaryTask = null; | ||
385 | + summaryEvent = null; | ||
386 | + } | ||
387 | + } | ||
388 | + | ||
389 | + | ||
331 | // Adds all internal listeners. | 390 | // Adds all internal listeners. |
332 | private void addListeners() { | 391 | private void addListeners() { |
333 | clusterService.addListener(clusterListener); | 392 | clusterService.addListener(clusterListener); |
... | @@ -385,26 +444,36 @@ public class TopologyViewWebSocket | ... | @@ -385,26 +444,36 @@ public class TopologyViewWebSocket |
385 | private class InternalIntentListener implements IntentListener { | 444 | private class InternalIntentListener implements IntentListener { |
386 | @Override | 445 | @Override |
387 | public void event(IntentEvent event) { | 446 | public void event(IntentEvent event) { |
388 | - if (monitorRequest != null) { | 447 | + if (trafficEvent != null) { |
389 | - requestTraffic(monitorRequest); | 448 | + requestTraffic(trafficEvent); |
390 | } | 449 | } |
391 | } | 450 | } |
392 | } | 451 | } |
393 | 452 | ||
394 | - private class IntentTrafficMonitor extends TimerTask { | 453 | + private class TrafficMonitor extends TimerTask { |
395 | @Override | 454 | @Override |
396 | public void run() { | 455 | public void run() { |
397 | - if (monitorRequest != null) { | 456 | + if (trafficEvent != null) { |
398 | - String type = string(monitorRequest, "event", "unknown"); | 457 | + String type = string(trafficEvent, "event", "unknown"); |
399 | if (type.equals("requestAllTraffic")) { | 458 | if (type.equals("requestAllTraffic")) { |
400 | - requestAllTraffic(monitorRequest); | 459 | + requestAllTraffic(trafficEvent); |
401 | } else if (type.equals("requestDeviceLinkFlows")) { | 460 | } else if (type.equals("requestDeviceLinkFlows")) { |
402 | - requestDeviceLinkFlows(monitorRequest); | 461 | + requestDeviceLinkFlows(trafficEvent); |
403 | } else { | 462 | } else { |
404 | - requestTraffic(monitorRequest); | 463 | + requestTraffic(trafficEvent); |
405 | } | 464 | } |
406 | } | 465 | } |
407 | } | 466 | } |
408 | } | 467 | } |
468 | + | ||
469 | + private class SummaryMonitor extends TimerTask { | ||
470 | + @Override | ||
471 | + public void run() { | ||
472 | + if (summaryEvent != null) { | ||
473 | + requestSummary(summaryEvent); | ||
474 | + } | ||
475 | + } | ||
476 | + } | ||
477 | + | ||
409 | } | 478 | } |
410 | 479 | ... | ... |
... | @@ -23,7 +23,7 @@ | ... | @@ -23,7 +23,7 @@ |
23 | #feedback svg { | 23 | #feedback svg { |
24 | position: absolute; | 24 | position: absolute; |
25 | bottom: 0; | 25 | bottom: 0; |
26 | - opacity: 0.5; | 26 | + opacity: 0.8; |
27 | } | 27 | } |
28 | 28 | ||
29 | #feedback svg g.feedbackItem { | 29 | #feedback svg g.feedbackItem { |
... | @@ -31,14 +31,11 @@ | ... | @@ -31,14 +31,11 @@ |
31 | } | 31 | } |
32 | 32 | ||
33 | #feedback svg g.feedbackItem rect { | 33 | #feedback svg g.feedbackItem rect { |
34 | - fill: #888; | 34 | + fill: #ccc; |
35 | - stroke: #666; | ||
36 | - stroke-width: 3; | ||
37 | - opacity: 0.5 | ||
38 | } | 35 | } |
39 | 36 | ||
40 | #feedback svg g.feedbackItem text { | 37 | #feedback svg g.feedbackItem text { |
41 | - fill: #000; | 38 | + fill: #333; |
42 | stroke: none; | 39 | stroke: none; |
43 | text-anchor: middle; | 40 | text-anchor: middle; |
44 | alignment-baseline: middle; | 41 | alignment-baseline: middle; | ... | ... |
... | @@ -33,7 +33,7 @@ | ... | @@ -33,7 +33,7 @@ |
33 | var w = '100%', | 33 | var w = '100%', |
34 | h = 200, | 34 | h = 200, |
35 | fade = 200, | 35 | fade = 200, |
36 | - showFor = 500, | 36 | + showFor = 1200, |
37 | vb = '-200 -' + (h/2) + ' 400 ' + h, | 37 | vb = '-200 -' + (h/2) + ' 400 ' + h, |
38 | xpad = 20, | 38 | xpad = 20, |
39 | ypad = 10; | 39 | ypad = 10; | ... | ... |
... | @@ -24,8 +24,8 @@ | ... | @@ -24,8 +24,8 @@ |
24 | position: absolute; | 24 | position: absolute; |
25 | z-index: 100; | 25 | z-index: 100; |
26 | display: block; | 26 | display: block; |
27 | - top: 10%; | 27 | + top: 64px; |
28 | - width: 280px; | 28 | + width: 260px; |
29 | right: -300px; | 29 | right: -300px; |
30 | opacity: 0; | 30 | opacity: 0; |
31 | background-color: rgba(255,255,255,0.8); | 31 | background-color: rgba(255,255,255,0.8); | ... | ... |
... | @@ -31,10 +31,10 @@ | ... | @@ -31,10 +31,10 @@ |
31 | } | 31 | } |
32 | 32 | ||
33 | #keymap svg text.title { | 33 | #keymap svg text.title { |
34 | - font-size: 12pt; | 34 | + font-size: 10pt; |
35 | font-style: italic; | 35 | font-style: italic; |
36 | text-anchor: middle; | 36 | text-anchor: middle; |
37 | - fill: #444; | 37 | + fill: #999; |
38 | } | 38 | } |
39 | 39 | ||
40 | #keymap svg g.keyItem { | 40 | #keymap svg g.keyItem { |
... | @@ -47,17 +47,17 @@ | ... | @@ -47,17 +47,17 @@ |
47 | } | 47 | } |
48 | 48 | ||
49 | #keymap svg text { | 49 | #keymap svg text { |
50 | - font-size: 10pt; | 50 | + font-size: 7pt; |
51 | alignment-baseline: middle; | 51 | alignment-baseline: middle; |
52 | } | 52 | } |
53 | 53 | ||
54 | #keymap svg text.key { | 54 | #keymap svg text.key { |
55 | - font-size: 10pt; | 55 | + font-size: 7pt; |
56 | - fill: #8aa; | 56 | + fill: #add; |
57 | } | 57 | } |
58 | 58 | ||
59 | #keymap svg text.desc { | 59 | #keymap svg text.desc { |
60 | - font-size: 10pt; | 60 | + font-size: 7pt; |
61 | - fill: #888; | 61 | + fill: #aaa; |
62 | } | 62 | } |
63 | 63 | ... | ... |
... | @@ -35,9 +35,9 @@ | ... | @@ -35,9 +35,9 @@ |
35 | fade = 500, | 35 | fade = 500, |
36 | vb = '-220 -220 440 440', | 36 | vb = '-220 -220 440 440', |
37 | paneW = 400, | 37 | paneW = 400, |
38 | - paneH = 340, | 38 | + paneH = 280, |
39 | offy = 65, | 39 | offy = 65, |
40 | - dy = 20, | 40 | + dy = 14, |
41 | offKey = 40, | 41 | offKey = 40, |
42 | offDesc = offKey + 50, | 42 | offDesc = offKey + 50, |
43 | lineW = paneW - (2*offKey); | 43 | lineW = paneW - (2*offKey); | ... | ... |
... | @@ -763,7 +763,8 @@ | ... | @@ -763,7 +763,8 @@ |
763 | var pos = position || 'TR', | 763 | var pos = position || 'TR', |
764 | cfg = fpConfig[pos], | 764 | cfg = fpConfig[pos], |
765 | el, | 765 | el, |
766 | - fp; | 766 | + fp, |
767 | + on = false; | ||
767 | 768 | ||
768 | if (fpanels[id]) { | 769 | if (fpanels[id]) { |
769 | buildError('Float panel with id "' + id + '" already exists.'); | 770 | buildError('Float panel with id "' + id + '" already exists.'); |
... | @@ -792,15 +793,20 @@ | ... | @@ -792,15 +793,20 @@ |
792 | id: id, | 793 | id: id, |
793 | el: el, | 794 | el: el, |
794 | pos: pos, | 795 | pos: pos, |
796 | + isVisible: function () { | ||
797 | + return on; | ||
798 | + }, | ||
795 | 799 | ||
796 | show: function () { | 800 | show: function () { |
797 | console.log('show pane: ' + id); | 801 | console.log('show pane: ' + id); |
802 | + on = true; | ||
798 | el.transition().duration(750) | 803 | el.transition().duration(750) |
799 | .style(cfg.side, pxShow()) | 804 | .style(cfg.side, pxShow()) |
800 | .style('opacity', 1); | 805 | .style('opacity', 1); |
801 | }, | 806 | }, |
802 | hide: function () { | 807 | hide: function () { |
803 | console.log('hide pane: ' + id); | 808 | console.log('hide pane: ' + id); |
809 | + on = false; | ||
804 | el.transition().duration(750) | 810 | el.transition().duration(750) |
805 | .style(cfg.side, pxHide()) | 811 | .style(cfg.side, pxHide()) |
806 | .style('opacity', 0); | 812 | .style('opacity', 0); | ... | ... |
... | @@ -177,10 +177,66 @@ svg .node.host circle { | ... | @@ -177,10 +177,66 @@ svg .node.host circle { |
177 | font-size: 9pt; | 177 | font-size: 9pt; |
178 | } | 178 | } |
179 | 179 | ||
180 | +/* Fly-in summary pane */ | ||
181 | + | ||
182 | +#topo-summary { | ||
183 | + /* gets base CSS from .fpanel in floatPanel.css */ | ||
184 | + top: 64px; | ||
185 | +} | ||
186 | + | ||
187 | +#topo-summary svg { | ||
188 | + display: inline-block; | ||
189 | + width: 42px; | ||
190 | + height: 42px; | ||
191 | +} | ||
192 | + | ||
193 | +#topo-summary svg .glyphIcon { | ||
194 | + fill: black; | ||
195 | + stroke: none; | ||
196 | + fill-rule: evenodd; | ||
197 | +} | ||
198 | + | ||
199 | +#topo-summary h2 { | ||
200 | + position: absolute; | ||
201 | + margin: 0px 4px; | ||
202 | + top: 20px; | ||
203 | + left: 50px; | ||
204 | + color: black; | ||
205 | +} | ||
206 | + | ||
207 | +#topo-summary h3 { | ||
208 | + margin: 0px 4px; | ||
209 | + top: 20px; | ||
210 | + left: 50px; | ||
211 | + color: black; | ||
212 | +} | ||
213 | + | ||
214 | +#topo-summary p, table { | ||
215 | + margin: 4px 4px; | ||
216 | +} | ||
217 | + | ||
218 | +#topo-summary td.label { | ||
219 | + font-style: italic; | ||
220 | + color: #777; | ||
221 | + padding-right: 12px; | ||
222 | +} | ||
223 | + | ||
224 | +#topo-summary td.value { | ||
225 | +} | ||
226 | + | ||
227 | +#topo-summary hr { | ||
228 | + height: 1px; | ||
229 | + color: #ccc; | ||
230 | + background-color: #ccc; | ||
231 | + border: 0; | ||
232 | +} | ||
233 | + | ||
180 | /* Fly-in details pane */ | 234 | /* Fly-in details pane */ |
181 | 235 | ||
182 | #topo-detail { | 236 | #topo-detail { |
183 | -/* gets base CSS from .fpanel in floatPanel.css */ | 237 | + /* gets base CSS from .fpanel in floatPanel.css */ |
238 | + top: 320px; | ||
239 | + | ||
184 | } | 240 | } |
185 | 241 | ||
186 | #topo-detail svg { | 242 | #topo-detail svg { | ... | ... |
... | @@ -141,6 +141,8 @@ | ... | @@ -141,6 +141,8 @@ |
141 | S: injectStartupEvents, | 141 | S: injectStartupEvents, |
142 | space: injectTestEvent, | 142 | space: injectTestEvent, |
143 | 143 | ||
144 | + O: [toggleSummary, 'Toggle ONOS summary pane'], | ||
145 | + I: [toggleInstances, 'Toggle ONOS instances pane'], | ||
144 | B: [toggleBg, 'Toggle background image'], | 146 | B: [toggleBg, 'Toggle background image'], |
145 | L: [cycleLabels, 'Cycle Device labels'], | 147 | L: [cycleLabels, 'Cycle Device labels'], |
146 | P: togglePorts, | 148 | P: togglePorts, |
... | @@ -182,6 +184,7 @@ | ... | @@ -182,6 +184,7 @@ |
182 | selections = {}, | 184 | selections = {}, |
183 | selectOrder = [], | 185 | selectOrder = [], |
184 | hovered = null, | 186 | hovered = null, |
187 | + summaryPane, | ||
185 | detailPane, | 188 | detailPane, |
186 | antTimer = null, | 189 | antTimer = null, |
187 | onosInstances = {}, | 190 | onosInstances = {}, |
... | @@ -329,7 +332,7 @@ | ... | @@ -329,7 +332,7 @@ |
329 | if (hoverMode === hoverModes.length) { | 332 | if (hoverMode === hoverModes.length) { |
330 | hoverMode = 0; | 333 | hoverMode = 0; |
331 | } | 334 | } |
332 | - view.flash('Hover Mode: ' + hoverModes[hoverMode]); | 335 | + view.flash('Mode: ' + hoverModes[hoverMode]); |
333 | } | 336 | } |
334 | 337 | ||
335 | function togglePorts(view) { | 338 | function togglePorts(view) { |
... | @@ -347,8 +350,12 @@ | ... | @@ -347,8 +350,12 @@ |
347 | function handleEscape(view) { | 350 | function handleEscape(view) { |
348 | if (oiShowMaster) { | 351 | if (oiShowMaster) { |
349 | cancelAffinity(); | 352 | cancelAffinity(); |
350 | - } else { | 353 | + } else if (detailPane.isVisible()) { |
351 | deselectAll(); | 354 | deselectAll(); |
355 | + } else if (oiBox.isVisible()) { | ||
356 | + oiBox.hide(); | ||
357 | + } else if (summaryPane.isVisible()) { | ||
358 | + cancelSummary(); | ||
352 | } | 359 | } |
353 | } | 360 | } |
354 | 361 | ||
... | @@ -585,6 +592,7 @@ | ... | @@ -585,6 +592,7 @@ |
585 | removeHost: removeHost, | 592 | removeHost: removeHost, |
586 | 593 | ||
587 | showDetails: showDetails, | 594 | showDetails: showDetails, |
595 | + showSummary: showSummary, | ||
588 | showTraffic: showTraffic | 596 | showTraffic: showTraffic |
589 | }; | 597 | }; |
590 | 598 | ||
... | @@ -737,6 +745,12 @@ | ... | @@ -737,6 +745,12 @@ |
737 | } | 745 | } |
738 | } | 746 | } |
739 | 747 | ||
748 | + function showSummary(data) { | ||
749 | + evTrace(data); | ||
750 | + populateSummary(data.payload); | ||
751 | + summaryPane.show(); | ||
752 | + } | ||
753 | + | ||
740 | function showDetails(data) { | 754 | function showDetails(data) { |
741 | evTrace(data); | 755 | evTrace(data); |
742 | populateDetails(data.payload); | 756 | populateDetails(data.payload); |
... | @@ -824,6 +838,33 @@ | ... | @@ -824,6 +838,33 @@ |
824 | return true; | 838 | return true; |
825 | } | 839 | } |
826 | 840 | ||
841 | + | ||
842 | + function toggleInstances() { | ||
843 | + if (!oiBox.isVisible()) { | ||
844 | + oiBox.show(); | ||
845 | + } else { | ||
846 | + oiBox.hide(); | ||
847 | + } | ||
848 | + } | ||
849 | + | ||
850 | + function toggleSummary() { | ||
851 | + if (!summaryPane.isVisible()) { | ||
852 | + requestSummary(); | ||
853 | + } else { | ||
854 | + cancelSummary(); | ||
855 | + } | ||
856 | + } | ||
857 | + | ||
858 | + // request overall summary data | ||
859 | + function requestSummary() { | ||
860 | + sendMessage('requestSummary', {}); | ||
861 | + } | ||
862 | + | ||
863 | + function cancelSummary() { | ||
864 | + sendMessage('cancelSummary', {}); | ||
865 | + summaryPane.hide(); | ||
866 | + } | ||
867 | + | ||
827 | // request details for the selected element | 868 | // request details for the selected element |
828 | // invoked from selection of a single node. | 869 | // invoked from selection of a single node. |
829 | function requestDetails() { | 870 | function requestDetails() { |
... | @@ -845,16 +886,20 @@ | ... | @@ -845,16 +886,20 @@ |
845 | } | 886 | } |
846 | 887 | ||
847 | function showTrafficAction() { | 888 | function showTrafficAction() { |
848 | - // force intents hover mode | 889 | + cancelTraffic(); |
849 | hoverMode = 1; | 890 | hoverMode = 1; |
850 | showSelectTraffic(); | 891 | showSelectTraffic(); |
851 | network.view.flash('Related Traffic'); | 892 | network.view.flash('Related Traffic'); |
852 | } | 893 | } |
853 | 894 | ||
895 | + function cancelTraffic() { | ||
896 | + sendMessage('cancelTraffic', {}); | ||
897 | + } | ||
898 | + | ||
854 | function showSelectTraffic() { | 899 | function showSelectTraffic() { |
855 | // if nothing is hovered over, and nothing selected, send cancel request | 900 | // if nothing is hovered over, and nothing selected, send cancel request |
856 | if (!hovered && nSel() === 0) { | 901 | if (!hovered && nSel() === 0) { |
857 | - sendMessage('cancelTraffic', {}); | 902 | + cancelTraffic(); |
858 | return; | 903 | return; |
859 | } | 904 | } |
860 | 905 | ||
... | @@ -870,12 +915,13 @@ | ... | @@ -870,12 +915,13 @@ |
870 | } | 915 | } |
871 | 916 | ||
872 | function showAllTrafficAction() { | 917 | function showAllTrafficAction() { |
918 | + cancelTraffic(); | ||
873 | sendMessage('requestAllTraffic', {}); | 919 | sendMessage('requestAllTraffic', {}); |
874 | network.view.flash('All Traffic'); | 920 | network.view.flash('All Traffic'); |
875 | } | 921 | } |
876 | 922 | ||
877 | function showDeviceLinkFlowsAction() { | 923 | function showDeviceLinkFlowsAction() { |
878 | - // force intents hover mode | 924 | + cancelTraffic(); |
879 | hoverMode = 2; | 925 | hoverMode = 2; |
880 | showDeviceLinkFlows(); | 926 | showDeviceLinkFlows(); |
881 | network.view.flash('Device Flows'); | 927 | network.view.flash('Device Flows'); |
... | @@ -884,7 +930,7 @@ | ... | @@ -884,7 +930,7 @@ |
884 | function showDeviceLinkFlows() { | 930 | function showDeviceLinkFlows() { |
885 | // if nothing is hovered over, and nothing selected, send cancel request | 931 | // if nothing is hovered over, and nothing selected, send cancel request |
886 | if (!hovered && nSel() === 0) { | 932 | if (!hovered && nSel() === 0) { |
887 | - sendMessage('cancelTraffic', {}); | 933 | + cancelTraffic(); |
888 | return; | 934 | return; |
889 | } | 935 | } |
890 | var hoverId = (flowsHover() && hovered && hovered.class === 'device') ? | 936 | var hoverId = (flowsHover() && hovered && hovered.class === 'device') ? |
... | @@ -907,7 +953,6 @@ | ... | @@ -907,7 +953,6 @@ |
907 | 'xlink:href': iid, | 953 | 'xlink:href': iid, |
908 | width: dim, | 954 | width: dim, |
909 | height: dim | 955 | height: dim |
910 | - | ||
911 | }); | 956 | }); |
912 | } | 957 | } |
913 | 958 | ||
... | @@ -940,6 +985,15 @@ | ... | @@ -940,6 +985,15 @@ |
940 | }); | 985 | }); |
941 | var dim = 30; | 986 | var dim = 30; |
942 | appendGlyph(svg, 2, 2, 30, '#node'); | 987 | appendGlyph(svg, 2, 2, 30, '#node'); |
988 | + svg.append('use') | ||
989 | + .attr({ | ||
990 | + class: 'birdBadge', | ||
991 | + transform: translate(8,10), | ||
992 | + 'xlink:href': '#bird', | ||
993 | + width: 18, | ||
994 | + height: 18, | ||
995 | + fill: '#fff' | ||
996 | + }); | ||
943 | 997 | ||
944 | $('<div>').attr('class', 'onosTitle').text(d.id).appendTo(el); | 998 | $('<div>').attr('class', 'onosTitle').text(d.id).appendTo(el); |
945 | 999 | ||
... | @@ -1720,6 +1774,8 @@ | ... | @@ -1720,6 +1774,8 @@ |
1720 | 1774 | ||
1721 | webSock.ws.onopen = function() { | 1775 | webSock.ws.onopen = function() { |
1722 | noWebSock(false); | 1776 | noWebSock(false); |
1777 | + requestSummary(); | ||
1778 | + oiBox.show(); | ||
1723 | }; | 1779 | }; |
1724 | 1780 | ||
1725 | webSock.ws.onmessage = function(m) { | 1781 | webSock.ws.onmessage = function(m) { |
... | @@ -1881,12 +1937,17 @@ | ... | @@ -1881,12 +1937,17 @@ |
1881 | updateDetailPane(); | 1937 | updateDetailPane(); |
1882 | } | 1938 | } |
1883 | 1939 | ||
1940 | + // update the state of the sumary pane | ||
1941 | + function updateSummaryPane() { | ||
1942 | + | ||
1943 | + } | ||
1944 | + | ||
1884 | // update the state of the detail pane, based on current selections | 1945 | // update the state of the detail pane, based on current selections |
1885 | function updateDetailPane() { | 1946 | function updateDetailPane() { |
1886 | var nSel = selectOrder.length; | 1947 | var nSel = selectOrder.length; |
1887 | if (!nSel) { | 1948 | if (!nSel) { |
1888 | detailPane.hide(); | 1949 | detailPane.hide(); |
1889 | - showTrafficAction(); // sends cancelTraffic event | 1950 | + cancelTraffic(); |
1890 | } else if (nSel === 1) { | 1951 | } else if (nSel === 1) { |
1891 | singleSelect(); | 1952 | singleSelect(); |
1892 | } else { | 1953 | } else { |
... | @@ -1936,6 +1997,40 @@ | ... | @@ -1936,6 +1997,40 @@ |
1936 | addMultiSelectActions(); | 1997 | addMultiSelectActions(); |
1937 | } | 1998 | } |
1938 | 1999 | ||
2000 | + // TODO: refactor to consolidate with populateDetails | ||
2001 | + function populateSummary(data) { | ||
2002 | + summaryPane.empty(); | ||
2003 | + | ||
2004 | + var svg = summaryPane.append('svg'), | ||
2005 | + iid = iconGlyphUrl(data); | ||
2006 | + | ||
2007 | + var title = summaryPane.append('h2'), | ||
2008 | + table = summaryPane.append('table'), | ||
2009 | + tbody = table.append('tbody'); | ||
2010 | + | ||
2011 | + appendGlyph(svg, 0, 0, 40, iid); | ||
2012 | + | ||
2013 | + svg.append('use') | ||
2014 | + .attr({ | ||
2015 | + class: 'birdBadge', | ||
2016 | + transform: translate(8,12), | ||
2017 | + 'xlink:href': '#bird', | ||
2018 | + width: 24, | ||
2019 | + height: 24, | ||
2020 | + fill: '#fff' | ||
2021 | + }); | ||
2022 | + | ||
2023 | + title.text('ONOS Summary'); | ||
2024 | + | ||
2025 | + data.propOrder.forEach(function(p) { | ||
2026 | + if (p === '-') { | ||
2027 | + addSep(tbody); | ||
2028 | + } else { | ||
2029 | + addProp(tbody, p, data.props[p]); | ||
2030 | + } | ||
2031 | + }); | ||
2032 | + } | ||
2033 | + | ||
1939 | function populateDetails(data) { | 2034 | function populateDetails(data) { |
1940 | detailPane.empty(); | 2035 | detailPane.empty(); |
1941 | 2036 | ||
... | @@ -2056,7 +2151,7 @@ | ... | @@ -2056,7 +2151,7 @@ |
2056 | // TODO: toggle button (and other widgets in the masthead) should be provided | 2151 | // TODO: toggle button (and other widgets in the masthead) should be provided |
2057 | // by the framework; not generated by the view. | 2152 | // by the framework; not generated by the view. |
2058 | 2153 | ||
2059 | - var showInstances; | 2154 | + //var showInstances; |
2060 | 2155 | ||
2061 | function addButtonBar(view) { | 2156 | function addButtonBar(view) { |
2062 | var bb = d3.select('#mast') | 2157 | var bb = d3.select('#mast') |
... | @@ -2069,20 +2164,20 @@ | ... | @@ -2069,20 +2164,20 @@ |
2069 | .on('click', cb); | 2164 | .on('click', cb); |
2070 | } | 2165 | } |
2071 | 2166 | ||
2072 | - showInstances = mkTogBtn('Show Instances', toggleInst); | 2167 | + //showInstances = mkTogBtn('Show Instances', toggleInst); |
2073 | } | 2168 | } |
2074 | 2169 | ||
2075 | - function instShown() { | 2170 | + //function instShown() { |
2076 | - return showInstances.classed('active'); | 2171 | + // return showInstances.classed('active'); |
2077 | - } | 2172 | + //} |
2078 | - function toggleInst() { | 2173 | + //function toggleInst() { |
2079 | - showInstances.classed('active', !instShown()); | 2174 | + // showInstances.classed('active', !instShown()); |
2080 | - if (instShown()) { | 2175 | + // if (instShown()) { |
2081 | - oiBox.show(); | 2176 | + // oiBox.show(); |
2082 | - } else { | 2177 | + // } else { |
2083 | - oiBox.hide(); | 2178 | + // oiBox.hide(); |
2084 | - } | 2179 | + // } |
2085 | - } | 2180 | + //} |
2086 | 2181 | ||
2087 | function panZoom() { | 2182 | function panZoom() { |
2088 | return false; | 2183 | return false; |
... | @@ -2370,6 +2465,7 @@ | ... | @@ -2370,6 +2465,7 @@ |
2370 | resize: resize | 2465 | resize: resize |
2371 | }); | 2466 | }); |
2372 | 2467 | ||
2468 | + summaryPane = onos.ui.addFloatingPanel('topo-summary'); | ||
2373 | detailPane = onos.ui.addFloatingPanel('topo-detail'); | 2469 | detailPane = onos.ui.addFloatingPanel('topo-detail'); |
2374 | oiBox = onos.ui.addFloatingPanel('topo-oibox', 'TL'); | 2470 | oiBox = onos.ui.addFloatingPanel('topo-oibox', 'TL'); |
2375 | 2471 | ... | ... |
-
Please register or login to post a comment