blob: 4c6e51705696a8ae85262be740f33e26271b9df0 [file] [log] [blame]
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -07001function convert_to_topodata(switches, links, registry){
2 var controllers={};
3 var nr_controllers=0;
4 var sws=[];
5 var ls=[];
6 var topo = new Array();
7 switches.forEach(function(item) {
8 var sw={}
9 sw.name=item.dpid;
10 sw.group=-1;
11 sws.push(sw);
12 });
13 for (var r in registry){
14 if ( ! (registry[r][0]['controllerId'] in controllers) ){
15 controllers[registry[r][0]['controllerId']] = ++nr_controllers;
16 }
17 }
18 for (var i = 0; i < sws.length; i++){
19 if (sws[i].name in registry){
20 sws[i].group=controllers[registry[sws[i].name][0]['controllerId']];
21 sws[i].controller=registry[sws[i].name][0]['controllerId'];
22 }
23 }
24 links.forEach(function(item) {
25 var link={};
26 for (var i = 0; i < sws.length; i++){
Jonathan Hartf10843d2014-07-09 13:54:44 -070027 if(sws[i].name == item['src']['dpid'])
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -070028 break;
29 }
30 link.source=i;
31 for (var i = 0; i < sws.length; i++){
Jonathan Hartf10843d2014-07-09 13:54:44 -070032 if(sws[i].name == item['dst']['dpid'])
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -070033 break;
34 }
35 link.target=i;
36 ls.push(link);
37 });
38 topo['nodes']=sws;
39 topo['links']=ls;
40 return topo;
41}
42
43var width = 1280;
44var height = 1280;
45var radius = 8;
46function gui(switch_url, link_url, registry_url){
47 var svg = d3.select("#topology")
48 .append("svg:svg")
49 .attr("width", width)
50 .attr("height", height);
51
Jonathan Hart40a2d132014-05-12 22:05:14 -070052 var status_header = svg.append("svg:text")
53 .attr("id", "status_header")
54 .attr("x", 50)
55 .attr("y", 20);
56
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -070057 var force = d3.layout.force()
58 .charge(-500)
59 .linkDistance(100)
60 .size([width, height]);
61
62 var node_drag = d3.behavior.drag()
63 .on("dragstart", dragstart)
64 .on("drag", dragmove)
65 .on("dragend", dragend);
66
67 var color = d3.scale.category20();
68 var topodata;
69 var nodes = force.nodes();
70 var links = force.links();
71
72 d3.json(switch_url, function(error, rest_switches) {
73 d3.json(link_url, function(error, rest_links) {
74 d3.json(registry_url, function(error, rest_registry) {
75 topodata = convert_to_topodata(rest_switches, rest_links, rest_registry);
76 init(topodata, nodes, links);
77 path = svg.append("svg:g").selectAll("path").data(links);
78 circle = svg.append("svg:g").selectAll("circle").data(nodes);
Jonathan Hart40a2d132014-05-12 22:05:14 -070079 text = svg.append("svg:g").selectAll("text.node-label").data(nodes);
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -070080 draw();
81 });
82 });
83 });
84
85 setInterval(function(){
86 d3.json(switch_url, function(error, rest_switches) {
87 d3.json(link_url, function(error, rest_links) {
88 d3.json(registry_url, function(error, rest_registry) {
89 topodata = convert_to_topodata(rest_switches, rest_links, rest_registry);
90 var changed = update(topodata, nodes, links);
91 path = svg.selectAll("path").data(links)
92 circle = svg.selectAll("circle").data(nodes);
Jonathan Hart40a2d132014-05-12 22:05:14 -070093 text = svg.selectAll("text.node-label").data(nodes);
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -070094 if ( changed ){
95 draw();
96 }
97 });
98 });
99 });
100 }, 3000);
101
102 function draw(){
103 force.stop();
Jonathan Hart40a2d132014-05-12 22:05:14 -0700104 svg.select("#status_header")
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -0700105 .text(function(){return "Switch: " + force.nodes().length + " (Active: " + nr_active_sw() + ")/ Link: " + force.links().length});
Jonathan Hart40a2d132014-05-12 22:05:14 -0700106
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -0700107 path.enter().append("svg:path")
108 .attr("class", function(d) { return "link"; })
109 .attr("marker-end", function(d) {
110 if(d.type == 1){
111 return "url(#TriangleRed)";
112 } else {
113 return "url(#Triangle)";
114 }
115 });
116
117 circle.enter().append("svg:circle")
118 .attr("r", function(d) {
119 if (d.group == 1000){
120 return radius;
121 }else{
122 return radius;
123 }
124 })
125 .call(node_drag);
126
127 text.enter().append("svg:text")
Jonathan Hart40a2d132014-05-12 22:05:14 -0700128 .classed("node-label", true)
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -0700129 .attr("x", radius)
130 .attr("y", ".31em")
131 .text(function(d) {
132 l=d.name.split(":").length
133 return d.name.split(":")[l-2] + ":" + d.name.split(":")[l-1]
134 });
135
136 circle.append("title")
137 .text(function(d) { return d.name; });
138
139 circle.attr("fill", function(d) {
140 if (d.group == 1){
141 return "red"
142 }else if (d.group == 2){
143 return "blue"
144 }else if (d.group == 3){
145 return "green"
146 }else if (d.group == 4){
147 return "orange"
148 }else if (d.group == 5){
149 return "cyan"
150 }else if (d.group == 6){
151 return "magenta"
152 }else if (d.group == 7){
153 return "yellow"
154 }else if (d.group == 8){
155 return "purple"
156 }else{
157 return "gray"
158 }
159 });
160
Masayoshi Kobayashie64d44b2014-04-23 11:40:02 -0700161 path.attr("stroke", function(d) {
162 if(d.type == 1){
163 return "red"
164 } else {
165 return "black"
166 }
167 }).attr("stroke-width", function(d) {
168 if(d.type == 1){
169 return "2px";
170 } else {
171 return "1.5px";
172 }
173 }).attr("marker-end", function(d) {
174 if(d.type == 1){
175 return "url(#TriangleRed)";
176 } else {
177 return "url(#Triangle)";
178 }
179 });
180 path.exit().remove();
181 circle.exit().remove();
182 text.exit().remove();
183 force.on("tick", tick);
184 force.start();
185 }
186 function nr_active_sw(){
187 var n=0;
188 var nodes = force.nodes();
189 for(var i=0;i<nodes.length;i++){
190 if(nodes[i].group!=0)
191 n++;
192 };
193 return n;
194 }
195 function dragstart(d, i) {
196 force.stop() // stops the force auto positioning before you start dragging
197 }
198 function dragmove(d, i) {
199 d.px += d3.event.dx;
200 d.py += d3.event.dy;
201 d.x += d3.event.dx;
202 d.y += d3.event.dy;
203 tick(); // this is the key to make it work together with updating both px,py,x,y on d !
204 }
205
206 function dragend(d, i) {
207 d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
208 tick();
209 force.resume();
210 }
211 function tick() {
212 path.attr("d", function(d) {
213 var dx = d.target.x - d.source.x,
214 dy = d.target.y - d.source.y,
215 dr = 1/d.linknum; //linknum is defined above
216 dr = 0; // 0 for direct line
217 return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
218 });
219 path.attr("stroke", function(d) {
220 if(d.type == 1){
221 return "red"
222 } else {
223 return "black"
224 }
225 }).attr("stroke-width", function(d) {
226 if(d.type == 1){
227 return "3px";
228 } else {
229 return "1.5px";
230 }
231 }).attr("marker-end", function(d) {
232 if(d.type == 1){
233 return "url(#TriangleRed)";
234 } else {
235 return "url(#Triangle)";
236 }
237 });
238// circle.attr("cx", function(d) { return d.x; }).attr("cy", function(d) { return d.y; });
239 circle.attr("transform", function(d) {
240 x = Math.max(radius, Math.min(width - radius, d.x));
241 y = Math.max(radius, Math.min(height - radius, d.y));
242// return "translate(" + d.x + "," + d.y + ")";
243 return "translate(" + x + "," + y + ")";
244 })
245 circle.attr("fill", function(d) {
246 if (d.group == 1){
247 return "red"
248 }else if (d.group == 2){
249 return "blue"
250 }else if (d.group == 3){
251 return "green"
252 }else if (d.group == 4){
253 return "orange"
254 }else if (d.group == 5){
255 return "cyan"
256 }else if (d.group == 6){
257 return "magenta"
258 }else if (d.group == 7){
259 return "yellow"
260 }else if (d.group == 8){
261 return "purple"
262 }else{
263 return "gray"
264 }
265 });
266// text.attr("x", function(d) { return d.x; }).attr("y", function(d) { return d.y; });
267 text.attr("transform", function(d) {
268 return "translate(" + d.x + "," + d.y + ")";
269 });
270 }
271
272}
273
274
275function init(topodata, nodes, links){
276 topodata.nodes.forEach(function(item) {
277 nodes.push(item);
278 });
279 topodata.links.forEach(function(item) {
280 links.push(item);
281 });
282 links.sort(compare_link);
283 // distinguish links that have the same src & dst node by 'linknum'
284 for (var i=1; i < links.length; i++) {
285 if (links[i].source == links[i-1].source &&
286 links[i].target == links[i-1].target) {
287 links[i].linknum = links[i-1].linknum + 1;
288 } else {
289 links[i].linknum = 1;
290 };
291 };
292}
293
294function compare_link (a, b){
295 if (a.source > b.source)
296 return 1;
297 else if (a.source < b.source)
298 return -1;
299 else {
300 if (a.target > b.target)
301 return 1;
302 else if (a.target < b.target)
303 return -1;
304 else
305 return 0;
306 }
307}
308
309/* Return nodes that is not in the current list of nodes */
310Array.prototype.node_diff = function(arr) {
311 return this.filter(function(i) {
312 for (var j = 0; j < arr.length ; j++) {
313 if (arr[j].name === i.name)
314 return false;
315 }
316 return true;
317 });
318};
319
320/* Return removed links */
321function gone_links (topo_json, links){
322 gone = []
323 for (var i = 0; i < links.length ; i ++){
324 var found = 0;
325 for (var j = 0; j < topo_json.links.length ; j ++){
326 if (links[i].source.name == topo_json.nodes[topo_json.links[j].source].name &&
327 links[i].target.name == topo_json.nodes[topo_json.links[j].target].name ){
328 found = 1;
329 break;
330 }
331 }
332 if ( found == 0 ){
333 gone.push(links[i]);
334 }
335 }
336 return gone;
337}
338
339/* Return added links */
340function added_links (topo_json, links) {
341 added = [];
342 for (var j = 0; j < topo_json.links.length ; j ++){
343 var found = 0;
344 for (var i = 0; i < links.length ; i ++){
345 if (links[i].source.name == topo_json.nodes[topo_json.links[j].source].name &&
346 links[i].target.name == topo_json.nodes[topo_json.links[j].target].name ){
347 found = 1;
348 break;
349 }
350 }
351 if ( found == 0 ){
352 added.push(topo_json.links[j]);
353 }
354 }
355 return added;
356}
357
358/* check if toplogy has changed and update node[] and link[] accordingly */
359function update(json, nodes, links){
360 var changed = false;
361 var n_adds = json.nodes.node_diff(nodes);
362 var n_rems = nodes.node_diff(json.nodes);
363 for (var i = 0; i < n_adds.length; i++) {
364 nodes.push(n_adds[i]);
365 changed = true;
366 }
367 for (var i = 0; i < n_rems.length; i++) {
368 for (var j = 0; j < nodes.length; j++) {
369 if ( nodes[j].name == n_rems[i].name ){
370 nodes.splice(j,1);
371 changed = true;
372 break;
373 }
374 }
375 }
376 var l_adds = added_links(json, links);
377 var l_rems = gone_links(json, links);
378 for (var i = 0; i < l_rems.length ; i++) {
379 for (var j = 0; j < links.length; j++) {
380 if (links[j].source.name == l_rems[i].source.name &&
381 links[j].target.name == l_rems[i].target.name) {
382 links.splice(j,1);
383 changed = true;
384 break;
385 }
386 }
387 }
388 // Sorce/target of an element of l_adds[] are corresponding to the index of json.node[]
389 // which is different from the index of node[] (new nodes are always added to the last)
390 // So update soure/target node indexes of l_add[] need to be fixed to point to the proper
391 // node in node[];
392 for (var i = 0; i < l_adds.length; i++) {
393 for (var j = 0; j < nodes.length; j++) {
394 if ( json.nodes[l_adds[i].source].name == nodes[j].name ){
395 l_adds[i].source = j;
396 break;
397 }
398 }
399 for (var j = 0; j < nodes.length; j++) {
400 if ( json.nodes[l_adds[i].target].name == nodes[j].name ){
401 l_adds[i].target = j;
402 break;
403 }
404 }
405 links.push(l_adds[i]);
406 changed = true;
407 }
408
409 // Update "group" attribute of nodes
410 for (var i = 0; i < nodes.length; i++) {
411 for (var j = 0; j < json.nodes.length; j++) {
412 if ( nodes[i].name == json.nodes[j].name ){
413 if (nodes[i].group != json.nodes[j].group){
414 nodes[i].group = json.nodes[j].group;
415 changed = true;
416 }
417 }
418 }
419 }
420 for (var i = 0; i < links.length; i++) {
421 for (var j = 0; j < json.links.length; j++) {
422 if (links[i].target.name == json.nodes[json.links[j].target].name &&
423 links[i].source.name == json.nodes[json.links[j].source].name ){
424 if (links[i].type != json.links[j].type){
425 links[i].type = json.links[j].type;
426 changed = true;
427 }
428 }
429 }
430 }
431 return changed
432}
433