second try
[vsorcdistro/.git] / ryu / build / lib.linux-armv6l-2.7 / ryu / app / gui_topology / html / ryu.topology.js
1 var CONF = {
2     image: {
3         width: 50,
4         height: 40
5     },
6     force: {
7         width: 960,
8         height: 500,
9         dist: 200,
10         charge: -600
11     }
12 };
13
14 var ws = new WebSocket("ws://" + location.host + "/v1.0/topology/ws");
15 ws.onmessage = function(event) {
16     var data = JSON.parse(event.data);
17
18     var result = rpc[data.method](data.params);
19
20     var ret = {"id": data.id, "jsonrpc": "2.0", "result": result};
21     this.send(JSON.stringify(ret));
22 }
23
24 function trim_zero(obj) {
25     return String(obj).replace(/^0+/, "");
26 }
27
28 function dpid_to_int(dpid) {
29     return Number("0x" + dpid);
30 }
31
32 var elem = {
33     force: d3.layout.force()
34         .size([CONF.force.width, CONF.force.height])
35         .charge(CONF.force.charge)
36         .linkDistance(CONF.force.dist)
37         .on("tick", _tick),
38     svg: d3.select("body").append("svg")
39         .attr("id", "topology")
40         .attr("width", CONF.force.width)
41         .attr("height", CONF.force.height),
42     console: d3.select("body").append("div")
43         .attr("id", "console")
44         .attr("width", CONF.force.width)
45 };
46 function _tick() {
47     elem.link.attr("x1", function(d) { return d.source.x; })
48         .attr("y1", function(d) { return d.source.y; })
49         .attr("x2", function(d) { return d.target.x; })
50         .attr("y2", function(d) { return d.target.y; });
51
52     elem.node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
53
54     elem.port.attr("transform", function(d) {
55         var p = topo.get_port_point(d);
56         return "translate(" + p.x + "," + p.y + ")";
57     });
58 }
59 elem.drag = elem.force.drag().on("dragstart", _dragstart);
60 function _dragstart(d) {
61     var dpid = dpid_to_int(d.dpid)
62     d3.json("/stats/flow/" + dpid, function(e, data) {
63         flows = data[dpid];
64         console.log(flows);
65         elem.console.selectAll("ul").remove();
66         li = elem.console.append("ul")
67             .selectAll("li");
68         li.data(flows).enter().append("li")
69             .text(function (d) { return JSON.stringify(d, null, " "); });
70     });
71     d3.select(this).classed("fixed", d.fixed = true);
72 }
73 elem.node = elem.svg.selectAll(".node");
74 elem.link = elem.svg.selectAll(".link");
75 elem.port = elem.svg.selectAll(".port");
76 elem.update = function () {
77     this.force
78         .nodes(topo.nodes)
79         .links(topo.links)
80         .start();
81
82     this.link = this.link.data(topo.links);
83     this.link.exit().remove();
84     this.link.enter().append("line")
85         .attr("class", "link");
86
87     this.node = this.node.data(topo.nodes);
88     this.node.exit().remove();
89     var nodeEnter = this.node.enter().append("g")
90         .attr("class", "node")
91         .on("dblclick", function(d) { d3.select(this).classed("fixed", d.fixed = false); })
92         .call(this.drag);
93     nodeEnter.append("image")
94         .attr("xlink:href", "./router.svg")
95         .attr("x", -CONF.image.width/2)
96         .attr("y", -CONF.image.height/2)
97         .attr("width", CONF.image.width)
98         .attr("height", CONF.image.height);
99     nodeEnter.append("text")
100         .attr("dx", -CONF.image.width/2)
101         .attr("dy", CONF.image.height-10)
102         .text(function(d) { return "dpid: " + trim_zero(d.dpid); });
103
104     var ports = topo.get_ports();
105     this.port.remove();
106     this.port = this.svg.selectAll(".port").data(ports);
107     var portEnter = this.port.enter().append("g")
108         .attr("class", "port");
109     portEnter.append("circle")
110         .attr("r", 8);
111     portEnter.append("text")
112         .attr("dx", -3)
113         .attr("dy", 3)
114         .text(function(d) { return trim_zero(d.port_no); });
115 };
116
117 function is_valid_link(link) {
118     return (link.src.dpid < link.dst.dpid)
119 }
120
121 var topo = {
122     nodes: [],
123     links: [],
124     node_index: {}, // dpid -> index of nodes array
125     initialize: function (data) {
126         this.add_nodes(data.switches);
127         this.add_links(data.links);
128     },
129     add_nodes: function (nodes) {
130         for (var i = 0; i < nodes.length; i++) {
131             this.nodes.push(nodes[i]);
132         }
133         this.refresh_node_index();
134     },
135     add_links: function (links) {
136         for (var i = 0; i < links.length; i++) {
137             if (!is_valid_link(links[i])) continue;
138             console.log("add link: " + JSON.stringify(links[i]));
139
140             var src_dpid = links[i].src.dpid;
141             var dst_dpid = links[i].dst.dpid;
142             var src_index = this.node_index[src_dpid];
143             var dst_index = this.node_index[dst_dpid];
144             var link = {
145                 source: src_index,
146                 target: dst_index,
147                 port: {
148                     src: links[i].src,
149                     dst: links[i].dst
150                 }
151             }
152             this.links.push(link);
153         }
154     },
155     delete_nodes: function (nodes) {
156         for (var i = 0; i < nodes.length; i++) {
157             console.log("delete switch: " + JSON.stringify(nodes[i]));
158
159             node_index = this.get_node_index(nodes[i]);
160             this.nodes.splice(node_index, 1);
161         }
162         this.refresh_node_index();
163     },
164     delete_links: function (links) {
165         for (var i = 0; i < links.length; i++) {
166             if (!is_valid_link(links[i])) continue;
167             console.log("delete link: " + JSON.stringify(links[i]));
168
169             link_index = this.get_link_index(links[i]);
170             this.links.splice(link_index, 1);
171         }
172     },
173     get_node_index: function (node) {
174         for (var i = 0; i < this.nodes.length; i++) {
175             if (node.dpid == this.nodes[i].dpid) {
176                 return i;
177             }
178         }
179         return null;
180     },
181     get_link_index: function (link) {
182         for (var i = 0; i < this.links.length; i++) {
183             if (link.src.dpid == this.links[i].port.src.dpid &&
184                     link.src.port_no == this.links[i].port.src.port_no &&
185                     link.dst.dpid == this.links[i].port.dst.dpid &&
186                     link.dst.port_no == this.links[i].port.dst.port_no) {
187                 return i;
188             }
189         }
190         return null;
191     },
192     get_ports: function () {
193         var ports = [];
194         var pushed = {};
195         for (var i = 0; i < this.links.length; i++) {
196             function _push(p, dir) {
197                 key = p.dpid + ":" + p.port_no;
198                 if (key in pushed) {
199                     return 0;
200                 }
201
202                 pushed[key] = true;
203                 p.link_idx = i;
204                 p.link_dir = dir;
205                 return ports.push(p);
206             }
207             _push(this.links[i].port.src, "source");
208             _push(this.links[i].port.dst, "target");
209         }
210
211         return ports;
212     },
213     get_port_point: function (d) {
214         var weight = 0.88;
215
216         var link = this.links[d.link_idx];
217         var x1 = link.source.x;
218         var y1 = link.source.y;
219         var x2 = link.target.x;
220         var y2 = link.target.y;
221
222         if (d.link_dir == "target") weight = 1.0 - weight;
223
224         var x = x1 * weight + x2 * (1.0 - weight);
225         var y = y1 * weight + y2 * (1.0 - weight);
226
227         return {x: x, y: y};
228     },
229     refresh_node_index: function(){
230         this.node_index = {};
231         for (var i = 0; i < this.nodes.length; i++) {
232             this.node_index[this.nodes[i].dpid] = i;
233         }
234     },
235 }
236
237 var rpc = {
238     event_switch_enter: function (params) {
239         var switches = [];
240         for(var i=0; i < params.length; i++){
241             switches.push({"dpid":params[i].dpid,"ports":params[i].ports});
242         }
243         topo.add_nodes(switches);
244         elem.update();
245         return "";
246     },
247     event_switch_leave: function (params) {
248         var switches = [];
249         for(var i=0; i < params.length; i++){
250             switches.push({"dpid":params[i].dpid,"ports":params[i].ports});
251         }
252         topo.delete_nodes(switches);
253         elem.update();
254         return "";
255     },
256     event_link_add: function (links) {
257         topo.add_links(links);
258         elem.update();
259         return "";
260     },
261     event_link_delete: function (links) {
262         topo.delete_links(links);
263         elem.update();
264         return "";
265     },
266 }
267
268 function initialize_topology() {
269     d3.json("/v1.0/topology/switches", function(error, switches) {
270         d3.json("/v1.0/topology/links", function(error, links) {
271             topo.initialize({switches: switches, links: links});
272             elem.update();
273         });
274     });
275 }
276
277 function main() {
278     initialize_topology();
279 }
280
281 main();