blob: 2be8429aeb82135598b4154a627a0164246f2644 [file] [log] [blame]
tom8bb16062014-09-12 14:47:46 -07001<!DOCTYPE html>
Thomas Vachuska781d18b2014-10-27 10:31:25 -07002<!--
3 ~ Licensed to the Apache Software Foundation (ASF) under one
4 ~ or more contributor license agreements. See the NOTICE file
5 ~ distributed with this work for additional information
6 ~ regarding copyright ownership. The ASF licenses this file
7 ~ to you under the Apache License, Version 2.0 (the
8 ~ "License"); you may not use this file except in compliance
9 ~ with the License. You may obtain a copy of the License at
10 ~
11 ~ http://www.apache.org/licenses/LICENSE-2.0
12 ~
13 ~ Unless required by applicable law or agreed to in writing,
14 ~ software distributed under the License is distributed on an
15 ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 ~ KIND, either express or implied. See the License for the
17 ~ specific language governing permissions and limitations
18 ~ under the License.
19 -->
tom8bb16062014-09-12 14:47:46 -070020<html>
21<head>
tom4009d2c2014-09-12 15:53:42 -070022 <title>Topology Viewer</title>
23 <script src="libs/d3.v3.min.js"></script>
24 <script src="libs/jquery-1.11.1.min.js"></script>
25 <style>
26 .link {
27 }
28
29 .node {
30 stroke-width: 3px;
31 }
32
33 .textClass {
34 stroke: #323232;
35 font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif;
36 font-weight: normal;
37 stroke-width: .5;
38 font-size: 14px;
39 }
40 </style>
tom8bb16062014-09-12 14:47:46 -070041</head>
42<body>
tom4009d2c2014-09-12 15:53:42 -070043 <script>
44 // Adapted from sample code @ ericcoopey’s block #6c602d7cb14b25c179a4
45 // by Thomas Vachuska
46
47 var graph,
48 topo = {vertexes:{}, edges:{}},
49 paths = [],
50 currentPath = 0;
51
52 // Set up the canvas
53 var w = 2048,
54 h = 2048;
55
56 // Allocate colors predictably
57 var color = d3.scale.category10(),
58 deviceColor = color(2),
59 sourceColor = color(0),
60 targetColor = color(1),
61 dummy1Color = color(9),
62 dummy2Color = color(8),
63 dummy3Color = color(7),
64 dummy4Color = color(6),
65 offlineColor = color(5),
66 dummy5Color = color(4),
67 hostColor = color(3);
68
69 var selectedNode,
70 sourceNode,
71 targetNode,
72 pathRequested;
73
74
75 function fillColor(d) {
76 return ((targetNode && targetNode.id == d.id) ? targetColor :
77 ((sourceNode && sourceNode.id == d.id) ? sourceColor :
78 d.online ? color(d.group) : offlineColor));
79 }
80
81 function strokeColor(d) {
82 return selectedNode && d.id == selectedNode.id ? "#f00" : "#aaa";
83 }
84
85 function linkColor(d) {
86 if (!paths || paths.length == 0) {
87 return "#666";
88 }
89
90 var path = paths[currentPath];
91 if (path) {
92 for (var i = 0, n = path.length; i < n; i++) {
93 var link = path[i];
94 if ((link.src == d.source.id || link.dst == d.source.id) &&
95 (link.dst == d.target.id || link.src == d.target.id)) {
96 return "#f00";
97 }
98 }
99 }
100 return "#666";
101 }
102
103 function linkKey(link) {
104 return link.source.id + "-" + link.target.id;
105
106 };
107
108 function toggleNode(node) {
109 pathRequested = false;
110 return selectedNode && selectedNode != node ? selectedNode : null;
111
112 };
113
114 function refreshPaths() {
115 d3.selectAll("line").attr("stroke", linkColor);
116 }
117
118 function fetchPaths() {
119 if (!pathRequested && sourceNode && targetNode) {
120 pathRequested = true;
121 d3.json("rs/topology/paths/" + sourceNode.id + "/" + targetNode.id, function(error, data) {
122 currentPath = 0;
123 paths = data.paths;
124 refreshPaths();
125 });
126 }
127 }
128
129 function resetSelections() {
130 selectedNode = null;
131 sourceNode = null;
132 targetNode = null;
133 paths = [];
134 currentPath = 0;
135 refreshPaths();
136 d3.selectAll(".nodeStrokeClass").attr("stroke", strokeColor);
137 }
138
139 function nextPath() {
140 currentPath = paths && currentPath < paths.length - 1 ? currentPath + 1 : 0
141 console.log("Showing path: " + currentPath);
142 refreshPaths();
143 }
144
145 function dblclick(d) {
146 d3.select(this).classed("fixed", d.fixed = false);
147 }
148
149 function dragstart(d) {
tom3897c032014-09-16 00:05:15 -0700150 // d3.select(this).classed("fixed", d.fixed = true);
tom4009d2c2014-09-12 15:53:42 -0700151 }
152
153
154 function topoGraph() {
155 // Add and remove elements on the graph object
156 this.addNode = function (vertex, stamp) {
157 var node = topo.vertexes[vertex.name];
158 if (node) {
159 var oldState = node.online;
160 node.online = vertex.online;
161 node.stamp = stamp;
162 if (oldState != node.online) {
163 update();
164 return true;
165 }
166 return false;
167 }
tom4d0c6632014-09-15 23:27:01 -0700168 node = {"id": vertex.name, "label": vertex.label,
169 "group": vertex.group, "online": vertex.online, "stamp": stamp};
tom4009d2c2014-09-12 15:53:42 -0700170 nodes.push(node);
171 topo.vertexes[vertex.name] = node;
172 update();
173 return true;
174 };
175
176 this.addLink = function (edge, stamp) {
177 var key = edge.source + "-" + edge.target;
178 var link = topo.edges[key];
179 if (link) {
180 var oldValue = link.value;
181 link.value = edge.value;
182 link.stamp = stamp;
183 if (oldValue != link.value) {
184 update();
185 return true;
186 }
187 return false;
188 }
189 link = {"source": findNode(edge.source), "target": findNode(edge.target),
190 "value": edge.value, "stamp": stamp};
191 links.push(link);
192 topo.edges[key] = link;
193 update();
194 return true;
195 };
196
197 this.prune = function (stamp) {
198 var linksChanged = pruneArray(links, stamp, topo.edges, linkKey);
199 var nodesChanged = pruneArray(nodes, stamp, topo.vertexes,
200 function(node) { return node.id; });
201 if (linksChanged || nodesChanged) {
202 update();
203 return true;
204 }
205 return false;
206 };
207
208 var pruneArray = function(array, stamp, map, key) {
209 var changed = false;
210 for (var i = 0; i < array.length; i++) {
211 if (array[i].stamp < stamp) {
212 changed = true;
213 map[key(array[i])] = null;
214 array.splice(i, 1);
215 i--;
216 }
217 }
218 return changed;
219 };
220
221 var findNode = function (id) {
222 for (var i in nodes) {
223 if (nodes[i]["id"] === id) return nodes[i];
224 }
225 };
226
227 var force = d3.layout.force();
228
229 var drag = force.drag()
230 .on("dragstart", dragstart);
231
232 var nodes = force.nodes(),
233 links = force.links();
234
235 var vis = d3.select("body")
236 .append("svg:svg")
237 .attr("width", w)
238 .attr("height", h)
239 .attr("id", "svg")
240 .attr("pointer-events", "all")
241 .attr("viewBox", "0 0 " + w + " " + h)
242 .attr("perserveAspectRatio", "xMinYMid")
243 .append('svg:g');
244
245 d3.select("body")
246 .on("keydown", function(d) {
247 console.log(d3.event.keyCode);
248 if (d3.event.keyCode == 27) {
249 resetSelections();
250 } else if (d3.event.keyCode == 83) {
251 sourceNode = toggleNode(sourceNode);
252 } else if (d3.event.keyCode == 68) {
253 targetNode = toggleNode(targetNode);
254 } else if (d3.event.keyCode == 65) {
255 var aux = sourceNode;
256 sourceNode = targetNode;
257 targetNode = aux;
258 } else if (d3.event.keyCode == 70) {
259 nextPath();
tom3897c032014-09-16 00:05:15 -0700260 } else if (d3.event.keyCode == 67 && selectedNode) {
261 selectedNode.fixed = !selectedNode.fixed;
tom4009d2c2014-09-12 15:53:42 -0700262 }
263
264 d3.selectAll(".nodeStrokeClass").attr("fill", fillColor);
265 fetchPaths();
266 });
267
268
269 var update = function () {
270 var link = vis.selectAll("line")
271 .data(links, linkKey);
272
273 link.enter().append("line")
274 .attr("id", linkKey)
275 .attr("class", "link")
276 .attr("stroke-width", function (d) { return d.value; })
277 .attr("stroke", linkColor);
278 link.append("title").text(function (d) { return d.id; });
279 link.exit().remove();
280
281 var node = vis.selectAll("g.node")
282 .data(nodes, function (d) { return d.id; })
283 .on("click", function(d) {
284 selectedNode = d;
285 d3.selectAll(".nodeStrokeClass").attr("stroke", strokeColor);
286 });
287
288 var nodeEnter = node.enter().append("g")
289 .attr("class", "node")
290 .on("dblclick", dblclick)
291 .call(force.drag);
292
293 nodeEnter.append("svg:circle")
294 .attr("r", function(d) { return 28 / 2; })
295 .attr("id", function (d) { return "n-" + d.id.replace(/[.:]/g, ""); })
296 .attr("class", "nodeStrokeClass")
297 .attr("fill", fillColor)
298 .attr("stroke", strokeColor);
299
300 nodeEnter.append("image")
301 .attr("xlink:href", function(d) { return d.group == 2 ? "images/switch.png" : "images/server.png"; })
302 .attr("x", -12)
303 .attr("y", -12)
304 .attr("width", 24)
305 .attr("height", 24);
306
307 nodeEnter.append("svg:text")
308 .attr("class", "textClass")
309 .attr("x", 20)
310 .attr("y", ".31em")
tom4d0c6632014-09-15 23:27:01 -0700311 .text(function (d) { return d.label; });
tom4009d2c2014-09-12 15:53:42 -0700312
313 node.exit().remove();
314
315 d3.selectAll("nodeStrokeClass").attr("stroke", strokeColor);
316
317 force.on("tick", function () {
318 node.attr("transform", function (d) {
319 return "translate(" + d.x + "," + d.y + ")";
320 });
321
322 link.attr("x1", function (d) { return d.source.x; })
323 .attr("y1", function (d) { return d.source.y; })
324 .attr("x2", function (d) { return d.target.x; })
325 .attr("y2", function (d) { return d.target.y; });
326 });
327
328 // Restart the force layout.
329 force
330 .gravity(0.3)
331 .charge(-15000)
332 .friction(0.1)
333 .linkDistance(function(d) { return d.value * 30; })
334 .linkStrength(function(d) { return d.value * 0.6; })
335 .size([w, h])
336 .start();
337 };
338
339 // Make it all go
340 update();
341 }
342
343 function drawGraph() {
344 graph = new topoGraph("#svgdiv");
345 bringNodesToFront();
346 }
347
348 function bringNodesToFront() {
349 $(".nodeStrokeClass").each(function( index ) {
350 var gnode = this.parentNode;
351 gnode.parentNode.appendChild(gnode);
352 });
353 }
354
355 function addNodes() {
356 d3.select("svg")
357 .remove();
358 drawGraph();
359 }
360
361 function fetchData() {
362 var stamp = new Date().getTime();
363 d3.json("rs/topology/graph", function(error, data) {
364 var changed = false;
365 data.vertexes.forEach(function(vertex) { changed = graph.addNode(vertex, stamp) || changed; });
366 data.edges.forEach(function(edge) { changed = graph.addLink(edge, stamp) || changed; });
367
368 changed = graph.prune(stamp) || changed;
369 if (changed) {
370 bringNodesToFront();
371 // Update node and links styles
372 d3.selectAll(".nodeStrokeClass").attr("fill", fillColor);
373 d3.selectAll(".line").attr("stroke-width", function (d) { return d.value; })
374 }
375
376 setTimeout(fetchData, 1000);
377 });
378 };
379
380 drawGraph();
381 setTimeout(fetchData, 500);
382
383 </script>
tom8bb16062014-09-12 14:47:46 -0700384</body>
385</html>