summaryrefslogtreecommitdiffstats
path: root/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js')
-rw-r--r--framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js418
1 files changed, 418 insertions, 0 deletions
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
new file mode 100644
index 00000000..7eb45ba4
--- /dev/null
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ ONOS GUI -- Topology Overlay Module.
+
+ Provides overlay capabilities, allowing ONOS apps to provide additional
+ custom data/behavior for the topology view.
+
+ */
+
+(function () {
+ 'use strict';
+
+ // constants
+ var tos = 'TopoOverlayService: ';
+
+ // injected refs
+ var $log, fs, gs, wss, ns, tss, tps, api;
+
+ // internal state
+ var overlays = {},
+ current = null;
+
+ function error(fn, msg) {
+ $log.error(tos + fn + '(): ' + msg);
+ }
+
+ function warn(fn, msg) {
+ $log.warn(tos + fn + '(): ' + msg);
+ }
+
+ function mkGlyphId(oid, gid) {
+ return (gid[0] === '*') ? oid + '-' + gid.slice(1) : gid;
+ }
+
+ function handleGlyphs(o) {
+ var gdata = fs.isO(o.glyphs),
+ oid = o.overlayId,
+ gid = o.glyphId || 'unknown',
+ data = {},
+ note = [];
+
+ o._glyphId = mkGlyphId(oid, gid);
+
+ o.mkGid = function (g) {
+ return mkGlyphId(oid, g);
+ };
+ o.mkId = function (s) {
+ return oid + '-' + s;
+ };
+
+ // process glyphs if defined
+ if (gdata) {
+ angular.forEach(gdata, function (value, key) {
+ var fullkey = oid + '-' + key;
+ data['_' + fullkey] = value.vb;
+ data[fullkey] = value.d;
+ note.push('*' + key);
+ });
+ gs.registerGlyphs(data);
+ $log.debug('registered overlay glyphs:', oid, note);
+ }
+ }
+
+ function register(overlay) {
+ var r = 'register',
+ over = fs.isO(overlay),
+ kb = over ? fs.isO(overlay.keyBindings) : null,
+ id = over ? over.overlayId : '';
+
+ if (!id) {
+ return error(r, 'not a recognized overlay');
+ }
+ if (overlays[id]) {
+ return warn(r, 'already registered: "' + id + '"');
+ }
+ overlays[id] = overlay;
+ handleGlyphs(overlay);
+
+ if (kb) {
+ if (!fs.isA(kb._keyOrder)) {
+ warn(r, 'no _keyOrder array defined on keyBindings');
+ } else {
+ kb._keyOrder.forEach(function (k) {
+ if (k !== '-' && !kb[k]) {
+ warn(r, 'no "' + k + '" property defined on keyBindings');
+ }
+ });
+ }
+ }
+
+ $log.debug(tos + 'registered overlay: ' + id, overlay);
+ }
+
+ // TODO: remove this redundant code.......
+ // NOTE: unregister needs to be called if an app is ever
+ // deactivated/uninstalled via the applications view
+/*
+ function unregister(overlay) {
+ var u = 'unregister',
+ over = fs.isO(overlay),
+ id = over ? over.overlayId : '';
+
+ if (!id) {
+ return error(u, 'not a recognized overlay');
+ }
+ if (!overlays[id]) {
+ return warn(u, 'not registered: "' + id + "'")
+ }
+ delete overlays[id];
+ $log.debug(tos + 'unregistered overlay: ' + id);
+ }
+*/
+
+
+ // returns the list of overlay identifiers
+ function list() {
+ return d3.map(overlays).keys();
+ }
+
+ // add a radio button for each registered overlay
+ function augmentRbset(rset, switchFn) {
+ angular.forEach(overlays, function (ov) {
+ rset.push({
+ gid: ov._glyphId,
+ tooltip: (ov.tooltip || '(no tooltip)'),
+ cb: function () {
+ tbSelection(ov.overlayId, switchFn);
+ }
+ });
+ });
+ }
+
+ // an overlay was selected via toolbar radio button press from user
+ function tbSelection(id, switchFn) {
+ var same = current && current.overlayId === id,
+ payload = {},
+ actions;
+
+ function doop(op) {
+ var oid = current.overlayId;
+ $log.debug('Overlay:', op, oid);
+ current[op]();
+ payload[op] = oid;
+ }
+
+ if (!same) {
+ current && doop('deactivate');
+ current = overlays[id];
+ current && doop('activate');
+ actions = current && fs.isO(current.keyBindings);
+ switchFn(id, actions);
+
+ wss.sendEvent('topoSelectOverlay', payload);
+
+ // Ensure summary and details panels are updated immediately..
+ wss.sendEvent('requestSummary');
+ tss.updateDetail();
+ }
+ }
+
+ var coreButtons = {
+ showDeviceView: {
+ gid: 'switch',
+ tt: 'Show Device View',
+ path: 'device'
+ },
+ showFlowView: {
+ gid: 'flowTable',
+ tt: 'Show Flow View for this Device',
+ path: 'flow'
+ },
+ showPortView: {
+ gid: 'portTable',
+ tt: 'Show Port View for this Device',
+ path: 'port'
+ },
+ showGroupView: {
+ gid: 'groupTable',
+ tt: 'Show Group View for this Device',
+ path: 'group'
+ }
+ };
+
+ // retrieves a button definition from the current overlay and generates
+ // a button descriptor to be added to the panel, with the data baked in
+ function _getButtonDef(id, data) {
+ var btns = current && current.buttons,
+ b = btns && btns[id],
+ cb = fs.isF(b.cb),
+ f = cb ? function () { cb(data); } : function () {};
+
+ return b ? {
+ id: current.mkId(id),
+ gid: current.mkGid(b.gid),
+ tt: b.tt,
+ cb: f
+ } : null;
+ }
+
+ // install core buttons, and include any additional from the current overlay
+ function installButtons(buttons, data, devId) {
+ buttons.forEach(function (id) {
+ var btn = coreButtons[id],
+ gid = btn && btn.gid,
+ tt = btn && btn.tt,
+ path = btn && btn.path;
+
+ if (btn) {
+ tps.addAction({
+ id: 'core-' + id,
+ gid: gid,
+ tt: tt,
+ cb: function () { ns.navTo(path, {devId: devId }); }
+ });
+ } else if (btn = _getButtonDef(id, data)) {
+ tps.addAction(btn);
+ }
+ });
+ }
+
+ function addDetailButton(id) {
+ var b = _getButtonDef(id);
+ if (b) {
+ tps.addAction({
+ id: current.mkId(id),
+ gid: current.mkGid(b.gid),
+ cb: b.cb,
+ tt: b.tt
+ });
+ }
+ }
+
+
+ // === -----------------------------------------------------
+ // Hooks for overlays
+
+ function _hook(x) {
+ var h = current && current.hooks;
+ return h && fs.isF(h[x]);
+ }
+
+ function escapeHook() {
+ var eh = _hook('escape');
+ return eh ? eh() : false;
+ }
+
+ function emptySelectHook() {
+ var cb = _hook('empty');
+ cb && cb();
+ }
+
+ function singleSelectHook(data) {
+ var cb = _hook('single');
+ cb && cb(data);
+ }
+
+ function multiSelectHook(selectOrder) {
+ var cb = _hook('multi');
+ cb && cb(selectOrder);
+ }
+
+ function mouseOverHook(what) {
+ var cb = _hook('mouseover');
+ cb && cb(what);
+ }
+
+ function mouseOutHook() {
+ var cb = _hook('mouseout');
+ cb && cb();
+ }
+
+ // === -----------------------------------------------------
+ // Event (from server) Handlers
+
+ function setApi(_api_, _tss_) {
+ api = _api_;
+ tss = _tss_;
+ }
+
+ function showHighlights(data) {
+ var less;
+
+ /*
+ API to topoForce
+ clearLinkTrafficStyle()
+ removeLinkLabels()
+ findLinkById( id )
+ findNodeById( id )
+ updateLinks()
+ updateNodes()
+ supLayers( bool, [less] )
+ unsupNode( id, [less] )
+ unsupLink( key, [less] )
+ */
+
+ // TODO: clear node highlighting
+ api.clearLinkTrafficStyle();
+ api.removeLinkLabels();
+
+ // handle element suppression
+ if (data.subdue) {
+ less = data.subdue === 'min';
+ api.supLayers(true, less);
+
+ } else {
+ api.supLayers(false);
+ api.supLayers(false, true);
+ }
+
+ data.hosts.forEach(function (host) {
+ var hdata = api.findNodeById(host.id);
+ if (hdata && !hdata.el.empty()) {
+ if (!host.subdue) {
+ api.unsupNode(hdata.id, less);
+ }
+ // TODO: further highlighting?
+ }
+ });
+
+ data.devices.forEach(function (device) {
+ var ddata = api.findNodeById(device.id);
+ if (ddata && !ddata.el.empty()) {
+ if (!device.subdue) {
+ api.unsupNode(ddata.id, less);
+ }
+ // TODO: further highlighting?
+ }
+ });
+
+ data.links.forEach(function (link) {
+ var ldata = api.findLinkById(link.id),
+ lab = link.label,
+ units, portcls, magnitude;
+
+ if (ldata && !ldata.el.empty()) {
+ if (!link.subdue) {
+ api.unsupLink(ldata.key, less);
+ }
+ ldata.el.classed(link.css, true);
+ ldata.label = lab;
+
+ // TODO: this needs to be pulled out into traffic overlay
+ // inject additional styling for port-based traffic
+ if (fs.endsWith(lab, 'bps')) {
+ units = lab.substring(lab.length-4);
+ portcls = 'port-traffic-' + units;
+
+ // for GBps
+ if (units.substring(0,1) === 'G') {
+ magnitude = fs.parseBitRate(lab);
+ if (magnitude >= 9) {
+ portcls += '-choked'
+ }
+ }
+ ldata.el.classed(portcls, true);
+ }
+ }
+ });
+
+ api.updateNodes();
+ api.updateLinks();
+ }
+
+ // ========================================================================
+
+ angular.module('ovTopo')
+ .factory('TopoOverlayService',
+ ['$log', 'FnService', 'GlyphService', 'WebSocketService', 'NavService',
+ 'TopoPanelService',
+
+ function (_$log_, _fs_, _gs_, _wss_, _ns_, _tps_) {
+ $log = _$log_;
+ fs = _fs_;
+ gs = _gs_;
+ wss = _wss_;
+ ns = _ns_;
+ tps = _tps_;
+
+ return {
+ register: register,
+ //unregister: unregister,
+ setApi: setApi,
+ list: list,
+ augmentRbset: augmentRbset,
+ mkGlyphId: mkGlyphId,
+ tbSelection: tbSelection,
+ installButtons: installButtons,
+ addDetailButton: addDetailButton,
+ hooks: {
+ escape: escapeHook,
+ emptySelect: emptySelectHook,
+ singleSelect: singleSelectHook,
+ multiSelect: multiSelectHook,
+ mouseOver: mouseOverHook,
+ mouseOut: mouseOutHook
+ },
+
+ showHighlights: showHighlights
+ }
+ }]);
+
+}()); \ No newline at end of file