Simon Hunt
Committed by Gerrit Code Review

ONOS-2186 - GUI Topo Overlay - (WIP)

- Showing traffic on selected intent now subdues other elements.
- Augmented Highlights to allow for retrieval by ID.
- Reparented HostHighlight and DeviceHighlight to NodeHighlight.
- Added a few extra highlight unit tests.

Change-Id: I0de1cefdcfda58a6fec6e90be5fe898d35aa1b37
......@@ -20,7 +20,7 @@ package org.onosproject.ui.topo;
/**
* Denotes the highlighting to apply to a device.
*/
public class DeviceHighlight extends AbstractHighlight {
public class DeviceHighlight extends NodeHighlight {
public DeviceHighlight(String deviceId) {
super(TopoElementType.DEVICE, deviceId);
......
......@@ -17,9 +17,10 @@
package org.onosproject.ui.topo;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
......@@ -53,9 +54,9 @@ public class Highlights {
}
}
private final Set<DeviceHighlight> devices = new HashSet<>();
private final Set<HostHighlight> hosts = new HashSet<>();
private final Set<LinkHighlight> links = new HashSet<>();
private final Map<String, DeviceHighlight> devices = new HashMap<>();
private final Map<String, HostHighlight> hosts = new HashMap<>();
private final Map<String, LinkHighlight> links = new HashMap<>();
private Amount subdueLevel = Amount.ZERO;
......@@ -67,7 +68,7 @@ public class Highlights {
* @return self, for chaining
*/
public Highlights add(DeviceHighlight dh) {
devices.add(dh);
devices.put(dh.elementId(), dh);
return this;
}
......@@ -78,7 +79,7 @@ public class Highlights {
* @return self, for chaining
*/
public Highlights add(HostHighlight hh) {
hosts.add(hh);
hosts.put(hh.elementId(), hh);
return this;
}
......@@ -89,7 +90,7 @@ public class Highlights {
* @return self, for chaining
*/
public Highlights add(LinkHighlight lh) {
links.add(lh);
links.put(lh.elementId(), lh);
return this;
}
......@@ -106,30 +107,30 @@ public class Highlights {
}
/**
* Returns the set of device highlights.
* Returns the collection of device highlights.
*
* @return device highlights
*/
public Set<DeviceHighlight> devices() {
return Collections.unmodifiableSet(devices);
public Collection<DeviceHighlight> devices() {
return Collections.unmodifiableCollection(devices.values());
}
/**
* Returns the set of host highlights.
* Returns the collection of host highlights.
*
* @return host highlights
*/
public Set<HostHighlight> hosts() {
return Collections.unmodifiableSet(hosts);
public Collection<HostHighlight> hosts() {
return Collections.unmodifiableCollection(hosts.values());
}
/**
* Returns the set of link highlights.
* Returns the collection of link highlights.
*
* @return link highlights
*/
public Set<LinkHighlight> links() {
return Collections.unmodifiableSet(links);
public Collection<LinkHighlight> links() {
return Collections.unmodifiableCollection(links.values());
}
/**
......@@ -141,4 +142,49 @@ public class Highlights {
public Amount subdueLevel() {
return subdueLevel;
}
/**
* Returns the node highlight (device or host) for the given element
* identifier, or null if no match.
*
* @param id element identifier
* @return corresponding node highlight
*/
public NodeHighlight getNode(String id) {
NodeHighlight nh = devices.get(id);
return nh != null ? nh : hosts.get(id);
}
/**
* Returns the device highlight for the given device identifier,
* or null if no match.
*
* @param id device identifier
* @return corresponding device highlight
*/
public DeviceHighlight getDevice(String id) {
return devices.get(id);
}
/**
* Returns the host highlight for the given host identifier,
* or null if no match.
*
* @param id host identifier
* @return corresponding host highlight
*/
public HostHighlight getHost(String id) {
return hosts.get(id);
}
/**
* Returns the link highlight for the given link identifier,
* or null if no match.
*
* @param id link identifier
* @return corresponding link highlight
*/
public LinkHighlight getLink(String id) {
return links.get(id);
}
}
......
......@@ -20,7 +20,7 @@ package org.onosproject.ui.topo;
/**
* Denotes the highlighting to apply to a host.
*/
public class HostHighlight extends AbstractHighlight {
public class HostHighlight extends NodeHighlight {
public HostHighlight(String hostId) {
super(TopoElementType.HOST, hostId);
......
/*
* 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.topo;
/**
* Parent class of {@link DeviceHighlight} and {@link HostHighlight}.
*/
public abstract class NodeHighlight extends AbstractHighlight {
public NodeHighlight(TopoElementType type, String elementId) {
super(type, elementId);
}
}
......@@ -17,28 +17,75 @@
package org.onosproject.ui.topo;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.ui.topo.Highlights.Amount;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* Unit tests for {@link Highlights}.
*/
public class HighlightsTest {
private Highlights hl;
private static final String DEV_1 = "dev-1";
private static final String DEV_2 = "dev-2";
private static final String HOST_A = "Host...A";
private Highlights highlights;
private DeviceHighlight dh1;
private DeviceHighlight dh2;
private HostHighlight hha;
@Before
public void setUp() {
highlights = new Highlights();
}
@Test
public void basic() {
hl = new Highlights();
assertEquals("devices", 0, highlights.devices().size());
assertEquals("hosts", 0, highlights.hosts().size());
assertEquals("links", 0, highlights.links().size());
assertEquals("sudue", Amount.ZERO, highlights.subdueLevel());
}
@Test
public void coupleOfDevices() {
dh1 = new DeviceHighlight(DEV_1);
dh2 = new DeviceHighlight(DEV_2);
highlights.add(dh1);
highlights.add(dh2);
assertTrue("missing dh1", highlights.devices().contains(dh1));
assertTrue("missing dh2", highlights.devices().contains(dh2));
}
assertEquals("devices", 0, hl.devices().size());
assertEquals("hosts", 0, hl.hosts().size());
assertEquals("links", 0, hl.links().size());
assertEquals("sudue", Highlights.Amount.ZERO, hl.subdueLevel());
@Test
public void alternateSubdue() {
highlights.subdueAllElse(Amount.MINIMALLY);
assertEquals("wrong level", Amount.MINIMALLY, highlights.subdueLevel());
}
@Test
public void highlightRetrieval() {
dh1 = new DeviceHighlight(DEV_1);
hha = new HostHighlight(HOST_A);
highlights.add(dh1)
.add(hha);
assertNull("dev as host", highlights.getHost(DEV_1));
assertNull("host as dev", highlights.getDevice(HOST_A));
assertEquals("missed dev as dev", dh1, highlights.getDevice(DEV_1));
assertEquals("missed dev as node", dh1, highlights.getNode(DEV_1));
assertEquals("missed host as host", hha, highlights.getHost(HOST_A));
assertEquals("missed host as node", hha, highlights.getNode(HOST_A));
}
// NOTE: further unit tests involving the Highlights class are done
// in TopoJsonTest.
}
......
......@@ -20,7 +20,9 @@ package org.onosproject.ui.impl;
import com.google.common.collect.ImmutableList;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.ElementId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.FlowEntry;
......@@ -40,8 +42,12 @@ import org.onosproject.ui.impl.topo.TopoIntentFilter;
import org.onosproject.ui.impl.topo.TrafficLink;
import org.onosproject.ui.impl.topo.TrafficLink.StatsType;
import org.onosproject.ui.impl.topo.TrafficLinkMap;
import org.onosproject.ui.topo.DeviceHighlight;
import org.onosproject.ui.topo.Highlights;
import org.onosproject.ui.topo.Highlights.Amount;
import org.onosproject.ui.topo.HostHighlight;
import org.onosproject.ui.topo.LinkHighlight.Flavor;
import org.onosproject.ui.topo.NodeHighlight;
import org.onosproject.ui.topo.NodeSelection;
import org.onosproject.ui.topo.TopoUtils;
import org.slf4j.Logger;
......@@ -59,9 +65,7 @@ import java.util.Timer;
import java.util.TimerTask;
import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
import static org.onosproject.ui.impl.TrafficMonitor.Mode.IDLE;
import static org.onosproject.ui.impl.TrafficMonitor.Mode.RELATED_INTENTS;
import static org.onosproject.ui.impl.TrafficMonitor.Mode.SELECTED_INTENT;
import static org.onosproject.ui.impl.TrafficMonitor.Mode.*;
/**
* Encapsulates the behavior of monitoring specific traffic patterns.
......@@ -442,6 +446,7 @@ public class TrafficMonitor {
current.id(), selectedIntents.index(), selectedIntents.size());
highlightIntentLinksWithTraffic(highlights, primary);
highlights.subdueAllElse(Amount.MINIMALLY);
}
return highlights;
}
......@@ -540,19 +545,20 @@ public class TrafficMonitor {
TrafficLinkMap linkMap = new TrafficLinkMap();
// NOTE: highlight secondary first, then primary, so that links shared
// by intents are colored correctly ("last man wins")
createTrafficLinks(linkMap, secondary, Flavor.SECONDARY_HIGHLIGHT, false);
createTrafficLinks(linkMap, primary, Flavor.PRIMARY_HIGHLIGHT, false);
createTrafficLinks(highlights, linkMap, secondary, Flavor.SECONDARY_HIGHLIGHT, false);
createTrafficLinks(highlights, linkMap, primary, Flavor.PRIMARY_HIGHLIGHT, false);
colorLinks(highlights, linkMap);
}
private void highlightIntentLinksWithTraffic(Highlights highlights,
Set<Intent> primary) {
TrafficLinkMap linkMap = new TrafficLinkMap();
createTrafficLinks(linkMap, primary, Flavor.PRIMARY_HIGHLIGHT, true);
createTrafficLinks(highlights, linkMap, primary, Flavor.PRIMARY_HIGHLIGHT, true);
colorLinks(highlights, linkMap);
}
private void createTrafficLinks(TrafficLinkMap linkMap, Set<Intent> intents,
private void createTrafficLinks(Highlights highlights,
TrafficLinkMap linkMap, Set<Intent> intents,
Flavor flavor, boolean showTraffic) {
for (Intent intent : intents) {
List<Intent> installables = servicesBundle.intentService()
......@@ -573,11 +579,33 @@ public class TrafficMonitor {
boolean isOptical = intent instanceof OpticalConnectivityIntent;
processLinks(linkMap, links, flavor, isOptical, showTraffic);
updateHighlights(highlights, links);
}
}
}
}
private void updateHighlights(Highlights highlights, Iterable<Link> links) {
for (Link link : links) {
ensureNodePresent(highlights, link.src().elementId());
ensureNodePresent(highlights, link.dst().elementId());
}
}
private void ensureNodePresent(Highlights highlights, ElementId eid) {
String id = eid.toString();
NodeHighlight nh = highlights.getNode(id);
if (nh == null) {
if (eid instanceof DeviceId) {
nh = new DeviceHighlight(id);
highlights.add((DeviceHighlight) nh);
} else if (eid instanceof HostId) {
nh = new HostHighlight(id);
highlights.add((HostHighlight) nh);
}
}
}
// Extracts links from the specified flow rule intent resources
private Collection<Link> linkResources(Intent installable) {
ImmutableList.Builder<Link> builder = ImmutableList.builder();
......
......@@ -90,13 +90,13 @@ public final class TopoJson {
}
private static ObjectNode json(DeviceHighlight dh) {
// TODO: implement this once we know what a device highlight looks like
return objectNode();
return objectNode()
.put(ID, dh.elementId());
}
private static ObjectNode json(HostHighlight hh) {
// TODO: implement this once we know what a host highlight looks like
return objectNode();
return objectNode()
.put(ID, hh.elementId());
}
private static ObjectNode json(LinkHighlight lh) {
......
......@@ -242,6 +242,10 @@
// ========================
function nodeById(id) {
return lu[id];
}
function makeNodeKey(node1, node2) {
return node1 + '-' + node2;
}
......@@ -515,10 +519,10 @@
});
}
function unsuppressLink(id, less) {
function unsuppressLink(key, less) {
var cls = supAmt(less);
link.each(function (n) {
if (n.id === id) {
if (n.key === key) {
n.el.classed(cls, false);
}
});
......@@ -922,6 +926,7 @@
clearLinkTrafficStyle: clearLinkTrafficStyle,
removeLinkLabels: removeLinkLabels,
findLinkById: tms.findLinkById,
findNodeById: nodeById,
updateLinks: updateLinks,
updateNodes: updateNodes,
supLayers: suppressLayers,
......
......@@ -301,11 +301,12 @@
clearLinkTrafficStyle()
removeLinkLabels()
findLinkById( id )
findNodeById( id )
updateLinks()
updateNodes()
supLayers( bool, [less] )
unsupNode( id, [less] )
unsupLink( id, [less] )
unsupLink( key, [less] )
*/
// TODO: clear node highlighting
......@@ -322,16 +323,30 @@
api.supLayers(false, true);
}
// TODO: device and host highlights
data.hosts.forEach(function (host) {
var hdata = api.findNodeById(host.id);
if (hdata && !hdata.el.empty()) {
api.unsupNode(hdata.id, less);
// TODO: further highlighting?
}
});
data.devices.forEach(function (device) {
var ddata = api.findNodeById(device.id);
if (ddata && !ddata.el.empty()) {
api.unsupNode(ddata.id, less);
// TODO: further highlighting?
}
});
data.links.forEach(function (lnk) {
var ldata = api.findLinkById(lnk.id),
lab = lnk.label,
data.links.forEach(function (link) {
var ldata = api.findLinkById(link.id),
lab = link.label,
units, portcls, magnitude;
if (ldata && !ldata.el.empty()) {
api.unsupLink(ldata.id, less);
ldata.el.classed(lnk.css, true);
api.unsupLink(ldata.key, less);
ldata.el.classed(link.css, true);
ldata.label = lab;
// inject additional styling for port-based traffic
......