ONOS-6327: Implement details panel for host view.
ONOS-6326: Add friendly names to hosts.
- PLENTY more YakShaving:
* some cleanup of the device view handler
* introduce navPath field to PropertyPanel
* introduce "-" name annotation to represent "use default"
* (and more...)
Change-Id: I2afc0f1f29c726b90e97e492527edde2d1345ece
diff --git a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
index 7b870b4..1a8db33 100644
--- a/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/config/basics/BasicHostConfig.java
@@ -31,6 +31,7 @@
private static final String IPS = "ips";
private static final String LOCATIONS = "locations";
+ private static final String DASH = "-";
@Override
public boolean isValid() {
@@ -41,6 +42,17 @@
GRID_Y, GRID_Y, UI_TYPE, RACK_ADDRESS, OWNER, IPS, LOCATIONS);
}
+ @Override
+ public String name() {
+ // NOTE:
+ // We don't want to default to host ID if friendly name is not set;
+ // (it isn't particularly friendly, e.g. "00:00:00:00:00:01/None").
+ // We'd prefer to clear the annotation, but if we pass null, then the
+ // value won't get set (see BasicElementOperator). So, instead we will
+ // return a DASH to signify "use the default friendly name".
+ return get(NAME, DASH);
+ }
+
/**
* Returns the location of the host.
*
diff --git a/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java b/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java
index 05565f7..b614ef4 100644
--- a/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java
+++ b/core/api/src/main/java/org/onosproject/ui/topo/PropertyPanel.java
@@ -33,6 +33,7 @@
private String title;
private String typeId;
private String id;
+ private String navPath;
private List<Prop> properties = new ArrayList<>();
private List<ButtonId> buttons = new ArrayList<>();
@@ -40,7 +41,7 @@
* Constructs a property panel model with the given title and
* type identifier (icon to display).
*
- * @param title title text
+ * @param title title text
* @param typeId type (icon) ID
*/
public PropertyPanel(String title, String typeId) {
@@ -67,6 +68,20 @@
}
/**
+ * Adds a navigation path field to the panel data, to be included in
+ * the returned JSON data to the client. This is typically used to
+ * configure the topology view with the appropriate navigation path for
+ * a hot-link to some other view.
+ *
+ * @param navPath the navigation path
+ * @return self for chaining
+ */
+ public PropertyPanel navPath(String navPath) {
+ this.navPath = navPath;
+ return this;
+ }
+
+ /**
* Adds an ID field to the panel data, to be included in
* the returned JSON data to the client.
*
@@ -81,7 +96,7 @@
/**
* Adds a property to the panel data.
*
- * @param key property key
+ * @param key property key
* @param value property value
* @return self, for chaining
*/
@@ -93,7 +108,7 @@
/**
* Adds a property to the panel data, using a decimal formatter.
*
- * @param key property key
+ * @param key property key
* @param value property value
* @return self, for chaining
*/
@@ -105,7 +120,7 @@
/**
* Adds a property to the panel data, using a decimal formatter.
*
- * @param key property key
+ * @param key property key
* @param value property value
* @return self, for chaining
*/
@@ -119,7 +134,7 @@
* {@link Object#toString toString()} method is used to convert the
* value to a string.
*
- * @param key property key
+ * @param key property key
* @param value property value
* @return self, for chaining
*/
@@ -134,8 +149,8 @@
* value to a string, from which the characters defined in the given
* regular expression string are stripped.
*
- * @param key property key
- * @param value property value
+ * @param key property key
+ * @param value property value
* @param reStrip regexp characters to strip from value string
* @return self, for chaining
*/
@@ -174,6 +189,15 @@
}
/**
+ * Returns the navigation path.
+ *
+ * @return the navigation path
+ */
+ public String navPath() {
+ return navPath;
+ }
+
+ /**
* Returns the internal ID.
*
* @return the ID
@@ -235,7 +259,7 @@
public PropertyPanel removeProps(String... keys) {
Set<String> forRemoval = Sets.newHashSet(keys);
List<Prop> toKeep = new ArrayList<>();
- for (Prop p: properties) {
+ for (Prop p : properties) {
if (!forRemoval.contains(p.key())) {
toKeep.add(p);
}
@@ -274,7 +298,7 @@
public PropertyPanel removeButtons(ButtonId... descriptors) {
Set<ButtonId> forRemoval = Sets.newHashSet(descriptors);
List<ButtonId> toKeep = new ArrayList<>();
- for (ButtonId bd: buttons) {
+ for (ButtonId bd : buttons) {
if (!forRemoval.contains(bd)) {
toKeep.add(bd);
}
@@ -306,7 +330,7 @@
/**
* Constructs a property data value.
*
- * @param key property key
+ * @param key property key
* @param value property value
*/
public Prop(String key, String value) {
diff --git a/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java b/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
index 653b0f9..13e131d 100644
--- a/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
+++ b/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
@@ -47,6 +47,7 @@
static final String TITLE = "title";
static final String TYPE = "type";
+ static final String NAV_PATH = "navPath";
static final String PROP_ORDER = "propOrder";
static final String PROPS = "props";
static final String BUTTONS = "buttons";
@@ -63,7 +64,8 @@
}
// non-instantiable
- private TopoJson() { }
+ private TopoJson() {
+ }
/**
* Returns a formatted message ready to send to the topology view
@@ -179,6 +181,10 @@
.put(TYPE, pp.typeId())
.put(ID, pp.id());
+ if (pp.navPath() != null) {
+ result.put(NAV_PATH, pp.navPath());
+ }
+
ObjectNode pnode = objectNode();
ArrayNode porder = arrayNode();
for (PropertyPanel.Prop p : pp.properties()) {
diff --git a/core/api/src/test/java/org/onosproject/net/ConnectPointTest.java b/core/api/src/test/java/org/onosproject/net/ConnectPointTest.java
index 74b162e..51835fe 100644
--- a/core/api/src/test/java/org/onosproject/net/ConnectPointTest.java
+++ b/core/api/src/test/java/org/onosproject/net/ConnectPointTest.java
@@ -121,12 +121,15 @@
switch (r) {
case BEFORE:
assertTrue("Bad before", cpA.compareTo(cpB) < 0);
+ assertTrue("Bad before", cpB.compareTo(cpA) > 0);
break;
case SAME_AS:
assertTrue("Bad same_as", cpA.compareTo(cpB) == 0);
+ assertTrue("Bad same_as", cpB.compareTo(cpA) == 0);
break;
case AFTER:
assertTrue("Bad after", cpA.compareTo(cpB) > 0);
+ assertTrue("Bad after", cpB.compareTo(cpA) < 0);
break;
default:
fail("Bad relation");
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/HostViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/HostViewMessageHandler.java
index 169b89f..2475937 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/HostViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/HostViewMessageHandler.java
@@ -23,6 +23,8 @@
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicHostConfig;
import org.onosproject.net.host.HostService;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiMessageHandler;
@@ -36,9 +38,11 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onosproject.net.HostId.hostId;
@@ -63,11 +67,13 @@
private static final String ID = "id";
private static final String MAC = "mac";
private static final String VLAN = "vlan";
+ private static final String IP = "ip";
private static final String IPS = "ips";
private static final String LOCATION = "location";
private static final String LOCATIONS = "locations";
private static final String CONFIGURED = "configured";
+ private static final String DASH = "-";
private static final String HOST_ICON_PREFIX = "hostIcon_";
@@ -92,10 +98,20 @@
(isNullOrEmpty(hostType) ? "endstation" : hostType);
}
+ // Returns the first of the given set of IP addresses as a string.
+ private String ip(Set<IpAddress> ipAddresses) {
+ Iterator<IpAddress> it = ipAddresses.iterator();
+ return it.hasNext() ? it.next().toString() : "unknown";
+ }
+
+ private boolean useDefaultName(String nameAnnotated) {
+ return isNullOrEmpty(nameAnnotated) || DASH.equals(nameAnnotated);
+ }
+
// returns the "friendly name" for the host
private String getHostName(Host host) {
- // TODO: acutally use the name field (not just the ID)
- return host.id().toString();
+ String name = host.annotations().value(AnnotationKeys.NAME);
+ return useDefaultName(name) ? ip(host.ipAddresses()) : name;
}
// handler for host table requests
@@ -151,7 +167,7 @@
public String format(Object value) {
Set<IpAddress> ips = (Set<IpAddress>) value;
if (ips.isEmpty()) {
- return "(No IP Addresses for this host)";
+ return "(No IP Addresses)";
}
StringBuilder sb = new StringBuilder();
for (IpAddress ip : ips) {
@@ -187,6 +203,7 @@
data.put(TYPE_IID, getTypeIconId(host))
.put(NAME, getHostName(host))
.put(ID, hostId.toString())
+ .put(IP, ip(host.ipAddresses()))
.put(MAC, host.mac().toString())
.put(VLAN, host.vlan().toString())
.put(CONFIGURED, host.configured())
@@ -216,17 +233,25 @@
}
private final class NameChangeHandler extends RequestHandler {
- public NameChangeHandler() {
+ private NameChangeHandler() {
super(HOST_NAME_CHANGE_REQ);
}
@Override
public void process(ObjectNode payload) {
- // TODO:
+ HostId hostId = hostId(string(payload, ID, ""));
+ String name = emptyToNull(string(payload, NAME, null));
+ log.debug("Name change request: {} -- '{}'", hostId, name);
- ObjectNode root = objectNode();
+ NetworkConfigService service = get(NetworkConfigService.class);
+ BasicHostConfig cfg =
+ service.addConfig(hostId, BasicHostConfig.class);
- sendMessage(HOST_NAME_CHANGE_RESP, root);
+ // Name attribute missing from the payload (or empty string)
+ // means that the friendly name should be unset.
+ cfg.name(name);
+ cfg.apply();
+ sendMessage(HOST_NAME_CHANGE_RESP, payload);
}
}
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
index 83a23ff..456d81a 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
@@ -74,6 +74,11 @@
public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
private static final String NO_GEO_VALUE = "0.0";
+ private static final String DASH = "-";
+
+ // nav paths are the view names for hot-link navigation from topo view...
+ private static final String DEVICE_NAV_PATH = "device";
+ private static final String HOST_NAV_PATH = "host";
// default to an "add" event...
private static final DefaultHashMap<ClusterEvent.Type, String> CLUSTER_EVENT =
@@ -139,7 +144,7 @@
version = ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
}
- // Returns the specified set of IP addresses as a string.
+ // Returns the first of the given set of IP addresses as a string.
private String ip(Set<IpAddress> ipAddresses) {
Iterator<IpAddress> it = ipAddresses.iterator();
return it.hasNext() ? it.next().toString() : "unknown";
@@ -210,6 +215,8 @@
String uiType = device.annotations().value(AnnotationKeys.UI_TYPE);
String devType = uiType != null ? uiType :
device.type().toString().toLowerCase();
+ String name = device.annotations().value(AnnotationKeys.NAME);
+ name = isNullOrEmpty(name) ? device.id().toString() : name;
ObjectNode payload = objectNode()
.put("id", device.id().toString())
@@ -217,15 +224,7 @@
.put("online", services.device().isAvailable(device.id()))
.put("master", master(device.id()));
- // Generate labels: id, chassis id, no-label, optional-name
- String name = device.annotations().value(AnnotationKeys.NAME);
- ArrayNode labels = arrayNode();
- labels.add("");
- labels.add(isNullOrEmpty(name) ? device.id().toString() : name);
- labels.add(device.id().toString());
-
- // Add labels, props and stuff the payload into envelope.
- payload.set("labels", labels);
+ payload.set("labels", labels("", name, device.id().toString()));
payload.set("props", props(device.annotations()));
addGeoLocation(device, payload);
addMetaUi(device.id().toString(), payload);
@@ -256,18 +255,19 @@
Host host = event.subject();
Host prevHost = event.prevSubject();
String hostType = host.annotations().value(AnnotationKeys.UI_TYPE);
+ String ip = ip(host.ipAddresses());
ObjectNode payload = objectNode()
.put("id", host.id().toString())
.put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
.put("ingress", compactLinkString(edgeLink(host, true)))
.put("egress", compactLinkString(edgeLink(host, false)));
+
payload.set("cp", hostConnect(host.location()));
if (prevHost != null && prevHost.location() != null) {
payload.set("prevCp", hostConnect(prevHost.location()));
}
- payload.set("labels", labels(ip(host.ipAddresses()),
- host.mac().toString()));
+ payload.set("labels", labels(nameForHost(host), ip, host.mac().toString()));
payload.set("props", props(host.annotations()));
addGeoLocation(host, payload);
addMetaUi(host.id().toString(), payload);
@@ -378,6 +378,7 @@
String typeId = device.type().toString().toLowerCase();
return new PropertyPanel(title, typeId)
+ .navPath(DEVICE_NAV_PATH)
.id(deviceId.toString())
.addProp(Properties.URI, deviceId.toString())
@@ -435,18 +436,25 @@
return count;
}
+ private boolean useDefaultName(String annotName) {
+ return isNullOrEmpty(annotName) || DASH.equals(annotName);
+ }
+
+ private String nameForHost(Host host) {
+ String name = host.annotations().value(AnnotationKeys.NAME);
+ return useDefaultName(name) ? ip(host.ipAddresses()) : name;
+ }
+
// Returns host details response.
protected PropertyPanel hostDetails(HostId hostId) {
Host host = services.host().getHost(hostId);
Annotations annot = host.annotations();
String type = annot.value(AnnotationKeys.TYPE);
- String name = annot.value(AnnotationKeys.NAME);
String vlan = host.vlan().toString();
-
- String title = isNullOrEmpty(name) ? hostId.toString() : name;
String typeId = isNullOrEmpty(type) ? "endstation" : type;
- return new PropertyPanel(title, typeId)
+ return new PropertyPanel(nameForHost(host), typeId)
+ .navPath(HOST_NAV_PATH)
.id(hostId.toString())
.addProp(Properties.MAC, host.mac())
.addProp(Properties.IP, host.ipAddresses(), "[\\[\\]]")
diff --git a/web/gui/src/main/webapp/app/common.css b/web/gui/src/main/webapp/app/common.css
index b8952c3..7aed6ef 100644
--- a/web/gui/src/main/webapp/app/common.css
+++ b/web/gui/src/main/webapp/app/common.css
@@ -22,3 +22,18 @@
cursor: pointer;
}
+.light .editable {
+ border-bottom: 1px dashed #ca504b;
+}
+
+.dark .editable {
+ border-bottom: 1px dashed #df4f4a;
+}
+
+.light svg.embeddedIcon .icon .glyph {
+ fill: #0071bd;
+}
+
+.dark svg.embeddedIcon .icon .glyph {
+ fill: #375b7f;
+}
diff --git a/web/gui/src/main/webapp/app/view/device/device-theme.css b/web/gui/src/main/webapp/app/view/device/device-theme.css
index 3aa67d2..f7e2873 100644
--- a/web/gui/src/main/webapp/app/view/device/device-theme.css
+++ b/web/gui/src/main/webapp/app/view/device/device-theme.css
@@ -18,15 +18,6 @@
ONOS GUI -- Device View (theme) -- CSS file
*/
-
-.light .dev-icon svg.embeddedIcon .icon .glyph {
- fill: #0071bd;
-}
-
-.light #device-details-panel .editable {
- border-bottom: 1px dashed #ca504b;
-}
-
.light #device-details-panel .bottom th {
background-color: #e5e5e6;
}
@@ -38,12 +29,3 @@
background-color: #f4f4f4;
}
-/* ========== DARK Theme ========== */
-
-.dark .dev-icon svg.embeddedIcon .icon .glyph {
- fill: #375b7f;
-}
-
-.dark #device-details-panel .editable {
- border-bottom: 1px dashed #df4f4a;
-}
diff --git a/web/gui/src/main/webapp/app/view/host/host.css b/web/gui/src/main/webapp/app/view/host/host.css
index 377918a..1aa6f86 100644
--- a/web/gui/src/main/webapp/app/view/host/host.css
+++ b/web/gui/src/main/webapp/app/view/host/host.css
@@ -58,7 +58,7 @@
#host-details-panel h2 input {
font-size: 0.90em;
- width: 106%;
+ width: 112%;
}
#host-details-panel .top-tables {
diff --git a/web/gui/src/main/webapp/app/view/host/host.js b/web/gui/src/main/webapp/app/view/host/host.js
index ee23f00..4e8153c 100644
--- a/web/gui/src/main/webapp/app/view/host/host.js
+++ b/web/gui/src/main/webapp/app/view/host/host.js
@@ -29,7 +29,6 @@
pStartY,
pHeight,
top,
- bottom,
iconDiv,
wSize,
editingName = false,
@@ -37,15 +36,19 @@
// constants
var topPdg = 28,
- ctnrPdg = 24,
- scrollSize = 17,
-
pName = 'host-details-panel',
detailsReq = 'hostDetailsRequest',
detailsResp = 'hostDetailsResponse',
nameChangeReq = 'hostNameChangeRequest',
nameChangeResp = 'hostNameChangeResponse';
+ var propOrder = [
+ 'id', 'ip', 'mac', 'vlan', 'configured', 'location'
+ ],
+ friendlyProps = [
+ 'Host ID', 'IP Address', 'MAC Address', 'VLAN',
+ 'Configured', 'Location'
+ ];
function closePanel() {
if (detailsPanel.isVisible()) {
@@ -71,12 +74,13 @@
function editNameSave() {
var nameH2 = top.select('h2'),
id = $scope.panelData.id,
+ ip = $scope.panelData.ip,
val,
newVal;
if (editingName) {
val = nameH2.select('input').property('value').trim();
- newVal = val || id;
+ newVal = val || ip;
exitEditMode(nameH2, newVal);
$scope.panelData.name = newVal;
@@ -115,7 +119,7 @@
}
function setUpPanel() {
- var container, closeBtn, tblDiv;
+ var container, closeBtn;
detailsPanel.empty();
container = detailsPanel.append('div').classed('container', true);
@@ -126,22 +130,29 @@
iconDiv = top.append('div').classed('host-icon', true);
top.append('h2').classed('editable clickable', true).on('click', editName);
- // tblDiv = top.append('div').classed('top-tables', true);
- // tblDiv.append('div').classed('left', true).append('table');
- // tblDiv.append('div').classed('right', true).append('table');
-
+ top.append('div').classed('top-tables', true);
top.append('hr');
+ }
- // bottom = container.append('div').classed('bottom', true);
- // bottom.append('h2').classed('ports-title', true).text('Ports');
- // bottom.append('table');
+ function addProp(tbody, index, value) {
+ var tr = tbody.append('tr');
+
+ function addCell(cls, txt) {
+ tr.append('td').attr('class', cls).text(txt);
+ }
+ addCell('label', friendlyProps[index] + ' :');
+ addCell('value', value);
}
function populateTop(details) {
+ var tab = top.select('.top-tables').append('tbody');
+
is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
top.select('h2').text(details.name);
- // TODO: still need to add host properties (one per line)
+ propOrder.forEach(function (prop, i) {
+ addProp(tab, i, details[prop]);
+ });
}
function populateDetails(details) {
@@ -149,7 +160,7 @@
populateTop(details);
detailsPanel.height(pHeight);
// configure width based on content.. for now hardcoded
- detailsPanel.width(600);
+ detailsPanel.width(400);
}
function respDetailsCb(data) {
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 8acebcc..29f149f 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -61,6 +61,7 @@
Z: [tos.toggleOblique, 'Toggle oblique view (Experimental)'],
N: [fltr.clickAction, 'Cycle node layers'],
L: [tfs.cycleDeviceLabels, 'Cycle device labels'],
+ 'shift-L': [tfs.cycleHostLabels, 'Cycle host labels'],
U: [tfs.unpin, 'Unpin node (hover mouse over)'],
R: [resetZoom, 'Reset pan / zoom'],
dot: [ttbs.toggleToolbar, 'Toggle Toolbar'],
@@ -83,7 +84,7 @@
_helpFormat: [
['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B', 'G', 'S' ],
- ['X', 'Z', 'N', 'L', 'U', 'R', '-', 'E', '-', 'dot'],
+ ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', '-', 'E', '-', 'dot'],
[] // this column reserved for overlay actions
]
};
@@ -494,6 +495,7 @@
toggleMap(prefsState.bg);
toggleSprites(prefsState.spr);
t3s.setDevLabIndex(prefsState.dlbls);
+ t3s.setHostLabIndex(prefsState.hlbls);
flash.enable(true);
}
diff --git a/web/gui/src/main/webapp/app/view/topo/topoD3.js b/web/gui/src/main/webapp/app/view/topo/topoD3.js
index e45f491..3d232ee 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoD3.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoD3.js
@@ -139,6 +139,22 @@
ps.setPrefs('topo_prefs', p);
}
+ function incHostLabIndex() {
+ setHostLabIndex(hostLabelIndex+1);
+ switch(hostLabelIndex) {
+ case 0: return 'Show friendly host labels';
+ case 1: return 'Show host IP Addresses';
+ case 2: return 'Show host MAC Addresses';
+ }
+ }
+
+ function setHostLabIndex(mode) {
+ hostLabelIndex = mode % 3;
+ var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
+ p.hlbls = hostLabelIndex;
+ ps.setPrefs('topo_prefs', p);
+ }
+
function hostLabel(d) {
var idx = (hostLabelIndex < d.labels.length) ? hostLabelIndex : 0;
return d.labels[idx];
@@ -617,6 +633,8 @@
incDevLabIndex: incDevLabIndex,
setDevLabIndex: setDevLabIndex,
+ incHostLabIndex: incHostLabIndex,
+ setHostLabIndex: setHostLabIndex,
hostLabel: hostLabel,
deviceLabel: deviceLabel,
trimLabel: trimLabel,
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 9b9a29c..f38fa56 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -516,6 +516,13 @@
});
}
+ function cycleHostLabels() {
+ flash.flash(td3.incHostLabIndex());
+ tms.findHosts().forEach(function (d) {
+ td3.updateHostLabel(d);
+ });
+ }
+
function unpin() {
var hov = tss.hovered();
if (hov) {
@@ -1240,6 +1247,7 @@
togglePorts: tls.togglePorts,
toggleOffline: toggleOffline,
cycleDeviceLabels: cycleDeviceLabels,
+ cycleHostLabels: cycleHostLabels,
unpin: unpin,
showMastership: showMastership,
showBadLinks: showBadLinks,
diff --git a/web/gui/src/main/webapp/app/view/topo/topoModel.js b/web/gui/src/main/webapp/app/view/topo/topoModel.js
index 3236ae5..e841907 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoModel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoModel.js
@@ -366,6 +366,16 @@
return a;
}
+ function findHosts() {
+ var hosts = [];
+ nodes.forEach(function (d) {
+ if (d.class === 'host') {
+ hosts.push(d);
+ }
+ });
+ return hosts;
+ }
+
function findAttachedHosts(devId) {
var hosts = [];
nodes.forEach(function (d) {
@@ -453,6 +463,7 @@
findLink: findLink,
findLinkById: findLinkById,
findDevices: findDevices,
+ findHosts: findHosts,
findAttachedHosts: findAttachedHosts,
findAttachedLinks: findAttachedLinks,
findBadLinks: findBadLinks
diff --git a/web/gui/src/main/webapp/app/view/topo/topoPanel.js b/web/gui/src/main/webapp/app/view/topo/topoPanel.js
index 100cd96..8f8eef5 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoPanel.js
@@ -35,8 +35,7 @@
sumMax = 226, // summary panel max height
padTop = 16, // summary panel padding below masthead
padding = 16, // panel internal padding
- padFudge = padTop + 2 * padding,
- devPath = 'device';
+ padFudge = padTop + 2 * padding;
// internal state
var useDetails = true, // should we show details if we have 'em?
@@ -230,10 +229,9 @@
// === -----------------------------------------------------
// Functions for populating the detail panel
- var isDevice = {
- switch: 1,
- roadm: 1,
- otn:1
+ var navPathIdKey = {
+ device: 'devId',
+ host: 'hostId'
};
function displaySingle(data) {
@@ -246,15 +244,19 @@
.classed('clickable', true),
table = detail.appendBody('table'),
tbody = table.append('tbody'),
- navFn;
+ navFn,
+ navPath;
gs.addGlyph(svg, (data.type || 'unknown'), 26);
title.text(data.title);
- // only add navigation when displaying a device
- if (isDevice[data.type]) {
+ // add navigation hot-link if defined
+ navPath = data.navPath;
+ if (navPath) {
navFn = function () {
- ns.navTo(devPath, { devId: data.id });
+ var arg = {};
+ arg[navPathIdKey[navPath]] = data.id;
+ ns.navTo(navPath, arg);
};
svg.on('click', navFn);
diff --git a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
index 351154c..82d14ac 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
@@ -75,6 +75,7 @@
hosts: 0,
offdev: 1,
dlbls: 0,
+ hlbls: 0,
porthl: 1,
bg: 0,
spr: 0,
@@ -278,7 +279,7 @@
toolbar.toggle();
persistTopoPrefs('toolbar');
}
-
+
function selectOverlay(ovid) {
var idx = ovIndex[defaultOverlay] || 0,
pidx = (ovid === null) ? 0 : ovIndex[ovid] || -1;
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js b/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js
index ef3698e..9d822c8 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js
@@ -26,6 +26,7 @@
hosts: 0,
offdev: 1,
dlbls: 0,
+ hlbls: 0,
porthl: 1,
bg: 0,
spr: 0,
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js
index f0a2914..d14e9ab 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js
@@ -41,8 +41,8 @@
'updateDeviceColors', 'toggleHosts',
'togglePorts', 'toggleOffline',
- 'cycleDeviceLabels', 'unpin', 'showMastership', 'showBadLinks',
- 'setNodeScale',
+ 'cycleDeviceLabels', 'cycleHostLabels', 'unpin',
+ 'showMastership', 'showBadLinks', 'setNodeScale',
'resetAllLocations', 'addDevice', 'updateDevice', 'removeDevice',
'addHost', 'updateHost', 'moveHost', 'removeHost',
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
index d1db42a..dd32491 100644
--- a/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoModel-spec.js
@@ -210,10 +210,11 @@
it('should define api functions', function () {
expect(fs.areFunctions(tms, [
'initModel', 'newDim', 'destroyModel',
- 'positionNode', 'resetAllLocations', 'createDeviceNode', 'createHostNode',
+ 'positionNode', 'resetAllLocations',
+ 'createDeviceNode', 'createHostNode',
'createHostLink', 'createLink',
'coordFromLngLat', 'lngLatFromCoord',
- 'findLink', 'findLinkById', 'findDevices',
+ 'findLink', 'findLinkById', 'findDevices', 'findHosts',
'findAttachedHosts', 'findAttachedLinks', 'findBadLinks'
])).toBeTruthy();
});