ONOS-2186 - GUI Topo Overlay - (WIP)
- Basic ability to visually suppress (two levels) other links/nodes, added to Highlights model.
- Added javadocs to Highlights class.
- Added unit tests for Highlights and TopoJson classes.
Change-Id: Id7a9990dcbad20139a4dab89cf54e476c2174ec0
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 45e0a6c..31f07f8 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
@@ -35,14 +35,14 @@
import org.onosproject.net.intent.PathIntent;
import org.onosproject.net.statistic.Load;
import org.onosproject.ui.impl.topo.IntentSelection;
-import org.onosproject.ui.topo.NodeSelection;
import org.onosproject.ui.impl.topo.ServicesBundle;
-import org.onosproject.ui.topo.TopoUtils;
import org.onosproject.ui.impl.topo.TopoIntentFilter;
import org.onosproject.ui.impl.topo.TrafficClass;
import org.onosproject.ui.impl.topo.TrafficLink;
import org.onosproject.ui.impl.topo.TrafficLinkMap;
import org.onosproject.ui.topo.Highlights;
+import org.onosproject.ui.topo.NodeSelection;
+import org.onosproject.ui.topo.TopoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoJson.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoJson.java
index f382e22..0a12e1c 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoJson.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoJson.java
@@ -31,19 +31,21 @@
* JSON utilities for the Topology View.
*/
public final class TopoJson {
- private static final String DEVICES = "devices";
- private static final String HOSTS = "hosts";
- private static final String LINKS = "links";
+ // package-private for unit test access
+ static final String DEVICES = "devices";
+ static final String HOSTS = "hosts";
+ static final String LINKS = "links";
+ static final String SUBDUE = "subdue";
- private static final String ID = "id";
- private static final String LABEL = "label";
- private static final String CSS = "css";
+ static final String ID = "id";
+ static final String LABEL = "label";
+ static final String CSS = "css";
- private static final String TITLE = "title";
- private static final String TYPE = "type";
- private static final String PROP_ORDER = "propOrder";
- private static final String PROPS = "props";
- private static final String BUTTONS = "buttons";
+ static final String TITLE = "title";
+ static final String TYPE = "type";
+ static final String PROP_ORDER = "propOrder";
+ static final String PROPS = "props";
+ static final String BUTTONS = "buttons";
private static final ObjectMapper MAPPER = new ObjectMapper();
@@ -80,6 +82,10 @@
highlights.hosts().forEach(hh -> hosts.add(json(hh)));
highlights.links().forEach(lh -> links.add(json(lh)));
+ Highlights.Amount toSubdue = highlights.subdueLevel();
+ if (!toSubdue.equals(Highlights.Amount.ZERO)) {
+ payload.put(SUBDUE, toSubdue.toString());
+ }
return payload;
}
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.css b/web/gui/src/main/webapp/app/view/topo/topo.css
index f26f478..f4b089a 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.css
+++ b/web/gui/src/main/webapp/app/view/topo/topo.css
@@ -323,6 +323,10 @@
/* --- Topo Nodes --- */
#ov-topo svg .suppressed {
+ opacity: 0.5 !important;
+}
+
+#ov-topo svg .suppressedmax {
opacity: 0.2 !important;
}
diff --git a/web/gui/src/main/webapp/app/view/topo/topoFilter.js b/web/gui/src/main/webapp/app/view/topo/topoFilter.js
index 1226664..f9b96ae 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoFilter.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoFilter.js
@@ -33,6 +33,8 @@
link() // get ref to D3 selection of links
*/
+ var smax = 'suppressedmax';
+
// which "layer" a particular item "belongs to"
var layerLookup = {
host: {
@@ -93,23 +95,21 @@
api.node().each(function (d) {
var node = d.el;
if (inLayer(d, which)) {
- node.classed('suppressed', false);
+ node.classed(smax, false);
}
});
api.link().each(function (d) {
var link = d.el;
if (inLayer(d, which)) {
- link.classed('suppressed', false);
+ link.classed(smax, false);
}
});
}
function suppressLayers(b) {
- api.node().classed('suppressed', b);
- api.link().classed('suppressed', b);
-// d3.selectAll('svg .port').classed('inactive', false);
-// d3.selectAll('svg .portText').classed('inactive', false);
+ api.node().classed(smax, b);
+ api.link().classed(smax, b);
}
function showLayer(which) {
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
index f3c053e..555fa50 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -491,16 +491,37 @@
suppressLayers(true);
node.each(function (n) {
if (n.master === id) {
- n.el.classed('suppressed', false);
+ n.el.classed('suppressedmax', false);
}
});
}
- function suppressLayers(b) {
- node.classed('suppressed', b);
- link.classed('suppressed', b);
-// d3.selectAll('svg .port').classed('inactive', b);
-// d3.selectAll('svg .portText').classed('inactive', b);
+ function supAmt(less) {
+ return less ? "suppressed" : "suppressedmax";
+ }
+
+ function suppressLayers(b, less) {
+ var cls = supAmt(less);
+ node.classed(cls, b);
+ link.classed(cls, b);
+ }
+
+ function unsuppressNode(id, less) {
+ var cls = supAmt(less);
+ node.each(function (n) {
+ if (n.id === id) {
+ n.el.classed(cls, false);
+ }
+ });
+ }
+
+ function unsuppressLink(id, less) {
+ var cls = supAmt(less);
+ link.each(function (n) {
+ if (n.id === id) {
+ n.el.classed(cls, false);
+ }
+ });
}
function showBadLinks() {
@@ -900,8 +921,12 @@
return {
clearLinkTrafficStyle: clearLinkTrafficStyle,
removeLinkLabels: removeLinkLabels,
+ findLinkById: tms.findLinkById,
updateLinks: updateLinks,
- findLinkById: tms.findLinkById
+ updateNodes: updateNodes,
+ supLayers: suppressLayers,
+ unsupNode: unsuppressNode,
+ unsupLink: unsuppressLink
};
}
diff --git a/web/gui/src/main/webapp/app/view/topo/topoOverlay.js b/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
index 85dc0ff..4a432e0 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
@@ -294,18 +294,34 @@
}
function showHighlights(data) {
+ var less;
+
/*
API to topoForce
clearLinkTrafficStyle()
removeLinkLabels()
- updateLinks()
findLinkById( id )
+ updateLinks()
+ updateNodes()
+ supLayers( bool, [less] )
+ unsupNode( id, [less] )
+ unsupLink( id, [less] )
*/
// TODO: clear node highlighting
api.clearLinkTrafficStyle();
api.removeLinkLabels();
+ // handle element suppression
+ if (data.subdue) {
+ less = data.subdue === 'min';
+ api.supLayers(true, less);
+
+ } else {
+ api.supLayers(false);
+ api.supLayers(false, true);
+ }
+
// TODO: device and host highlights
data.links.forEach(function (lnk) {
@@ -314,6 +330,7 @@
units, portcls, magnitude;
if (ldata && !ldata.el.empty()) {
+ api.unsupLink(ldata.id, less);
ldata.el.classed(lnk.css, true);
ldata.label = lab;
@@ -334,7 +351,7 @@
}
});
- // TODO: api.updateNodes()
+ api.updateNodes();
api.updateLinks();
}
diff --git a/web/gui/src/test/java/org/onosproject/ui/impl/topo/TopoJsonTest.java b/web/gui/src/test/java/org/onosproject/ui/impl/topo/TopoJsonTest.java
new file mode 100644
index 0000000..aab650c
--- /dev/null
+++ b/web/gui/src/test/java/org/onosproject/ui/impl/topo/TopoJsonTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 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.onosproject.ui.impl.topo;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Test;
+import org.onosproject.ui.JsonUtils;
+import org.onosproject.ui.topo.Highlights;
+import org.onosproject.ui.topo.Highlights.Amount;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for {@link TopoJson}.
+ */
+public class TopoJsonTest {
+
+ private ObjectNode payload;
+
+ private void checkArrayLength(String key, int expLen) {
+ ArrayNode a = (ArrayNode) payload.get(key);
+ assertEquals("wrong size: " + key, expLen, a.size());
+ }
+
+ private void checkEmptyArrays() {
+ checkArrayLength(TopoJson.DEVICES, 0);
+ checkArrayLength(TopoJson.HOSTS, 0);
+ checkArrayLength(TopoJson.LINKS, 0);
+ }
+
+ @Test
+ public void basicHighlights() {
+ Highlights h = new Highlights();
+ payload = TopoJson.json(h);
+ checkEmptyArrays();
+ String subdue = JsonUtils.string(payload, TopoJson.SUBDUE);
+ assertEquals("subdue", "", subdue);
+ }
+
+ @Test
+ public void subdueMinimalHighlights() {
+ Highlights h = new Highlights().subdueAllElse(Amount.MINIMALLY);
+ payload = TopoJson.json(h);
+ checkEmptyArrays();
+ String subdue = JsonUtils.string(payload, TopoJson.SUBDUE);
+ assertEquals("not min", "min", subdue);
+ }
+
+ @Test
+ public void subdueMaximalHighlights() {
+ Highlights h = new Highlights().subdueAllElse(Amount.MAXIMALLY);
+ payload = TopoJson.json(h);
+ checkEmptyArrays();
+ String subdue = JsonUtils.string(payload, TopoJson.SUBDUE);
+ assertEquals("not max", "max", subdue);
+ }
+}