Initial commit. Implemented OAuth, Linodes, volumes, and images
This commit is contained in:
199
linodes/dashboard/dashboard.css
Normal file
199
linodes/dashboard/dashboard.css
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* 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');
|
||||
|
||||
.backups-enabled {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#boot {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.disk-icon {
|
||||
height: 24px;
|
||||
width: 26px;
|
||||
}
|
||||
|
||||
.extra-event {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h3 {
|
||||
border-bottom: 1px solid #E8E8E8;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: lighter;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.job-failed {
|
||||
background-color: #C13100;
|
||||
color: #FFF;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.job-info {
|
||||
color: #777;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.job-notice {
|
||||
background-color: #3683DC;
|
||||
color: #FFF;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#job-progress {
|
||||
background-color: #A9A9A9;
|
||||
display: block;
|
||||
color: #31B96E;
|
||||
width: calc(100% - 2px);
|
||||
}
|
||||
|
||||
#job-progress::-moz-progress-bar {
|
||||
background-color: #31B96E;
|
||||
}
|
||||
|
||||
#job-progress::-webkit-progress-bar {
|
||||
background-color: #A9A9A9;
|
||||
}
|
||||
|
||||
#job-progress::-webkit-progress-value {
|
||||
background-color: #31B96E;
|
||||
}
|
||||
|
||||
#job-progress-row {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#job-progress-row td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.job-running {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.job-success {
|
||||
background-color: #31B96E;
|
||||
color: #FFF;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.job-type {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#last-backup {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#linode-details {
|
||||
display: inline-block;
|
||||
padding: 0px 15px 20px;
|
||||
text-overflow: ellipsis;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
#linode-sidebar {
|
||||
float: right;
|
||||
margin-right: 15px;
|
||||
width: calc(25% - 45px);
|
||||
}
|
||||
|
||||
.lmc-table td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.lmc-table td:nth-child(2) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#moar-jobs {
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#notifications {
|
||||
padding: 0px 15px;
|
||||
}
|
||||
|
||||
#reboot {
|
||||
display: none;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#server-status {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#shut-down {
|
||||
display: none;
|
||||
font-size: 14px;
|
||||
margin: 0 auto 15px;
|
||||
}
|
||||
|
||||
.sidebar-box {
|
||||
border: 1px dotted #999;
|
||||
font-size: 12px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.sidebar-box a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sidebar-box h4 {
|
||||
background-color: #E5E5E5;
|
||||
font-size: 15px;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.sidebar-box meter {
|
||||
display: block;
|
||||
margin: 0 auto 10px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.sidebar-box p {
|
||||
margin: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sidebar-box ul {
|
||||
list-style-position: inside;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.sub-links-inline {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#uptime {
|
||||
display: none;
|
||||
}
|
887
linodes/dashboard/dashboard.js
Normal file
887
linodes/dashboard/dashboard.js
Normal file
@ -0,0 +1,887 @@
|
||||
/*
|
||||
* 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, apiDelete, apiGet, apiPost, eventTitles, parseParams, setupHeader, timeString, translateKernel } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
// Element names specific to this page
|
||||
elements.backups = "backups";
|
||||
elements.backupsEnabled = "backups-enabled";
|
||||
elements.boot = "boot";
|
||||
elements.configRadioName = "config-radio";
|
||||
elements.configRemovePrefix = "config-remove-";
|
||||
elements.configTable = "config-table";
|
||||
elements.diskIcon = "disk-icon";
|
||||
elements.diskIconImg = "/img/disk.gif";
|
||||
elements.diskRemovePrefix = "disk-remove-";
|
||||
elements.diskTable = "disk-table";
|
||||
elements.diskUsage = "disk-usage";
|
||||
elements.eventRowPrefix = "event-row-";
|
||||
elements.eventTable = "event-table";
|
||||
elements.extraEvent = "extra-event";
|
||||
elements.info = "info";
|
||||
elements.jobFailed = "job-failed";
|
||||
elements.jobInfo = "job-info";
|
||||
elements.jobNotice = "job-notice";
|
||||
elements.jobProgress = "job-progress";
|
||||
elements.jobProgressRow = "job-progress-row";
|
||||
elements.jobRunning = "job-running";
|
||||
elements.jobSuccess = "job-success";
|
||||
elements.jobType = "job-type";
|
||||
elements.lastBackup = "last-backup";
|
||||
elements.lastBackupTime = "last-backup-time";
|
||||
elements.linodeLabel = "linode-label";
|
||||
elements.linodeTag = "linode-tag";
|
||||
elements.linodeTagLink = "linode-tag-link";
|
||||
elements.lmcRow = "lmc-tr3";
|
||||
elements.loadingConfigs = "loading-configs";
|
||||
elements.loadingDisks = "loading-disks";
|
||||
elements.loadingJobs = "loading-jobs";
|
||||
elements.loadingVolumes = "loading-volumes";
|
||||
elements.moreJobs = "moar-jobs";
|
||||
elements.netUsage = "net-usage";
|
||||
elements.notification = "notification";
|
||||
elements.notifications = "notifications";
|
||||
elements.reboot = "reboot";
|
||||
elements.serverStatus = "server-status";
|
||||
elements.shutDown = "shut-down";
|
||||
elements.spinnerImg = "/img/spinner-trans.gif";
|
||||
elements.storageFree = "storage-free";
|
||||
elements.storageTotal = "storage-total";
|
||||
elements.storageUsed = "storage-used";
|
||||
elements.transferMonthly = "transfer-monthly";
|
||||
elements.transferOverage = "transfer-overage";
|
||||
elements.transferUsed = "transfer-used";
|
||||
elements.volumeTable = "volume-table";
|
||||
|
||||
// Data recieved from API calls
|
||||
var data = {};
|
||||
data.params = {};
|
||||
data.configs = [];
|
||||
data.disks = [];
|
||||
data.events = [];
|
||||
data.linode = {};
|
||||
data.linodeTransfer = {};
|
||||
data.notifications = [];
|
||||
data.volumes = [];
|
||||
|
||||
// Static references to UI elements
|
||||
var ui = {};
|
||||
ui.backups = {};
|
||||
ui.boot = {};
|
||||
ui.configTable = {};
|
||||
ui.diskTable = {};
|
||||
ui.diskUsage = {};
|
||||
ui.eventTable = {};
|
||||
ui.jobProgress = {};
|
||||
ui.jobProgressRow = {};
|
||||
ui.lastBackup = {};
|
||||
ui.lastBackupTime = {};
|
||||
ui.linodeLabel = {};
|
||||
ui.linodeTag = {};
|
||||
ui.linodeTagLink = {};
|
||||
ui.loadingConfigs = {};
|
||||
ui.loadingDisks = {};
|
||||
ui.loadingJobs = {};
|
||||
ui.loadingVolumes = {};
|
||||
ui.moreJobs = {};
|
||||
ui.netUsage = {};
|
||||
ui.notifications = {};
|
||||
ui.reboot = {};
|
||||
ui.serverStatus = {};
|
||||
ui.shutDown = {};
|
||||
ui.storageFree = {};
|
||||
ui.storageTotal = {};
|
||||
ui.storageUsed = {};
|
||||
ui.transferMonthly = {};
|
||||
ui.transferOverage = {};
|
||||
ui.transferUsed = {};
|
||||
ui.volumeTable = {};
|
||||
|
||||
// Temporary state
|
||||
var state = {};
|
||||
state.diskRefresh = false;
|
||||
state.eventsComplete = 0;
|
||||
state.linodeRefresh = false;
|
||||
state.showExtraEvents = false;
|
||||
|
||||
// Button handler for boot button
|
||||
var bootLinode = function(event)
|
||||
{
|
||||
if (!confirm("Boot this Linode?"))
|
||||
return;
|
||||
|
||||
var config = getSelectedConfig();
|
||||
var request = {};
|
||||
if (config)
|
||||
request.config_id = config;
|
||||
|
||||
// Reload the page on successful API return
|
||||
var callback = function()
|
||||
{
|
||||
location.reload();
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
var prefix = "KMGTPEZY";
|
||||
var unit = "";
|
||||
|
||||
for (var i = 0; i < prefix.length; i++) {
|
||||
if (count >= 1024) {
|
||||
count /= 1024;
|
||||
unit = prefix.charAt(i);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return count.toFixed(2) + " " + unit + "B";
|
||||
};
|
||||
|
||||
// Generate a config profile table row
|
||||
var createConfigRow = function(config)
|
||||
{
|
||||
var row = document.createElement("tr");
|
||||
row.className = elements.lmcRow;
|
||||
|
||||
// Radio button selector
|
||||
var radioCell = document.createElement("td");
|
||||
var radioInput = document.createElement("input");
|
||||
radioInput.name = elements.configRadioName;
|
||||
radioInput.className = elements.configRadioName;
|
||||
radioInput.id = elements.configRadioName + "-" + config.id;
|
||||
radioInput.type = "radio";
|
||||
radioCell.appendChild(radioInput);
|
||||
row.appendChild(radioCell);
|
||||
|
||||
// Config profile name and kernel
|
||||
var nameCell = document.createElement("td");
|
||||
var nameLink = document.createElement("a");
|
||||
nameLink.href = "/linodes/config?cid=" + config.id + "&lid=" + data.params.lid;
|
||||
if (config.label.length > 41)
|
||||
nameLink.innerHTML = config.label.substring(0, 41) + "...";
|
||||
else
|
||||
nameLink.innerHTML = config.label;
|
||||
nameCell.appendChild(nameLink);
|
||||
var prefix = document.createElement("span");
|
||||
prefix.className = elements.info;
|
||||
prefix.innerHTML = " (";
|
||||
nameCell.appendChild(prefix);
|
||||
var kernel = document.createElement("span");
|
||||
kernel.className = elements.info;
|
||||
translateKernel(config.kernel, kernel);
|
||||
nameCell.appendChild(kernel);
|
||||
var suffix = document.createElement("span");
|
||||
suffix.className = elements.info;
|
||||
suffix.innerHTML = ")";
|
||||
nameCell.appendChild(suffix);
|
||||
row.appendChild(nameCell);
|
||||
|
||||
// Option links
|
||||
var options = document.createElement("td");
|
||||
var editLink = document.createElement("a");
|
||||
editLink.href = "/linodes/config?cid=" + config.id + "&lid=" + data.params.lid;
|
||||
editLink.innerHTML = "Edit";
|
||||
var separator = document.createElement("span");
|
||||
separator.innerHTML = " | ";
|
||||
var removeLink = document.createElement("a");
|
||||
removeLink.href = "#";
|
||||
removeLink.id = elements.configRemovePrefix + config.id;
|
||||
removeLink.innerHTML = "Remove";
|
||||
removeLink.addEventListener("click", deleteConfig);
|
||||
options.appendChild(editLink);
|
||||
options.appendChild(separator);
|
||||
options.appendChild(removeLink);
|
||||
row.appendChild(options);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
// Generate a disk table row
|
||||
var createDiskRow = function(disk)
|
||||
{
|
||||
var row = document.createElement("tr");
|
||||
row.className = elements.lmcRow;
|
||||
|
||||
// Disk icon
|
||||
var iconCell = document.createElement("td");
|
||||
var diskIcon = document.createElement("img");
|
||||
diskIcon.className = elements.diskIcon;
|
||||
diskIcon.src = elements.diskIconImg;
|
||||
iconCell.appendChild(diskIcon);
|
||||
row.appendChild(iconCell);
|
||||
|
||||
// Disk name and size
|
||||
var nameCell = document.createElement("td");
|
||||
var nameLink = document.createElement("a");
|
||||
nameLink.href = "/linodes/disk?did=" + disk.id + "&lid=" + data.params.lid;
|
||||
nameLink.innerHTML = disk.label;
|
||||
var size = document.createElement("span");
|
||||
size.className = elements.info;
|
||||
size.innerHTML = " (" + disk.size + " MB, " + disk.filesystem + ")";
|
||||
nameCell.appendChild(nameLink);
|
||||
nameCell.appendChild(size);
|
||||
row.appendChild(nameCell);
|
||||
|
||||
// Options links
|
||||
var options = document.createElement("td");
|
||||
var editLink = document.createElement("a");
|
||||
editLink.href = "/linodes/disk?did=" + disk.id + "&lid=" + data.params.lid;
|
||||
editLink.innerHTML = "Edit";
|
||||
var separator = document.createElement("span");
|
||||
separator.innerHTML = " | ";
|
||||
var removeLink = document.createElement("a");
|
||||
removeLink.href = "#";
|
||||
removeLink.id = elements.diskRemovePrefix + disk.id;
|
||||
removeLink.innerHTML = "Remove";
|
||||
removeLink.addEventListener("click", deleteDisk);
|
||||
options.appendChild(editLink);
|
||||
options.appendChild(separator);
|
||||
options.appendChild(removeLink);
|
||||
row.appendChild(options);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
// Generate an event table row
|
||||
var createEventRow = function(event, extra)
|
||||
{
|
||||
var row = document.createElement("tr");
|
||||
row.id = elements.eventRowPrefix + event.id;
|
||||
row.className = elements.lmcRow;
|
||||
if (extra)
|
||||
row.className += " " + elements.extraEvent;
|
||||
|
||||
// Status cell
|
||||
var statusCell = document.createElement("td");
|
||||
if (event.status.match(/scheduled|started/)) {
|
||||
statusCell.className = elements.jobRunning;
|
||||
var spinner = document.createElement("img");
|
||||
spinner.src = elements.spinnerImg;
|
||||
statusCell.appendChild(spinner);
|
||||
} else if (event.status == "failed") {
|
||||
var failed = document.createElement("span");
|
||||
failed.className = elements.jobFailed;
|
||||
failed.innerHTML = "Failed";
|
||||
statusCell.appendChild(failed);
|
||||
} else if (event.status == "notification") {
|
||||
var notice = document.createElement("span");
|
||||
notice.className = elements.jobNotice;
|
||||
notice.innerHTML = "Notice";
|
||||
statusCell.appendChild(notice);
|
||||
} else if (event.status == "finished") {
|
||||
var success = document.createElement("span");
|
||||
success.className = elements.jobSuccess;
|
||||
success.innerHTML = "Success";
|
||||
statusCell.appendChild(success);
|
||||
}
|
||||
row.appendChild(statusCell);
|
||||
|
||||
// Details cell
|
||||
var detailsCell = document.createElement("td");
|
||||
var jobType = document.createElement("span");
|
||||
jobType.className = elements.jobType;
|
||||
if (eventTitles[event.action])
|
||||
jobType.innerHTML = eventTitles[event.action];
|
||||
else
|
||||
jobType.innerHTML = event.action;
|
||||
var jobDetails = document.createElement("span");
|
||||
if (event.secondary_entity)
|
||||
jobDetails.innerHTML = " - " + event.secondary_entity.label;
|
||||
var br = document.createElement("br");
|
||||
var jobInfo = document.createElement("span");
|
||||
jobInfo.className = elements.jobInfo;
|
||||
var eventDate = new Date(event.created + "Z");
|
||||
var curDate = new Date();
|
||||
jobInfo.innerHTML = "Entered: " + timeString(curDate - eventDate, true);
|
||||
if (event.percent_complete && event.percent_complete != 100)
|
||||
jobInfo.innerHTML += " - " + event.percent_complete + "%";
|
||||
if (event.time_remaining) {
|
||||
var times = event.time_remaining.split(":");
|
||||
var ms = 0;
|
||||
ms += parseInt(times[0]) * 3600000;
|
||||
ms += parseInt(times[1]) * 60000;
|
||||
ms += parseInt(times[2]) * 1000;
|
||||
jobInfo.innerHTML += " - " + timeString(ms, false) + " Remaining";
|
||||
}
|
||||
detailsCell.appendChild(jobType);
|
||||
detailsCell.appendChild(jobDetails);
|
||||
detailsCell.appendChild(br);
|
||||
detailsCell.appendChild(jobInfo);
|
||||
row.appendChild(detailsCell);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
// Generate a volume table row
|
||||
var createVolumeRow = function(volume)
|
||||
{
|
||||
var row = document.createElement("tr");
|
||||
row.className = elements.lmcRow;
|
||||
|
||||
// Disk icon
|
||||
var iconCell = document.createElement("td");
|
||||
var diskIcon = document.createElement("img");
|
||||
diskIcon.className = elements.diskIcon;
|
||||
diskIcon.src = elements.diskIconImg;
|
||||
iconCell.appendChild(diskIcon);
|
||||
row.appendChild(iconCell);
|
||||
|
||||
// Volume name and size
|
||||
var nameCell = document.createElement("td");
|
||||
var nameLink = document.createElement("a");
|
||||
nameLink.href = "/volumes/settings?vid=" + volume.id;
|
||||
nameLink.innerHTML = volume.label;
|
||||
var size = document.createElement("span");
|
||||
size.className = elements.info;
|
||||
size.innerHTML = " (" + volume.size + " GiB)";
|
||||
nameCell.appendChild(nameLink);
|
||||
nameCell.appendChild(size);
|
||||
row.appendChild(nameCell);
|
||||
|
||||
// Options links
|
||||
var options = document.createElement("td");
|
||||
var editLink = document.createElement("a");
|
||||
editLink.href = "/volumes/settings?vid=" + volume.id;
|
||||
editLink.innerHTML = "Edit";
|
||||
var separator = document.createElement("span");
|
||||
separator.innerHTML = " | ";
|
||||
var cloneLink = document.createElement("a");
|
||||
cloneLink.href = "/volumes/clone?vid=" + volume.id;
|
||||
cloneLink.innerHTML = "Clone";
|
||||
var detachLink = document.createElement("a");
|
||||
detachLink.href = "/volumes/detach?vid=" + volume.id;
|
||||
detachLink.innerHTML = "Detach";
|
||||
var removeLink = document.createElement("a");
|
||||
removeLink.href = "/volumes/remove?vid=" + volume.id;
|
||||
removeLink.innerHTML = "Remove";
|
||||
options.appendChild(editLink);
|
||||
options.appendChild(separator);
|
||||
options.appendChild(cloneLink);
|
||||
options.appendChild(separator.cloneNode(true));
|
||||
options.appendChild(detachLink);
|
||||
options.appendChild(separator.cloneNode(true));
|
||||
options.appendChild(removeLink);
|
||||
row.appendChild(options);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
// Handler for config delete
|
||||
var deleteConfig = function(event)
|
||||
{
|
||||
if (!confirm("Delete this Configuration Profile?"))
|
||||
return;
|
||||
|
||||
var cid = event.currentTarget.id.substring(elements.configRemovePrefix.length);
|
||||
apiDelete("/linode/instances/" + data.params.lid + "/configs/" + cid, function()
|
||||
{
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
// Handler for disk delete
|
||||
var deleteDisk = function(event)
|
||||
{
|
||||
var did = parseInt(event.currentTarget.id.substring(elements.diskRemovePrefix.length));
|
||||
var disk = null;
|
||||
for (var i = 0; i < data.disks.length; i++) {
|
||||
if (data.disks[i].id == did) {
|
||||
disk = data.disks[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!disk)
|
||||
return;
|
||||
|
||||
if (!confirm("Delete " + disk.label + "?"))
|
||||
return;
|
||||
|
||||
apiDelete("/linode/instances/" + data.params.lid + "/disks/" + did, function()
|
||||
{
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
// Callback for backups API call
|
||||
var displayBackups = function(response)
|
||||
{
|
||||
// Find time of most recent backup
|
||||
var mostRecent = new Date(0);
|
||||
for (var i = 0; i < response.automatic.length; i++) {
|
||||
if (response.automatic[i].status == "successful") {
|
||||
var time = new Date(response.automatic[i].finished + "Z");
|
||||
if (time > mostRecent)
|
||||
mostRecent = time;
|
||||
}
|
||||
}
|
||||
if (response.snapshot.current && response.snapshot.current.status == "successful") {
|
||||
var time = new Date(response.snapshot.current.finished + "Z");
|
||||
if (time > mostRecent)
|
||||
mostRecent = time;
|
||||
}
|
||||
|
||||
// 1970 means no backups happened
|
||||
if (mostRecent.getUTCFullYear() == 1970)
|
||||
return;
|
||||
|
||||
var curDate = new Date();
|
||||
ui.lastBackupTime.innerHTML = timeString(curDate - mostRecent, true);
|
||||
ui.lastBackup.style.display = "block";
|
||||
};
|
||||
|
||||
// Callback for config profile API call
|
||||
var displayConfigs = function(response)
|
||||
{
|
||||
// Add configs to array
|
||||
data.configs = data.configs.concat(response.data);
|
||||
|
||||
// Request the next page if there are more
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/linode/instances/" + data.params.lid + "/configs?page=" + (response.page + 1), displayConfigs, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove loading row
|
||||
ui.loadingConfigs.remove();
|
||||
|
||||
// Insert config profile rows into table
|
||||
for (var i = 0; i < data.configs.length; i++)
|
||||
ui.configTable.appendChild(createConfigRow(data.configs[i]));
|
||||
};
|
||||
|
||||
// Callback for linode details API call
|
||||
var displayDetails = function(response)
|
||||
{
|
||||
data.linode = response;
|
||||
|
||||
// Set page title and header stuff
|
||||
if (document.title.indexOf("//") == -1)
|
||||
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";
|
||||
} else {
|
||||
ui.linodeTag.style.display = "none";
|
||||
}
|
||||
|
||||
// Set running status
|
||||
ui.serverStatus.innerHTML = data.linode.status.charAt(0).toUpperCase() + data.linode.status.slice(1).replace(/_/g, " ");
|
||||
if (data.linode.status == "running") {
|
||||
ui.shutDown.disabled = false;
|
||||
ui.shutDown.style.display = "block";
|
||||
} else {
|
||||
ui.shutDown.style.display = "none";
|
||||
}
|
||||
if (data.linode.status.match(/offline|provisioning|migrating|rebuilding|restoring|shutting_down|resizing/)) {
|
||||
ui.reboot.style.display = "none";
|
||||
ui.boot.disabled = false;
|
||||
ui.boot.style.display = "inline";
|
||||
} else if (data.linode.status != "cloning") {
|
||||
ui.boot.style.display = "none";
|
||||
ui.reboot.disabled = false;
|
||||
ui.reboot.style.display = "inline";
|
||||
}
|
||||
|
||||
// Update storage info
|
||||
displayStorage();
|
||||
|
||||
// Set backup status
|
||||
if (data.linode.backups.enabled) {
|
||||
ui.backups.innerHTML = "Enabled!";
|
||||
ui.backups.className += " " + elements.backupsEnabled;
|
||||
} else {
|
||||
ui.backups.innerHTML = "";
|
||||
var text = document.createElement("span");
|
||||
text.innerHTML = "No - ";
|
||||
var backupLink = document.createElement("a");
|
||||
backupLink.href = "/linodes/backups_enable?lid=" + data.params.lid;
|
||||
backupLink.innerHTML = "Enable";
|
||||
backups.appendChild(text);
|
||||
backups.appendChild(backupLink);
|
||||
}
|
||||
|
||||
state.linodeRefresh = false;
|
||||
};
|
||||
|
||||
// Callback for disks API call
|
||||
var displayDisks = function(response)
|
||||
{
|
||||
// Add disks to array
|
||||
data.disks = data.disks.concat(response.data);
|
||||
|
||||
// Request the next page if there are more
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/linode/instances/" + data.params.lid + "/disks?page=" + (response.page + 1), displayDisks, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove loading row
|
||||
ui.loadingDisks.remove();
|
||||
|
||||
// Update storage info
|
||||
displayStorage();
|
||||
|
||||
// Insert disk rows into table
|
||||
ui.diskTable.innerHTML = "";
|
||||
for (var i = 0; i < data.disks.length; i++)
|
||||
ui.diskTable.appendChild(createDiskRow(data.disks[i]));
|
||||
|
||||
state.diskRefresh = false;
|
||||
};
|
||||
|
||||
// Callback for events API call
|
||||
var displayEvents = function(response)
|
||||
{
|
||||
// Only collect a max of 8 finished jobs
|
||||
for (var i = 0; i < response.data.length && state.eventsComplete < 8; i++) {
|
||||
if (response.data[i].status.match(/failed|finished|notification/))
|
||||
state.eventsComplete++;
|
||||
data.events.push(response.data[i]);
|
||||
}
|
||||
|
||||
// Request the next page if there are more
|
||||
if (state.eventsComplete < 8 && response.page != response.pages) {
|
||||
var filter = {
|
||||
"entity.type": "linode",
|
||||
"entity.id": parseInt(data.params.lid)
|
||||
};
|
||||
apiGet("/account/events?page=" + (response.page + 1), displayEvents, filter);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove loading row
|
||||
ui.loadingJobs.remove();
|
||||
|
||||
// Insert events into table and compute total progress
|
||||
var progress = 0;
|
||||
var totalProgress = 0;
|
||||
for (var i = 0; i < data.events.length; i++) {
|
||||
var extra = (data.events[i].status.match(/failed|finished|notification/) && i >= 4);
|
||||
ui.eventTable.appendChild(createEventRow(data.events[i], extra));
|
||||
if (!data.events[i].status.match(/failed|finished|notification/)) {
|
||||
totalProgress += 100;
|
||||
progress += data.events[i].percent_complete;
|
||||
// Set refresh timer for event
|
||||
window.setTimeout(getEvent, settings.refreshRate, data.events[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
// If there are ongoing events, show the progress bar
|
||||
if (totalProgress) {
|
||||
ui.jobProgress.max = totalProgress;
|
||||
ui.jobProgress.value = progress;
|
||||
ui.jobProgress.innerHTML = (progress / totalProgress * 100).toFixed(0) + "%";
|
||||
ui.jobProgress.title = ui.jobProgress.innerHTML;
|
||||
ui.jobProgressRow.style.display = "table-row";
|
||||
}
|
||||
};
|
||||
|
||||
// Callback for notifications API call
|
||||
var displayNotifications = function(response)
|
||||
{
|
||||
// Add notifications to array
|
||||
data.notifications = data.notifications.concat(response.data);
|
||||
|
||||
// Request the next page if there are more pages
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/account/notifications?page=" + (response.page + 1), displayNotifications, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Display notifications
|
||||
for (var i = 0; i < data.notifications.length; i++) {
|
||||
if (!data.notifications[i].entity || data.notifications[i].entity.type != "linode" || data.notifications[i].entity.id != data.params.lid)
|
||||
continue;
|
||||
|
||||
var notification = document.createElement("div");
|
||||
notification.className = elements.notification;
|
||||
var header = document.createElement("h1");
|
||||
header.innerHTML = data.notifications[i].label;
|
||||
notification.appendChild(header);
|
||||
if (data.notifications[i].type == "migration_pending") {
|
||||
var migrateButton = document.createElement("button");
|
||||
migrateButton.type = "button";
|
||||
migrateButton.innerHTML = "Migrate " + data.notifications[i].entity.label + " NOW";
|
||||
migrateButton.addEventListener("click", initiateMigration);
|
||||
notification.appendChild(migrateButton);
|
||||
}
|
||||
var body = document.createElement("p");
|
||||
body.innerHTML = data.notifications[i].message;
|
||||
notification.appendChild(body);
|
||||
ui.notifications.appendChild(notification);
|
||||
}
|
||||
};
|
||||
|
||||
// Show storage totals
|
||||
var displayStorage = function()
|
||||
{
|
||||
if (!data.linode.specs)
|
||||
return;
|
||||
|
||||
ui.storageTotal.innerHTML = data.linode.specs.disk + " MB";
|
||||
var storageUsed = 0;
|
||||
for (var i = 0; i < data.disks.length; i++)
|
||||
storageUsed += data.disks[i].size;
|
||||
ui.storageUsed.innerHTML = storageUsed + " MB";
|
||||
ui.storageFree.innerHTML = (data.linode.specs.disk - storageUsed) + " MB";
|
||||
ui.diskUsage.value = (storageUsed / data.linode.specs.disk * 100).toFixed(0);
|
||||
ui.diskUsage.innerHTML = ui.diskUsage.value + "%";
|
||||
ui.diskUsage.title = ui.diskUsage.innerHTML;
|
||||
};
|
||||
|
||||
// Callback for network transfer API call
|
||||
var displayTransfer = function(response)
|
||||
{
|
||||
data.linodeTransfer = response;
|
||||
|
||||
// Display network transfer info
|
||||
ui.transferMonthly.innerHTML = data.linodeTransfer.quota + " GB";
|
||||
ui.transferUsed.innerHTML = byteString(data.linodeTransfer.used);
|
||||
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 + "%";
|
||||
ui.netUsage.title = ui.netUsage.innerHTML;
|
||||
};
|
||||
|
||||
// Callback for volumes API call
|
||||
var displayVolumes = function(response)
|
||||
{
|
||||
// Add volumes to array
|
||||
data.volumes = data.volumes.concat(response.data);
|
||||
|
||||
// Request the next page if there are more
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/linode/instances/" + data.params.lid + "/volumes?page=" + (response.page + 1), displayVolumes, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove loading row
|
||||
ui.loadingVolumes.remove();
|
||||
|
||||
// Insert volume rows into table
|
||||
for (var i = 0; i < data.volumes.length; i++)
|
||||
ui.volumeTable.appendChild(createVolumeRow(data.volumes[i]));
|
||||
};
|
||||
|
||||
// Retrieve the given event from the API
|
||||
var getEvent = function(eventID)
|
||||
{
|
||||
apiGet("/account/events/" + eventID, updateEvent, null);
|
||||
};
|
||||
|
||||
// Get selected config profile
|
||||
var getSelectedConfig = function()
|
||||
{
|
||||
var configs = document.getElementsByClassName(elements.configRadioName);
|
||||
for (var i = 0; i < configs.length; i++) {
|
||||
if (configs[i].checked)
|
||||
return parseInt(configs[i].id.replace(elements.configRadioName + "-", ""));
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Button handler to initiate migration
|
||||
var initiateMigration = function(event)
|
||||
{
|
||||
if (event.currentTarget.disabled)
|
||||
return;
|
||||
|
||||
var req = {
|
||||
"upgrade": false
|
||||
};
|
||||
apiPost("/linode/instances/" + data.params.lid + "/migrate", req, function(response)
|
||||
{
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
// Button handler for reboot button
|
||||
var rebootLinode = function(event)
|
||||
{
|
||||
if (!confirm("Are you sure you want to issue a Reboot?"))
|
||||
return;
|
||||
|
||||
var config = getSelectedConfig();
|
||||
var request = {};
|
||||
if (config)
|
||||
request.config_id = config;
|
||||
|
||||
// Reload the page on successful API return
|
||||
var callback = function()
|
||||
{
|
||||
location.reload();
|
||||
};
|
||||
|
||||
apiPost("/linode/instances/" + data.params.lid + "/reboot", request, callback);
|
||||
};
|
||||
|
||||
// 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.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.volumeTable = document.getElementById(elements.volumeTable);
|
||||
|
||||
// Attach button handlers
|
||||
ui.boot.addEventListener("click", bootLinode);
|
||||
ui.moreJobs.addEventListener("click", toggleEvents);
|
||||
ui.reboot.addEventListener("click", rebootLinode);
|
||||
ui.shutDown.addEventListener("click", shutDownLinode);
|
||||
|
||||
// Get data from the API
|
||||
apiGet("/linode/instances/" + data.params.lid, displayDetails, null);
|
||||
apiGet("/linode/instances/" + data.params.lid + "/configs", displayConfigs, null);
|
||||
apiGet("/linode/instances/" + data.params.lid + "/disks", displayDisks, null);
|
||||
apiGet("/linode/instances/" + data.params.lid + "/volumes", displayVolumes, null);
|
||||
apiGet("/linode/instances/" + data.params.lid + "/transfer", displayTransfer, null);
|
||||
apiGet("/linode/instances/" + data.params.lid + "/backups", displayBackups, null);
|
||||
var filter = {
|
||||
"entity.type": "linode",
|
||||
"entity.id": parseInt(data.params.lid)
|
||||
};
|
||||
apiGet("/account/events", displayEvents, filter);
|
||||
apiGet("/account/notifications", displayNotifications, null);
|
||||
};
|
||||
|
||||
// Button handler for shutdown button
|
||||
var shutDownLinode = function(event)
|
||||
{
|
||||
if (!confirm("Are you sure you want to issue a Shutdown?"))
|
||||
return;
|
||||
|
||||
// Reload the page on successful API return
|
||||
var callback = function()
|
||||
{
|
||||
location.reload();
|
||||
};
|
||||
|
||||
apiPost("/linode/instances/" + data.params.lid + "/shutdown", {}, callback);
|
||||
};
|
||||
|
||||
// Toggle visibility of "extra" events
|
||||
var toggleEvents = function(event)
|
||||
{
|
||||
// Flip
|
||||
state.showExtraEvents = !state.showExtraEvents;
|
||||
|
||||
var display = "none";
|
||||
if (state.showExtraEvents)
|
||||
display = "table-row";
|
||||
|
||||
// Get extra events and show/hide them
|
||||
var extras = document.getElementsByClassName(elements.extraEvent);
|
||||
for (var i = 0; i < extras.length; i++)
|
||||
extras[i].style.display = display;
|
||||
};
|
||||
|
||||
// Update an existing event
|
||||
var updateEvent = function(event)
|
||||
{
|
||||
// Update the local copy of the event
|
||||
var progress = 0;
|
||||
for (var i = 0; i < data.events.length; i++) {
|
||||
if (data.events[i].id == event.id) {
|
||||
progress = event.percent_complete - data.events[i].percent_complete;
|
||||
// Refresh details if event status changed
|
||||
if (event.status != data.events[i].status && !state.linodeRefresh && !state.diskRefresh) {
|
||||
state.linodeRefresh = true;
|
||||
state.diskRefresh = true;
|
||||
data.linode = {};
|
||||
data.disks = [];
|
||||
apiGet("/linode/instances/" + data.params.lid, displayDetails, null);
|
||||
apiGet("/linode/instances/" + data.params.lid + "/disks", displayDisks, null);
|
||||
}
|
||||
data.events[i] = event;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new event row and replace the existing one
|
||||
var eventRow = document.getElementById(elements.eventRowPrefix + event.id);
|
||||
var extra = (eventRow.className.indexOf(elements.extraEvent) != -1);
|
||||
eventRow.replaceWith(createEventRow(event, extra));
|
||||
|
||||
// Update progress bar
|
||||
ui.jobProgress.value += progress;
|
||||
ui.jobProgress.innerHTML = (ui.jobProgress.value / ui.jobProgress.max * 100).toFixed(0) + "%";
|
||||
ui.jobProgress.title = ui.jobProgress.innerHTML;
|
||||
if (ui.jobProgress.value == ui.jobProgress.max)
|
||||
ui.jobProgressRow.style.display = "none";
|
||||
|
||||
// Refresh again if not complete
|
||||
if (!event.status.match(/failed|finished|notification/))
|
||||
window.setTimeout(getEvent, settings.refreshRate, event.id);
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
142
linodes/dashboard/index.shtml
Normal file
142
linodes/dashboard/index.shtml
Normal file
@ -0,0 +1,142 @@
|
||||
<!--
|
||||
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 - Dashboard</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="dashboard.css" />
|
||||
<script src="dashboard.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><span id="linode-label" class="top-links-title"></span></div>
|
||||
<div id="notifications"></div>
|
||||
<div id="linode-details">
|
||||
<table class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="3">Dashboard</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Select</td>
|
||||
<td>Configuration Profiles</td>
|
||||
<td>Options</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="config-table">
|
||||
<tr id="loading-configs" class="lmc-tr3">
|
||||
<td></td>
|
||||
<td>Loading configuration profiles...</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button id="boot" type="button" disabled>Boot</button>
|
||||
<button id="reboot" type="button">Reboot</button>
|
||||
<p class="sub-links sub-links-inline">
|
||||
<a href="/linodes/rebuild?lid=0">Rebuild</a> | <a href="/linodes/deploy?lid=0">Deploy an Image</a> | <a href="/linodes/config?cid=0&lid=0">Create a new Configuration Profile</a>
|
||||
</p>
|
||||
<table class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="3">Disks</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="disk-table">
|
||||
<tr id="loading-disks" class="lmc-tr3">
|
||||
<td></td>
|
||||
<td>Loading disks...</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="sub-links"><a href="/linodes/disk?did=0&lid=0">Create a new Disk</a></p>
|
||||
<table class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="3">Volumes</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="volume-table">
|
||||
<tr id="loading-volumes" class="lmc-tr3">
|
||||
<td></td>
|
||||
<td>Loading volumes...</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="sub-links">
|
||||
<a href="/volumes">View all Volumes</a> | <a href="/volumes/add?lid=0">Create a new Volume</a>
|
||||
</p>
|
||||
<table class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="2">Host Job Queue <a id="moar-jobs" href="#">(more)</a></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="event-table">
|
||||
<tr id="job-progress-row" class="lmc-tr1">
|
||||
<td colspan="2"><progress id="job-progress" max="100" value="50">50%</progress></td>
|
||||
</tr>
|
||||
<tr id="loading-jobs" class="lmc-tr3">
|
||||
<td></td>
|
||||
<td>Loading jobs...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Graphs</h3>
|
||||
</div>
|
||||
<div id="linode-sidebar">
|
||||
<div class="sidebar-box">
|
||||
<h4>Server Status</h4>
|
||||
<p>Your Linode is currently</p>
|
||||
<p id="server-status"></p>
|
||||
<button id="shut-down" type="button">Shut down</button>
|
||||
</div>
|
||||
<div class="sidebar-box">
|
||||
<h4>Network</h4>
|
||||
<ul>
|
||||
<li>Transfer/mo: <span id="transfer-monthly"></span></li>
|
||||
<li>Used: <span id="transfer-used"></span></li>
|
||||
<li>Overage: <span id="transfer-overage"></span></li>
|
||||
</ul>
|
||||
<p>Monthly transfer usage:</p>
|
||||
<meter id="net-usage" max="100" low="50" optimum="30" high="80" value="0"></meter>
|
||||
</div>
|
||||
<div class="sidebar-box">
|
||||
<h4>Storage</h4>
|
||||
<ul>
|
||||
<li>Total: <span id="storage-total"></span></li>
|
||||
<li>Used: <span id="storage-used"></span></li>
|
||||
<li>Free: <span id="storage-free"></span></li>
|
||||
</ul>
|
||||
<p>Storage allocation:</p>
|
||||
<meter id="disk-usage" max="100" low="50" optimum="30" high="80" value="0"></meter>
|
||||
</div>
|
||||
<div class="sidebar-box">
|
||||
<h4>Backups</h4>
|
||||
<p id="backups"></p>
|
||||
<p id="last-backup">Last backup was <span id="last-backup-time"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user