blob: 49e129c8e6a31e9166bd11f89180d41d8dd8cf57 [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 Hunt48e61672015-01-30 14:48:25 -080026 var $log, ps, sus, gs, ts, fs;
Simon Hunt4b668592015-01-29 17:33:53 -080027
28 // configuration
29 var instCfg = {
30 rectPad: 8,
31 nodeOx: 9,
32 nodeOy: 9,
33 nodeDim: 40,
34 birdOx: 19,
35 birdOy: 21,
36 birdDim: 21,
37 uiDy: 45,
38 titleDy: 30,
39 textYOff: 20,
40 textYSpc: 15
41 },
42 showLogicErrors = true,
43 idIns = 'topo-p-instance',
44 instOpts = {
45 edge: 'left',
46 width: 20
47 };
48
49 // internal state
50 var onosInstances,
51 onosOrder,
52 oiShowMaster,
Simon Hunt245a88e2015-02-02 13:26:04 -080053 oiBox,
54 themeListener;
Simon Hunt4b668592015-01-29 17:33:53 -080055
56
57 // ==========================
Simon Hunt4b668592015-01-29 17:33:53 -080058
59 function addInstance(data) {
60 var id = data.id;
61
62 if (onosInstances[id]) {
63 updateInstance(data);
64 return;
65 }
66 onosInstances[id] = data;
67 onosOrder.push(data);
68 updateInstances();
69 }
70
71 function updateInstance(data) {
72 var id = data.id,
73 d = onosInstances[id];
74 if (d) {
75 angular.extend(d, data);
76 updateInstances();
77 } else {
78 logicError('updateInstance: lookup fail: ID = "' + id + '"');
79 }
80 }
81
Simon Hunt48e61672015-01-30 14:48:25 -080082 function removeInstance(data) {
83 var id = data.id,
84 d = onosInstances[id];
85 if (d) {
86 var idx = fs.find(id, onosOrder);
87 if (idx >= 0) {
88 onosOrder.splice(idx, 1);
89 }
90 delete onosInstances[id];
91 updateInstances();
92 } else {
93 logicError('removeInstance lookup fail. ID = "' + id + '"');
94 }
95 }
96
97 // ==========================
98
Simon Hunt4b668592015-01-29 17:33:53 -080099 function computeDim(self) {
100 var css = window.getComputedStyle(self);
101 return {
102 w: sus.stripPx(css.width),
103 h: sus.stripPx(css.height)
104 };
105 }
106
107 function clickInst(d) {
108 var el = d3.select(this),
109 aff = el.classed('affinity');
110 if (!aff) {
111 setAffinity(el, d);
112 } else {
113 cancelAffinity();
114 }
115 }
116
117 function setAffinity(el, d) {
118 d3.selectAll('.onosInst')
119 .classed('mastership', true)
120 .classed('affinity', false);
121 el.classed('affinity', true);
122
123 // TODO: suppress the layers and highlight only specific nodes...
124 //suppressLayers(true);
125 //node.each(function (n) {
126 // if (n.master === d.id) {
127 // n.el.classed('suppressed', false);
128 // }
129 //});
130 oiShowMaster = true;
131 }
132
133 function cancelAffinity() {
134 d3.selectAll('.onosInst')
135 .classed('mastership affinity', false);
136
137 // TODO: restore layer state
138 //restoreLayerState();
139 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
287 function initInst() {
288 oiBox = ps.createPanel(idIns, instOpts);
289 oiBox.show();
290
291 onosInstances = {};
292 onosOrder = [];
293 oiShowMaster = false;
Simon Hunt245a88e2015-02-02 13:26:04 -0800294
295 // we want to update the instances, each time the theme changes
296 themeListener = ts.addListener(updateInstances);
Simon Hunt4b668592015-01-29 17:33:53 -0800297 }
298
299 function destroyInst() {
Simon Hunt245a88e2015-02-02 13:26:04 -0800300 ts.removeListener(themeListener);
301 themeListener = null;
302
Simon Hunt4b668592015-01-29 17:33:53 -0800303 ps.destroyPanel(idIns);
304 oiBox = null;
305 }
306
307 // ==========================
308
309 angular.module('ovTopo')
310 .factory('TopoInstService',
311 ['$log', 'PanelService', 'SvgUtilService', 'GlyphService',
Simon Hunt48e61672015-01-30 14:48:25 -0800312 'ThemeService', 'FnService',
Simon Hunt4b668592015-01-29 17:33:53 -0800313
Simon Hunt48e61672015-01-30 14:48:25 -0800314 function (_$log_, _ps_, _sus_, _gs_, _ts_, _fs_) {
Simon Hunt4b668592015-01-29 17:33:53 -0800315 $log = _$log_;
316 ps = _ps_;
317 sus = _sus_;
318 gs = _gs_;
Simon Hunt48e61672015-01-30 14:48:25 -0800319 ts = _ts_;
320 fs = _fs_;
Simon Hunt4b668592015-01-29 17:33:53 -0800321
322 return {
323 initInst: initInst,
324 destroyInst: destroyInst,
Simon Hunt1894d792015-02-04 17:09:20 -0800325
Simon Hunt48e61672015-01-30 14:48:25 -0800326 addInstance: addInstance,
327 updateInstance: updateInstance,
Simon Huntac4c6f72015-02-03 19:50:53 -0800328 removeInstance: removeInstance,
Simon Hunt1894d792015-02-04 17:09:20 -0800329
Simon Huntac4c6f72015-02-03 19:50:53 -0800330 isVisible: function () { return oiBox.isVisible(); },
331 show: function () { oiBox.show(); },
Simon Hunt5724fb42015-02-05 16:59:40 -0800332 hide: function () { oiBox.hide(); },
333 toggle: function () { oiBox.toggle(); }
Simon Hunt4b668592015-01-29 17:33:53 -0800334 };
335 }]);
336}());