1 define(['app/ovsdb/lib/d3.min', 'app/ovsdb/OvsCore', 'underscore'], function(d3, OvsCore, _) {
7 bbox = { x:0, y:15, width: 0, height: 0},
11 defaultRouterWidth = 66,
12 defaultRouterHeight = 66,
13 networkMargin = { width: 120, height: 15},
14 routerMargin = { width: 120, height: 40},
15 vmMargin = { width: 90, height: 30},
17 defaultVmsHeight = 48,
18 ipNetworkTextMaxLength = 60,
27 // d3 layer over datas
32 randomize = OvsCore.Util.Math.Random(42);
34 function LogicalGraph(id, width , height) {
36 canvasHeight = height;
38 nodeHeight = height - 15;
40 var tmp = d3.select(id).append("svg")
42 .attr('height', height)
44 .attr('class', 'layer_0');
46 tmp.append('svg:rect')
48 .attr('height', height)
49 .attr('fill', 'white');
51 root = tmp.call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
53 tmp.on("dblclick.zoom", null);
57 // Define reusable svg item like box-shadow
60 var defs = d3.select('svg').insert('svg:defs', ':first-child');
61 var filter = defs.append('svg:filter').attr('id', 'boxShadow').attr('x', '0').attr('y', '0').attr('width', '200%').attr('height', '200%');
62 filter.append('feOffset').attr('in', 'SourceAlpha').attr('result', 'offOut').attr('dx', 0).attr('dy', 0);
63 filter.append('feGaussianBlur').attr('stdDeviation', '5').attr('in','offOut').attr('result', 'blurOut');
64 filter.append('feOffset').attr('in', 'SourceGraphic').attr('in2', 'blurOut').attr('mode', 'normal');
68 root.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
71 Object.defineProperties(LogicalGraph.prototype, {
73 set: function(value) {
75 value.forEach(function(net) {
76 routerData = routerData.concat(net.routers);
78 value.forEach(function(net) {
79 vmData = vmData.concat(net.instances);
85 LogicalGraph.prototype.start = function() {
86 setTopologyPosition.call(this, networkData);
87 addLinksToDom.call(this, linkData);
88 addNetWorkRouterVmsToDom.call(this, networkData, routerData, vmData);
92 LogicalGraph.prototype.freeDOM = function() {
102 bbox = { x:0, y:15, width: 0, height: 0};
105 function addLinksToDom(linksData) {
106 d3Link = root.selectAll('.llink')
107 .data(linksData).enter().append('svg:g');
109 d3Link.append('rect')
110 .attr('width', function(d) {
111 return d.target.x - d.source.x;
113 .attr('height', linkHeight)
114 .style('fill', function(d) {
118 d3Link.append('text')
121 .text(function(d) {return d.text;});
124 function addNetWorkRouterVmsToDom(networks, routers, vms) {
128 function getAbsPos() {
129 var elem = d3.select(this)[0][0],
130 elemAbsBBox = elem.getBoundingClientRect(),
131 parentAbsBox = d3.select('#l_graph').node().getBoundingClientRect(),
132 left = elemAbsBBox.left - parentAbsBox.left + (elemAbsBBox.right - elemAbsBBox.left),
133 top = elemAbsBBox.top - parentAbsBox.top;
144 d3Node = root.selectAll('.network')
145 .data(networks).enter()
147 d3Router = root.selectAll('.routers')
148 .data(routers).enter()
150 d3Vm = root.selectAll('.vm')
154 // append coresponding form
155 d3Node.append('svg:rect')
156 .attr('width', nodeWidth)
157 .attr('height', nodeHeight)
160 .style('fill', function(d) {
162 }).on('click', function(d) {
163 if (d3.event.defaultPrevented) return;
164 timer = setTimeout(function() {
165 var e = getAbsPos.call(this);
168 }).on('dblclick', function(d) {
173 // append the network name text
174 d3Node.append('text')
175 .attr('x', nodeWidth / 2 )
176 .attr('y', nodeHeight /2 )
177 .style('text-anchor', 'middle')
178 .style('writing-mode', 'tb')
179 .style('font-size', '12px')
180 .style('glyph-orientation-vertical', '0')
181 .text(function(d) { return d.name; });
183 // text info for the network ip
184 d3Node.append('text')
185 .attr('x', nodeWidth + 10)
186 .attr('y', nodeHeight - 15)
188 OvsCore.Util.String.Format('translate({0} {1}) rotate(-90) translate(-{0} -{1})', nodeWidth + 10, nodeHeight - 15))
189 .attr('class', 'linfolabel')
190 .text(function(d) {return d.ip;});
193 d3Vm.append('svg:image')
194 .attr('width', defaultVmsWidth)
195 .attr('height', defaultVmsHeight)
196 .attr('filter', 'url(#boxShadow)')
197 .attr('xlink:href', function(d) {
198 return d.type === 'network:dhcp' ?
199 'src/app/ovsdb/assets/dhcp.png' : 'src/app/ovsdb/assets/vm.png';
201 .on('click', function(d) {
202 if (d3.event.defaultPrevented) return;
203 timer = setTimeout(function() {
204 var e = getAbsPos.call(this);
207 }).on('dblclick', function(d) {
213 d3Router.append('svg:image')
214 .attr('width', defaultRouterWidth)
215 .attr('height', defaultRouterHeight)
216 .attr('xlink:href', 'src/app/ovsdb/assets/router.png')
217 .on('click', function(d) {
218 if (d3.event.defaultPrevented) return;
219 timer = setTimeout(function() {
220 var e = getAbsPos.call(this);
223 }).on('dblclick', function(d) {
229 d3Router.append('text')
230 .attr('x', defaultRouterWidth * 0.5)
231 .attr('y', defaultRouterHeight + 15)
232 .attr('text-anchor', 'middle')
233 .attr('class', 'linfolabel')
234 .text(function(d) { return d.name; });
238 .attr('x', defaultVmsWidth * 0.5)
239 .attr('y', defaultVmsHeight + 15)
240 .attr('text-anchor', 'middle')
241 .attr('class', 'linfolabel')
242 .text(function(d) { return d.name; });
244 // vm floating ip label
248 .attr('text-anchor', 'middle')
250 return (d.floatingIp) ? d.floatingIp.ip : '';
255 function findNetworkWithRouter(router) {
257 _.each(router.interfaces, function(inter) {
258 if (inter.type === 'router_interface') {
259 var net = tmpNetHolder[inter.networkId] || null;
262 result.push({network: net, interface: inter});
270 function positionateNetwork(network, x, y, margin) {
273 margin = margin || 0;
274 network.color = d3.hsl(randomize.next() * 360, 1, 0.6).toString();
276 // look is the network is the highest
277 bbox.height = network.y > bbox.height ? network.y : bbox.height ;
278 bbox.width = network.x > bbox.width ? network.x : bbox.width;
280 // get the number of "childs" (router, vm)
281 var nbRouter = network.routers.length;
282 var nbVm = network.instances.length;
284 if (!network.external) {
285 _.each(network.subnets, function(subnet, i) {
286 network.ip += subnet.cidr;
287 if (i < network.subnets.length -1) {
293 // if needed, ajust the height of the network
294 // to be able to display all children
295 ajustHeighBaseOnChilds(nbRouter, nbVm);
297 var py = positionateRouter(network, x + routerMargin.width, y + margin);
299 positionateVm(network, x + vmMargin.width, py + 35 + margin);
300 delete tmpNetHolder[network.id];
303 function positionateRouter(network, x, y) {
307 // loop over all routers
308 _.each(network.routers, function(router, i) {
309 router.x = getRouterCentroid(x, py).x ;
311 py += getRouterMarginHeight();
313 if (network.external) {
314 // find network ip with the gateway ip
315 var gateway = router.externalGateway.external_fixed_ips[0].ip_address;
316 var netIp = gateway.slice(0, gateway.lastIndexOf('.')) + '.0';
320 // look is the router is the highest
321 bbox.height = router.y > bbox.height ? router.y : bbox.height ;
322 bbox.width = router.x > bbox.width ? router.x : bbox.width;
325 source: {x: network.x + (nodeWidth * 0.5), y: router.y + (defaultRouterHeight * 0.5)},
326 target: {x: router.x + (defaultRouterWidth * 0.5), y: router.y + (nodeWidth * 0.5)},
327 color: network.color,
328 text: router.externalGateway.external_fixed_ips[0].ip_address
331 // go to the next layer
332 var nets = findNetworkWithRouter(router),
333 step = defaultRouterHeight / (nets.length + 1);
335 _.forEach(nets, function(net, i) {
336 var netPos = getNetworkLayerPosition(bbox.width + defaultRouterWidth);
338 positionateNetwork(net.network, netPos.x, netPos.y);
340 source: {x: router.x + (2 * nodeWidth), y: router.y + step * (i + 1) },
341 target: {x: net.network.x + (nodeWidth * 0.5), y: router.y + (nodeWidth * 0.5 )},
342 color: net.network.color,
343 text: net.interface.ip.ip_address
350 function positionateVm(network, x, y) {
352 // I do vm before router because router
353 // will step to another BUS
354 _.each(network.instances, function(vm) {
358 // look is the network is the highest
359 bbox.height = vm.y > bbox.height ? vm.y : bbox.height ;
360 bbox.width = vm.x > bbox.width ? vm.x : bbox.width;
362 y += getVmMarginHeight();
364 source: {x: network.x + (nodeWidth * 0.5), y: vm.y + (defaultVmsHeight * 0.5 )},
365 target: {x: vm.x + (defaultVmsWidth * 0.5), y: vm.y + (nodeWidth * 0.5)},
366 color: network.color,
373 * Scan the whole "BUS" to display it properly
374 * ------------------------------------------------
375 * I build it in a virtual space, if it need to be
376 * resize it at the end when the overal bounding
379 function setTopologyPosition(networks) {
380 _.each(networks, function(net) {
381 tmpNetHolder[net.id] = net;
385 for(var key in tmpNetHolder) {
386 var margin = (i === 0) ? 5 : networkMargin.width,
387 net = tmpNetHolder[key];
388 if (net.routers.length > 0) {
389 positionateNetwork(net, bbox.x + bbox.width + margin, bbox.y);
394 for(var key in tmpNetHolder) {
395 var margin = networkMargin.width,
396 net = tmpNetHolder[key];
397 positionateNetwork(net, bbox.x + bbox.width + margin, bbox.y);
402 * Check and ajust the height for a network.
404 function ajustHeighBaseOnChilds(nbRouter, nbVm) {
405 // calculate the height for the number of childs
406 var childHeight = nbRouter * (getRouterMarginHeight()) +
407 nbVm * (getVmMarginHeight()) + ipNetworkTextMaxLength;
409 // if heigh bigger than the default network height resize it
410 if (childHeight > nodeHeight) {
411 nodeHeight = childHeight + networkOffset;
416 * Set the view to the modal position
420 d3Node.attr('transform', function(d) {
421 return OvsCore.Util.String.Format("translate({0}, {1})",
426 d3Router.attr('transform', function(d, i) {
427 return OvsCore.Util.String.Format("translate({0}, {1})",
432 d3Vm.attr('transform', function(d) {
433 return OvsCore.Util.String.Format("translate({0}, {1})",
438 d3Link.attr('transform', function(d) {
439 return OvsCore.Util.String.Format("translate({0}, {1})",
440 d.source.x, d.source.y
444 // resize the graph if bigger than the canvas
445 var bbox = root.node().getBBox();
446 if (bbox.width > canvasWidth || bbox.height > canvasHeight) {
447 var sx = (canvasWidth - 30) / bbox.width,
448 sy = (canvasHeight - 30) / bbox.height,
449 s = sx < sy ? sx : sy;
450 d3.select('.layer_0').attr('transform', 'scale(' + s + ')');
451 console.log(root.node().getBBox());
455 function getRouterCentroid(x, y) {
457 x : x + defaultRouterWidth * 0.5,
458 y : y + defaultRouterHeight * 0.5
462 function getRouterMarginHeight() {
463 return (defaultRouterHeight + routerMargin.height);
466 function getVmMarginHeight() {
467 return (defaultVmsHeight + vmMargin.height);
470 function getNetworkLayerPosition(x) {
472 x : x + networkMargin.width,
473 y : networkMargin.height
477 function getVmLayerPosition(nbRouter, x) {
479 x : x + vmMargin.width * 2,
480 y : getRoutersDim(nbRouter).height + getVmMarginHeight()
485 LogicalGraph.prototype.onClick = _.noop;
486 LogicalGraph.prototype.dblClick = _.noop;