diff --git a/global.css b/global.css
index 72837d3..e1a9e70 100644
--- a/global.css
+++ b/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%;
diff --git a/global.js b/global.js
index 378a3aa..d895aec 100644
--- a/global.js
+++ b/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 };
diff --git a/include/linode_subnav.html b/include/linode_subnav.html
index ab72d4f..8f41a98 100644
--- a/include/linode_subnav.html
+++ b/include/linode_subnav.html
@@ -6,7 +6,6 @@
Rescue
Resize
Clone
- Graphs
Backups
Settings
diff --git a/linodes/dashboard/dashboard.css b/linodes/dashboard/dashboard.css
index 8655485..01af940 100644
--- a/linodes/dashboard/dashboard.css
+++ b/linodes/dashboard/dashboard.css
@@ -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;
diff --git a/linodes/dashboard/dashboard.js b/linodes/dashboard/dashboard.js
index 7655746..806ae56 100644
--- a/linodes/dashboard/dashboard.js
+++ b/linodes/dashboard/dashboard.js
@@ -15,7 +15,7 @@
* along with Linode Manager Classic. If not, see .
*/
-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);
})();
diff --git a/linodes/dashboard/index.shtml b/linodes/dashboard/index.shtml
index 073a3fd..7caf205 100644
--- a/linodes/dashboard/index.shtml
+++ b/linodes/dashboard/index.shtml
@@ -109,6 +109,145 @@ along with Linode Manager Classic. If not, see .
Graphs
+
+ Last 24 Hours
+
+
+
CPU (%)
+
+
+
+
+
+ Max
+ Avg
+ Last
+
+
+
+
+
CPU %
+
+
+
+
+
+
+
Disk I/O (blocks/s)
+
+
+
+
+
+ Max
+ Avg
+ Last
+
+
+
+
+
I/O Rate
+
+
+
+
+
+
Swap Rate
+
+
+
+
+
+
+
Network - IPv4 (bits/s)
+
+
+
+
+
+ Max
+ Avg
+ Last
+
+
+
+
+
Private Out
+
+
+
+
+
+
Private In
+
+
+
+
+
+
Public Out
+
+
+
+
+
+
Public In
+
+
+
+
+
+ Total Traffic
+ In:
+ Out:
+ Combined:
+
+
+
+
Network - IPv6 (bits/s)
+
+
+
+
+
+ Max
+ Avg
+ Last
+
+
+
+
+
Private Out
+
+
+
+
+
+
Private In
+
+
+
+
+
+
Public Out
+
+
+
+
+
+
Public In
+
+
+
+
+
+ Total Traffic
+ In:
+ Out:
+ Combined:
+
+
+
+