mirror of
https://github.com/status-im/consul.git
synced 2025-01-11 06:16:08 +00:00
Implementation of a per-node tomography graph
Adds a new section to the node information, Network Tomography. There's a radar plot of the distances (in ms) between the current node and its peers as well as min, avg, and max.
This commit is contained in:
parent
359541a67d
commit
ba6d402e85
@ -594,6 +594,14 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
<p class="light small">No sessions</p>
|
<p class="light small">No sessions</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
<h5>Network Tomography</h5>
|
||||||
|
|
||||||
|
{{ tomographyGraph tomography 336 }}
|
||||||
|
|
||||||
|
<p class="light small">Minimum: {{ tomography.min }}ms</p>
|
||||||
|
<p class="light small">Average: {{ tomography.avg }}ms</p>
|
||||||
|
<p class="light small">Maximum: {{ tomography.max }}ms</p>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/x-handlebars" id="acls">
|
<script type="text/x-handlebars" id="acls">
|
||||||
|
@ -91,3 +91,59 @@ function notify(message, ttl) {
|
|||||||
window.notifications = [];
|
window.notifications = [];
|
||||||
window.notifications.push(notification);
|
window.notifications.push(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tomography
|
||||||
|
|
||||||
|
Ember.Handlebars.helper('tomographyGraph', function(tomography, size) {
|
||||||
|
|
||||||
|
// This is ugly, but I'm working around bugs with Handlebars and templating
|
||||||
|
// parts of svgs. Basically things render correctly the first time, but when
|
||||||
|
// stuff is updated for subsequent go arounds the templated parts don't show.
|
||||||
|
// It appears (based on google searches) that the replaced elements aren't
|
||||||
|
// being interpreted as http://www.w3.org/2000/svg. Anyway, this works and
|
||||||
|
// if/when Handlebars fixes the underlying issues all of this can be cleaned
|
||||||
|
// up drastically.
|
||||||
|
|
||||||
|
var n = tomography.n;
|
||||||
|
var max = Math.max.apply(null, tomography.distances);
|
||||||
|
var insetSize = size / 2 - 8;
|
||||||
|
var buf = '' +
|
||||||
|
' <svg width="' + size + '" height="' + size + '">' +
|
||||||
|
' <g class="tomography" transform="translate(' + (size / 2) + ', ' + (size / 2) + ')">' +
|
||||||
|
' <g>' +
|
||||||
|
' <circle class="background" r="' + insetSize + '"/>' +
|
||||||
|
' <circle class="axis" r="' + (insetSize * 0.25) + '"/>' +
|
||||||
|
' <circle class="axis" r="' + (insetSize * 0.5) + '"/>' +
|
||||||
|
' <circle class="axis" r="' + (insetSize * 0.75) + '"/>' +
|
||||||
|
' <circle class="border" r="' + insetSize + '"/>' +
|
||||||
|
' </g>' +
|
||||||
|
' <g class="lines">';
|
||||||
|
tomography.distances.forEach(function (distance, i) {
|
||||||
|
buf += ' <line transform="rotate(' + (i * 360 / n) + ')" y2="' + (-insetSize * (distance / max)) + '"></line>';
|
||||||
|
});
|
||||||
|
buf += '' +
|
||||||
|
' </g>' +
|
||||||
|
' <g class="labels">' +
|
||||||
|
' <circle class="point" r="5"/>' +
|
||||||
|
' <g class="tick" transform="translate(0, ' + (insetSize * -0.25 ) + ')">' +
|
||||||
|
' <line x2="70"/>' +
|
||||||
|
' <text x="75" y="0" dy=".32em">' + (parseInt(max * 25) / 100) + 'ms</text>' +
|
||||||
|
' </g>' +
|
||||||
|
' <g class="tick" transform="translate(0, ' + (insetSize * -0.5 ) + ')">' +
|
||||||
|
' <line x2="70"/>' +
|
||||||
|
' <text x="75" y="0" dy=".32em">' + (parseInt(max * 50) / 100) + 'ms</text>' +
|
||||||
|
' </g>' +
|
||||||
|
' <g class="tick" transform="translate(0, ' + (insetSize * -0.75 ) + ')">' +
|
||||||
|
' <line x2="70"/>' +
|
||||||
|
' <text x="75" y="0" dy=".32em">' + (parseInt(max * 75) / 100) + 'ms</text>' +
|
||||||
|
' </g>' +
|
||||||
|
' <g class="tick" transform="translate(0, ' + (insetSize * -1) + ')">' +
|
||||||
|
' <line x2="70"/>' +
|
||||||
|
' <text x="75" y="0" dy=".32em">' + (parseInt(max * 100) / 100) + 'ms</text>' +
|
||||||
|
' </g>' +
|
||||||
|
' </g>' +
|
||||||
|
' </g>' +
|
||||||
|
' </svg>';
|
||||||
|
|
||||||
|
return new Handlebars.SafeString(buf);
|
||||||
|
});
|
||||||
|
@ -104,6 +104,9 @@ App.DcRoute = App.BaseRoute.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return objs;
|
return objs;
|
||||||
|
}),
|
||||||
|
coordinates: Ember.$.getJSON(formatUrl(consulHost + '/v1/coordinate/nodes', params.dc, token)).then(function(data) {
|
||||||
|
return data;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -112,6 +115,7 @@ App.DcRoute = App.BaseRoute.extend({
|
|||||||
controller.set('content', models.dc);
|
controller.set('content', models.dc);
|
||||||
controller.set('nodes', models.nodes);
|
controller.set('nodes', models.nodes);
|
||||||
controller.set('dcs', models.dcs);
|
controller.set('dcs', models.dcs);
|
||||||
|
controller.set('coordinates', models.coordinates);
|
||||||
controller.set('isDropdownVisible', false);
|
controller.set('isDropdownVisible', false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -257,19 +261,61 @@ App.ServicesShowRoute = App.BaseRoute.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function distance(a, b) {
|
||||||
|
a = a.Coord;
|
||||||
|
b = b.Coord;
|
||||||
|
var sum = 0;
|
||||||
|
for (var i = 0; i < a.Vec.length; i++) {
|
||||||
|
var diff = a.Vec[i] - b.Vec[i];
|
||||||
|
sum += diff * diff;
|
||||||
|
}
|
||||||
|
var rtt = Math.sqrt(sum) + a.Height + b.Height;
|
||||||
|
|
||||||
|
var adjusted = rtt + a.Adjustment + b.Adjustment;
|
||||||
|
if (adjusted > 0.0) {
|
||||||
|
rtt = adjusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round(rtt * 100000.0) / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
App.NodesShowRoute = App.BaseRoute.extend({
|
App.NodesShowRoute = App.BaseRoute.extend({
|
||||||
model: function(params) {
|
model: function(params) {
|
||||||
var dc = this.modelFor('dc').dc;
|
var dc = this.modelFor('dc');
|
||||||
var token = App.get('settings.token');
|
var token = App.get('settings.token');
|
||||||
|
|
||||||
|
var sum = 0;
|
||||||
|
var distances = [];
|
||||||
|
dc.coordinates.forEach(function (node) {
|
||||||
|
if (params.name == node.Node) {
|
||||||
|
dc.coordinates.forEach(function (other) {
|
||||||
|
// TODO: ignore self
|
||||||
|
var dist = distance(node, other);
|
||||||
|
distances.push(dist);
|
||||||
|
sum += dist;
|
||||||
|
});
|
||||||
|
distances.sort();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var min = Math.min.apply(null, distances);
|
||||||
|
var avg = sum / distances.length;
|
||||||
|
var max = Math.max.apply(null, distances);
|
||||||
|
|
||||||
// Return a promise hash of the node and nodes
|
// Return a promise hash of the node and nodes
|
||||||
return Ember.RSVP.hash({
|
return Ember.RSVP.hash({
|
||||||
dc: dc,
|
dc: dc.dc,
|
||||||
token: token,
|
token: token,
|
||||||
node: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/node/' + params.name, dc, token)).then(function(data) {
|
tomography: {
|
||||||
|
distances: distances,
|
||||||
|
n: distances.length,
|
||||||
|
min: parseInt(min * 100) / 100,
|
||||||
|
avg: parseInt(avg * 100) / 100,
|
||||||
|
max: parseInt(max * 100) / 100
|
||||||
|
},
|
||||||
|
node: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/node/' + params.name, dc.dc, token)).then(function(data) {
|
||||||
return App.Node.create(data);
|
return App.Node.create(data);
|
||||||
}),
|
}),
|
||||||
nodes: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/node/' + params.name, dc, token)).then(function(data) {
|
nodes: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/node/' + params.name, dc.dc, token)).then(function(data) {
|
||||||
return App.Node.create(data);
|
return App.Node.create(data);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -286,6 +332,7 @@ App.NodesShowRoute = App.BaseRoute.extend({
|
|||||||
setupController: function(controller, models) {
|
setupController: function(controller, models) {
|
||||||
controller.set('content', models.node);
|
controller.set('content', models.node);
|
||||||
controller.set('sessions', models.sessions);
|
controller.set('sessions', models.sessions);
|
||||||
|
controller.set('tomography', models.tomography);
|
||||||
//
|
//
|
||||||
// Since we have 2 column layout, we need to also display the
|
// Since we have 2 column layout, we need to also display the
|
||||||
// list of nodes on the left. Hence setting the attribute
|
// list of nodes on the left. Hence setting the attribute
|
||||||
|
File diff suppressed because one or more lines are too long
@ -148,3 +148,30 @@ a {
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
margin-top: -3px;
|
margin-top: -3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tomography .background {
|
||||||
|
fill: $gray-background;
|
||||||
|
}
|
||||||
|
.tomography .axis {
|
||||||
|
fill: none;
|
||||||
|
stroke: $gray-light;
|
||||||
|
stroke-dasharray: 4 4;
|
||||||
|
}
|
||||||
|
.tomography .border {
|
||||||
|
fill: none;
|
||||||
|
stroke: $gray-darker;
|
||||||
|
}
|
||||||
|
.tomography .point {
|
||||||
|
stroke: $gray-darker;
|
||||||
|
fill: $green-faded;
|
||||||
|
}
|
||||||
|
.tomography .lines line {
|
||||||
|
stroke: $red;
|
||||||
|
}
|
||||||
|
.tomography .tick line {
|
||||||
|
stroke: $gray-light;
|
||||||
|
}
|
||||||
|
.tomography .tick text {
|
||||||
|
font-size: 14px;
|
||||||
|
text-anchor: start;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user