Thomas Vachuska
Committed by Gerrit Code Review

ONOS-3182 Starting on path visualization app.

Change-Id: Id9b074afb22599473b1849acc380fa189061e8bb
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-apps</artifactId>
<version>1.4.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-app-pp</artifactId>
<packaging>bundle</packaging>
<description>Path visualization application</description>
<properties>
<onos.app.name>org.onosproject.pathpainter</onos.app.name>
</properties>
</project>
/*
* Copyright 2014,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.pathpainter;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import org.onosproject.ui.topo.BiLink;
import org.onosproject.ui.topo.LinkHighlight;
import org.onosproject.ui.topo.LinkHighlight.Flavor;
import java.util.Set;
/**
* Bi-directional link capable of different hilights.
*/
public class PathLink extends BiLink {
private boolean primary = false;
private boolean secondary = false;
public PathLink(LinkKey key, Link link) {
super(key, link);
}
public void computeHilight(Set<Link> selectedLinks, Set<Link> allLinks) {
primary = selectedLinks.contains(this.one()) ||
(two() != null && selectedLinks.contains(two()));
secondary = allLinks.contains(this.one()) ||
(two() != null && allLinks.contains(two()));
}
@Override
public LinkHighlight highlight(Enum<?> anEnum) {
Flavor flavor = primary ? Flavor.PRIMARY_HIGHLIGHT :
(secondary ? Flavor.SECONDARY_HIGHLIGHT : Flavor.NO_HIGHLIGHT);
return new LinkHighlight(this.linkId(), flavor);
}
}
/*
* Copyright 2014,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.pathpainter;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import org.onosproject.ui.topo.BiLinkMap;
/**
* Our concrete link map.
*/
public class PathLinkMap extends BiLinkMap<PathLink> {
@Override
protected PathLink create(LinkKey linkKey, Link link) {
return new PathLink(linkKey, link);
}
}
/*
* Copyright 2014,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.pathpainter;
import com.google.common.collect.ImmutableList;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.ui.UiExtension;
import org.onosproject.ui.UiExtensionService;
import org.onosproject.ui.UiMessageHandlerFactory;
import org.onosproject.ui.UiTopoOverlayFactory;
import org.onosproject.ui.UiView;
import org.onosproject.ui.UiViewHidden;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Skeletal ONOS UI Topology-Overlay application component.
*/
@Component(immediate = true)
public class PathPainter {
private static final ClassLoader CL = PathPainter.class.getClassLoader();
private static final String VIEW_ID = "ppTopov";
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected UiExtensionService uiExtensionService;
// List of application views
private final List<UiView> uiViews = ImmutableList.of(
new UiViewHidden(VIEW_ID)
);
// Factory for UI message handlers
private final UiMessageHandlerFactory messageHandlerFactory =
() -> ImmutableList.of(
new PathPainterTopovMessageHandler()
);
// Factory for UI topology overlays
private final UiTopoOverlayFactory topoOverlayFactory =
() -> ImmutableList.of(
new PathPainterTopovOverlay()
);
// Application UI extension
protected UiExtension extension =
new UiExtension.Builder(CL, uiViews)
.resourcePath(VIEW_ID)
.messageHandlerFactory(messageHandlerFactory)
.topoOverlayFactory(topoOverlayFactory)
.build();
@Activate
protected void activate() {
uiExtensionService.register(extension);
log.info("Started");
}
@Deactivate
protected void deactivate() {
uiExtensionService.unregister(extension);
log.info("Stopped");
}
}
/*
* Copyright 2014,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.pathpainter;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.onlab.osgi.ServiceDirectory;
import org.onosproject.net.DeviceId;
import org.onosproject.net.ElementId;
import org.onosproject.net.HostId;
import org.onosproject.net.Link;
import org.onosproject.net.Path;
import org.onosproject.net.topology.PathService;
import org.onosproject.ui.RequestHandler;
import org.onosproject.ui.UiConnection;
import org.onosproject.ui.UiMessageHandler;
import org.onosproject.ui.topo.Highlights;
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;
/**
* Skeletal ONOS UI Topology-Overlay message handler.
*/
public class PathPainterTopovMessageHandler extends UiMessageHandler {
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 Set<Link> allPathLinks;
private enum Mode {
SHORTEST, DISJOINT, SRLG
}
private final Logger log = LoggerFactory.getLogger(getClass());
private PathService pathService;
private Mode currentMode = Mode.SHORTEST;
private ElementId src, dst;
private Mode mode = Mode.SHORTEST;
private List<Path> paths;
private int pathIndex;
// ===============-=-=-=-=-=-======================-=-=-=-=-=-=-================================
@Override
public void init(UiConnection connection, ServiceDirectory directory) {
super.init(connection, directory);
pathService = directory.get(PathService.class);
}
@Override
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new SetSrcHandler(),
new SetDstHandler(),
new NextPathHandler(),
new PrevPathHandler()
);
}
// === -------------------------
// === Handler classes
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);
if (src.equals(dst)) {
dst = null;
}
findAndSendPaths();
}
}
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);
if (src.equals(dst)) {
src = null;
}
findAndSendPaths();
}
}
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 ElementId elementId(String id) {
try {
return DeviceId.deviceId(id);
} catch (IllegalArgumentException e) {
return HostId.hostId(id);
}
}
private void findAndSendPaths() {
log.info("src={}; dst={}; mode={}", src, dst, mode);
if (src != null && dst != null) {
paths = ImmutableList.copyOf(pathService.getPaths(src, dst));
pathIndex = 0;
ImmutableSet.Builder<Link> builder = ImmutableSet.builder();
paths.forEach(path -> path.links().forEach(builder::add));
allPathLinks = builder.build();
} else {
paths = ImmutableList.of();
allPathLinks = ImmutableSet.of();
}
hilightAndSendPaths();
}
private void hilightAndSendPaths() {
PathLinkMap linkMap = new PathLinkMap();
allPathLinks.forEach(linkMap::add);
// Prepare two working sets; one containing selected path links and
// the other containing all paths links.
Set<Link> selectedPathLinks = paths.isEmpty() ?
ImmutableSet.of() : ImmutableSet.copyOf(paths.get(pathIndex).links());
Highlights highlights = new Highlights();
for (PathLink plink : linkMap.biLinks()) {
plink.computeHilight(selectedPathLinks, allPathLinks);
highlights.add(plink.highlight(null));
}
sendMessage(TopoJson.highlightsMessage(highlights));
}
/*
private void addDeviceBadge(Highlights h, DeviceId devId, int n) {
DeviceHighlight dh = new DeviceHighlight(devId.toString());
dh.setBadge(createBadge(n));
h.add(dh);
}
private NodeBadge createBadge(int n) {
Status status = n > 3 ? Status.ERROR : Status.WARN;
String noun = n > 3 ? "(critical)" : "(problematic)";
String msg = "Egress links: " + n + " " + noun;
return NodeBadge.number(status, n, msg);
}
*/
}
\ No newline at end of file
/*
* Copyright 2014,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.pathpainter;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.ui.UiTopoOverlay;
import org.onosproject.ui.topo.ButtonId;
import org.onosproject.ui.topo.PropertyPanel;
/**
* Our topology overlay.
*/
public class PathPainterTopovOverlay extends UiTopoOverlay {
// NOTE: this must match the ID defined in ppTopovOverlay.js
private static final String OVERLAY_ID = "pp-overlay";
private static final ButtonId SRC_BUTTON = new ButtonId("src");
private static final ButtonId DST_BUTTON = new ButtonId("dst");
public PathPainterTopovOverlay() {
super(OVERLAY_ID);
}
@Override
public void modifyDeviceDetails(PropertyPanel pp, DeviceId deviceId) {
pp.addButton(SRC_BUTTON).addButton(DST_BUTTON);
}
@Override
public void modifyHostDetails(PropertyPanel pp, HostId hostId) {
pp.addButton(SRC_BUTTON).addButton(DST_BUTTON);
}
}
/*
* Copyright 2014-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.
*/
/**
* Path visualization GUI topology view overlay.
*/
package org.onosproject.pathpainter;
\ No newline at end of file
<!-- partial HTML -->
<div id="ov-pp-topov">
<p>This is a hidden view .. just a placeholder to house the javascript</p>
</div>
/*
* 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.
*/
/*
Sample Demo module. This contains the "business logic" for the topology
overlay that we are implementing.
*/
(function () {
'use strict';
// injected refs
var $log, fs, flash, wss;
// constants
var srcMessage = 'ppTopovSetSrc',
dstMessage = 'ppTopovSetDst',
modeMessage = 'ppTopovSetMode',
nextPathMessage = 'ppTopovNextPath',
prevPathMessage = 'ppTopovPrevPath';
// internal state
var currentMode = null;
// === ---------------------------
// === Helper functions
// === ---------------------------
// === Main API functions
function setSrc(node) {
wss.sendEvent(srcMessage, {
id: node.id
});
flash.flash('Source node: ' + node.id);
}
function setDst(node) {
wss.sendEvent(dstMessage, {
id: node.id
});
flash.flash('Destination node: ' + node.id);
}
function nextPath(node) {
wss.sendEvent(nextPathMessage);
}
function prevPath(node) {
wss.sendEvent(prevPathMessage);
}
function setMode(mode) {
if (currentMode === mode) {
$log.debug('(in mode', mode, 'already)');
} else {
currentMode = mode;
wss.sendEvent(modeMessage, {
mode: mode
});
flash.flash('Path mode: ' + mode);
}
}
// === ---------------------------
// === Module Factory Definition
angular.module('ovPpTopov', [])
.factory('PathPainterTopovService',
['$log', 'FnService', 'FlashService', 'WebSocketService',
function (_$log_, _fs_, _flash_, _wss_) {
$log = _$log_;
fs = _fs_;
flash = _flash_;
wss = _wss_;
return {
setSrc: setSrc,
setDst: setDst,
setMode: setMode,
nextPath: nextPath,
prevPath: prevPath
};
}]);
}());
// path painter topology overlay - client side
//
// This is the glue that binds our business logic (in ppTopovDemo.js)
// to the overlay framework.
(function () {
'use strict';
// injected refs
var $log, tov, pps;
// internal state should be kept in the service module (not here)
var selection;
// our overlay definition
var overlay = {
// NOTE: this must match the ID defined in AppUiTopovOverlay
overlayId: 'pp-overlay',
// FIXME: new icon for the overlay
glyphId: '*star4',
tooltip: 'Path Painter Topo Overlay',
// These glyphs get installed using the overlayId as a prefix.
// e.g. 'star4' is installed as 'meowster-overlay-star4'
// They can be referenced (from this overlay) as '*star4'
// That is, the '*' prefix stands in for 'meowster-overlay-'
glyphs: {
star4: {
vb: '0 0 8 8',
d: 'M1,4l2,-1l1,-2l1,2l2,1l-2,1l-1,2l-1,-2z'
},
banner: {
vb: '0 0 6 6',
d: 'M1,1v4l2,-2l2,2v-4z'
}
},
activate: function () {
$log.debug("Path painter topology overlay ACTIVATED");
},
deactivate: function () {
$log.debug("Path painter topology overlay DEACTIVATED");
},
// detail panel button definitions
// FIXME: new icons for src/dst
buttons: {
src: {
gid: 'triangleUp',
tt: 'Set source node',
cb: function (data) {
$log.debug('Set src action invoked with data:', data);
pps.setSrc(selection);
}
},
dst: {
gid: 'triangleDown',
tt: 'Set destination node',
cb: function (data) {
$log.debug('Set dst action invoked with data:', data);
pps.setDst(selection);
}
}
},
// Key bindings for traffic overlay buttons
// NOTE: fully qual. button ID is derived from overlay-id and key-name
// FIXME: use into [ and ] instead of 1 and 2
// FIXME: new icons for src/dst
// TODO: add keys for shortest paths & disjoint paths modes
// TODO: add key for src/dst swap; with its own icon
keyBindings: {
1: {
cb: function () { pps.setSrc(selection); },
tt: 'Set source node',
gid: 'triangleUp'
},
2: {
cb: function () { pps.setDst(selection); },
tt: 'Set destination node',
gid: 'triangleDown'
},
leftArrow: {
cb: function () { pps.prevPath(); },
tt: 'Highlight previous path',
gid: 'prevIntent'
},
rightArrow: {
cb: function () { pps.nextPath(); },
tt: 'Highlight next path',
gid: 'nextIntent'
},
_keyOrder: [
'1', '2', 'leftArrow', 'rightArrow'
]
},
hooks: {
// hook for handling escape key
// Must return true to consume ESC, false otherwise.
escape: function () {
selectionCallback();
pps.setSrc();
pps.setDst();
},
// hooks for when the selection changes...
empty: function () {
selectionCallback();
},
single: function (data) {
selectionCallback(data);
}
}
};
function buttonCallback(x) {
$log.debug('Toolbar-button callback', x);
}
function selectionCallback(d) {
$log.debug('Selection callback', d);
selection = d;
}
// invoke code to register with the overlay service
angular.module('ovPpTopov')
.run(['$log', 'TopoOverlayService', 'PathPainterTopovService',
function (_$log_, _tov_, _pps_) {
$log = _$log_;
tov = _tov_;
pps = _pps_;
tov.register(overlay);
}]);
}());
<link rel="stylesheet" href="app/view/ppTopov/ppTopov.css">
\ No newline at end of file
<script src="app/view/ppTopov/ppTopov.js"></script>
<script src="app/view/ppTopov/ppTopovOverlay.js"></script>
\ No newline at end of file
......@@ -59,6 +59,7 @@
<module>pim</module>
<module>mlb</module>
<module>openstackswitching</module>
<module>pathpainter</module>
</modules>
<properties>
......