Fix for ONOS-291. Highlighting intents in ONOS GUI for selected links.
Change-Id: I757aa40b96d92014fa2d720539da20dd309ec9b1
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
index fdfdd1b..1c7e22e 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
@@ -534,7 +534,7 @@
@Override
public void process(long sid, ObjectNode payload) {
NodeSelection nodeSelection =
- new NodeSelection(payload, deviceService, hostService);
+ new NodeSelection(payload, deviceService, hostService, linkService);
traffic.monitor(Mode.DEV_LINK_FLOWS, nodeSelection);
}
}
@@ -547,7 +547,7 @@
@Override
public void process(long sid, ObjectNode payload) {
NodeSelection nodeSelection =
- new NodeSelection(payload, deviceService, hostService);
+ new NodeSelection(payload, deviceService, hostService, linkService);
traffic.monitor(Mode.RELATED_INTENTS, nodeSelection);
}
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/IntentSelection.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/IntentSelection.java
index 58c649e..dd48768 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/IntentSelection.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/IntentSelection.java
@@ -49,7 +49,10 @@
*/
public IntentSelection(NodeSelection nodes, TopoIntentFilter filter) {
this.nodes = nodes;
- intents = filter.findPathIntents(nodes.hostsWithHover(), nodes.devicesWithHover());
+ intents = filter.findPathIntents(
+ nodes.hostsWithHover(),
+ nodes.devicesWithHover(),
+ nodes.linksWithHover());
if (intents.size() == 1) {
index = 0; // pre-select a single intent
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/TopoIntentFilter.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/TopoIntentFilter.java
index 2b65762..54a43d4 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/TopoIntentFilter.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/util/TopoIntentFilter.java
@@ -74,9 +74,12 @@
*
* @param hosts set of hosts to query by
* @param devices set of devices to query by
- * @return set of intents that 'match' all hosts and devices given
+ * @param links set of links to query by
+ * @return set of intents that 'match' all hosts, devices and links given
*/
- public List<Intent> findPathIntents(Set<Host> hosts, Set<Device> devices) {
+ public List<Intent> findPathIntents(Set<Host> hosts,
+ Set<Device> devices,
+ Set<Link> links) {
// start with all intents
Iterable<Intent> sourceIntents = intentService.getIntents();
@@ -85,7 +88,7 @@
// Iterate over all intents and produce a set that contains only those
// intents that target all selected hosts or derived edge connect points.
- return getIntents(hosts, devices, edgePoints, sourceIntents);
+ return getIntents(hosts, devices, links, edgePoints, sourceIntents);
}
@@ -98,12 +101,12 @@
return edgePoints;
}
- // Produces a list of intents that target all selected hosts, devices or connect points.
- private List<Intent> getIntents(Set<Host> hosts, Set<Device> devices,
+ // Produces a list of intents that target all selected hosts, devices, links or connect points.
+ private List<Intent> getIntents(Set<Host> hosts, Set<Device> devices, Set<Link> links,
Set<ConnectPoint> edgePoints,
Iterable<Intent> sourceIntents) {
List<Intent> intents = new ArrayList<>();
- if (hosts.isEmpty() && devices.isEmpty()) {
+ if (hosts.isEmpty() && devices.isEmpty() && links.isEmpty()) {
return intents;
}
@@ -115,13 +118,13 @@
boolean isRelevant = false;
if (intent instanceof HostToHostIntent) {
isRelevant = isIntentRelevantToHosts((HostToHostIntent) intent, hosts) &&
- isIntentRelevantToDevices(intent, devices);
+ isIntentRelevantToDevices(intent, devices) && isIntentRelevantToLinks(intent, links);
} else if (intent instanceof PointToPointIntent) {
isRelevant = isIntentRelevant((PointToPointIntent) intent, edgePoints) &&
- isIntentRelevantToDevices(intent, devices);
+ isIntentRelevantToDevices(intent, devices) && isIntentRelevantToLinks(intent, links);
} else if (intent instanceof MultiPointToSinglePointIntent) {
isRelevant = isIntentRelevant((MultiPointToSinglePointIntent) intent, edgePoints) &&
- isIntentRelevantToDevices(intent, devices);
+ isIntentRelevantToDevices(intent, devices) && isIntentRelevantToLinks(intent, links);
} else if (intent instanceof OpticalConnectivityIntent) {
opticalIntents.add((OpticalConnectivityIntent) intent);
}
@@ -167,6 +170,17 @@
return true;
}
+ // Indicates whether the specified intent involves all of the given links.
+ private boolean isIntentRelevantToLinks(Intent intent, Iterable<Link> links) {
+ List<Intent> installables = intentService.getInstallableIntents(intent.key());
+ for (Link link : links) {
+ if (!isIntentRelevantToLink(installables, link)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
// Indicates whether the specified intent involves the given device.
private boolean isIntentRelevantToDevice(List<Intent> installables, Device device) {
if (installables != null) {
@@ -196,6 +210,38 @@
return false;
}
+ // Indicates whether the specified intent involves the given link.
+ private boolean isIntentRelevantToLink(List<Intent> installables, Link link) {
+ Link reverseLink = linkService.getLink(link.dst(), link.src());
+
+ if (installables != null) {
+ for (Intent installable : installables) {
+ if (installable instanceof PathIntent) {
+ PathIntent pathIntent = (PathIntent) installable;
+ return pathIntent.path().links().contains(link) ||
+ pathIntent.path().links().contains(reverseLink);
+
+ } else if (installable instanceof FlowRuleIntent) {
+ FlowRuleIntent flowRuleIntent = (FlowRuleIntent) installable;
+ return flowRuleIntent.resources().contains(link) ||
+ flowRuleIntent.resources().contains(reverseLink);
+
+ } else if (installable instanceof FlowObjectiveIntent) {
+ FlowObjectiveIntent objectiveIntent = (FlowObjectiveIntent) installable;
+ return objectiveIntent.resources().contains(link) ||
+ objectiveIntent.resources().contains(reverseLink);
+
+ } else if (installable instanceof LinkCollectionIntent) {
+ LinkCollectionIntent linksIntent = (LinkCollectionIntent) installable;
+ return linksIntent.links().contains(link) ||
+ linksIntent.links().contains(reverseLink);
+
+ }
+ }
+ }
+ return false;
+ }
+
// Indicates whether the specified links involve the given device.
private boolean pathContainsDevice(Iterable<Link> links, DeviceId id) {
for (Link link : links) {
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 a3041a3..14ca96f 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -192,7 +192,7 @@
// else if we have node selections, deselect them all
// (work already done)
- } else if (tls.deselectLink()) {
+ } else if (tls.deselectAllLinks()) {
// else if we have a link selected, deselect it
// (work already done)
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 6d992f4..948939b 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -971,7 +971,7 @@
node: function () { return node; },
zoomingOrPanning: zoomingOrPanning,
updateDeviceColors: td3.updateDeviceColors,
- deselectLink: tls.deselectLink
+ deselectAllLinks: tls.deselectAllLinks
};
}
diff --git a/web/gui/src/main/webapp/app/view/topo/topoLink.js b/web/gui/src/main/webapp/app/view/topo/topoLink.js
index 95a9daa..bfe0df5 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoLink.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoLink.js
@@ -31,7 +31,7 @@
network,
showPorts = true, // enable port highlighting by default
enhancedLink = null, // the link over which the mouse is hovering
- selectedLink = null; // the link which is currently selected
+ selectedLinks = {}; // the links which are already selected
// SVG elements;
var svg;
@@ -210,25 +210,33 @@
function selectLink(ldata) {
// if the new link is same as old link, do nothing
- if (selectedLink && ldata && selectedLink.key === ldata.key) return;
+ if (d3.event.shiftKey && ldata.el.classed('selected')) {
+ unselLink(ldata);
+ return;
+ }
- // make sure no nodes are selected
- tss.deselectAll();
+ if (d3.event.shiftKey && !ldata.el.classed('selected')) {
+ selLink(ldata);
+ return;
+ }
- // first, unenhance the currently enhanced link
- if (selectedLink) {
- unselLink(selectedLink);
- }
- selectedLink = ldata;
- if (selectedLink) {
- selLink(selectedLink);
- }
+ tss.deselectAll();
+
+ if (!ldata.el.classed('selected')) {
+ selLink(ldata);
+ return;
+ }
+
+ if (ldata.el.classed('selected')) {
+ unselLink(ldata);
+ }
}
function unselLink(d) {
// guard against link element not set
if (d.el) {
d.el.classed('selected', false);
+ delete selectedLinks[d.key];
}
}
@@ -237,6 +245,7 @@
if (!d.el) return;
d.el.classed('selected', true);
+ selectedLinks[d.key] = {key : d};
tps.displayLink(d, tov.hooks.modifyLinkData);
tps.displaySomething();
@@ -252,6 +261,9 @@
function mouseClickHandler() {
var mp, link, node;
+ if (!d3.event.shiftKey) {
+ deselectAllLinks();
+ }
if (!tss.clickConsumed()) {
mp = getLogicalMousePosition(this);
@@ -262,6 +274,7 @@
} else {
link = computeNearestLink(mp);
selectLink(link);
+ tss.selectObject(link);
}
}
}
@@ -285,13 +298,15 @@
return on;
}
- function deselectLink() {
- if (selectedLink) {
- unselLink(selectedLink);
- selectedLink = null;
- return true;
+ function deselectAllLinks() {
+
+ if (Object.keys(selectedLinks).length > 0) {
+ network.links.forEach(function (d) {
+ if (selectedLinks[d.key]) {
+ unselLink(d);
+ }
+ });
}
- return false;
}
// ==========================
@@ -333,7 +348,7 @@
initLink: initLink,
destroyLink: destroyLink,
togglePorts: togglePorts,
- deselectLink: deselectLink
+ deselectAllLinks: deselectAllLinks
};
}]);
}());
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 fb96df5..f5d1cbd 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoPanel.js
@@ -264,7 +264,7 @@
table = detail.appendBody('table'),
tbody = table.append('tbody');
- title.text('Selected Nodes');
+ title.text('Selected Items');
ids.forEach(function (d, i) {
addProp(tbody, i+1, d);
});
diff --git a/web/gui/src/main/webapp/app/view/topo/topoSelect.js b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
index 77010df..1cd211b 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoSelect.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
@@ -31,7 +31,7 @@
node() // get ref to D3 selection of nodes
zoomingOrPanning( ev )
updateDeviceColors( [dev] )
- deselectLink()
+ deselectAllLinks()
*/
// internal state
@@ -106,12 +106,27 @@
}
});
}
- if (!n) return;
+
+ if (obj.class === 'link') {
+
+ if (selections[obj.key]) {
+ deselectObject(obj.key);
+ } else {
+ selections[obj.key] = { obj: obj, el: el };
+ selectOrder.push(obj.key);
+ }
+
+ updateDetail();
+ return;
+ }
+
+ if (!n) {
+ return;
+ }
if (nodeEv) {
consumeClick = true;
}
- api.deselectLink();
if (ev.shiftKey && n.classed('selected')) {
deselectObject(obj.id);
@@ -196,6 +211,11 @@
function singleSelect() {
var data = getSel(0).obj;
+
+ //the link details are already taken care of in topoLink.js
+ if (data.class === 'link') {
+ return;
+ }
requestDetails(data);
// NOTE: detail panel is shown as a response to receiving
// a 'showDetails' event from the server. See 'showDetails'
diff --git a/web/gui/src/main/webapp/app/view/topo/topoTraffic.js b/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
index 1836e1e..215f3e3 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
@@ -75,8 +75,10 @@
var hov = api.hovered();
function hoverValid() {
- return hoverMode === 'intents' &&
- hov && (hov.class === 'host' || hov.class === 'device');
+ return hoverMode === 'intents' && hov && (
+ hov.class === 'host' ||
+ hov.class === 'device' ||
+ hov.class === 'link');
}
if (api.somethingSelected()) {