Basic graph implementation
This commit is contained in:
parent
148c9cd0db
commit
99e0e497c2
18
global.css
18
global.css
@ -77,6 +77,24 @@ header {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.lmc-graph canvas {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.lmc-graph h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lmc-graph table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.lmc-graph-color {
|
||||
height: 15px;
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.lmc-table {
|
||||
background-color: #CECECE;
|
||||
width: 100%;
|
||||
|
59
global.js
59
global.js
@ -452,6 +452,63 @@ function displayUser(response)
|
||||
profilePic.style = "display: initial;";
|
||||
}
|
||||
|
||||
// Draw timeseries data with the given canvas in the given color and fill
|
||||
// series is an array of objects, with each object containing the color/fill settings and an array of data points
|
||||
function drawSeries(series, canvas)
|
||||
{
|
||||
// Compute scale and totals
|
||||
var xMin = series[0].points[0][0];
|
||||
var xMax = series[0].points[series[0].points.length - 1][0];
|
||||
var yMax = 0;
|
||||
for (var i = 0; i < series.length; i++) {
|
||||
xMin = Math.min(xMin, series[i].points[0][0]);
|
||||
xMax = Math.max(xMax, series[i].points[series[i].points.length - 1][0]);
|
||||
series[i].max = 0, series[i].avg = 0;
|
||||
for (var j = 0; j < series[i].points.length; j++) {
|
||||
series[i].max = Math.max(series[i].max, series[i].points[j][1]);
|
||||
series[i].avg += series[i].points[j][1];
|
||||
}
|
||||
series[i].avg /= series[i].points.length;
|
||||
yMax = Math.max(yMax, series[i].max);
|
||||
}
|
||||
xMax -= xMin;
|
||||
|
||||
// Setup drawing context
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
// Clear the canvas
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
for (var i = 0; i < series.length; i++) {
|
||||
ctx.fillStyle = series[i].color;
|
||||
ctx.strokeStyle = series[i].color;
|
||||
|
||||
// Draw data
|
||||
ctx.beginPath();
|
||||
ctx.moveTo((series[i].points[0][0] - xMin) / xMax * canvas.width, canvas.height - (series[i].points[0][1] / yMax * canvas.height));
|
||||
for (var j = 1; j < series[i].points.length; j++)
|
||||
ctx.lineTo((series[i].points[j][0] - xMin) / xMax * canvas.width, canvas.height - (series[i].points[j][1] / yMax * canvas.height));
|
||||
if (series[i].fill) {
|
||||
ctx.lineTo((series[i].points[series[i].points.length-1][0] - xMin) / xMax * canvas.width, canvas.height);
|
||||
ctx.lineTo((series[i].points[0][0] - xMin) / xMax * canvas.width, canvas.height);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
} else {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw axis lines
|
||||
ctx.strokeStyle = "black";
|
||||
ctx.lineWidth = 2.5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(0, canvas.height);
|
||||
ctx.lineTo(canvas.width, canvas.height);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Return an MD5 hash of the given string
|
||||
function md5(str, binary)
|
||||
{
|
||||
@ -793,4 +850,4 @@ function translateKernel(slug, element)
|
||||
apiGet("/linode/kernels/" + slug, callback, null);
|
||||
}
|
||||
|
||||
export { settings, elements, regionNames, apiDelete, apiGet, apiPost, apiPut, md5, migrateETA, oauthPost, oauthScopes, objPut, parseParams, setupHeader, eventTitles, timeString, translateKernel };
|
||||
export { settings, elements, regionNames, apiDelete, apiGet, apiPost, apiPut, drawSeries, md5, migrateETA, oauthPost, oauthScopes, objPut, parseParams, setupHeader, eventTitles, timeString, translateKernel };
|
||||
|
@ -6,7 +6,6 @@
|
||||
<a class="subnav-link" href="/linodes/rescue?lid=0">Rescue</a>
|
||||
<a class="subnav-link" href="/linodes/resize?lid=0">Resize</a>
|
||||
<a class="subnav-link" href="/linodes/clone?lid=0">Clone</a>
|
||||
<a class="subnav-link" href="/linodes/graphs?lid=0">Graphs</a>
|
||||
<a class="subnav-link" href="/linodes/backups?lid=0">Backups</a>
|
||||
<a class="subnav-link" href="/linodes/settings?lid=0">Settings</a>
|
||||
</nav>
|
||||
|
@ -25,10 +25,18 @@
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
canvas {
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
#config-table tr:last-of-type {
|
||||
border-bottom: 1px solid #E8E8E8;
|
||||
}
|
||||
|
||||
#cpu-color {
|
||||
background-color: #03C;
|
||||
}
|
||||
|
||||
.disk-icon {
|
||||
height: 24px;
|
||||
width: 26px;
|
||||
@ -42,12 +50,51 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#graph-range {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
border-bottom: 1px solid #E8E8E8;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: lighter;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
#io-rate-color {
|
||||
background-color: #FFD04B;
|
||||
}
|
||||
|
||||
#ipv4-in-color {
|
||||
background-color: #03C;
|
||||
}
|
||||
|
||||
#ipv4-out-color {
|
||||
background-color: #32CD32;
|
||||
}
|
||||
|
||||
#ipv4-privin-color {
|
||||
background-color: #C09;
|
||||
}
|
||||
|
||||
#ipv4-privout-color {
|
||||
background-color: #FF9;
|
||||
}
|
||||
|
||||
#ipv6-in-color {
|
||||
background-color: #03C;
|
||||
}
|
||||
|
||||
#ipv6-out-color {
|
||||
background-color: #32CD32;
|
||||
}
|
||||
|
||||
#ipv6-privin-color {
|
||||
background-color: #C09;
|
||||
}
|
||||
|
||||
#ipv6-privout-color {
|
||||
background-color: #FF9;
|
||||
}
|
||||
|
||||
.job-failed {
|
||||
@ -202,6 +249,10 @@ h3 {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#swap-rate-color {
|
||||
background-color: #FA373E;
|
||||
}
|
||||
|
||||
#upgrade {
|
||||
background-color: #F0F0F0;
|
||||
display: none;
|
||||
|
@ -15,7 +15,7 @@
|
||||
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParams, setupHeader, timeString, translateKernel } from "/global.js";
|
||||
import { settings, elements, apiDelete, apiGet, apiPost, drawSeries, eventTitles, parseParams, setupHeader, timeString, translateKernel } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
@ -26,6 +26,10 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
elements.configRadioName = "config-radio";
|
||||
elements.configRemovePrefix = "config-remove-";
|
||||
elements.configTable = "config-table";
|
||||
elements.cpuAvg = "cpu-avg";
|
||||
elements.cpuGraph = "cpu-graph";
|
||||
elements.cpuLast = "cpu-last";
|
||||
elements.cpuMax = "cpu-max";
|
||||
elements.diskIcon = "disk-icon";
|
||||
elements.diskIconImg = "/img/disk.gif";
|
||||
elements.diskRemovePrefix = "disk-remove-";
|
||||
@ -34,7 +38,44 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
elements.eventRowPrefix = "event-row-";
|
||||
elements.eventTable = "event-table";
|
||||
elements.extraEvent = "extra-event";
|
||||
elements.graphRange = "graph-range";
|
||||
elements.info = "info";
|
||||
elements.ioGraph = "io-graph";
|
||||
elements.ioRateAvg = "io-rate-avg";
|
||||
elements.ioRateLast = "io-rate-last";
|
||||
elements.ioRateMax = "io-rate-max";
|
||||
elements.ipv4Graph = "ipv4-graph";
|
||||
elements.ipv4InAvg = "ipv4-in-avg";
|
||||
elements.ipv4InLast = "ipv4-in-last";
|
||||
elements.ipv4InMax = "ipv4-in-max";
|
||||
elements.ipv4OutAvg = "ipv4-out-avg";
|
||||
elements.ipv4OutLast = "ipv4-out-last";
|
||||
elements.ipv4OutMax = "ipv4-out-max";
|
||||
elements.ipv4PrivInAvg = "ipv4-privin-avg";
|
||||
elements.ipv4PrivInLast = "ipv4-privin-last";
|
||||
elements.ipv4PrivInMax = "ipv4-privin-max";
|
||||
elements.ipv4PrivOutAvg = "ipv4-privout-avg";
|
||||
elements.ipv4PrivOutLast = "ipv4-privout-last";
|
||||
elements.ipv4PrivOutMax = "ipv4-privout-max";
|
||||
elements.ipv4Total = "ipv4-total";
|
||||
elements.ipv4TotalIn = "ipv4-total-in";
|
||||
elements.ipv4TotalOut = "ipv4-total-out";
|
||||
elements.ipv6Graph = "ipv6-graph";
|
||||
elements.ipv6InAvg = "ipv6-in-avg";
|
||||
elements.ipv6InLast = "ipv6-in-last";
|
||||
elements.ipv6InMax = "ipv6-in-max";
|
||||
elements.ipv6OutAvg = "ipv6-out-avg";
|
||||
elements.ipv6OutLast = "ipv6-out-last";
|
||||
elements.ipv6OutMax = "ipv6-out-max";
|
||||
elements.ipv6PrivInAvg = "ipv6-privin-avg";
|
||||
elements.ipv6PrivInLast = "ipv6-privin-last";
|
||||
elements.ipv6PrivInMax = "ipv6-privin-max";
|
||||
elements.ipv6PrivOutAvg = "ipv6-privout-avg";
|
||||
elements.ipv6PrivOutLast = "ipv6-privout-last";
|
||||
elements.ipv6PrivOutMax = "ipv6-privout-max";
|
||||
elements.ipv6Total = "ipv6-total";
|
||||
elements.ipv6TotalIn = "ipv6-total-in";
|
||||
elements.ipv6TotalOut = "ipv6-total-out";
|
||||
elements.jobFailed = "job-failed";
|
||||
elements.jobInfo = "job-info";
|
||||
elements.jobNotice = "job-notice";
|
||||
@ -64,6 +105,9 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
elements.storageFree = "storage-free";
|
||||
elements.storageTotal = "storage-total";
|
||||
elements.storageUsed = "storage-used";
|
||||
elements.swapRateAvg = "swap-rate-avg";
|
||||
elements.swapRateLast = "swap-rate-last";
|
||||
elements.swapRateMax = "swap-rate-max";
|
||||
elements.transferMonthly = "transfer-monthly";
|
||||
elements.transferOverage = "transfer-overage";
|
||||
elements.transferUsed = "transfer-used";
|
||||
@ -80,6 +124,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
data.linodeTransfer = {};
|
||||
data.notifications = [];
|
||||
data.plan = {};
|
||||
data.stats = {};
|
||||
data.volumes = [];
|
||||
|
||||
// Static references to UI elements
|
||||
@ -87,9 +132,50 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
ui.backups = {};
|
||||
ui.boot = {};
|
||||
ui.configTable = {};
|
||||
ui.cpuAvg = {};
|
||||
ui.cpuGraph = {};
|
||||
ui.cpuLast = {};
|
||||
ui.cpuMax = {};
|
||||
ui.diskTable = {};
|
||||
ui.diskUsage = {};
|
||||
ui.eventTable = {};
|
||||
ui.graphRange = {};
|
||||
ui.ioGraph = {};
|
||||
ui.ioRateAvg = {};
|
||||
ui.ioRateLast = {};
|
||||
ui.ioRateMax = {};
|
||||
ui.ipv4Graph = {};
|
||||
ui.ipv4InAvg = {};
|
||||
ui.ipv4InLast = {};
|
||||
ui.ipv4InMax = {};
|
||||
ui.ipv4OutAvg = {};
|
||||
ui.ipv4OutLast = {};
|
||||
ui.ipv4OutMax = {};
|
||||
ui.ipv4PrivInAvg = {};
|
||||
ui.ipv4PrivInLast = {};
|
||||
ui.ipv4PrivInMax = {};
|
||||
ui.ipv4PrivOutAvg = {};
|
||||
ui.ipv4PrivOutLast = {};
|
||||
ui.ipv4PrivOutMax = {};
|
||||
ui.ipv4Total = {};
|
||||
ui.ipv4TotalIn = {};
|
||||
ui.ipv4TotalOut = {};
|
||||
ui.ipv6Graph = {};
|
||||
ui.ipv6InAvg = {};
|
||||
ui.ipv6InLast = {};
|
||||
ui.ipv6InMax = {};
|
||||
ui.ipv6OutAvg = {};
|
||||
ui.ipv6OutLast = {};
|
||||
ui.ipv6OutMax = {};
|
||||
ui.ipv6PrivInAvg = {};
|
||||
ui.ipv6PrivInLast = {};
|
||||
ui.ipv6PrivInMax = {};
|
||||
ui.ipv6PrivOutAvg = {};
|
||||
ui.ipv6PrivOutLast = {};
|
||||
ui.ipv6PrivOutMax = {};
|
||||
ui.ipv6Total = {};
|
||||
ui.ipv6TotalIn = {};
|
||||
ui.ipv6TotalOut = {};
|
||||
ui.jobProgress = {};
|
||||
ui.jobProgressRow = {};
|
||||
ui.lastBackup = {};
|
||||
@ -110,6 +196,9 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
ui.storageFree = {};
|
||||
ui.storageTotal = {};
|
||||
ui.storageUsed = {};
|
||||
ui.swapRateAvg = {};
|
||||
ui.swapRateLast = {};
|
||||
ui.swapRateMax = {};
|
||||
ui.transferMonthly = {};
|
||||
ui.transferOverage = {};
|
||||
ui.transferUsed = {};
|
||||
@ -120,6 +209,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
var state = {};
|
||||
state.diskRefresh = false;
|
||||
state.eventsComplete = 0;
|
||||
state.haveRanges = false;
|
||||
state.linodeRefresh = false;
|
||||
state.showExtraEvents = false;
|
||||
|
||||
@ -143,8 +233,8 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
apiPost("/linode/instances/" + data.params.lid + "/boot", request, callback);
|
||||
};
|
||||
|
||||
// Convert a byte count into a "friendly" byte string (KB, MB, GB, etc)
|
||||
var byteString = function(count)
|
||||
// Convert an unqualified count into a string with an SI prefix (i.e. bytes to MB/GB/etc)
|
||||
var countSI = function(count)
|
||||
{
|
||||
var prefix = "KMGTPEZY";
|
||||
var unit = "";
|
||||
@ -158,7 +248,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
}
|
||||
}
|
||||
|
||||
return count.toFixed(2) + " " + unit + "B";
|
||||
return count.toFixed(2) + " " + unit;
|
||||
};
|
||||
|
||||
// Generate a config profile table row
|
||||
@ -525,6 +615,33 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
if (data.linode.type && !data.plan.id)
|
||||
apiGet("/linode/types/" + data.linode.type, displayPlan, null);
|
||||
|
||||
// Populate graph range picker
|
||||
if (!state.haveRanges) {
|
||||
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
var created = new Date(data.linode.created + "Z");
|
||||
var now = new Date();
|
||||
var last30 = document.createElement("option");
|
||||
last30.value = "/" + now.getFullYear() + "/" + (now.getMonth() + 1).toString().padStart(2, "0");
|
||||
last30.innerHTML = "Last 30 Days";
|
||||
ui.graphRange.appendChild(last30);
|
||||
|
||||
while (!(now.getFullYear() == created.getFullYear() && now.getMonth() == created.getMonth())) {
|
||||
if (now.getMonth() == 0) {
|
||||
now.setMonth(11);
|
||||
now.setFullYear(now.getFullYear() - 1);
|
||||
} else {
|
||||
now.setMonth(now.getMonth() - 1);
|
||||
}
|
||||
|
||||
var yearMonth = document.createElement("option");
|
||||
yearMonth.value = "/" + now.getFullYear() + "/" + (now.getMonth() + 1).toString().padStart(2, "0");
|
||||
yearMonth.innerHTML = months[now.getMonth()] + " " + now.getFullYear();
|
||||
ui.graphRange.appendChild(yearMonth);
|
||||
}
|
||||
|
||||
state.haveRanges = true;
|
||||
}
|
||||
|
||||
state.linodeRefresh = false;
|
||||
};
|
||||
|
||||
@ -662,6 +779,159 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
ui.upgrade.style.display = "block";
|
||||
};
|
||||
|
||||
// Show stats graphs
|
||||
var displayStats = function(response)
|
||||
{
|
||||
// Insert dummy points in case of blank data
|
||||
if (!response.data.cpu.length)
|
||||
response.data.cpu = [[0,0]];
|
||||
if (!response.data.io.io.length)
|
||||
response.data.io.io = [[0,0]];
|
||||
if (!response.data.io.swap.length)
|
||||
response.data.io.swap = [[0,0]];
|
||||
if (!response.data.netv4.private_out.length)
|
||||
response.data.netv4.private_out = [[0,0]];
|
||||
if (!response.data.netv4.private_in.length)
|
||||
response.data.netv4.private_in = [[0,0]];
|
||||
if (!response.data.netv4.out.length)
|
||||
response.data.netv4.out = [[0,0]];
|
||||
if (!response.data.netv4.in.length)
|
||||
response.data.netv4.in = [[0,0]];
|
||||
if (!response.data.netv6.private_out.length)
|
||||
response.data.netv6.private_out = [[0,0]];
|
||||
if (!response.data.netv6.private_in.length)
|
||||
response.data.netv6.private_in = [[0,0]];
|
||||
if (!response.data.netv6.out.length)
|
||||
response.data.netv6.out = [[0,0]];
|
||||
if (!response.data.netv6.in.length)
|
||||
response.data.netv6.in = [[0,0]];
|
||||
|
||||
data.stats.cpu = [{
|
||||
"color": "#03C",
|
||||
"fill": false,
|
||||
"points": response.data.cpu
|
||||
}];
|
||||
data.stats.io = [
|
||||
{
|
||||
"color": "#FFD04B",
|
||||
"fill": true,
|
||||
"points": response.data.io.io
|
||||
},
|
||||
{
|
||||
"color": "#FA373E",
|
||||
"fill": false,
|
||||
"points": response.data.io.swap
|
||||
}
|
||||
];
|
||||
data.stats.netv4 = [
|
||||
{
|
||||
"color": "#FF9",
|
||||
"fill": true,
|
||||
"points": response.data.netv4.private_out
|
||||
},
|
||||
{
|
||||
"color": "#C09",
|
||||
"fill": false,
|
||||
"points": response.data.netv4.private_in
|
||||
},
|
||||
{
|
||||
"color": "#32CD32",
|
||||
"fill": true,
|
||||
"points": response.data.netv4.out
|
||||
},
|
||||
{
|
||||
"color": "#03C",
|
||||
"fill": false,
|
||||
"points": response.data.netv4.in
|
||||
}
|
||||
];
|
||||
data.stats.netv6 = [
|
||||
{
|
||||
"color": "#FF9",
|
||||
"fill": true,
|
||||
"points": response.data.netv6.private_out
|
||||
},
|
||||
{
|
||||
"color": "#C09",
|
||||
"fill": false,
|
||||
"points": response.data.netv6.private_in
|
||||
},
|
||||
{
|
||||
"color": "#32CD32",
|
||||
"fill": true,
|
||||
"points": response.data.netv6.out
|
||||
},
|
||||
{
|
||||
"color": "#03C",
|
||||
"fill": false,
|
||||
"points": response.data.netv6.in
|
||||
}
|
||||
];
|
||||
|
||||
// Draw graphs
|
||||
drawSeries(data.stats.cpu, ui.cpuGraph);
|
||||
drawSeries(data.stats.io, ui.ioGraph);
|
||||
drawSeries(data.stats.netv4, ui.ipv4Graph);
|
||||
drawSeries(data.stats.netv6, ui.ipv6Graph);
|
||||
|
||||
// Compute traffic totals
|
||||
var ipv4Out = data.stats.netv4[0].avg * (data.stats.netv4[0].points[data.stats.netv4[0].points.length-1][0] - data.stats.netv4[0].points[0][0]) / 1000;
|
||||
ipv4Out += data.stats.netv4[2].avg * (data.stats.netv4[2].points[data.stats.netv4[2].points.length-1][0] - data.stats.netv4[2].points[0][0]) / 1000;
|
||||
ipv4Out /= 8;
|
||||
var ipv4In = data.stats.netv4[1].avg * (data.stats.netv4[1].points[data.stats.netv4[1].points.length-1][0] - data.stats.netv4[1].points[0][0]) / 1000;
|
||||
ipv4In += data.stats.netv4[3].avg * (data.stats.netv4[3].points[data.stats.netv4[3].points.length-1][0] - data.stats.netv4[3].points[0][0]) / 1000;
|
||||
ipv4In /= 8;
|
||||
var ipv6Out = data.stats.netv6[0].avg * (data.stats.netv6[0].points[data.stats.netv6[0].points.length-1][0] - data.stats.netv6[0].points[0][0]) / 1000;
|
||||
ipv6Out += data.stats.netv6[2].avg * (data.stats.netv6[2].points[data.stats.netv6[2].points.length-1][0] - data.stats.netv6[2].points[0][0]) / 1000;
|
||||
ipv6Out /= 8;
|
||||
var ipv6In = data.stats.netv6[1].avg * (data.stats.netv6[1].points[data.stats.netv6[1].points.length-1][0] - data.stats.netv6[1].points[0][0]) / 1000;
|
||||
ipv6In += data.stats.netv6[3].avg * (data.stats.netv6[3].points[data.stats.netv6[3].points.length-1][0] - data.stats.netv6[3].points[0][0]) / 1000;
|
||||
ipv6In /= 8;
|
||||
|
||||
// Update tables
|
||||
ui.cpuMax.innerHTML = data.stats.cpu[0].max + "%";
|
||||
ui.cpuAvg.innerHTML = data.stats.cpu[0].avg.toFixed(2) + "%";
|
||||
ui.cpuLast.innerHTML = data.stats.cpu[0].points[data.stats.cpu[0].points.length - 1][1] + "%";
|
||||
ui.ioRateMax.innerHTML = data.stats.io[0].max;
|
||||
ui.ioRateAvg.innerHTML = data.stats.io[0].avg.toFixed(2);
|
||||
ui.ioRateLast.innerHTML = data.stats.io[0].points[data.stats.io[0].points.length - 1][1];
|
||||
ui.swapRateMax.innerHTML = data.stats.io[1].max;
|
||||
ui.swapRateAvg.innerHTML = data.stats.io[1].avg.toFixed(2);
|
||||
ui.swapRateLast.innerHTML = data.stats.io[1].points[data.stats.io[1].points.length - 1][1];
|
||||
ui.ipv4PrivOutMax.innerHTML = countSI(data.stats.netv4[0].max) + "b/s";
|
||||
ui.ipv4PrivOutAvg.innerHTML = countSI(data.stats.netv4[0].avg) + "b/s";
|
||||
ui.ipv4PrivOutLast.innerHTML = countSI(data.stats.netv4[0].points[data.stats.netv4[0].points.length - 1][1]) + "b/s";
|
||||
ui.ipv4PrivInMax.innerHTML = countSI(data.stats.netv4[1].max) + "b/s";
|
||||
ui.ipv4PrivInAvg.innerHTML = countSI(data.stats.netv4[1].avg) + "b/s";
|
||||
ui.ipv4PrivInLast.innerHTML = countSI(data.stats.netv4[1].points[data.stats.netv4[1].points.length - 1][1]) + "b/s";
|
||||
ui.ipv4OutMax.innerHTML = countSI(data.stats.netv4[2].max) + "b/s";
|
||||
ui.ipv4OutAvg.innerHTML = countSI(data.stats.netv4[2].avg) + "b/s";
|
||||
ui.ipv4OutLast.innerHTML = countSI(data.stats.netv4[2].points[data.stats.netv4[2].points.length - 1][1]) + "b/s";
|
||||
ui.ipv4InMax.innerHTML = countSI(data.stats.netv4[3].max) + "b/s";
|
||||
ui.ipv4InAvg.innerHTML = countSI(data.stats.netv4[3].avg) + "b/s";
|
||||
ui.ipv4InLast.innerHTML = countSI(data.stats.netv4[3].points[data.stats.netv4[3].points.length - 1][1]) + "b/s";
|
||||
ui.ipv4TotalIn.innerHTML = countSI(ipv4In) + "B";
|
||||
ui.ipv4TotalOut.innerHTML = countSI(ipv4Out) + "B";
|
||||
ui.ipv4Total.innerHTML = countSI(ipv4In + ipv4Out) + "B";
|
||||
ui.ipv6PrivOutMax.innerHTML = countSI(data.stats.netv6[0].max) + "b/s";
|
||||
ui.ipv6PrivOutAvg.innerHTML = countSI(data.stats.netv6[0].avg) + "b/s";
|
||||
ui.ipv6PrivOutLast.innerHTML = countSI(data.stats.netv6[0].points[data.stats.netv6[0].points.length - 1][1]) + "b/s";
|
||||
ui.ipv6PrivInMax.innerHTML = countSI(data.stats.netv6[1].max) + "b/s";
|
||||
ui.ipv6PrivInAvg.innerHTML = countSI(data.stats.netv6[1].avg) + "b/s";
|
||||
ui.ipv6PrivInLast.innerHTML = countSI(data.stats.netv6[1].points[data.stats.netv6[1].points.length - 1][1]) + "b/s";
|
||||
ui.ipv6OutMax.innerHTML = countSI(data.stats.netv6[2].max) + "b/s";
|
||||
ui.ipv6OutAvg.innerHTML = countSI(data.stats.netv6[2].avg) + "b/s";
|
||||
ui.ipv6OutLast.innerHTML = countSI(data.stats.netv6[2].points[data.stats.netv6[2].points.length - 1][1]) + "b/s";
|
||||
ui.ipv6InMax.innerHTML = countSI(data.stats.netv6[3].max) + "b/s";
|
||||
ui.ipv6InAvg.innerHTML = countSI(data.stats.netv6[3].avg) + "b/s";
|
||||
ui.ipv6InLast.innerHTML = countSI(data.stats.netv6[3].points[data.stats.netv6[3].points.length - 1][1]) + "b/s";
|
||||
ui.ipv6TotalIn.innerHTML = countSI(ipv6In) + "B";
|
||||
ui.ipv6TotalOut.innerHTML = countSI(ipv6Out) + "B";
|
||||
ui.ipv6Total.innerHTML = countSI(ipv6In + ipv6Out) + "B";
|
||||
|
||||
ui.graphRange.disabled = false;
|
||||
};
|
||||
|
||||
// Show storage totals
|
||||
var displayStorage = function()
|
||||
{
|
||||
@ -686,7 +956,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
|
||||
// Display network transfer info
|
||||
ui.transferMonthly.innerHTML = data.linodeTransfer.quota + " GB";
|
||||
ui.transferUsed.innerHTML = byteString(data.linodeTransfer.used);
|
||||
ui.transferUsed.innerHTML = countSI(data.linodeTransfer.used) + "B";
|
||||
ui.transferOverage.innerHTML = data.linodeTransfer.billable + " GB";
|
||||
ui.netUsage.value = ((data.linodeTransfer.used / 1024 / 1024 / 1024) / data.linodeTransfer.quota * 100).toFixed(0);
|
||||
ui.netUsage.innerHTML = ui.netUsage.value + "%";
|
||||
@ -786,43 +1056,25 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
anchors[i].href = anchors[i].href.replace("lid=0", "lid=" + data.params.lid);
|
||||
|
||||
// Get element references
|
||||
ui.backups = document.getElementById(elements.backups);
|
||||
ui.boot = document.getElementById(elements.boot);
|
||||
ui.configTable = document.getElementById(elements.configTable);
|
||||
ui.diskTable = document.getElementById(elements.diskTable);
|
||||
ui.diskUsage = document.getElementById(elements.diskUsage);
|
||||
ui.eventTable = document.getElementById(elements.eventTable);
|
||||
ui.jobProgress = document.getElementById(elements.jobProgress);
|
||||
ui.jobProgressRow = document.getElementById(elements.jobProgressRow);
|
||||
ui.lastBackup = document.getElementById(elements.lastBackup);
|
||||
ui.lastBackupTime = document.getElementById(elements.lastBackupTime);
|
||||
ui.linodeLabel = document.getElementById(elements.linodeLabel);
|
||||
ui.linodeTag = document.getElementById(elements.linodeTag);
|
||||
ui.linodeTagLink = document.getElementById(elements.linodeTagLink);
|
||||
ui.loadingConfigs = document.getElementById(elements.loadingConfigs);
|
||||
ui.loadingDisks = document.getElementById(elements.loadingDisks);
|
||||
ui.loadingJobs = document.getElementById(elements.loadingJobs);
|
||||
ui.loadingVolumes = document.getElementById(elements.loadingVolumes);
|
||||
ui.moreJobs = document.getElementById(elements.moreJobs);
|
||||
ui.netUsage = document.getElementById(elements.netUsage);
|
||||
ui.notifications = document.getElementById(elements.notifications);
|
||||
ui.reboot = document.getElementById(elements.reboot);
|
||||
ui.serverStatus = document.getElementById(elements.serverStatus);
|
||||
ui.shutDown = document.getElementById(elements.shutDown);
|
||||
ui.storageFree = document.getElementById(elements.storageFree);
|
||||
ui.storageTotal = document.getElementById(elements.storageTotal);
|
||||
ui.storageUsed = document.getElementById(elements.storageUsed);
|
||||
ui.transferMonthly = document.getElementById(elements.transferMonthly);
|
||||
ui.transferOverage = document.getElementById(elements.transferOverage);
|
||||
ui.transferUsed = document.getElementById(elements.transferUsed);
|
||||
ui.upgrade = document.getElementById(elements.upgrade);
|
||||
ui.volumeTable = document.getElementById(elements.volumeTable);
|
||||
for (var i in ui)
|
||||
ui[i] = document.getElementById(elements[i]);
|
||||
|
||||
// Attach button handlers
|
||||
ui.boot.addEventListener("click", bootLinode);
|
||||
ui.moreJobs.addEventListener("click", toggleEvents);
|
||||
ui.reboot.addEventListener("click", rebootLinode);
|
||||
ui.shutDown.addEventListener("click", shutDownLinode);
|
||||
ui.graphRange.addEventListener("input", updateGraphs);
|
||||
|
||||
// Set graph resolutions
|
||||
ui.cpuGraph.height = ui.cpuGraph.clientHeight;
|
||||
ui.cpuGraph.width = ui.cpuGraph.clientWidth;
|
||||
ui.ioGraph.height = ui.ioGraph.clientHeight;
|
||||
ui.ioGraph.width = ui.ioGraph.clientWidth;
|
||||
ui.ipv4Graph.height = ui.ipv4Graph.clientHeight;
|
||||
ui.ipv4Graph.width = ui.ipv4Graph.clientWidth;
|
||||
ui.ipv6Graph.height = ui.ipv6Graph.clientHeight;
|
||||
ui.ipv6Graph.width = ui.ipv6Graph.clientWidth;
|
||||
|
||||
// Get data from the API
|
||||
apiGet("/linode/instances/" + data.params.lid, displayDetails, null);
|
||||
@ -836,6 +1088,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
};
|
||||
apiGet("/account/events", displayEvents, filter);
|
||||
apiGet("/account/notifications", displayNotifications, null);
|
||||
apiGet("/linode/instances/" + data.params.lid + "/stats", displayStats, null);
|
||||
};
|
||||
|
||||
// Button handler for shutdown button
|
||||
@ -908,6 +1161,16 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
|
||||
window.setTimeout(getEvent, settings.refreshRate, event.id);
|
||||
};
|
||||
|
||||
// Re-populate graphs with selected range
|
||||
var updateGraphs = function(event)
|
||||
{
|
||||
if (event.currentTarget.disabled)
|
||||
return;
|
||||
|
||||
apiGet("/linode/instances/" + data.params.lid + "/stats" + ui.graphRange.value, displayStats, null);
|
||||
ui.graphRange.disabled = true;
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
||||
|
@ -109,6 +109,145 @@ along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Graphs</h3>
|
||||
<select disabled id="graph-range">
|
||||
<option selected value="">Last 24 Hours</option>
|
||||
</select>
|
||||
<div class="lmc-graph">
|
||||
<h4>CPU (%)</h4>
|
||||
<canvas id="cpu-graph"></canvas>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Max</td>
|
||||
<td>Avg</td>
|
||||
<td>Last</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div id="cpu-color" class="lmc-graph-color"></div> CPU %</td>
|
||||
<td id="cpu-max"></td>
|
||||
<td id="cpu-avg"></td>
|
||||
<td id="cpu-last"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4>Disk I/O (blocks/s)</h4>
|
||||
<canvas id="io-graph"></canvas>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Max</td>
|
||||
<td>Avg</td>
|
||||
<td>Last</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div id="io-rate-color" class="lmc-graph-color"></div> I/O Rate</td>
|
||||
<td id="io-rate-max"></td>
|
||||
<td id="io-rate-avg"></td>
|
||||
<td id="io-rate-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="swap-rate-color" class="lmc-graph-color"></div> Swap Rate</td>
|
||||
<td id="swap-rate-max"></td>
|
||||
<td id="swap-rate-avg"></td>
|
||||
<td id="swap-rate-last"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4>Network - IPv4 (bits/s)</h4>
|
||||
<canvas id="ipv4-graph"></canvas>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Max</td>
|
||||
<td>Avg</td>
|
||||
<td>Last</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div id="ipv4-privout-color" class="lmc-graph-color"></div> Private Out</td>
|
||||
<td id="ipv4-privout-max"></td>
|
||||
<td id="ipv4-privout-avg"></td>
|
||||
<td id="ipv4-privout-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="ipv4-privin-color" class="lmc-graph-color"></div> Private In</td>
|
||||
<td id="ipv4-privin-max"></td>
|
||||
<td id="ipv4-privin-avg"></td>
|
||||
<td id="ipv4-privin-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="ipv4-out-color" class="lmc-graph-color"></div> Public Out</td>
|
||||
<td id="ipv4-out-max"></td>
|
||||
<td id="ipv4-out-avg"</td>
|
||||
<td id="ipv4-out-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="ipv4-in-color" class="lmc-graph-color"></div> Public In</td>
|
||||
<td id="ipv4-in-max"></td>
|
||||
<td id="ipv4-in-avg"></td>
|
||||
<td id="ipv4-in-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Traffic</td>
|
||||
<td>In: <span id="ipv4-total-in"></span></td>
|
||||
<td>Out: <span id="ipv4-total-out"></span></td>
|
||||
<td>Combined: <span id="ipv4-total"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4>Network - IPv6 (bits/s)</h4>
|
||||
<canvas id="ipv6-graph"></canvas>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Max</td>
|
||||
<td>Avg</td>
|
||||
<td>Last</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div id="ipv6-privout-color" class="lmc-graph-color"></div> Private Out</td>
|
||||
<td id="ipv6-privout-max"></td>
|
||||
<td id="ipv6-privout-avg"></td>
|
||||
<td id="ipv6-privout-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="ipv6-privin-color" class="lmc-graph-color"></div> Private In</td>
|
||||
<td id="ipv6-privin-max"></td>
|
||||
<td id="ipv6-privin-avg"></td>
|
||||
<td id="ipv6-privin-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="ipv6-out-color" class="lmc-graph-color"></div> Public Out</td>
|
||||
<td id="ipv6-out-max"></td>
|
||||
<td id="ipv6-out-avg"</td>
|
||||
<td id="ipv6-out-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="ipv6-in-color" class="lmc-graph-color"></div> Public In</td>
|
||||
<td id="ipv6-in-max"></td>
|
||||
<td id="ipv6-in-avg"></td>
|
||||
<td id="ipv6-in-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Traffic</td>
|
||||
<td>In: <span id="ipv6-total-in"></span></td>
|
||||
<td>Out: <span id="ipv6-total-out"></span></td>
|
||||
<td>Combined: <span id="ipv6-total"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="linode-sidebar">
|
||||
<div class="sidebar-box">
|
||||
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* This file is part of Linode Manager Classic.
|
||||
*
|
||||
* Linode Manager Classic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Linode Manager Classic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import url('/global.css');
|
||||
|
||||
#graphs {
|
||||
padding: 0px 15px 15px;
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
* This file is part of Linode Manager Classic.
|
||||
*
|
||||
* Linode Manager Classic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Linode Manager Classic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { settings, elements, apiGet, parseParams, setupHeader } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
// Element names specific to this page
|
||||
elements.linodeLabel = "linode-label";
|
||||
elements.linodeTag = "linode-tag";
|
||||
elements.linodeTagLink = "linode-tag-link";
|
||||
|
||||
// Data recieved from API calls
|
||||
var data = {};
|
||||
data.linode = {};
|
||||
|
||||
// Static references to UI elements
|
||||
var ui = {};
|
||||
ui.linodeLabel = {};
|
||||
ui.linodeTag = {};
|
||||
ui.linodeTagLink = {};
|
||||
|
||||
// Callback for linode details API call
|
||||
var displayDetails = function(response)
|
||||
{
|
||||
data.linode = response;
|
||||
|
||||
// Set page title and header stuff
|
||||
document.title += " // " + data.linode.label;
|
||||
ui.linodeLabel.innerHTML = data.linode.label;
|
||||
if (data.linode.tags.length == 1) {
|
||||
ui.linodeTagLink.href = "/linodes?tag=" + data.linode.tags[0];
|
||||
ui.linodeTagLink.innerHTML = "(" + data.linode.tags[0] + ")";
|
||||
ui.linodeTag.style.display = "inline";
|
||||
}
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
var setup = function()
|
||||
{
|
||||
// Parse URL parameters
|
||||
data.params = parseParams();
|
||||
|
||||
// We need a Linode ID, so die if we don't have it
|
||||
if (!data.params.lid) {
|
||||
alert("No Linode ID supplied!");
|
||||
return;
|
||||
}
|
||||
|
||||
setupHeader();
|
||||
|
||||
// Update links on page to include proper Linode ID
|
||||
var anchors = document.getElementsByTagName("a");
|
||||
for (var i = 0; i < anchors.length; i++)
|
||||
anchors[i].href = anchors[i].href.replace("lid=0", "lid=" + data.params.lid);
|
||||
|
||||
// Get element references
|
||||
ui.linodeLabel = document.getElementById(elements.linodeLabel);
|
||||
ui.linodeTag = document.getElementById(elements.linodeTag);
|
||||
ui.linodeTagLink = document.getElementById(elements.linodeTagLink);
|
||||
|
||||
// Get data from API
|
||||
apiGet("/linode/instances/" + data.params.lid, displayDetails, null);
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
@ -1,35 +0,0 @@
|
||||
<!--
|
||||
This file is part of Linode Manager Classic.
|
||||
|
||||
Linode Manager Classic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Linode Manager Classic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>LMC - Graphs</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="graphs.css" />
|
||||
<script src="graphs.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--#include virtual="/include/header.html"-->
|
||||
<!--#include virtual="/include/linode_subnav.html"-->
|
||||
<div id="main-content" class="wrapper">
|
||||
<div id="top-links"><a href="/linodes">Linodes</a> » <span id="linode-tag"><a id="linode-tag-link" href=""></a> » </span><a id="linode-label" href="/linodes/dashboard?lid=0"></a> » <span class="top-links-title">Graphs</span></div>
|
||||
<div id="graphs">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user