blob: 7e9299779a2a0b17943b7cc4d3ba4c2c105785d8 [file] [log] [blame]
Simon Hunt4b668592015-01-29 17:33:53 -08001/*
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 ONOS GUI -- Topology Instances Module.
19 Defines modeling of ONOS instances.
20 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Simon Huntee7a3ce2015-04-09 13:28:37 -070026 var $log, ps, sus, gs, ts, fs, flash;
Simon Hunt4b668592015-01-29 17:33:53 -080027
Simon Hunta142dd22015-02-12 22:07:51 -080028 // api from topo
29 var api;
30 /*
31 showMastership( id )
32 */
33
Simon Hunt4b668592015-01-29 17:33:53 -080034 // configuration
35 var instCfg = {
36 rectPad: 8,
37 nodeOx: 9,
38 nodeOy: 9,
39 nodeDim: 40,
40 birdOx: 19,
41 birdOy: 21,
42 birdDim: 21,
43 uiDy: 45,
44 titleDy: 30,
45 textYOff: 20,
46 textYSpc: 15
47 },
48 showLogicErrors = true,
49 idIns = 'topo-p-instance',
50 instOpts = {
51 edge: 'left',
52 width: 20
53 };
54
55 // internal state
56 var onosInstances,
57 onosOrder,
58 oiShowMaster,
Simon Hunt245a88e2015-02-02 13:26:04 -080059 oiBox,
60 themeListener;
Simon Hunt4b668592015-01-29 17:33:53 -080061
62
63 // ==========================
Simon Hunt4b668592015-01-29 17:33:53 -080064
65 function addInstance(data) {
66 var id = data.id;
67
68 if (onosInstances[id]) {
69 updateInstance(data);
70 return;
71 }
72 onosInstances[id] = data;
73 onosOrder.push(data);
74 updateInstances();
75 }
76
77 function updateInstance(data) {
78 var id = data.id,
79 d = onosInstances[id];
80 if (d) {
81 angular.extend(d, data);
82 updateInstances();
83 } else {
84 logicError('updateInstance: lookup fail: ID = "' + id + '"');
85 }
86 }
87
Simon Hunt48e61672015-01-30 14:48:25 -080088 function removeInstance(data) {
89 var id = data.id,
90 d = onosInstances[id];
91 if (d) {
92 var idx = fs.find(id, onosOrder);
93 if (idx >= 0) {
94 onosOrder.splice(idx, 1);
95 }
96 delete onosInstances[id];
97 updateInstances();
98 } else {
99 logicError('removeInstance lookup fail. ID = "' + id + '"');
100 }
101 }
102
103 // ==========================
104
Simon Hunt4b668592015-01-29 17:33:53 -0800105 function computeDim(self) {
106 var css = window.getComputedStyle(self);
107 return {
108 w: sus.stripPx(css.width),
109 h: sus.stripPx(css.height)
110 };
111 }
112
113 function clickInst(d) {
114 var el = d3.select(this),
115 aff = el.classed('affinity');
116 if (!aff) {
117 setAffinity(el, d);
118 } else {
119 cancelAffinity();
120 }
121 }
122
123 function setAffinity(el, d) {
124 d3.selectAll('.onosInst')
125 .classed('mastership', true)
126 .classed('affinity', false);
127 el.classed('affinity', true);
128
Simon Hunta142dd22015-02-12 22:07:51 -0800129 // suppress all elements except nodes whose master is this instance
130 api.showMastership(d.id);
Simon Hunt4b668592015-01-29 17:33:53 -0800131 oiShowMaster = true;
132 }
133
134 function cancelAffinity() {
135 d3.selectAll('.onosInst')
136 .classed('mastership affinity', false);
137
Simon Hunta142dd22015-02-12 22:07:51 -0800138 api.showMastership(null);
Simon Hunt4b668592015-01-29 17:33:53 -0800139 oiShowMaster = false;
140 }
141
142 function instRectAttr(dim) {
143 var pad = instCfg.rectPad;
144 return {
145 x: pad,
146 y: pad,
147 width: dim.w - pad*2,
148 height: dim.h - pad*2,
149 rx: 6
150 };
151 }
152
153 function viewBox(dim) {
154 return '0 0 ' + dim.w + ' ' + dim.h;
155 }
156
157 function attachUiBadge(svg) {
158 gs.addGlyph(svg, 'uiAttached', 30, true, [12, instCfg.uiDy])
159 .classed('badgeIcon uiBadge', true);
160 }
161
162 function instColor(id, online) {
Simon Hunt48e61672015-01-30 14:48:25 -0800163 return sus.cat7().getColor(id, !online, ts.theme());
Simon Hunt4b668592015-01-29 17:33:53 -0800164 }
165
166 // ==============================
167
168 function updateInstances() {
169 var onoses = oiBox.el().selectAll('.onosInst')
170 .data(onosOrder, function (d) { return d.id; }),
171 instDim = {w:0,h:0},
172 c = instCfg;
173
174 function nSw(n) {
175 return '# Switches: ' + n;
176 }
177
178 // operate on existing onos instances if necessary
179 onoses.each(function (d) {
180 var el = d3.select(this),
181 svg = el.select('svg');
182 instDim = computeDim(this);
183
184 // update online state
185 el.classed('online', d.online);
186
187 // update ui-attached state
188 svg.select('use.uiBadge').remove();
189 if (d.uiAttached) {
190 attachUiBadge(svg);
191 }
192
193 function updAttr(id, value) {
194 svg.select('text.instLabel.'+id).text(value);
195 }
196
197 updAttr('ip', d.ip);
198 updAttr('ns', nSw(d.switches));
199 });
200
201
202 // operate on new onos instances
203 var entering = onoses.enter()
204 .append('div')
205 .attr('class', 'onosInst')
206 .classed('online', function (d) { return d.online; })
207 .on('click', clickInst);
208
209 entering.each(function (d) {
210 var el = d3.select(this),
211 rectAttr,
212 svg;
213 instDim = computeDim(this);
214 rectAttr = instRectAttr(instDim);
215
216 svg = el.append('svg').attr({
217 width: instDim.w,
218 height: instDim.h,
219 viewBox: viewBox(instDim)
220 });
221
222 svg.append('rect').attr(rectAttr);
223
224 gs.addGlyph(svg, 'bird', 28, true, [14, 14])
225 .classed('badgeIcon', true);
226
227 if (d.uiAttached) {
228 attachUiBadge(svg);
229 }
230
231 var left = c.nodeOx + c.nodeDim,
232 len = rectAttr.width - left,
233 hlen = len / 2,
234 midline = hlen + left;
235
236 // title
237 svg.append('text')
238 .attr({
239 class: 'instTitle',
240 x: midline,
241 y: c.titleDy
242 })
243 .text(d.id);
244
245 // a couple of attributes
246 var ty = c.titleDy + c.textYOff;
247
248 function addAttr(id, label) {
249 svg.append('text').attr({
250 class: 'instLabel ' + id,
251 x: midline,
252 y: ty
253 }).text(label);
254 ty += c.textYSpc;
255 }
256
257 addAttr('ip', d.ip);
258 addAttr('ns', nSw(d.switches));
259 });
260
261 // operate on existing + new onoses here
262 // set the affinity colors...
263 onoses.each(function (d) {
264 var el = d3.select(this),
265 rect = el.select('svg').select('rect'),
266 col = instColor(d.id, d.online);
267 rect.style('fill', col);
268 });
269
270 // adjust the panel size appropriately...
271 oiBox.width(instDim.w * onosOrder.length);
272 oiBox.height(instDim.h);
273
274 // remove any outgoing instances
275 onoses.exit().remove();
276 }
277
278
279 // ==========================
280
281 function logicError(msg) {
282 if (showLogicErrors) {
283 $log.warn('TopoInstService: ' + msg);
284 }
285 }
286
Simon Hunta142dd22015-02-12 22:07:51 -0800287 function initInst(_api_) {
288 api = _api_;
Simon Hunt4b668592015-01-29 17:33:53 -0800289 oiBox = ps.createPanel(idIns, instOpts);
290 oiBox.show();
291
292 onosInstances = {};
293 onosOrder = [];
294 oiShowMaster = false;
Simon Hunt245a88e2015-02-02 13:26:04 -0800295
296 // we want to update the instances, each time the theme changes
297 themeListener = ts.addListener(updateInstances);
Simon Hunt4b668592015-01-29 17:33:53 -0800298 }
299
300 function destroyInst() {
Simon Hunt245a88e2015-02-02 13:26:04 -0800301 ts.removeListener(themeListener);
302 themeListener = null;
303
Simon Hunt4b668592015-01-29 17:33:53 -0800304 ps.destroyPanel(idIns);
305 oiBox = null;
Simon Hunt3ab20282015-02-26 20:32:19 -0800306
307 onosInstances = {};
308 onosOrder = [];
309 oiShowMaster = false;
Simon Hunt4b668592015-01-29 17:33:53 -0800310 }
311
Simon Huntee7a3ce2015-04-09 13:28:37 -0700312 function showInsts() {
313 oiBox.show();
314 }
315
316 function hideInsts() {
317 oiBox.hide();
318 }
319
320 function toggleInsts(x) {
321 var kev = (x === 'keyev'),
322 on,
323 verb;
324
325 if (kev) {
326 on = oiBox.toggle();
327 } else {
328 on = !!x;
329 if (on) {
330 showInsts();
331 } else {
332 hideInsts();
333 }
334 }
335 verb = on ? 'Show' : 'Hide';
336 flash.flash(verb + ' instances panel');
337 return on;
338 }
339
Simon Hunt4b668592015-01-29 17:33:53 -0800340 // ==========================
341
342 angular.module('ovTopo')
343 .factory('TopoInstService',
344 ['$log', 'PanelService', 'SvgUtilService', 'GlyphService',
Simon Huntee7a3ce2015-04-09 13:28:37 -0700345 'ThemeService', 'FnService', 'FlashService',
Simon Hunt4b668592015-01-29 17:33:53 -0800346
Simon Huntee7a3ce2015-04-09 13:28:37 -0700347 function (_$log_, _ps_, _sus_, _gs_, _ts_, _fs_, _flash_) {
Simon Hunt4b668592015-01-29 17:33:53 -0800348 $log = _$log_;
349 ps = _ps_;
350 sus = _sus_;
351 gs = _gs_;
Simon Hunt48e61672015-01-30 14:48:25 -0800352 ts = _ts_;
353 fs = _fs_;
Simon Huntee7a3ce2015-04-09 13:28:37 -0700354 flash = _flash_;
Simon Hunt4b668592015-01-29 17:33:53 -0800355
356 return {
357 initInst: initInst,
358 destroyInst: destroyInst,
Simon Hunt1894d792015-02-04 17:09:20 -0800359
Simon Hunt48e61672015-01-30 14:48:25 -0800360 addInstance: addInstance,
361 updateInstance: updateInstance,
Simon Huntac4c6f72015-02-03 19:50:53 -0800362 removeInstance: removeInstance,
Simon Hunt1894d792015-02-04 17:09:20 -0800363
Simon Hunta142dd22015-02-12 22:07:51 -0800364 cancelAffinity: cancelAffinity,
365
Simon Huntac4c6f72015-02-03 19:50:53 -0800366 isVisible: function () { return oiBox.isVisible(); },
Simon Huntee7a3ce2015-04-09 13:28:37 -0700367 show: showInsts,
368 hide: hideInsts,
369 toggle: toggleInsts,
Simon Hunta142dd22015-02-12 22:07:51 -0800370 showMaster: function () { return oiShowMaster; }
Simon Hunt4b668592015-01-29 17:33:53 -0800371 };
372 }]);
373}());