blob: b24bd858370724bb3fed9f88562500a9900573c5 [file] [log] [blame]
/*
* Copyright 2015-present 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.pathpainter;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.net.DeviceId;
import org.onosproject.net.DisjointPath;
import org.onosproject.net.ElementId;
import org.onosproject.net.HostId;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.topology.GeoDistanceLinkWeight;
import org.onosproject.net.topology.LinkWeight;
import org.onosproject.net.topology.PathService;
import org.onosproject.net.topology.TopologyEvent;
import org.onosproject.net.topology.TopologyListener;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.topo.DeviceHighlight;
import org.onosproject.ui.topo.Highlights;
import org.onosproject.ui.topo.HostHighlight;
import org.onosproject.ui.topo.NodeBadge;
import org.onosproject.ui.topo.TopoJson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* ONOS UI PathPainter Topology-Overlay message handler.
*/
public class PathPainterTopovMessageHandler extends UiMessageHandler {
private static final String PAINTER_CLEAR = "ppTopovClear";
private static final String PAINTER_SET_SRC = "ppTopovSetSrc";
private static final String PAINTER_SET_DST = "ppTopovSetDst";
private static final String PAINTER_SWAP_SRC_DST = "ppTopovSwapSrcDst";
private static final String PAINTER_SET_MODE = "ppTopovSetMode";
private static final String PAINTER_NEXT_PATH = "ppTopovNextPath";
private static final String PAINTER_PREV_PATH = "ppTopovPrevPath";
private static final String ID = "id";
private static final String MODE = "mode";
private static final String TYPE = "type";
private static final String SWITCH = "switch";
private static final String ENDSTATION = "endstation";
public static final String DST = "Dst";
public static final String SRC = "Src";
// Delay for showHighlights event processing on GUI client side to
// account for addLink animation.
public static final int DELAY_MS = 1100;
private final TopologyListener topologyListener = new InternalTopologyListener();
private Set<Link> allPathLinks;
private boolean listenersRemoved;
private LinkWeight linkData;
private int highlightDelay;
private enum Mode {
SHORTEST, DISJOINT, GEODATA, SRLG, INVALID
}
private final Logger log = LoggerFactory.getLogger(getClass());
private PathService pathService;
private ElementId src, dst;
private String srcType, dstType;
private Mode currentMode = Mode.SHORTEST;
private List<Path> paths;
private int pathIndex;
protected TopologyService topologyService;
// ===============-=-=-=-=-=-======================-=-=-=-=-=-=-================================
@Override
public void init(UiConnection connection, ServiceDirectory directory) {
super.init(connection, directory);
pathService = directory.get(PathService.class);
topologyService = directory.get(TopologyService.class);
linkData = new GeoDistanceLinkWeight(directory.get(DeviceService.class));
addListeners();
}
@Override
public void destroy() {
removeListeners();
super.destroy();
}
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new ClearHandler(),
new SetSrcHandler(),
new SetDstHandler(),
new SwapSrcDstHandler(),
new NextPathHandler(),
new PrevPathHandler(),
new SetModeHandler()
);
}
// === -------------------------
// === Handler classes
private final class ClearHandler extends RequestHandler {
public ClearHandler() {
super(PAINTER_CLEAR);
}
@Override
public void process(long sid, ObjectNode payload) {
src = null;
dst = null;
sendMessage(TopoJson.highlightsMessage(new Highlights()));
}
}
private final class SetSrcHandler extends RequestHandler {
public SetSrcHandler() {
super(PAINTER_SET_SRC);
}
@Override
public void process(long sid, ObjectNode payload) {
String id = string(payload, ID);
src = elementId(id);
srcType = string(payload, TYPE);
if (src.equals(dst)) {
dst = null;
}
sendMessage(TopoJson.highlightsMessage(addBadge(new Highlights(),
srcType,
src.toString(),
SRC)));
findAndSendPaths(currentMode);
}
}
private final class SetDstHandler extends RequestHandler {
public SetDstHandler() {
super(PAINTER_SET_DST);
}
@Override
public void process(long sid, ObjectNode payload) {
String id = string(payload, ID);
dst = elementId(id);
dstType = string(payload, TYPE);
if (src.equals(dst)) {
src = null;
}
sendMessage(TopoJson.highlightsMessage(addBadge(new Highlights(),
dstType,
dst.toString(),
DST)));
findAndSendPaths(currentMode);
}
}
private final class SwapSrcDstHandler extends RequestHandler {
public SwapSrcDstHandler() {
super(PAINTER_SWAP_SRC_DST);
}
@Override
public void process(long sid, ObjectNode payload) {
ElementId temp = src;
src = dst;
dst = temp;
String s = srcType;
srcType = dstType;
dstType = s;
findAndSendPaths(currentMode);
}
}
private final class NextPathHandler extends RequestHandler {
public NextPathHandler() {
super(PAINTER_NEXT_PATH);
}
@Override
public void process(long sid, ObjectNode payload) {
pathIndex = (pathIndex >= paths.size() - 1 ? 0 : pathIndex + 1);
hilightAndSendPaths();
}
}
private final class PrevPathHandler extends RequestHandler {
public PrevPathHandler() {
super(PAINTER_PREV_PATH);
}
@Override
public void process(long sid, ObjectNode payload) {
pathIndex = (pathIndex <= 0 ? paths.size() - 1 : pathIndex - 1);
hilightAndSendPaths();
}
}
private final class SetModeHandler extends RequestHandler {
public SetModeHandler() {
super(PAINTER_SET_MODE);
}
@Override
public void process(long sid, ObjectNode payload) {
String mode = string(payload, MODE);
switch (mode) {
case "shortest":
currentMode = Mode.SHORTEST;
break;
case "disjoint":
currentMode = Mode.DISJOINT;
break;
case "geodata":
currentMode = Mode.GEODATA;
break;
case "srlg":
currentMode = Mode.SRLG;
break;
default:
currentMode = Mode.INVALID;
break;
}
//TODO: add support for SRLG
findAndSendPaths(currentMode);
}
}
// === ------------
private ElementId elementId(String id) {
try {
return DeviceId.deviceId(id);
} catch (IllegalArgumentException e) {
return HostId.hostId(id);
}
}
private void findAndSendPaths(Mode mode) {
log.debug("src={}; dst={}; mode={}", src, dst, currentMode);
if (src != null && dst != null) {
pathIndex = 0;
ImmutableSet.Builder<Link> builder = ImmutableSet.builder();
if (mode.equals(Mode.SHORTEST)) {
paths = ImmutableList.copyOf(pathService.getPaths(src, dst));
allPathLinks = buildPaths(builder).build();
} else if (mode.equals(Mode.DISJOINT)) {
paths = ImmutableList.copyOf(pathService.getDisjointPaths(src, dst));
allPathLinks = buildDisjointPaths(builder).build();
} else if (mode.equals(Mode.GEODATA)) {
paths = ImmutableList.copyOf(pathService.getPaths(src, dst, linkData));
allPathLinks = buildPaths(builder).build();
} else {
log.info("Unsupported MODE");
}
} else {
paths = ImmutableList.of();
allPathLinks = ImmutableSet.of();
}
hilightAndSendPaths();
}
private ImmutableSet.Builder<Link> buildPaths(ImmutableSet.Builder<Link> pathBuilder) {
paths.forEach(path -> path.links().forEach(pathBuilder::add));
return pathBuilder;
}
private ImmutableSet.Builder<Link> buildDisjointPaths(ImmutableSet.Builder<Link> pathBuilder) {
paths.forEach(path -> {
DisjointPath dp = (DisjointPath) path;
pathBuilder.addAll(dp.primary().links());
pathBuilder.addAll(dp.backup().links());
});
return pathBuilder;
}
private void hilightAndSendPaths() {
PathLinkMap linkMap = new PathLinkMap();
allPathLinks.forEach(linkMap::add);
Set<Link> selectedPathLinks;
// Prepare two working sets; one containing selected path links and
// the other containing all paths links.
if (currentMode.equals(Mode.DISJOINT)) {
DisjointPath dp = (DisjointPath) paths.get(pathIndex);
selectedPathLinks = paths.isEmpty() ?
ImmutableSet.of() : Sets.newHashSet(dp.primary().links());
selectedPathLinks.addAll(dp.backup().links());
} else {
selectedPathLinks = paths.isEmpty() ?
ImmutableSet.of() : ImmutableSet.copyOf(paths.get(pathIndex).links());
}
Highlights highlights = new Highlights();
if (highlightDelay > 0) {
highlights.delay(highlightDelay);
}
for (PathLink plink : linkMap.biLinks()) {
plink.computeHilight(selectedPathLinks, allPathLinks);
highlights.add(plink.highlight(null));
}
if (src != null) {
highlights = addBadge(highlights, srcType, src.toString(), SRC);
}
if (dst != null) {
highlights = addBadge(highlights, dstType, dst.toString(), DST);
}
sendMessage(TopoJson.highlightsMessage(highlights));
}
private Highlights addBadge(Highlights highlights, String type, String elemId, String src) {
if (SWITCH.equals(type)) {
highlights = addDeviceBadge(highlights, elemId, src);
} else if (ENDSTATION.equals(type)) {
highlights = addHostBadge(highlights, elemId, src);
}
return highlights;
}
private Highlights addDeviceBadge(Highlights h, String elemId, String type) {
DeviceHighlight dh = new DeviceHighlight(elemId);
dh.setBadge(createBadge(type));
h.add(dh);
return h;
}
private Highlights addHostBadge(Highlights h, String elemId, String type) {
HostHighlight hh = new HostHighlight(elemId);
hh.setBadge(createBadge(type));
h.add(hh);
return h;
}
private NodeBadge createBadge(String type) {
return NodeBadge.text(type);
}
private synchronized void addListeners() {
listenersRemoved = false;
topologyService.addListener(topologyListener);
}
private synchronized void removeListeners() {
if (!listenersRemoved) {
listenersRemoved = true;
topologyService.removeListener(topologyListener);
}
}
// Link event listener.
private class InternalTopologyListener implements TopologyListener {
@Override
public void event(TopologyEvent event) {
highlightDelay = DELAY_MS;
findAndSendPaths(currentMode);
highlightDelay = 0;
}
}
}