Documentacion agregada - previo a la presentacion
[VSoRC/.git] / js / topology / topology.js
1 // Copyright (c) 2018 Maen Artimy
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //   http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 $(function () {
16     const tabObj = Tabs('topology');
17     const size = 60;
18     const radius = 8;
19
20     // Define the div for the tooltip
21     const div = d3.select("body").append("div")
22         .attr("class", "tooltip")
23         .style("opacity", 0);
24
25     function add_prefix(obj) {
26         return String(obj).replace(/^0+/, "Switch_");
27     }
28
29     function trim_zeros(obj) {
30         return String(obj).replace(/^0+/, "");
31     }
32
33     // Takes JSON data and convert to a graph format that D3 understands:
34     // var topDesc = {
35     //     "nodes": [
36     //       {"id": "0000000000000001", "type": "switch"},
37     //       {"id": "0000000000000002", "type": "switch"},
38     //       {"id": "16:18:83:a8:4d:1c", "type": "host"},
39     //       ...
40     //     ],
41     //     "links": [
42     //         {"source": "0000000000000002", "target": "0000000000000003",
43     //          "port": {"source": "00000003", "target": "00000002"}, "value": 4},
44     //       ...
45     //     ]}
46 function displayMessage(msg) {
47     var $x = $("#snackbar");
48     $x.text(msg)
49     $x.toggleClass("show");
50     setTimeout(function () { $x.toggleClass("show"); }, 3000);
51 }
52
53
54     function toGraph(top) {
55         var nodes = [];
56         var links = [];
57
58         var lst = top.switches;
59
60         for (var i = 0; i < lst.length; i++) {
61             nodes.push({ "id": lst[i].dpid, "type": "switch" });
62         }
63
64         if (top.links.length > 0) {
65             lst = top.links;
66             for (var i = 0; i < lst.length; i++) {
67                 if (lst[i].src.dpid < lst[i].dst.dpid) { // prevent duplicate links
68                     links.push({
69                         "source": lst[i].src.dpid, "target": lst[i].dst.dpid, "value": 4,
70                         "port": { "source": lst[i].src.port_no, "target": lst[i].dst.port_no }
71                     });
72                 }
73             }
74         } else if (top.switches.length > 1) { // represent the network with a cloud
75             nodes.push({ "id": 0, "type": "cloud" });
76             for (var i = 0; i < lst.length; i++) {
77                 links.push({
78                     "source": 0, "target": lst[i].dpid, "value": 4,
79                     "port": { "source": 0, "target": 0 }
80                 });
81             }
82         }
83
84         lst = top.hosts;
85         console.log(top.hosts);
86         for (var i = 0; i < lst.length; i++) {
87             nodes.push({ "id": lst[i].mac, "type": "host","ip": lst[i].ipv4});
88             links.push({
89                 "source": lst[i].port.dpid, "target": lst[i].mac, "value": 2,
90                 "port": { "source": lst[i].port.port_no, "target": 0 }
91             });
92         }
93
94         return { "nodes": nodes, "links": links };
95     }
96
97     // Plot the topology using D3.js
98     // Many online tutorials explain how this works. Example: www.puzzlr.org/force-graphs-with-d3
99     function plotGraph(graph) {
100         var svg = d3.select("svg");
101         var width = +svg.attr("width");
102         var height = +svg.attr("height");
103
104         //custom force to put everything in a box
105         function box_force() {
106             var curr_node;
107             for (var i = 0, n = graph.nodes.length; i < n; ++i) {
108                 curr_node = graph.nodes[i];
109                 curr_node.x = Math.max(radius, Math.min(width - radius, curr_node.x));
110                 curr_node.y = Math.max(radius, Math.min(height - radius, curr_node.y));
111             }
112         }
113
114         // Create a force layout simulation
115
116         // Link: attraction force
117         // chanrge: repulsion force
118         // x: attracts the nodes to the horizontal centre
119         // y: attracts the hosts the bottom while other nodes are attrcated to the top
120         // centre: atracts to the centre
121         // collision: avoids node collision
122         // box: keeps all nodes inside
123         var simulation = d3.forceSimulation()
124             .nodes(graph.nodes)
125             .force("link", d3.forceLink(graph.links).id(function (d) { return d.id; }).distance(size * 2))
126             .force("charge", d3.forceManyBody().strength(-size * 30))
127             .force("x", d3.forceX(width / 2))
128             .force("y", d3.forceY(function (d) {
129                 if (d.type === "host") {
130                     return 3 * height / 4
131                 } else {
132                     return 1 * height / 4
133                 }
134             }).strength(0.25))
135             .force("centre", d3.forceCenter(width / 2, height / 2))
136             .force("collision", d3.forceCollide().radius(35))
137             .force("box", box_force);
138
139         // Create nodes with image and text
140         var node = svg.append("g")
141             .attr("class", "nodes")
142             .selectAll(".node")
143             .data(graph.nodes)
144             .enter().append("g")
145             // .attr("id", function (d) { return "N" + d.id; })
146             .attr("class", "node");
147
148         node.append("image")
149             .attr("xlink:href", function (d) {
150                 if (d.type === "switch") {
151                     return "img/switch.png"
152                 } else if (d.type === "cloud") {
153                     return "img/cloud.svg"
154                 } else {
155                     return "img/pc.svg"
156                 }
157             })
158             .on("mouseover", handleMouseOver)
159             .on("mouseout", handleMouseOut)
160
161         node.append("text")
162             .attr("class", "label")
163             .attr("dy", size + 14)
164             .text(function (d) { return d.id; });
165
166
167        node.append("text")
168             .attr("class", "label")
169             .attr("dy", size + 26)
170             .text(function (d) { if (d.type === "host")return (d.ip); });
171             // .text(function (d) { return d.id.replace(/^0+/, ''); });
172
173
174         // Create links with lines, circles, and text
175         var link = svg.append("g")
176             .attr("class", "links")
177             .selectAll(".link")
178             .data(graph.links)
179             .enter().append("g")
180             .attr("class", "link");
181
182         link.append("line")
183             .attr("stroke-width", function (d) { return d.value; });
184
185         link.append("circle")
186             .attr("class", "start")
187             .attr("r", radius)
188
189         link.append("circle")
190             .attr("class", "end")
191             .attr("r", radius)
192
193         link.append("text")
194             .attr("class", "start")
195             .text(function (d) { return trim_zeros(d.port.source); })
196
197         link.append("text")
198             .attr("class", "end")
199             .text(function (d) { return trim_zeros(d.port.target); })
200
201         // Simulation steps
202         function tickActions() {
203             function norm(d) {
204                 return Math.sqrt((d.target.x - d.source.x) ** 2 + (d.target.y - d.source.y) ** 2);
205             }
206
207             node
208                 .attr("transform", function (d) { return "translate(" + (d.x - size / 2) + "," + (d.y - size / 2) + ")"; });
209             link
210                 .attr("transform", function (d) { return "translate(" + d.source.x + "," + d.source.y + ")"; });
211
212             link.selectAll("line")
213                 .attr("x1", function (d) { return (d.target.x - d.source.x) * size / 2 / norm(d); })
214                 .attr("y1", function (d) { return (d.target.y - d.source.y) * size / 2 / norm(d); })
215                 .attr("x2", function (d) { return (d.target.x - d.source.x) * (1 - size / 2 / norm(d)); })
216                 .attr("y2", function (d) { return (d.target.y - d.source.y) * (1 - size / 2 / norm(d)); })
217
218             // position of the link start port
219             link.selectAll("circle.start")
220                 .attr("cx", function (d) { return (d.target.x - d.source.x) * size / 2 / norm(d); })
221                 .attr("cy", function (d) { return (d.target.y - d.source.y) * size / 2 / norm(d); })
222
223             // psotion of the link end port
224             link.selectAll("circle.end")
225                 .attr("cx", function (d) { return (d.target.x - d.source.x) * (1 - size / 2 / norm(d)); })
226                 .attr("cy", function (d) { return (d.target.y - d.source.y) * (1 - size / 2 / norm(d)); })
227
228             link.selectAll("text.start")
229                 .attr("dx", function (d) { return (d.target.x - d.source.x) * size / 2 / norm(d); })
230                 .attr("dy", function (d) { return (d.target.y - d.source.y) * size / 2 / norm(d); })
231
232             link.selectAll("text.end")
233                 .attr("dx", function (d) { return (d.target.x - d.source.x) * (1 - size / 2 / norm(d)); })
234                 .attr("dy", function (d) { return (d.target.y - d.source.y) * (1 - size / 2 / norm(d)); })
235
236         }
237
238         // Handling mouse drag
239         node.call(d3.drag()
240             .on("start", drag_start)
241             .on("drag", drag_drag)
242             .on("end", drag_end));
243
244         function drag_start(d) {
245             if (!d3.event.active) simulation.alphaTarget(0.3).restart();
246             d.fx = d.x;
247             d.fy = d.y;
248         }
249
250         function drag_drag(d) {
251             d.fx = d3.event.x;
252             d.fy = d3.event.y;
253         }
254
255         function drag_end(d) {
256             if (!d3.event.active) simulation.alphaTarget(0);
257             d.fx = null;
258             d.fy = null;
259         }
260
261         // Handling mouse over
262         function handleMouseOver(d) {
263             div.transition()
264                 .duration(200)
265                 .style("opacity", .9);
266             div.html(d.type + ": " + d.id)
267                 .style("left", (d3.event.pageX) + "px")
268                 .style("top", (d3.event.pageY - 28) + "px");
269         }
270
271         function handleMouseOut(d) {
272             div.transition()
273                 .duration(500)
274                 .style("opacity", 0);
275         }
276
277         // drag_handler(node);
278
279         // run tickActions in every simulation step
280         simulation.on("tick", tickActions);
281
282     }
283
284     // Display the raw topology data
285     function listTopology(network) {
286         data = "<h1>Switches</h1>" + JSON.stringify(network.switches) + "<br>";
287         data += "<h1>Links</h1>" + JSON.stringify(network.links) + "<br>";
288         data += "<h1>Hosts</h1>" + JSON.stringify(network.hosts) + "<br>";
289         $('#data').html(data);
290     }
291
292     function getTopology() {
293         tabObj.buildTabs("#main", ["Graph", "Tables"], "Nothing to show!");
294         var $svg = $('<svg width="800" height="600"></svg>');
295         var $data = $('<div id="data"></div>');
296         tabObj.buildContent('Graph', $svg);
297         tabObj.buildContent('Tables', $data);
298
299
300
301
302 // La funcion jsonget fue creada para sustituir el metodo json de D3
303
304         function jsonget() {
305           let xhr = new XMLHttpRequest();
306           xhr.open('GET', "/gettopo" , true);
307           //console.log(xhr); //para ver en la consola
308           xhr.onload = function() {
309             if (xhr.status == 200) { //can use this.status instead
310               //console.log(xhr.responseText);// para ver en la consola
311                 if(xhr.response === null || xhr.response === ""){
312                 displayMessage("No response from controller")
313                 }
314               plotGraph(toGraph(JSON.parse(xhr.responseText)));
315             }
316           }
317           xhr.send();
318         }
319       jsonget();
320
321
322
323
324
325         // d3.json(location.hostname+":8080/topology").then(function (data) {
326         //     listTopology(data)
327         //     plotGraph(toGraph(data));
328         // });
329         tabObj.setActive();
330     }
331
332     // When the refresh button is clicked, clear the page and start over
333     $('.refresh').on('click', function () {
334         //$('svg').html("");
335         getTopology();
336     });
337
338     getTopology();
339
340 });