mirror of
https://github.com/bloomberg/goldpinger.git
synced 2026-02-14 18:09:50 +00:00
Signed-off-by: kitfoman <thaddeusgetachew@gmail.com> make timeout flags backwards compatible Signed-off-by: kitfoman <thaddeusgetachew@gmail.com>
491 lines
21 KiB
HTML
491 lines
21 KiB
HTML
<!--
|
|
|
|
Copyright 2018 Bloomberg Finance L.P.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
-->
|
|
|
|
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
|
|
|
|
<title>Goldpinger</title>
|
|
|
|
<!-- Utilities -->
|
|
<script type="text/javascript" src="static/jquery/3.1.0/jquery.min.js"></script>
|
|
|
|
<!-- Sigma.js -->
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/sigma.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.exporters.svg.min.js"></script>:
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.layout.forceAtlas2.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.neo4j.cypher.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.parsers.cypher.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.parsers.gexf.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.parsers.json.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.pathfinding.astar.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.plugins.animate.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.plugins.dragNodes.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.plugins.filter.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.plugins.neighborhoods.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.plugins.relativeSize.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.renderers.customEdgeShapes.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.renderers.customShapes.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.renderers.edgeLabels.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.renderers.parallelEdges.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.renderers.snapshot.min.js"></script>
|
|
<script type="text/javascript" src="static/sigma.js/1.1.0/plugins/sigma.statistics.HITS.min.js"></script>
|
|
|
|
<!-- Bootstrap -->
|
|
<link rel="stylesheet" href="static/bootstrap/3.3.7/css/bootstrap.min.css" crossorigin="anonymous">
|
|
<link rel="stylesheet" href="static/bootstrap/3.3.7/css/bootstrap-theme.min.css" crossorigin="anonymous">
|
|
<script src="static/bootstrap/3.3.7/js/bootstrap.min.js" crossorigin="anonymous"></script>
|
|
|
|
<style type="text/css">
|
|
body {
|
|
margin: 0;
|
|
}
|
|
#container {
|
|
}
|
|
#graph-container {
|
|
position: absolute;
|
|
top: 0px;
|
|
bottom: 0px;
|
|
left: 0px;
|
|
right: 0px;
|
|
/*background-color: white;*/
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<nav class="navbar navbar-inverse navbar-fixed-top">
|
|
<div class="container">
|
|
<div class="navbar-header">
|
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
|
<span class="sr-only">Toggle navigation</span>
|
|
<span class="icon-bar"></span>
|
|
</button>
|
|
<a class="navbar-brand" href="#">Goldpinger</a>
|
|
</div>
|
|
<div id="navbar" class="collapse navbar-collapse">
|
|
<ul class="nav navbar-nav">
|
|
<li class="active"><a href="#">Graph</a></li>
|
|
<li><a href="#" id="show-data">Data</a></li>
|
|
<li><a href="#" id="show-heatmap">Heatmap</a></li>
|
|
<li><a href="check_all" >Raw</a></li>
|
|
<li><a href="metrics" >Metrics</a></li>
|
|
</ul>
|
|
<div class="navbar-form navbar-left" role="search">
|
|
<div class="form-group">
|
|
<!-- <input id="timeout" type="number" step="any" class="form-control" placeholder="5.0" value="5.0"> -->
|
|
</div>
|
|
<button id="reload-graph" class="btn btn-default">reload</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container">
|
|
|
|
<div id="graph-container"></div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="modal-window-code" class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
<h4 class="modal-title" id="modal-title">title</h4>
|
|
</div>
|
|
<div class="modal-body" id="modal-body">
|
|
body
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="modal-window-heatmap" class="modal fade bs-example-modal-lg" tabindex="-2" role="dialog" aria-labelledby="myLargeModalLabel">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
|
<h4 class="modal-title" id="modal-title">Heatmap</h4>
|
|
</div>
|
|
<div class="modal-body">
|
|
|
|
<div id="heatmap-body" style="padding: 10px"></div>
|
|
|
|
<div class="input-group">
|
|
<span class="input-group-addon" style="width: 200px">Good threshold</span>
|
|
<input id="t0" type="number" step="1" class="form-control" placeholder="2" value="2">
|
|
<span class="input-group-addon">milliseconds</span>
|
|
</div>
|
|
<div class="input-group">
|
|
<span class="input-group-addon" style="width: 200px">Warning threshold</span>
|
|
<input id="t1" type="number" step="1" class="form-control" placeholder="5" value="5">
|
|
<span class="input-group-addon">milliseconds</span>
|
|
</div>
|
|
<div class="input-group">
|
|
<span class="input-group-addon" style="width: 200px">Problem threshold</span>
|
|
<input id="t2" type="number" step="1" class="form-control" placeholder="100" value="100">
|
|
<span class="input-group-addon">milliseconds</span>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-success" id="update-heatmap" type="button">refresh</button>
|
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<script>
|
|
|
|
var getHeatmapUrl = function(){
|
|
return "heatmap.png?"
|
|
+ "t0=" + Number($("#t0").val())
|
|
+ "&t1=" + Number($("#t1").val())
|
|
+ "&t2=" + Number($("#t2").val())
|
|
+ "&now=" + Date.now();
|
|
}
|
|
|
|
var fetchJSON = function(url) {
|
|
return new Promise(function(resolve, reject) {
|
|
console.log("calling " + url);
|
|
var req = new XMLHttpRequest();
|
|
req.open('get', url, true);
|
|
req.responseType = 'json';
|
|
req.onload = function() {
|
|
if (req.status == 200) {
|
|
resolve(req.response);
|
|
} else {
|
|
reject({
|
|
status: req.status,
|
|
response: req.response,
|
|
statusText: req.statusText
|
|
});
|
|
}
|
|
};
|
|
req.send();
|
|
});
|
|
};
|
|
|
|
var prepareJSON = function(subject){
|
|
return "<code style='white-space: pre;'>"
|
|
+ JSON.stringify(subject, undefined, 4)
|
|
+ "</code>"
|
|
}
|
|
|
|
var loadingDialog = function(enable){
|
|
$('#modal-title').html("Loading");
|
|
$('#modal-body').html(prepareJSON({stuff:{will_be_there:true, soon: "Math.random() > 0.5"}}));
|
|
|
|
// show the things
|
|
if (enable){
|
|
$('#modal-window-code').modal('show');
|
|
} else {
|
|
$('#modal-window-code').modal('hide');
|
|
}
|
|
};
|
|
|
|
var global_data = undefined;
|
|
var s; // the sigma graph
|
|
|
|
|
|
var main = function(timeout){
|
|
|
|
timeout = timeout || Number($("#timeout").val()) || 5.0;
|
|
|
|
loadingDialog(true);
|
|
|
|
fetchJSON("check_all?timeout="+timeout)
|
|
.then(function(data){
|
|
|
|
loadingDialog(false);
|
|
|
|
//console.log(data);
|
|
global_data = data;
|
|
|
|
// prepare nodes
|
|
var nodes = [];
|
|
var podIPs = [];
|
|
var resp = data.responses;
|
|
for (let podIP in resp) {
|
|
let value = resp[podIP];
|
|
nodes.push({
|
|
"label": podIP + " (" + value.HostIP + ")",
|
|
"id": podIP,
|
|
"x": 0,
|
|
"y": 0,
|
|
_data: value,
|
|
});
|
|
podIPs.push(podIP);
|
|
};
|
|
|
|
// prepare the edges
|
|
var edges = [];
|
|
var resp = data.responses;
|
|
for (let callId in resp) {
|
|
// If there was an error, the response can be undefined,
|
|
// especially if there was a timeout/context exceeded
|
|
if (resp[callId].response !== undefined) {
|
|
var call = resp[callId].response['podResults'];
|
|
if (typeof call !== 'string'){
|
|
for (let target in call) {
|
|
edges.push({
|
|
source: callId,
|
|
target: target,
|
|
_data: call[target]
|
|
});
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
console.log(edges);
|
|
console.log(nodes);
|
|
|
|
// if running goldpinger with PING_NUMBER, then we need to add nodes for the edges
|
|
// that are not reporting but are reported to
|
|
edges.forEach(function(edge) {
|
|
[edge.source, edge.target].forEach(function(value) {
|
|
if (podIPs.indexOf(value) == -1){
|
|
nodes.push({
|
|
"label": value,
|
|
"id": value,
|
|
"x": 0,
|
|
"y": 0,
|
|
_data: {},
|
|
});
|
|
podIPs.push(value);
|
|
}
|
|
})
|
|
})
|
|
console.log(nodes);
|
|
|
|
var probeResultsNodes = [];
|
|
if ('probeResults' in data) {
|
|
var yoffset = 0;
|
|
for (let checkedHost in data.probeResults) {
|
|
let value = data.probeResults[checkedHost];
|
|
var allOk = true;
|
|
for (let pod in value) {
|
|
for (var i = 0; i < value[pod].length; i++){
|
|
var elapsed = 0;
|
|
var podData = value[pod][i];
|
|
if ('response-time-ms' in podData) {
|
|
elapsed = podData['response-time-ms'];
|
|
}
|
|
var podOk = (!('error' in podData));
|
|
allOk = allOk && podOk
|
|
edges.push({
|
|
source: pod,
|
|
target: checkedHost,
|
|
_data: {
|
|
"OK": podOk,
|
|
"elapsed": elapsed,
|
|
"isprobeResultsNode": true,
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
value["OK"] = allOk
|
|
probeResultsNodes.push({
|
|
"label": checkedHost,
|
|
"id": checkedHost,
|
|
"x": 0,
|
|
"y": 0,
|
|
_data: value,
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
// build the actual graph
|
|
s = new sigma({
|
|
graph: {
|
|
nodes: [],
|
|
edges: []
|
|
},
|
|
renderer: {
|
|
container: document.getElementById('graph-container'),
|
|
type: 'canvas'
|
|
},
|
|
settings: {
|
|
edgeLabelSize: 'proportional',
|
|
minArrowSize: 7,
|
|
minNodeSize: 10,
|
|
sideMargin: 0.45
|
|
}
|
|
});
|
|
|
|
// generate the nodes on the graph
|
|
nodes.forEach(function(node, i, a){
|
|
node.x = Math.cos(Math.PI * 2 * i / a.length);
|
|
node.y = Math.sin(Math.PI * 2 * i / a.length);
|
|
node.size = 10;
|
|
node.color = "#4CC40B";
|
|
if (!node._data.OK) {
|
|
node.color = "red";
|
|
}
|
|
//console.log(node);
|
|
s.graph.addNode(node);
|
|
});
|
|
|
|
// generate any dns nodes on the graph
|
|
probeResultsNodes.forEach(function(node, i, a){
|
|
node.x = 2;
|
|
node.y = (0.6 * i / a.length) - 0.8;
|
|
node.size = 10;
|
|
node.color = "#4CC40B";
|
|
if (!node._data.OK) {
|
|
node.color = "red";
|
|
}
|
|
s.graph.addNode(node);
|
|
});
|
|
|
|
// generate the edges
|
|
edges.forEach(function(edge, i){
|
|
var color = "#ccc";
|
|
var type = "curvedArrow";
|
|
if (edge.source == edge.target){
|
|
color = "gray";
|
|
type = "curve";
|
|
}
|
|
if (!edge._data.OK) {
|
|
color = "red";
|
|
}
|
|
if ("isprobeResultsNode" in edge._data) {
|
|
type = "dashed";
|
|
}
|
|
var edge = {
|
|
id: "e" + i,
|
|
source: edge.source,
|
|
target: edge.target,
|
|
type: type,
|
|
color: color,
|
|
label: edge._data.elapsed,
|
|
size: 7
|
|
}
|
|
//console.log(edge);
|
|
s.graph.addEdge(edge)
|
|
});
|
|
|
|
console.log(s.graph.nodes());
|
|
console.log(s.graph.edges());
|
|
|
|
s.refresh();
|
|
|
|
var hideNotTouching = function(dst){
|
|
s.graph.edges().forEach(function(edge){
|
|
if (dst == undefined) {
|
|
// revert everything to the original state
|
|
if (edge._color){
|
|
edge.color = edge._color;
|
|
delete edge._color;
|
|
}
|
|
} else if (edge.source != dst){
|
|
// hide
|
|
if (!edge._color){
|
|
edge._color = edge.color;
|
|
}
|
|
edge.color = "#faf8f6";
|
|
} else {
|
|
// show
|
|
if (edge._color) {
|
|
edge.color = edge._color;
|
|
}
|
|
}
|
|
});
|
|
s.refresh();
|
|
}
|
|
|
|
s.bind("clickNode", function (e) {
|
|
var node = e.data.node;
|
|
hideNotTouching(node.id);
|
|
|
|
// fill the voids
|
|
$('#modal-title').html(node.id);
|
|
$('#modal-body').html(prepareJSON(node._data));
|
|
|
|
// show the things
|
|
$('#modal-window-code').modal('show');
|
|
});
|
|
s.bind("clickStage", function (e) {
|
|
hideNotTouching();
|
|
});
|
|
|
|
s.bind("clickEdge", function (e) {
|
|
var edge = e.data.edge;
|
|
|
|
console.log(edge);
|
|
|
|
// fill the voids
|
|
$('#modal-title').html(edge.id);
|
|
$('#modal-body').html(prepareJSON(edge._data));
|
|
|
|
// show the things
|
|
$('#modal-window-code').modal('show');
|
|
});
|
|
})
|
|
.catch(function(err){
|
|
console.error(err);
|
|
$('#modal-title').html("Error");
|
|
$('#modal-body').html(prepareJSON(err));
|
|
$('#modal-window-code').modal('show');
|
|
});
|
|
};
|
|
|
|
main();
|
|
|
|
$("#show-data").click(function (e) {
|
|
// fill the voids
|
|
$('#modal-title').html("check_all");
|
|
$('#modal-body').html(prepareJSON(global_data));
|
|
|
|
// show the things
|
|
$('#modal-window-code').modal('show');
|
|
});
|
|
$("#reload-graph").click(function (e) {
|
|
s.kill();
|
|
main();
|
|
});
|
|
$("#show-heatmap").click(function (e) {
|
|
updateHeatmap();
|
|
$('#modal-window-heatmap').modal('show');
|
|
});
|
|
var updateHeatmap = function(){
|
|
$('#heatmap-body').html(
|
|
'<img src="' + getHeatmapUrl() + '" />'
|
|
);
|
|
}
|
|
$("#update-heatmap").click(function (e) {
|
|
updateHeatmap();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|