blob: b04bd531689f8e3688b5c00f4aacaca5db8d3234 [file] [log] [blame]
Simon Hunt72e44bf2015-07-21 21:34:20 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Simon Hunt72e44bf2015-07-21 21:34:20 -07003 *
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
Andrea Campanella2dc91dc2015-12-07 12:17:02 -080033 var $log, $timeout, 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 = {},
Simon Hunt4a6b54b2015-10-27 22:08:25 -070037 current = null,
38 reset = true;
Simon Hunt72e44bf2015-07-21 21:34:20 -070039
40 function error(fn, msg) {
41 $log.error(tos + fn + '(): ' + msg);
42 }
43
44 function warn(fn, msg) {
45 $log.warn(tos + fn + '(): ' + msg);
46 }
47
Simon Huntfb940112015-07-29 18:36:35 -070048 function mkGlyphId(oid, gid) {
49 return (gid[0] === '*') ? oid + '-' + gid.slice(1) : gid;
50 }
Simon Hunt72e44bf2015-07-21 21:34:20 -070051
Simon Huntfb940112015-07-29 18:36:35 -070052 function handleGlyphs(o) {
53 var gdata = fs.isO(o.glyphs),
54 oid = o.overlayId,
55 gid = o.glyphId || 'unknown',
56 data = {},
57 note = [];
58
59 o._glyphId = mkGlyphId(oid, gid);
60
61 o.mkGid = function (g) {
62 return mkGlyphId(oid, g);
63 };
64 o.mkId = function (s) {
65 return oid + '-' + s;
66 };
67
68 // process glyphs if defined
69 if (gdata) {
70 angular.forEach(gdata, function (value, key) {
71 var fullkey = oid + '-' + key;
72 data['_' + fullkey] = value.vb;
73 data[fullkey] = value.d;
74 note.push('*' + key);
75 });
76 gs.registerGlyphs(data);
77 $log.debug('registered overlay glyphs:', oid, note);
Simon Hunt72e44bf2015-07-21 21:34:20 -070078 }
79 }
80
81 function register(overlay) {
82 var r = 'register',
83 over = fs.isO(overlay),
Simon Hunt8d22c4b2015-08-06 16:24:43 -070084 kb = over ? fs.isO(overlay.keyBindings) : null,
Simon Hunt72e44bf2015-07-21 21:34:20 -070085 id = over ? over.overlayId : '';
86
87 if (!id) {
88 return error(r, 'not a recognized overlay');
89 }
90 if (overlays[id]) {
91 return warn(r, 'already registered: "' + id + '"');
92 }
93 overlays[id] = overlay;
Simon Huntfb940112015-07-29 18:36:35 -070094 handleGlyphs(overlay);
Simon Hunt8d22c4b2015-08-06 16:24:43 -070095
96 if (kb) {
97 if (!fs.isA(kb._keyOrder)) {
98 warn(r, 'no _keyOrder array defined on keyBindings');
99 } else {
100 kb._keyOrder.forEach(function (k) {
101 if (k !== '-' && !kb[k]) {
102 warn(r, 'no "' + k + '" property defined on keyBindings');
103 }
104 });
105 }
106 }
107
Simon Hunt72e44bf2015-07-21 21:34:20 -0700108 $log.debug(tos + 'registered overlay: ' + id, overlay);
109 }
110
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700111 // returns the list of overlay identifiers
Simon Hunt72e44bf2015-07-21 21:34:20 -0700112 function list() {
113 return d3.map(overlays).keys();
114 }
115
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700116 // add a radio button for each registered overlay
Simon Hunta5b53af2015-10-12 15:56:40 -0700117 // return an overlay id to index map
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700118 function augmentRbset(rset, switchFn) {
Simon Hunta5b53af2015-10-12 15:56:40 -0700119 var map = {},
120 idx = 1;
121
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700122 angular.forEach(overlays, function (ov) {
123 rset.push({
124 gid: ov._glyphId,
125 tooltip: (ov.tooltip || '(no tooltip)'),
126 cb: function () {
127 tbSelection(ov.overlayId, switchFn);
128 }
129 });
Simon Hunta5b53af2015-10-12 15:56:40 -0700130 map[ov.overlayId] = idx++;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700131 });
Simon Hunta5b53af2015-10-12 15:56:40 -0700132 return map;
Simon Hunt72e44bf2015-07-21 21:34:20 -0700133 }
134
Simon Hunte05cae42015-07-23 17:35:24 -0700135 // an overlay was selected via toolbar radio button press from user
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700136 function tbSelection(id, switchFn) {
Simon Hunte05cae42015-07-23 17:35:24 -0700137 var same = current && current.overlayId === id,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700138 payload = {},
139 actions;
Simon Hunte05cae42015-07-23 17:35:24 -0700140
141 function doop(op) {
142 var oid = current.overlayId;
143 $log.debug('Overlay:', op, oid);
144 current[op]();
145 payload[op] = oid;
146 }
147
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700148 if (reset || !same) {
149 reset = false;
Simon Hunte05cae42015-07-23 17:35:24 -0700150 current && doop('deactivate');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700151 current = overlays[id];
Simon Hunte05cae42015-07-23 17:35:24 -0700152 current && doop('activate');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700153 actions = current && fs.isO(current.keyBindings);
154 switchFn(id, actions);
155
Simon Hunte05cae42015-07-23 17:35:24 -0700156 wss.sendEvent('topoSelectOverlay', payload);
Simon Hunt0af1ec32015-07-24 12:17:55 -0700157
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700158 // Ensure summary and details panels are updated immediately..
Simon Hunt0af1ec32015-07-24 12:17:55 -0700159 wss.sendEvent('requestSummary');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700160 tss.updateDetail();
Simon Hunte05cae42015-07-23 17:35:24 -0700161 }
162 }
163
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700164 var coreButtons = {
165 showDeviceView: {
166 gid: 'switch',
167 tt: 'Show Device View',
168 path: 'device'
169 },
170 showFlowView: {
171 gid: 'flowTable',
172 tt: 'Show Flow View for this Device',
173 path: 'flow'
174 },
175 showPortView: {
176 gid: 'portTable',
177 tt: 'Show Port View for this Device',
178 path: 'port'
179 },
180 showGroupView: {
181 gid: 'groupTable',
182 tt: 'Show Group View for this Device',
183 path: 'group'
Jian Li79f67322016-01-06 18:22:37 -0800184 },
185 showMeterView: {
186 gid: 'meterTable',
187 tt: 'Show Meter View for this Device',
188 path: 'meter'
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700189 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700190 };
Simon Huntfb940112015-07-29 18:36:35 -0700191
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700192 // retrieves a button definition from the current overlay and generates
193 // a button descriptor to be added to the panel, with the data baked in
194 function _getButtonDef(id, data) {
195 var btns = current && current.buttons,
196 b = btns && btns[id],
197 cb = fs.isF(b.cb),
198 f = cb ? function () { cb(data); } : function () {};
199
200 return b ? {
201 id: current.mkId(id),
202 gid: current.mkGid(b.gid),
203 tt: b.tt,
204 cb: f
205 } : null;
206 }
207
Simon Hunt3a0598f2015-08-04 19:59:04 -0700208 // install core buttons, and include any additional from the current overlay
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700209 function installButtons(buttons, data, devId) {
210 buttons.forEach(function (id) {
211 var btn = coreButtons[id],
212 gid = btn && btn.gid,
213 tt = btn && btn.tt,
214 path = btn && btn.path;
Simon Hunt3a0598f2015-08-04 19:59:04 -0700215
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700216 if (btn) {
217 tps.addAction({
218 id: 'core-' + id,
219 gid: gid,
220 tt: tt,
221 cb: function () { ns.navTo(path, {devId: devId }); }
222 });
223 } else if (btn = _getButtonDef(id, data)) {
224 tps.addAction(btn);
Simon Hunt3a0598f2015-08-04 19:59:04 -0700225 }
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700226 });
227 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700228
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700229 function addDetailButton(id) {
230 var b = _getButtonDef(id);
231 if (b) {
232 tps.addAction({
233 id: current.mkId(id),
234 gid: current.mkGid(b.gid),
235 cb: b.cb,
236 tt: b.tt
237 });
238 }
239 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700240
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700241
242 // === -----------------------------------------------------
243 // Hooks for overlays
244
245 function _hook(x) {
246 var h = current && current.hooks;
247 return h && fs.isF(h[x]);
248 }
249
250 function escapeHook() {
251 var eh = _hook('escape');
252 return eh ? eh() : false;
253 }
254
255 function emptySelectHook() {
256 var cb = _hook('empty');
257 cb && cb();
258 }
259
260 function singleSelectHook(data) {
261 var cb = _hook('single');
262 cb && cb(data);
263 }
264
265 function multiSelectHook(selectOrder) {
266 var cb = _hook('multi');
267 cb && cb(selectOrder);
268 }
269
Simon Hunt584e92d2015-08-24 11:27:22 -0700270 function mouseOverHook(what) {
271 var cb = _hook('mouseover');
272 cb && cb(what);
273 }
274
275 function mouseOutHook() {
276 var cb = _hook('mouseout');
277 cb && cb();
278 }
279
Simon Hunt5c1a9382016-06-01 19:35:35 -0700280 // Temporary function to allow overlays to modify link detail data
281 // in the client. (In the near future, this will be done on the server).
282 function modifyLinkDataHook(data, extra) {
283 var cb = _hook('modifylinkdata');
284 return cb && extra ? cb(data, extra) : data;
285 }
286
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700287 // === -----------------------------------------------------
288 // Event (from server) Handlers
289
290 function setApi(_api_, _tss_) {
291 api = _api_;
292 tss = _tss_;
293 }
294
Andrea Campanella2dc91dc2015-12-07 12:17:02 -0800295 //process highlight event with optional delay
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700296 function showHighlights(data) {
Andrea Campanella2dc91dc2015-12-07 12:17:02 -0800297 function doHighlight() {
298 _showHighlights(data);
299 }
300
301 if (data.delay) {
302 $timeout(doHighlight, data.delay);
303 } else {
304 doHighlight();
305 }
306 }
307
308 function _showHighlights(data) {
Simon Hunt743a8492015-08-25 16:18:19 -0700309 var less;
310
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700311 /*
312 API to topoForce
313 clearLinkTrafficStyle()
314 removeLinkLabels()
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700315 findLinkById( id )
Simon Hunt94f7dae2015-08-26 17:40:59 -0700316 findNodeById( id )
Simon Hunt743a8492015-08-25 16:18:19 -0700317 updateLinks()
318 updateNodes()
319 supLayers( bool, [less] )
320 unsupNode( id, [less] )
Simon Hunt94f7dae2015-08-26 17:40:59 -0700321 unsupLink( key, [less] )
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700322 */
323
Simon Hunte9343f32015-10-21 18:07:46 -0700324 api.clearNodeDeco();
325 api.removeNodeBadges();
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700326 api.clearLinkTrafficStyle();
327 api.removeLinkLabels();
328
Simon Hunt743a8492015-08-25 16:18:19 -0700329 // handle element suppression
330 if (data.subdue) {
331 less = data.subdue === 'min';
332 api.supLayers(true, less);
333
334 } else {
335 api.supLayers(false);
336 api.supLayers(false, true);
337 }
338
Simon Hunt94f7dae2015-08-26 17:40:59 -0700339 data.hosts.forEach(function (host) {
Andrea Campanella52125412015-12-03 14:50:40 -0800340 var hdata = api.findNodeById(host.id),
341 badgeData = host.badge || null;
342
Simon Hunta1f1c022016-03-03 15:54:57 -0800343 if (hdata && hdata.el && !hdata.el.empty()) {
Andrea Campanella52125412015-12-03 14:50:40 -0800344 hdata.badge = badgeData;
Simon Hunt5b3ff902015-08-27 09:46:27 -0700345 if (!host.subdue) {
346 api.unsupNode(hdata.id, less);
347 }
Simon Hunt94f7dae2015-08-26 17:40:59 -0700348 // TODO: further highlighting?
Simon Huntb3442482016-03-03 17:30:07 -0800349 } else {
350 $log.warn('HILITE: no host element:', host.id);
Simon Hunt94f7dae2015-08-26 17:40:59 -0700351 }
352 });
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700353
Simon Hunt94f7dae2015-08-26 17:40:59 -0700354 data.devices.forEach(function (device) {
Simon Hunte9343f32015-10-21 18:07:46 -0700355 var ddata = api.findNodeById(device.id),
356 badgeData = device.badge || null;
357
Simon Hunta1f1c022016-03-03 15:54:57 -0800358 if (ddata && ddata.el && !ddata.el.empty()) {
Simon Hunte9343f32015-10-21 18:07:46 -0700359 ddata.badge = badgeData;
Simon Hunt5b3ff902015-08-27 09:46:27 -0700360 if (!device.subdue) {
361 api.unsupNode(ddata.id, less);
362 }
Simon Hunt94f7dae2015-08-26 17:40:59 -0700363 // TODO: further highlighting?
Simon Huntb3442482016-03-03 17:30:07 -0800364 } else {
365 $log.warn('HILITE: no device element:', device.id);
Simon Hunt94f7dae2015-08-26 17:40:59 -0700366 }
367 });
368
369 data.links.forEach(function (link) {
370 var ldata = api.findLinkById(link.id),
371 lab = link.label,
Simon Huntd3ceffa2015-08-25 12:44:35 -0700372 units, portcls, magnitude;
Simon Hunta1f1c022016-03-03 15:54:57 -0800373
374 if (ldata && ldata.el && !ldata.el.empty()) {
Simon Hunt5b3ff902015-08-27 09:46:27 -0700375 if (!link.subdue) {
376 api.unsupLink(ldata.key, less);
377 }
Simon Hunt94f7dae2015-08-26 17:40:59 -0700378 ldata.el.classed(link.css, true);
Simon Huntd3ceffa2015-08-25 12:44:35 -0700379 ldata.label = lab;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700380
Simon Hunt5b3ff902015-08-27 09:46:27 -0700381 // TODO: this needs to be pulled out into traffic overlay
Simon Huntd3ceffa2015-08-25 12:44:35 -0700382 // inject additional styling for port-based traffic
383 if (fs.endsWith(lab, 'bps')) {
384 units = lab.substring(lab.length-4);
385 portcls = 'port-traffic-' + units;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700386
Simon Huntd3ceffa2015-08-25 12:44:35 -0700387 // for GBps
388 if (units.substring(0,1) === 'G') {
389 magnitude = fs.parseBitRate(lab);
390 if (magnitude >= 9) {
391 portcls += '-choked'
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700392 }
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700393 }
Simon Huntd3ceffa2015-08-25 12:44:35 -0700394 ldata.el.classed(portcls, true);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700395 }
Simon Huntb3442482016-03-03 17:30:07 -0800396 } else {
397 $log.warn('HILITE: no link element:', link.id);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700398 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700399 });
Simon Huntfb940112015-07-29 18:36:35 -0700400
Simon Hunt743a8492015-08-25 16:18:19 -0700401 api.updateNodes();
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700402 api.updateLinks();
Simon Huntfb940112015-07-29 18:36:35 -0700403 }
404
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700405 // ========================================================================
406
Simon Hunt72e44bf2015-07-21 21:34:20 -0700407 angular.module('ovTopo')
408 .factory('TopoOverlayService',
Andrea Campanella2dc91dc2015-12-07 12:17:02 -0800409 ['$log', '$timeout', 'FnService', 'GlyphService', 'WebSocketService',
410 'NavService', 'TopoPanelService',
Simon Hunt72e44bf2015-07-21 21:34:20 -0700411
Andrea Campanella2dc91dc2015-12-07 12:17:02 -0800412 function (_$log_, _$timeout_, _fs_, _gs_, _wss_, _ns_, _tps_) {
Simon Hunt72e44bf2015-07-21 21:34:20 -0700413 $log = _$log_;
Andrea Campanella2dc91dc2015-12-07 12:17:02 -0800414 $timeout = _$timeout_;
Simon Hunt72e44bf2015-07-21 21:34:20 -0700415 fs = _fs_;
416 gs = _gs_;
Simon Hunte05cae42015-07-23 17:35:24 -0700417 wss = _wss_;
Simon Hunt3a0598f2015-08-04 19:59:04 -0700418 ns = _ns_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700419 tps = _tps_;
Simon Hunt72e44bf2015-07-21 21:34:20 -0700420
421 return {
422 register: register,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700423 setApi: setApi,
Simon Hunt72e44bf2015-07-21 21:34:20 -0700424 list: list,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700425 augmentRbset: augmentRbset,
426 mkGlyphId: mkGlyphId,
Simon Huntfb940112015-07-29 18:36:35 -0700427 tbSelection: tbSelection,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700428 installButtons: installButtons,
429 addDetailButton: addDetailButton,
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700430 resetOnToolbarDestroy: function () { reset = true; },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700431 hooks: {
432 escape: escapeHook,
433 emptySelect: emptySelectHook,
434 singleSelect: singleSelectHook,
Simon Hunt584e92d2015-08-24 11:27:22 -0700435 multiSelect: multiSelectHook,
436 mouseOver: mouseOverHook,
Simon Hunt5c1a9382016-06-01 19:35:35 -0700437 mouseOut: mouseOutHook,
438 modifyLinkData: modifyLinkDataHook
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700439 },
440
441 showHighlights: showHighlights
Simon Hunt72e44bf2015-07-21 21:34:20 -0700442 }
443 }]);
444
445}());