blob: 9a3b4358a96caedcb769aa63c9a8fd6227d08a05 [file] [log] [blame]
Simon Hunt72e44bf2015-07-21 21:34:20 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18/*
19 ONOS GUI -- Topology Overlay Module.
20
21 Provides overlay capabilities, allowing ONOS apps to provide additional
22 custom data/behavior for the topology view.
23
24 */
25
26(function () {
27 'use strict';
28
29 // constants
30 var tos = 'TopoOverlayService: ';
31
32 // injected refs
Simon Hunt8d22c4b2015-08-06 16:24:43 -070033 var $log, fs, gs, wss, ns, tss, tps, api;
Simon Hunt72e44bf2015-07-21 21:34:20 -070034
35 // internal state
Simon Hunte05cae42015-07-23 17:35:24 -070036 var overlays = {},
37 current = null;
Simon Hunt72e44bf2015-07-21 21:34:20 -070038
39 function error(fn, msg) {
40 $log.error(tos + fn + '(): ' + msg);
41 }
42
43 function warn(fn, msg) {
44 $log.warn(tos + fn + '(): ' + msg);
45 }
46
Simon Huntfb940112015-07-29 18:36:35 -070047 function mkGlyphId(oid, gid) {
48 return (gid[0] === '*') ? oid + '-' + gid.slice(1) : gid;
49 }
Simon Hunt72e44bf2015-07-21 21:34:20 -070050
Simon Huntfb940112015-07-29 18:36:35 -070051 function handleGlyphs(o) {
52 var gdata = fs.isO(o.glyphs),
53 oid = o.overlayId,
54 gid = o.glyphId || 'unknown',
55 data = {},
56 note = [];
57
58 o._glyphId = mkGlyphId(oid, gid);
59
60 o.mkGid = function (g) {
61 return mkGlyphId(oid, g);
62 };
63 o.mkId = function (s) {
64 return oid + '-' + s;
65 };
66
67 // process glyphs if defined
68 if (gdata) {
69 angular.forEach(gdata, function (value, key) {
70 var fullkey = oid + '-' + key;
71 data['_' + fullkey] = value.vb;
72 data[fullkey] = value.d;
73 note.push('*' + key);
74 });
75 gs.registerGlyphs(data);
76 $log.debug('registered overlay glyphs:', oid, note);
Simon Hunt72e44bf2015-07-21 21:34:20 -070077 }
78 }
79
80 function register(overlay) {
81 var r = 'register',
82 over = fs.isO(overlay),
Simon Hunt8d22c4b2015-08-06 16:24:43 -070083 kb = over ? fs.isO(overlay.keyBindings) : null,
Simon Hunt72e44bf2015-07-21 21:34:20 -070084 id = over ? over.overlayId : '';
85
86 if (!id) {
87 return error(r, 'not a recognized overlay');
88 }
89 if (overlays[id]) {
90 return warn(r, 'already registered: "' + id + '"');
91 }
92 overlays[id] = overlay;
Simon Huntfb940112015-07-29 18:36:35 -070093 handleGlyphs(overlay);
Simon Hunt8d22c4b2015-08-06 16:24:43 -070094
95 if (kb) {
96 if (!fs.isA(kb._keyOrder)) {
97 warn(r, 'no _keyOrder array defined on keyBindings');
98 } else {
99 kb._keyOrder.forEach(function (k) {
100 if (k !== '-' && !kb[k]) {
101 warn(r, 'no "' + k + '" property defined on keyBindings');
102 }
103 });
104 }
105 }
106
Simon Hunt72e44bf2015-07-21 21:34:20 -0700107 $log.debug(tos + 'registered overlay: ' + id, overlay);
108 }
109
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700110 // returns the list of overlay identifiers
Simon Hunt72e44bf2015-07-21 21:34:20 -0700111 function list() {
112 return d3.map(overlays).keys();
113 }
114
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700115 // add a radio button for each registered overlay
Simon Hunta5b53af2015-10-12 15:56:40 -0700116 // return an overlay id to index map
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700117 function augmentRbset(rset, switchFn) {
Simon Hunta5b53af2015-10-12 15:56:40 -0700118 var map = {},
119 idx = 1;
120
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700121 angular.forEach(overlays, function (ov) {
122 rset.push({
123 gid: ov._glyphId,
124 tooltip: (ov.tooltip || '(no tooltip)'),
125 cb: function () {
126 tbSelection(ov.overlayId, switchFn);
127 }
128 });
Simon Hunta5b53af2015-10-12 15:56:40 -0700129 map[ov.overlayId] = idx++;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700130 });
Simon Hunta5b53af2015-10-12 15:56:40 -0700131 return map;
Simon Hunt72e44bf2015-07-21 21:34:20 -0700132 }
133
Simon Hunte05cae42015-07-23 17:35:24 -0700134 // an overlay was selected via toolbar radio button press from user
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700135 function tbSelection(id, switchFn) {
Simon Hunte05cae42015-07-23 17:35:24 -0700136 var same = current && current.overlayId === id,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700137 payload = {},
138 actions;
Simon Hunte05cae42015-07-23 17:35:24 -0700139
140 function doop(op) {
141 var oid = current.overlayId;
142 $log.debug('Overlay:', op, oid);
143 current[op]();
144 payload[op] = oid;
145 }
146
147 if (!same) {
148 current && doop('deactivate');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700149 current = overlays[id];
Simon Hunte05cae42015-07-23 17:35:24 -0700150 current && doop('activate');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700151 actions = current && fs.isO(current.keyBindings);
152 switchFn(id, actions);
153
Simon Hunte05cae42015-07-23 17:35:24 -0700154 wss.sendEvent('topoSelectOverlay', payload);
Simon Hunt0af1ec32015-07-24 12:17:55 -0700155
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700156 // Ensure summary and details panels are updated immediately..
Simon Hunt0af1ec32015-07-24 12:17:55 -0700157 wss.sendEvent('requestSummary');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700158 tss.updateDetail();
Simon Hunte05cae42015-07-23 17:35:24 -0700159 }
160 }
161
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700162 var coreButtons = {
163 showDeviceView: {
164 gid: 'switch',
165 tt: 'Show Device View',
166 path: 'device'
167 },
168 showFlowView: {
169 gid: 'flowTable',
170 tt: 'Show Flow View for this Device',
171 path: 'flow'
172 },
173 showPortView: {
174 gid: 'portTable',
175 tt: 'Show Port View for this Device',
176 path: 'port'
177 },
178 showGroupView: {
179 gid: 'groupTable',
180 tt: 'Show Group View for this Device',
181 path: 'group'
182 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700183 };
Simon Huntfb940112015-07-29 18:36:35 -0700184
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700185 // retrieves a button definition from the current overlay and generates
186 // a button descriptor to be added to the panel, with the data baked in
187 function _getButtonDef(id, data) {
188 var btns = current && current.buttons,
189 b = btns && btns[id],
190 cb = fs.isF(b.cb),
191 f = cb ? function () { cb(data); } : function () {};
192
193 return b ? {
194 id: current.mkId(id),
195 gid: current.mkGid(b.gid),
196 tt: b.tt,
197 cb: f
198 } : null;
199 }
200
Simon Hunt3a0598f2015-08-04 19:59:04 -0700201 // install core buttons, and include any additional from the current overlay
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700202 function installButtons(buttons, data, devId) {
203 buttons.forEach(function (id) {
204 var btn = coreButtons[id],
205 gid = btn && btn.gid,
206 tt = btn && btn.tt,
207 path = btn && btn.path;
Simon Hunt3a0598f2015-08-04 19:59:04 -0700208
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700209 if (btn) {
210 tps.addAction({
211 id: 'core-' + id,
212 gid: gid,
213 tt: tt,
214 cb: function () { ns.navTo(path, {devId: devId }); }
215 });
216 } else if (btn = _getButtonDef(id, data)) {
217 tps.addAction(btn);
Simon Hunt3a0598f2015-08-04 19:59:04 -0700218 }
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700219 });
220 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700221
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700222 function addDetailButton(id) {
223 var b = _getButtonDef(id);
224 if (b) {
225 tps.addAction({
226 id: current.mkId(id),
227 gid: current.mkGid(b.gid),
228 cb: b.cb,
229 tt: b.tt
230 });
231 }
232 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700233
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700234
235 // === -----------------------------------------------------
236 // Hooks for overlays
237
238 function _hook(x) {
239 var h = current && current.hooks;
240 return h && fs.isF(h[x]);
241 }
242
243 function escapeHook() {
244 var eh = _hook('escape');
245 return eh ? eh() : false;
246 }
247
248 function emptySelectHook() {
249 var cb = _hook('empty');
250 cb && cb();
251 }
252
253 function singleSelectHook(data) {
254 var cb = _hook('single');
255 cb && cb(data);
256 }
257
258 function multiSelectHook(selectOrder) {
259 var cb = _hook('multi');
260 cb && cb(selectOrder);
261 }
262
Simon Hunt584e92d2015-08-24 11:27:22 -0700263 function mouseOverHook(what) {
264 var cb = _hook('mouseover');
265 cb && cb(what);
266 }
267
268 function mouseOutHook() {
269 var cb = _hook('mouseout');
270 cb && cb();
271 }
272
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700273 // === -----------------------------------------------------
274 // Event (from server) Handlers
275
276 function setApi(_api_, _tss_) {
277 api = _api_;
278 tss = _tss_;
279 }
280
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700281 function showHighlights(data) {
Simon Hunt743a8492015-08-25 16:18:19 -0700282 var less;
283
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700284 /*
285 API to topoForce
286 clearLinkTrafficStyle()
287 removeLinkLabels()
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700288 findLinkById( id )
Simon Hunt94f7dae2015-08-26 17:40:59 -0700289 findNodeById( id )
Simon Hunt743a8492015-08-25 16:18:19 -0700290 updateLinks()
291 updateNodes()
292 supLayers( bool, [less] )
293 unsupNode( id, [less] )
Simon Hunt94f7dae2015-08-26 17:40:59 -0700294 unsupLink( key, [less] )
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700295 */
296
Simon Hunte9343f32015-10-21 18:07:46 -0700297 api.clearNodeDeco();
298 api.removeNodeBadges();
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700299 api.clearLinkTrafficStyle();
300 api.removeLinkLabels();
301
Simon Hunt743a8492015-08-25 16:18:19 -0700302 // handle element suppression
303 if (data.subdue) {
304 less = data.subdue === 'min';
305 api.supLayers(true, less);
306
307 } else {
308 api.supLayers(false);
309 api.supLayers(false, true);
310 }
311
Simon Hunt94f7dae2015-08-26 17:40:59 -0700312 data.hosts.forEach(function (host) {
313 var hdata = api.findNodeById(host.id);
314 if (hdata && !hdata.el.empty()) {
Simon Hunt5b3ff902015-08-27 09:46:27 -0700315 if (!host.subdue) {
316 api.unsupNode(hdata.id, less);
317 }
Simon Hunt94f7dae2015-08-26 17:40:59 -0700318 // TODO: further highlighting?
319 }
320 });
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700321
Simon Hunt94f7dae2015-08-26 17:40:59 -0700322 data.devices.forEach(function (device) {
Simon Hunte9343f32015-10-21 18:07:46 -0700323 var ddata = api.findNodeById(device.id),
324 badgeData = device.badge || null;
325
Simon Hunt94f7dae2015-08-26 17:40:59 -0700326 if (ddata && !ddata.el.empty()) {
Simon Hunte9343f32015-10-21 18:07:46 -0700327 ddata.badge = badgeData;
Simon Hunt5b3ff902015-08-27 09:46:27 -0700328 if (!device.subdue) {
329 api.unsupNode(ddata.id, less);
330 }
Simon Hunt94f7dae2015-08-26 17:40:59 -0700331 // TODO: further highlighting?
332 }
333 });
334
335 data.links.forEach(function (link) {
336 var ldata = api.findLinkById(link.id),
337 lab = link.label,
Simon Huntd3ceffa2015-08-25 12:44:35 -0700338 units, portcls, magnitude;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700339
Simon Huntd3ceffa2015-08-25 12:44:35 -0700340 if (ldata && !ldata.el.empty()) {
Simon Hunt5b3ff902015-08-27 09:46:27 -0700341 if (!link.subdue) {
342 api.unsupLink(ldata.key, less);
343 }
Simon Hunt94f7dae2015-08-26 17:40:59 -0700344 ldata.el.classed(link.css, true);
Simon Huntd3ceffa2015-08-25 12:44:35 -0700345 ldata.label = lab;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700346
Simon Hunt5b3ff902015-08-27 09:46:27 -0700347 // TODO: this needs to be pulled out into traffic overlay
Simon Huntd3ceffa2015-08-25 12:44:35 -0700348 // inject additional styling for port-based traffic
349 if (fs.endsWith(lab, 'bps')) {
350 units = lab.substring(lab.length-4);
351 portcls = 'port-traffic-' + units;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700352
Simon Huntd3ceffa2015-08-25 12:44:35 -0700353 // for GBps
354 if (units.substring(0,1) === 'G') {
355 magnitude = fs.parseBitRate(lab);
356 if (magnitude >= 9) {
357 portcls += '-choked'
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700358 }
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700359 }
Simon Huntd3ceffa2015-08-25 12:44:35 -0700360 ldata.el.classed(portcls, true);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700361 }
362 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700363 });
Simon Huntfb940112015-07-29 18:36:35 -0700364
Simon Hunt743a8492015-08-25 16:18:19 -0700365 api.updateNodes();
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700366 api.updateLinks();
Simon Huntfb940112015-07-29 18:36:35 -0700367 }
368
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700369 // ========================================================================
370
Simon Hunt72e44bf2015-07-21 21:34:20 -0700371 angular.module('ovTopo')
372 .factory('TopoOverlayService',
Simon Hunt3a0598f2015-08-04 19:59:04 -0700373 ['$log', 'FnService', 'GlyphService', 'WebSocketService', 'NavService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700374 'TopoPanelService',
Simon Hunt72e44bf2015-07-21 21:34:20 -0700375
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700376 function (_$log_, _fs_, _gs_, _wss_, _ns_, _tps_) {
Simon Hunt72e44bf2015-07-21 21:34:20 -0700377 $log = _$log_;
378 fs = _fs_;
379 gs = _gs_;
Simon Hunte05cae42015-07-23 17:35:24 -0700380 wss = _wss_;
Simon Hunt3a0598f2015-08-04 19:59:04 -0700381 ns = _ns_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700382 tps = _tps_;
Simon Hunt72e44bf2015-07-21 21:34:20 -0700383
384 return {
385 register: register,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700386 setApi: setApi,
Simon Hunt72e44bf2015-07-21 21:34:20 -0700387 list: list,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700388 augmentRbset: augmentRbset,
389 mkGlyphId: mkGlyphId,
Simon Huntfb940112015-07-29 18:36:35 -0700390 tbSelection: tbSelection,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700391 installButtons: installButtons,
392 addDetailButton: addDetailButton,
393 hooks: {
394 escape: escapeHook,
395 emptySelect: emptySelectHook,
396 singleSelect: singleSelectHook,
Simon Hunt584e92d2015-08-24 11:27:22 -0700397 multiSelect: multiSelectHook,
398 mouseOver: mouseOverHook,
399 mouseOut: mouseOutHook
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700400 },
401
402 showHighlights: showHighlights
Simon Hunt72e44bf2015-07-21 21:34:20 -0700403 }
404 }]);
405
406}());