1 define(['app/ovsdb/lib/d3.min', 'app/ovsdb/OvsCore', 'underscore'], function (d3, OvsCore, _) {
16 defaultRouterWidth = 52,
17 defaultRouterHeight = 52,
31 defaultVmsHeight = 48,
32 ipNetworkTextMaxLength = 60,
41 // d3 layer over datas
46 randomize = OvsCore.Util.Math.Random(42);
48 function LogicalGraph(id, width, height) {
50 canvasHeight = height;
52 nodeHeight = height - 15;
54 var tmp = d3.select(id).append("svg")
56 .attr('height', height)
58 .attr('class', 'layer_0');
60 tmp.append('svg:rect')
62 .attr('height', height)
63 .attr('fill', 'white');
65 root = tmp.call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
67 tmp.on("dblclick.zoom", null);
71 // Define reusable svg item like box-shadow
74 var defs = d3.select('svg').insert('svg:defs', ':first-child');
75 var filter = defs.append('svg:filter').attr('id', 'boxShadow').attr('x', '0').attr('y', '0').attr('width', '200%').attr('height', '200%');
76 filter.append('feOffset').attr('in', 'SourceAlpha').attr('result', 'offOut').attr('dx', 0).attr('dy', 0);
77 filter.append('feGaussianBlur').attr('stdDeviation', '5').attr('in', 'offOut').attr('result', 'blurOut');
78 filter.append('feOffset').attr('in', 'SourceGraphic').attr('in2', 'blurOut').attr('mode', 'normal');
82 root.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
85 Object.defineProperties(LogicalGraph.prototype, {
87 set: function (value) {
89 value.forEach(function (net) {
90 routerData = routerData.concat(net.routers);
92 value.forEach(function (net) {
93 vmData = vmData.concat(net.instances);
99 LogicalGraph.prototype.start = function () {
100 setTopologyPosition.call(this, networkData);
101 addLinksToDom.call(this, linkData);
102 addNetWorkRouterVmsToDom.call(this, networkData, routerData, vmData);
106 LogicalGraph.prototype.freeDOM = function () {
124 function addLinksToDom(linksData) {
125 d3Link = root.selectAll('.llink')
126 .data(linksData).enter().append('svg:g');
128 d3Link.append('rect')
129 .attr('width', function (d) {
130 return d.target.x - d.source.x;
132 .attr('height', linkHeight)
133 .style('fill', function (d) {
137 d3Link.append('text')
145 function addNetWorkRouterVmsToDom(networks, routers, vms) {
149 d3Node = root.selectAll('.network')
150 .data(networks).enter()
152 d3Router = root.selectAll('.routers')
153 .data(routers).enter()
155 d3Vm = root.selectAll('.vm')
159 // append coresponding form
160 d3Node.append('svg:rect')
161 .attr('width', nodeWidth)
162 .attr('height', nodeHeight)
165 .style('fill', function (d) {
167 }).on('click', function (d) {
168 if (d3.event.defaultPrevented) return;
169 timer = setTimeout(function () {
172 }).on('dblclick', function (d) {
177 // append the network name text
178 d3Node.append('text')
179 .attr('x', nodeWidth / 2)
180 .attr('y', nodeHeight / 2)
181 .style('text-anchor', 'middle')
182 .style('writing-mode', 'tb')
183 .style('font-size', '12px')
184 .style('glyph-orientation-vertical', '0')
189 // text info for the network ip
190 d3Node.append('text')
191 .attr('x', nodeWidth + 10)
192 .attr('y', nodeHeight - 15)
194 OvsCore.Util.String.Format('translate({0} {1}) rotate(-90) translate(-{0} -{1})', nodeWidth + 10, nodeHeight - 15))
195 .attr('class', 'linfolabel')
201 d3Vm.append('svg:image')
202 .attr('width', defaultVmsWidth)
203 .attr('height', defaultVmsHeight)
204 .attr('filter', 'url(#boxShadow)')
205 .attr('xlink:href', function (d) {
206 return d.type === 'network:dhcp' ?
207 'src/app/ovsdb/assets/dhcp.png' : 'src/app/ovsdb/assets/vm.png';
209 .on('click', function (d) {
210 if (d3.event.defaultPrevented) return;
211 timer = setTimeout(function () {
214 }).on('dblclick', function (d) {
220 d3Router.append('svg:image')
221 .attr('width', defaultRouterWidth)
222 .attr('height', defaultRouterHeight)
223 .attr('xlink:href', 'src/app/ovsdb/assets/router.png')
224 .on('click', function (d) {
225 if (d3.event.defaultPrevented) return;
226 timer = setTimeout(function () {
229 }).on('dblclick', function (d) {
235 d3Router.append('text')
236 .attr('x', defaultRouterWidth * 0.5)
237 .attr('y', defaultRouterHeight + 15)
238 .attr('text-anchor', 'middle')
239 .attr('class', 'linfolabel')
246 .attr('x', defaultVmsWidth * 0.5)
247 .attr('y', defaultVmsHeight + 15)
248 .attr('text-anchor', 'middle')
249 .attr('class', 'linfolabel')
254 // vm floating ip label
258 .attr('text-anchor', 'middle')
260 return (d.floatingIp) ? d.floatingIp.ip : '';
265 function findNetworkWithRouter(router) {
267 _.each(router.interfaces, function (inter) {
268 if (inter.type === 'router_interface') {
269 var net = tmpNetHolder[inter.networkId] || null;
283 function positionateNetwork(network, x, y, margin) {
286 margin = margin || 0;
287 network.color = d3.hsl(randomize.next() * 360, 1, 0.6).toString();
289 // look is the network is the highest
290 bbox.height = network.y > bbox.height ? network.y : bbox.height;
291 bbox.width = network.x > bbox.width ? network.x : bbox.width;
293 // get the number of "childs" (router, vm)
294 var nbRouter = network.routers.length;
295 var nbVm = network.instances.length;
297 if (!network.external) {
298 _.each(network.subnets, function (subnet, i) {
299 network.ip += subnet.cidr;
300 if (i < network.subnets.length - 1) {
306 // if needed, ajust the height of the network
307 // to be able to display all children
308 ajustHeighBaseOnChilds(nbRouter, nbVm);
310 var py = positionateRouter(network, x + routerMargin.width, y + margin);
312 positionateVm(network, x + vmMargin.width, py + 35 + margin);
313 delete tmpNetHolder[network.id];
316 function positionateRouter(network, x, y) {
320 // loop over all routers
321 _.each(network.routers, function (router, i) {
322 router.x = getRouterCentroid(x, py).x;
324 py += getRouterMarginHeight();
326 if (network.external) {
327 // find network ip with the gateway ip
328 var gateway = router.externalGateway.external_fixed_ips[0].ip_address;
329 var netIp = gateway.slice(0, gateway.lastIndexOf('.')) + '.0';
333 // look is the router is the highest
334 bbox.height = router.y > bbox.height ? router.y : bbox.height;
335 bbox.width = router.x > bbox.width ? router.x : bbox.width;
339 x: network.x + (nodeWidth * 0.5),
340 y: router.y + (defaultRouterHeight * 0.5)
343 x: router.x + (defaultRouterWidth * 0.5),
344 y: router.y + (nodeWidth * 0.5)
346 color: network.color,
347 text: router.externalGateway.external_fixed_ips[0].ip_address
350 // go to the next layer
351 var nets = findNetworkWithRouter(router),
352 step = defaultRouterHeight / (nets.length + 1);
354 _.forEach(nets, function (net, i) {
355 var netPos = getNetworkLayerPosition(bbox.width + defaultRouterWidth);
357 positionateNetwork(net.network, netPos.x, netPos.y);
360 x: router.x + (2 * nodeWidth),
361 y: router.y + step * (i + 1)
364 x: net.network.x + (nodeWidth * 0.5),
365 y: router.y + (nodeWidth * 0.5)
367 color: net.network.color,
368 text: net.interface.ip.ip_address
375 function positionateVm(network, x, y) {
377 // I do vm before router because router
378 // will step to another BUS
379 _.each(network.instances, function (vm) {
383 // look is the network is the highest
384 bbox.height = vm.y > bbox.height ? vm.y : bbox.height;
385 bbox.width = vm.x > bbox.width ? vm.x : bbox.width;
387 y += getVmMarginHeight();
390 x: network.x + (nodeWidth * 0.5),
391 y: vm.y + (defaultVmsHeight * 0.5)
394 x: vm.x + (defaultVmsWidth * 0.5),
395 y: vm.y + (nodeWidth * 0.5)
397 color: network.color,
404 * Scan the whole "BUS" to display it properly
405 * ------------------------------------------------
406 * I build it in a virtual space, if it need to be
407 * resize it at the end when the overal bounding
410 function setTopologyPosition(networks) {
411 _.each(networks, function (net) {
412 tmpNetHolder[net.id] = net;
416 for (var key in tmpNetHolder) {
417 var margin = (i === 0) ? 5 : networkMargin.width,
418 net = tmpNetHolder[key];
419 if (net.routers.length > 0) {
420 positionateNetwork(net, bbox.x + bbox.width + margin, bbox.y);
425 for (var key in tmpNetHolder) {
426 var margin = networkMargin.width,
427 net = tmpNetHolder[key];
428 positionateNetwork(net, bbox.x + bbox.width + margin, bbox.y);
433 * Check and ajust the height for a network.
435 function ajustHeighBaseOnChilds(nbRouter, nbVm) {
436 // calculate the height for the number of childs
437 var childHeight = nbRouter * (getRouterMarginHeight()) +
438 nbVm * (getVmMarginHeight()) + ipNetworkTextMaxLength;
440 // if heigh bigger than the default network height resize it
441 if (childHeight > nodeHeight) {
442 nodeHeight = childHeight + networkOffset;
447 * Set the view to the modal position
451 d3Node.attr('transform', function (d) {
452 return OvsCore.Util.String.Format("translate({0}, {1})",
457 d3Router.attr('transform', function (d, i) {
458 return OvsCore.Util.String.Format("translate({0}, {1})",
463 d3Vm.attr('transform', function (d) {
464 return OvsCore.Util.String.Format("translate({0}, {1})",
469 d3Link.attr('transform', function (d) {
470 return OvsCore.Util.String.Format("translate({0}, {1})",
471 d.source.x, d.source.y
475 // resize the graph if bigger than the canvas
476 var bbox = root.node().getBBox();
477 if (bbox.width > canvasWidth || bbox.height > canvasHeight) {
478 var sx = (canvasWidth - 30) / bbox.width,
479 sy = (canvasHeight - 30) / bbox.height,
480 s = sx < sy ? sx : sy;
481 d3.select('.layer_0').attr('transform', 'scale(' + s + ')');
482 console.log(root.node().getBBox());
486 function getRouterCentroid(x, y) {
488 x: x + defaultRouterWidth * 0.5,
489 y: y + defaultRouterHeight * 0.5
493 function getRouterMarginHeight() {
494 return (defaultRouterHeight + routerMargin.height);
497 function getVmMarginHeight() {
498 return (defaultVmsHeight + vmMargin.height);
501 function getNetworkLayerPosition(x) {
503 x: x + networkMargin.width,
504 y: networkMargin.height
508 function getVmLayerPosition(nbRouter, x) {
510 x: x + vmMargin.width * 2,
511 y: getRoutersDim(nbRouter).height + getVmMarginHeight()
516 LogicalGraph.prototype.onClick = _.noop;
517 LogicalGraph.prototype.dblClick = _.noop;