blob: 6828abb7c94765e56eaab5aa3f14c954aa458202 [file] [log] [blame]
Matteo Scandolo38ba3312016-02-09 16:01:49 -08001(function () {
2 'use strict';
3
4 const shapes = {
5 cloud: ' M 79.72 49.60 C 86.00 37.29 98.57 29.01 111.96 26.42 C 124.27 24.11 137.53 26.15 148.18 32.90 C 158.08 38.78 165.39 48.87 167.65 60.20 C 176.20 57.90 185.14 56.01 194.00 57.73 C 206.08 59.59 217.92 66.01 224.37 76.66 C 227.51 81.54 228.85 87.33 229.23 93.06 C 237.59 93.33 246.22 95.10 253.04 100.19 C 256.69 103.13 259.87 107.67 258.91 112.59 C 257.95 118.43 252.78 122.38 247.78 124.82 C 235.27 130.43 220.23 130.09 207.98 123.93 C 199.33 127.88 189.76 129.43 180.30 128.57 C 173.70 139.92 161.70 147.65 148.86 149.93 C 133.10 153.26 116.06 148.15 104.42 137.08 C 92.98 143.04 78.96 143.87 66.97 139.04 C 57.75 135.41 49.70 128.00 46.60 118.43 C 43.87 109.95 45.81 100.29 51.30 93.32 C 57.38 85.18 67.10 80.44 76.99 78.89 C 74.38 69.20 74.87 58.52 79.72 49.60 Z'
6 }
7
Matteo Scandolo14183932016-02-11 08:58:04 -08008 var computeNodeId = 0;
9 var instanceId = 0;
10
Matteo Scandolo4b3d8722016-02-24 11:22:48 -080011 angular.module('xos.diagnostic')
Matteo Scandolo06afdfe2016-02-23 13:47:14 -080012 .service('NodeDrawer', function(d3, serviceTopologyConfig, RackHelper, lodash){
Matteo Scandoloba2d63d2016-02-17 13:54:11 -080013
14 var _this = this;
15
Matteo Scandolo38ba3312016-02-09 16:01:49 -080016 this.addNetworks = (nodes) => {
Matteo Scandolo105954e2016-03-11 11:16:58 -080017
18 // clean childs
19 nodes.selectAll('*').remove();
20
Matteo Scandolo38ba3312016-02-09 16:01:49 -080021 nodes.append('path')
22 .attr({
23 d: shapes.cloud,
Matteo Scandolo105954e2016-03-11 11:16:58 -080024 transform: 'translate(-100, -72), scale(0.7)',
Matteo Scandolo38ba3312016-02-09 16:01:49 -080025 class: 'cloud'
26 });
27
28 nodes.append('text')
29 .attr({
Matteo Scandolobd663742016-03-07 16:07:13 -080030 'text-anchor': 'middle',
31 y: -5,
32 x: 5,
Matteo Scandolo38ba3312016-02-09 16:01:49 -080033 })
34 .text(d => d.name)
Matteo Scandolodffc1382016-02-22 14:53:44 -080035
Matteo Scandolobd663742016-03-07 16:07:13 -080036 nodes.append('text')
37 .attr({
38 'text-anchor': 'middle',
39 y: 8,
40 x: 5,
41 class: 'small'
42 })
43 .text(d => d.subtitle)
44
Matteo Scandolodffc1382016-02-22 14:53:44 -080045 nodes.each(function(n){
46 let currentNode = d3.select(this);
47 // cicle trouch node to add Tags and Public IP
Matteo Scandolod54a7fd2016-03-08 09:33:26 -080048 if(n.name === 'LAN-Side' && angular.isDefined(n.subscriberTag)){
Matteo Scandolodffc1382016-02-22 14:53:44 -080049 currentNode.append('text')
50 .attr({
51 'text-anchor': 'middle',
Matteo Scandolo105954e2016-03-11 11:16:58 -080052 y: 50
Matteo Scandolodffc1382016-02-22 14:53:44 -080053 })
54 .text(() => `C-Tag: ${n.subscriberTag.cTag}`);
55
56 currentNode.append('text')
57 .attr({
58 'text-anchor': 'middle',
Matteo Scandolo105954e2016-03-11 11:16:58 -080059 y: 70
Matteo Scandolodffc1382016-02-22 14:53:44 -080060 })
61 .text(() => `S-Tag: ${n.subscriberTag.sTag}`);
62 }
Matteo Scandolo0344ef32016-02-22 16:53:22 -080063
Matteo Scandolod54a7fd2016-03-08 09:33:26 -080064 if(n.name === 'WAN-Side' && angular.isDefined(n.subscriberIP)){
Matteo Scandolo0344ef32016-02-22 16:53:22 -080065 currentNode.append('text')
66 .attr({
67 'text-anchor': 'middle',
Matteo Scandolo105954e2016-03-11 11:16:58 -080068 y: 50
Matteo Scandolo0344ef32016-02-22 16:53:22 -080069 })
70 .text(() => `Public IP: ${n.subscriberIP}`);
71 }
Matteo Scandolodffc1382016-02-22 14:53:44 -080072 });
Matteo Scandolo38ba3312016-02-09 16:01:49 -080073 }
74
75 this.addRack = (nodes) => {
Matteo Scandolo14183932016-02-11 08:58:04 -080076
Matteo Scandolo7fd4d042016-02-16 14:44:51 -080077 // loop because of D3
78 // rack will be only one
79 nodes.each(d => {
80 let [w, h] = RackHelper.getRackSize(d.computeNodes);
Matteo Scandolob785b572016-02-16 11:50:51 -080081
Matteo Scandolo06afdfe2016-02-23 13:47:14 -080082 // TODO update instead of delete and redraw
83 nodes.select('g').remove();
84
Matteo Scandolob785b572016-02-16 11:50:51 -080085 let rack = nodes
Matteo Scandolo7fd4d042016-02-16 14:44:51 -080086 .append('g');
87
88 rack
89 .attr({
90 transform: `translate(0,0)`
91 })
92 .transition()
93 .duration(serviceTopologyConfig.duration)
94 .attr({
Matteo Scandolo06afdfe2016-02-23 13:47:14 -080095 transform: () => `translate(${- (w / 2)}, ${- (h / 2)})`
Matteo Scandolob785b572016-02-16 11:50:51 -080096 });
97
98 rack
99 .append('rect')
100 .attr({
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800101 width: 0,
102 height: 0
103 })
104 .transition()
105 .duration(serviceTopologyConfig.duration)
106 .attr({
Matteo Scandolob785b572016-02-16 11:50:51 -0800107 width: w,
108 height: h
109 });
110
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800111 rack.append('text')
Matteo Scandolob785b572016-02-16 11:50:51 -0800112 .attr({
113 'text-anchor': 'middle',
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800114 y: - 10,
115 x: w / 2,
116 opacity: 0
Matteo Scandolob785b572016-02-16 11:50:51 -0800117 })
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800118 .text(d => d.name)
119 .transition()
120 .duration(serviceTopologyConfig.duration)
121 .attr({
122 opacity: 1
123 })
Matteo Scandolob785b572016-02-16 11:50:51 -0800124
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800125 this.drawComputeNodes(rack, d.computeNodes);
126
Matteo Scandolo14183932016-02-11 08:58:04 -0800127 });
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800128
Matteo Scandolo14183932016-02-11 08:58:04 -0800129 };
130
131 this.drawComputeNodes = (container, nodes) => {
Matteo Scandolob785b572016-02-16 11:50:51 -0800132
Matteo Scandolo14183932016-02-11 08:58:04 -0800133 let elements = container.selectAll('.compute-nodes')
134 .data(nodes, d => {
135 if(!angular.isString(d.d3Id)){
136 d.d3Id = `compute-node-${++computeNodeId}`;
137 }
138 return d.d3Id;
139 });
140
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800141 let {width, height} = container.node().getBoundingClientRect();
142
143 var nodeContainer = elements.enter().append('g');
144
145 nodeContainer
Matteo Scandolo14183932016-02-11 08:58:04 -0800146 .attr({
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800147 transform: `translate(${width / 2}, ${ height / 2})`,
Matteo Scandolob785b572016-02-16 11:50:51 -0800148 class: 'compute-node',
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800149 })
150 .transition()
151 .duration(serviceTopologyConfig.duration)
152 .attr({
Matteo Scandolob785b572016-02-16 11:50:51 -0800153 transform: (d) => `translate(${RackHelper.getComputeNodePosition(nodes, d.d3Id.replace('compute-node-', '') - 1)})`
Matteo Scandolo14183932016-02-11 08:58:04 -0800154 });
155
156 nodeContainer.append('rect')
Matteo Scandolob785b572016-02-16 11:50:51 -0800157 .attr({
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800158 width: 0,
159 height: 0
160 })
161 .transition()
162 .duration(serviceTopologyConfig.duration)
163 .attr({
Matteo Scandolob785b572016-02-16 11:50:51 -0800164 width: d => RackHelper.getComputeNodeSize(d.instances)[0],
165 height: d => RackHelper.getComputeNodeSize(d.instances)[1],
166 });
167
168 nodeContainer.append('text')
169 .attr({
170 'text-anchor': 'start',
Matteo Scandolo105954e2016-03-11 11:16:58 -0800171 y: 17, //FIXME
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800172 x: 10, //FIXME
173 opacity: 0
Matteo Scandolob785b572016-02-16 11:50:51 -0800174 })
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800175 .text(d => d.humanReadableName.split('.')[0])
176 .transition()
177 .duration(serviceTopologyConfig.duration)
178 .attr({
179 opacity: 1
180 })
Matteo Scandolo14183932016-02-11 08:58:04 -0800181
182 // if there are Compute Nodes
183 if(nodeContainer.length > 0){
Matteo Scandoloba2d63d2016-02-17 13:54:11 -0800184 // draw instances for each compute node
185 nodeContainer.each(function(a){
186 _this.drawInstances(d3.select(this), a.instances);
187 })
Matteo Scandolo14183932016-02-11 08:58:04 -0800188 }
189
190 };
191
Matteo Scandolo0cfd5a22016-02-16 13:29:26 -0800192 // NOTE Stripping unuseful names to shorten labels.
193 // This is not elegant
194 const formatInstanceName = (name) => {
195 return name
196 .replace('app_', '')
197 .replace('service_', '')
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800198 // .replace('ovs_', '')
Matteo Scandolo0cfd5a22016-02-16 13:29:26 -0800199 .replace('mysite_', '')
200 .replace('_instance', '');
201 };
202
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800203 const getInstanceStatusColor = (instance) => {
204 function startWith(val, string){
205 return string.substring(0, val.length) === val;
206 }
207
208 if(startWith('0 - ', instance.backend_status)){
209 return 'provisioning';
210 }
211 if(startWith('1 - ', instance.backend_status)){
212 return 'good';
213 }
214 if(startWith('2 - ', instance.backend_status)){
215 return 'bad';
216 }
217 else {
218 return '';
219 }
220 };
221
Matteo Scandolo653c5092016-02-24 11:14:01 -0800222 const drawContainer = (container, docker) => {
223
224 const containerBox = container.append('g')
225 .attr({
226 class: 'container',
227 transform: `translate(${serviceTopologyConfig.instance.margin}, 115)`
228 });
229
230 containerBox.append('rect')
231 .attr({
232 width: 250 - (serviceTopologyConfig.container.margin * 2),
233 height: serviceTopologyConfig.container.height,
234 });
235
236 containerBox.append('text')
237 .attr({
238 y: 20,
239 x: serviceTopologyConfig.instance.margin,
240 class: 'name'
241 })
242 .text(docker.name)
243
244 // add stats
245 const interestingMeters = ['memory', 'memory.usage', 'cpu_util'];
246
247 interestingMeters.forEach((m, i) => {
248 const meter = lodash.find(docker.stats, {meter: m});
249 // if there is no meter stats skip rendering
250 if(!angular.isDefined(meter)){
251 return;
252 }
253 containerBox.append('text')
254 .attr({
255 y: 40 + (i * 15),
256 x: serviceTopologyConfig.instance.margin,
257 opacity: 0
258 })
259 .text(`${meter.description}: ${Math.round(meter.value)} ${meter.unit}`)
260 .transition()
261 .duration(serviceTopologyConfig.duration)
262 .attr({
263 opacity: 1
264 });
265 });
266
267 // add port stats
268 const ports = ['eth0', 'eth1'];
269 const interestingPortMeters = [
270 {
271 meter: 'network.incoming.bytes.rate',
272 label: 'Incoming'
273 },
274 {
275 meter: 'network.outgoing.bytes.rate',
276 label: 'Outgoing'
277 }
278 ];
279
280 ports.forEach((p, j) => {
281
282 // if there are no port stats skip rendering
283 if(docker.port[p].length === 0){
284 return;
285 }
286
287 containerBox.append('text')
288 .attr({
289 y: 90,
290 x: serviceTopologyConfig.instance.margin + (120 * j),
291 class: 'name'
292 })
293 .text(`${docker.name}-${p}`)
294
295 interestingPortMeters.forEach((m, i) => {
296
297 const meter = lodash.find(docker.port[p], {meter: m.meter});
298 // if there is no meter stats skip rendering
299 if(!angular.isDefined(meter)){
300 return;
301 }
302 containerBox.append('text')
303 .attr({
304 y: 105 + (i * 15),
305 x: serviceTopologyConfig.instance.margin + (120 * j),
306 opacity: 0
307 })
308 .text(`${m.label}: ${Math.round(meter.value)} ${meter.unit}`)
309 .transition()
310 .duration(serviceTopologyConfig.duration)
311 .attr({
312 opacity: 1
313 });
314 });
315 });
316 }
317
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800318 const showInstanceStats = (container, instance) => {
319
320 // NOTE this should be dinamically positioned
321 // base on the number of element present
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800322
323 // fake the position
324 let translation = {
325 'mysite_vsg-1': '200, -120',
326 'mysite_vsg-2': '-300, 30',
327 'mysite_vsg-3': '-300, -250',
328 };
329
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800330 const statsContainer = container.append('g')
331 .attr({
Matteo Scandolo44e55912016-03-10 11:21:19 -0800332 transform: `translate(${translation[instance.humanReadableName] || translation['mysite_vsg-1']})`,
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800333 class: 'stats-container'
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800334 })
335 .on('click', function(d) {
336 // toggling visisbility
337 d.fade = !d.fade;
338 let opacity;
339 if(d.fade){
340 opacity = 0.1;
341 }
342 else{
343 opacity = 1;
344 }
345
346 d3.select(this)
347 .transition()
348 .duration(serviceTopologyConfig.duration)
349 .attr({
350 opacity: opacity
351 })
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800352 });
353
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800354 let lines = {
355 'mysite_vsg-1': {
Matteo Scandolo653c5092016-02-24 11:14:01 -0800356 x1: -160,
357 y1: 120,
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800358 x2: 0,
359 y2: 50,
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800360 },
361 'mysite_vsg-2': {
362 x1: 250,
363 y1: 50,
364 x2: 300,
365 y2: -10
366 },
367 'mysite_vsg-3': {
368 x1: 250,
369 y1: 50,
370 x2: 300,
371 y2: 270
372 }
373 }
374
375 statsContainer.append('line')
376 .attr({
Matteo Scandolo44e55912016-03-10 11:21:19 -0800377 x1: d => lines[d.humanReadableName].x1 || lines['mysite_vsg-1'],
378 y1: d => lines[d.humanReadableName].y1 || lines['mysite_vsg-1'],
379 x2: d => lines[d.humanReadableName].x2 || lines['mysite_vsg-1'],
380 y2: d => lines[d.humanReadableName].y2 || lines['mysite_vsg-1'],
Matteo Scandolo78185102016-02-23 14:03:03 -0800381 stroke: 'black',
382 opacity: 0
383 })
384 .transition()
385 .duration(serviceTopologyConfig.duration)
386 .attr({
387 opacity: 1
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800388 })
389
390 // NOTE rect should be dinamically sized base on the presence of a container
391 let statsHeight = 110;
Matteo Scandolo653c5092016-02-24 11:14:01 -0800392 let statsWidth = 250;
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800393
394 if (instance.container){
395 statsHeight += serviceTopologyConfig.container.height + (serviceTopologyConfig.container.margin * 2)
396 }
397
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800398 const statsVignette = statsContainer.append('rect')
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800399 .attr({
400 width: statsWidth,
Matteo Scandolo78185102016-02-23 14:03:03 -0800401 height: statsHeight,
402 opacity: 0
403 })
404 .transition()
405 .duration(serviceTopologyConfig.duration)
406 .attr({
407 opacity: 1
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800408 });
409
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800410
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800411 // add instance info
412 statsContainer.append('text')
413 .attr({
414 y: 15,
415 x: serviceTopologyConfig.instance.margin,
Matteo Scandolo78185102016-02-23 14:03:03 -0800416 class: 'name',
417 opacity: 0
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800418 })
419 .text(instance.humanReadableName)
Matteo Scandolo78185102016-02-23 14:03:03 -0800420 .transition()
421 .duration(serviceTopologyConfig.duration)
422 .attr({
423 opacity: 1
424 })
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800425
426 statsContainer.append('text')
427 .attr({
428 y: 30,
429 x: serviceTopologyConfig.instance.margin,
Matteo Scandolo78185102016-02-23 14:03:03 -0800430 class: 'ip',
431 opacity: 0
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800432 })
433 .text(instance.ip)
Matteo Scandolo78185102016-02-23 14:03:03 -0800434 .transition()
435 .duration(serviceTopologyConfig.duration)
436 .attr({
437 opacity: 1
438 })
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800439
440 // add stats
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800441 const interestingMeters = ['memory', 'memory.usage', 'cpu', 'cpu_util'];
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800442
443 interestingMeters.forEach((m, i) => {
444 const meter = lodash.find(instance.stats, {meter: m});
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800445
446 if(meter){
447
448 statsContainer.append('text')
449 .attr({
450 y: 55 + (i * 15),
451 x: serviceTopologyConfig.instance.margin,
452 opacity: 0
453 })
454 .text(`${meter.description}: ${Math.round(meter.value)} ${meter.unit}`)
455 .transition()
456 .duration(serviceTopologyConfig.duration)
457 .attr({
458 opacity: 1
459 });
460 }
461
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800462 });
463
464 if(instance.container){
465 // draw container
Matteo Scandolo653c5092016-02-24 11:14:01 -0800466 drawContainer(statsContainer, instance.container);
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800467 }
468
469 };
470
Matteo Scandolo14183932016-02-11 08:58:04 -0800471 this.drawInstances = (container, instances) => {
Matteo Scandoloba2d63d2016-02-17 13:54:11 -0800472
Matteo Scandoloc303fd02016-02-17 15:11:33 -0800473 // TODO check for stats field in instance and draw popup
474
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800475 let {width, height} = container.node().getBoundingClientRect();
476
Matteo Scandolo14183932016-02-11 08:58:04 -0800477 let elements = container.selectAll('.instances')
478 .data(instances, d => angular.isString(d.d3Id) ? d.d3Id : d.d3Id = `instance-${++instanceId}`)
479
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800480 var instanceContainer = elements.enter().append('g');
481
482 instanceContainer
Matteo Scandolo14183932016-02-11 08:58:04 -0800483 .attr({
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800484 transform: `translate(${width / 2}, ${ height / 2})`,
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800485 class: d => `instance ${d.selected ? 'active' : ''} ${getInstanceStatusColor(d)}`,
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800486 })
487 .transition()
488 .duration(serviceTopologyConfig.duration)
489 .attr({
Matteo Scandoloba2d63d2016-02-17 13:54:11 -0800490 transform: (d, i) => `translate(${RackHelper.getInstancePosition(i)})`
Matteo Scandolo14183932016-02-11 08:58:04 -0800491 });
492
493 instanceContainer.append('rect')
Matteo Scandolob785b572016-02-16 11:50:51 -0800494 .attr({
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800495 width: 0,
496 height: 0
497 })
498 .transition()
499 .duration(serviceTopologyConfig.duration)
500 .attr({
Matteo Scandolob785b572016-02-16 11:50:51 -0800501 width: serviceTopologyConfig.instance.width,
502 height: serviceTopologyConfig.instance.height
503 });
504
505 instanceContainer.append('text')
506 .attr({
Matteo Scandolo0cfd5a22016-02-16 13:29:26 -0800507 'text-anchor': 'middle',
508 y: 23, //FIXME
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800509 x: 40, //FIXME
510 opacity: 0
Matteo Scandolob785b572016-02-16 11:50:51 -0800511 })
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800512 .text(d => formatInstanceName(d.humanReadableName))
Matteo Scandolo7fd4d042016-02-16 14:44:51 -0800513 .transition()
514 .duration(serviceTopologyConfig.duration)
515 .attr({
516 opacity: 1
517 });
Matteo Scandolo0cfd5a22016-02-16 13:29:26 -0800518
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800519 // if stats are attached and instance is active,
520 // draw stats
Matteo Scandolo78185102016-02-23 14:03:03 -0800521 instanceContainer.each(function(instance, i){
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800522
523 const container = d3.select(this);
524
525 if(angular.isDefined(instance.stats) && instance.selected){
Matteo Scandolo78185102016-02-23 14:03:03 -0800526 showInstanceStats(container, instance, i);
Matteo Scandolo06afdfe2016-02-23 13:47:14 -0800527 }
528 });
529
Matteo Scandolo7de629e2016-03-10 08:59:27 -0800530 // instanceContainer
531 // .on('click', function(d){
532 // console.log(`Draw vignette with stats for instance: ${d.name}`);
533 // });
Matteo Scandolo14183932016-02-11 08:58:04 -0800534 };
Matteo Scandolo38ba3312016-02-09 16:01:49 -0800535
536 this.addPhisical = (nodes) => {
Matteo Scandolod1b3bf52016-03-07 16:42:03 -0800537
538 nodes.select('rect').remove();
539 nodes.select('text').remove();
540
Matteo Scandolo38ba3312016-02-09 16:01:49 -0800541 nodes.append('rect')
542 .attr(serviceTopologyConfig.square);
543
544 nodes.append('text')
545 .attr({
546 'text-anchor': 'middle',
547 y: serviceTopologyConfig.square.y - 10
548 })
Matteo Scandoloa03110c2016-03-01 15:20:29 -0800549 .text(d => {
550 return d.name || d.humanReadableName
551 });
Matteo Scandolo38ba3312016-02-09 16:01:49 -0800552 }
553
554 this.addDevice = (nodes) => {
555 nodes.append('circle')
556 .attr(serviceTopologyConfig.circle);
557
558 nodes.append('text')
559 .attr({
560 'text-anchor': 'end',
561 x: - serviceTopologyConfig.circle.r - 10,
562 y: serviceTopologyConfig.circle.r / 2
563 })
Matteo Scandolodb8a1852016-02-23 10:04:36 -0800564 .text(d => d.name || d.mac);
Matteo Scandolo38ba3312016-02-09 16:01:49 -0800565 }
566 });
567})();