454 lines
13 KiB
JavaScript
454 lines
13 KiB
JavaScript
|
/*
|
|||
|
* 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, apiPost, parseParams, regionNames, setupHeader, timeString } from "/global.js";
|
|||
|
|
|||
|
(function()
|
|||
|
{
|
|||
|
// Element names specific to this page
|
|||
|
elements.backupAge = "backup-age";
|
|||
|
elements.backupConfigs = "backup-configs";
|
|||
|
elements.backupDate = "backup-date";
|
|||
|
elements.backupDisks = "backup-disks";
|
|||
|
elements.backupLabel = "backup-label";
|
|||
|
elements.backupLocation = "backup-location";
|
|||
|
elements.backupSize = "backup-size";
|
|||
|
elements.backupType = "backup-type";
|
|||
|
elements.destLabel = "dest-label";
|
|||
|
elements.destLinodes = "dest-linodes";
|
|||
|
elements.destLocation = "dest-location";
|
|||
|
elements.destPlan = "dest-plan";
|
|||
|
elements.destSpace = "dest-space";
|
|||
|
elements.linodeLabel = "linode-label";
|
|||
|
elements.linodeTag = "linode-tag";
|
|||
|
elements.linodeTagLink = "linode-tag-link";
|
|||
|
elements.loading = "loading-linodes";
|
|||
|
elements.lmcRow = "lmc-tr3";
|
|||
|
elements.newLinode = "new-linode";
|
|||
|
elements.restorePrefix = "restore-";
|
|||
|
elements.selectPrefix = "select-";
|
|||
|
elements.subnav = "subnav-link";
|
|||
|
elements.subnavActive = "subnav-link-active";
|
|||
|
elements.unallocatedPrefix = "unallocated-";
|
|||
|
|
|||
|
// Data recieved from API calls
|
|||
|
var data = {};
|
|||
|
data.backup = {};
|
|||
|
data.linode = {};
|
|||
|
data.linodes = [];
|
|||
|
data.types = [];
|
|||
|
|
|||
|
// Static references to UI elements
|
|||
|
var ui = {};
|
|||
|
ui.backupAge = {};
|
|||
|
ui.backupConfigs = {};
|
|||
|
ui.backupDate = {};
|
|||
|
ui.backupDisks = {};
|
|||
|
ui.backupLabel = {};
|
|||
|
ui.backupLocation = {};
|
|||
|
ui.backupSize = {};
|
|||
|
ui.backupType = {};
|
|||
|
ui.destLabel = {};
|
|||
|
ui.destLinodes = {};
|
|||
|
ui.destLocation = {};
|
|||
|
ui.destPlan = {};
|
|||
|
ui.destSpace = {};
|
|||
|
ui.linodeLabel = {};
|
|||
|
ui.linodeTag = {};
|
|||
|
ui.linodeTagLink = {};
|
|||
|
ui.loading = {};
|
|||
|
ui.newLinode = {};
|
|||
|
|
|||
|
// Temporary state
|
|||
|
var state = {};
|
|||
|
state.haveTypes = false;
|
|||
|
|
|||
|
// Generate a linode table row
|
|||
|
var createLinodeRow = function(linode)
|
|||
|
{
|
|||
|
var row = document.createElement("tr");
|
|||
|
row.className = elements.lmcRow;
|
|||
|
|
|||
|
var label = document.createElement("td");
|
|||
|
label.innerHTML = linode.label;
|
|||
|
row.appendChild(label);
|
|||
|
|
|||
|
var plan = document.createElement("td");
|
|||
|
if (linode.type) {
|
|||
|
for (var i = 0; i < data.types.length; i++) {
|
|||
|
if (data.types[i].id == linode.type) {
|
|||
|
plan.innerHTML = data.types[i].label;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
plan.innerHTML = "Unknown";
|
|||
|
}
|
|||
|
if (plan.innerHTML == "")
|
|||
|
translatePlan(linode.type, plan);
|
|||
|
row.appendChild(plan);
|
|||
|
|
|||
|
var location = document.createElement("td");
|
|||
|
if (regionNames[linode.region])
|
|||
|
location.innerHTML = regionNames[linode.region];
|
|||
|
else
|
|||
|
location.innerHTML = linode.region;
|
|||
|
row.appendChild(location);
|
|||
|
|
|||
|
var freeSpace = document.createElement("td");
|
|||
|
freeSpace.id = elements.unallocatedPrefix + linode.id;
|
|||
|
row.appendChild(freeSpace);
|
|||
|
|
|||
|
var select = document.createElement("td");
|
|||
|
select.id = elements.selectPrefix + linode.id;
|
|||
|
row.appendChild(select);
|
|||
|
|
|||
|
return row;
|
|||
|
};
|
|||
|
|
|||
|
// Callback for backup details API call
|
|||
|
var displayBackup = function(response)
|
|||
|
{
|
|||
|
data.backup = response;
|
|||
|
|
|||
|
// Populate info table
|
|||
|
if (data.backup.label)
|
|||
|
ui.backupLabel.innerHTML = data.backup.label;
|
|||
|
|
|||
|
var now = new Date();
|
|||
|
var backupStart = new Date(data.backup.created + "Z");
|
|||
|
ui.backupDate.innerHTML = backupStart.toLocaleString();
|
|||
|
ui.backupAge.innerHTML = timeString(now - backupStart, true);
|
|||
|
|
|||
|
ui.backupType.innerHTML = data.backup.type;
|
|||
|
|
|||
|
for (var i = 0; i < data.backup.configs.length; i++) {
|
|||
|
var li = document.createElement("li");
|
|||
|
li.innerHTML = data.backup.configs[i];
|
|||
|
ui.backupConfigs.appendChild(li);
|
|||
|
}
|
|||
|
|
|||
|
data.backup.totalSize = 0;
|
|||
|
for (var i = 0; i < data.backup.disks.length; i++) {
|
|||
|
data.backup.totalSize += data.backup.disks[i].size;
|
|||
|
var li = document.createElement("li");
|
|||
|
li.innerHTML = data.backup.disks[i].label + " (" + data.backup.disks[i].filesystem + ") – " + data.backup.disks[i].size + "MB";
|
|||
|
ui.backupDisks.appendChild(li);
|
|||
|
}
|
|||
|
|
|||
|
ui.backupSize.innerHTML = data.backup.totalSize + " MB";
|
|||
|
|
|||
|
if (state.haveTypes && data.linode.id)
|
|||
|
insertTypes();
|
|||
|
};
|
|||
|
|
|||
|
// 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";
|
|||
|
}
|
|||
|
|
|||
|
// Display location
|
|||
|
if (regionNames[data.linode.region]) {
|
|||
|
ui.backupLocation.innerHTML = regionNames[data.linode.region];
|
|||
|
ui.destLocation.innerHTML = regionNames[data.linode.region];
|
|||
|
} else {
|
|||
|
ui.backupLocation.innerHTML = data.linode.region;
|
|||
|
ui.destLocation.innerHTML = data.linode.region;
|
|||
|
}
|
|||
|
|
|||
|
if (state.haveTypes && data.backup.id)
|
|||
|
insertTypes();
|
|||
|
};
|
|||
|
|
|||
|
// Callback for linode disks API call
|
|||
|
var displayDisks = function(response)
|
|||
|
{
|
|||
|
// Find the linode this response is for
|
|||
|
var lid = parseInt(response['_endpoint'].split("/")[3]);
|
|||
|
var index = -1;
|
|||
|
for (var i = 0; i < data.linodes.length; i++) {
|
|||
|
if (data.linodes[i].id == lid) {
|
|||
|
index = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (index == -1)
|
|||
|
return;
|
|||
|
|
|||
|
// Add disks to array
|
|||
|
data.linodes[index].disks = data.linodes[index].disks.concat(response.data);
|
|||
|
|
|||
|
// Request the next page if there are more pages
|
|||
|
if (response.page != response.pages) {
|
|||
|
apiGet("/linode/instances/" + data.linodes[index].id + "/disks?page=" + (response.page + 1), displayDisks, null);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Calculate Linode's free space
|
|||
|
var free = data.linodes[index].specs.disk;
|
|||
|
for (var i = 0; i < data.linodes[index].disks.length; i++)
|
|||
|
free -= data.linodes[index].disks[i].size;
|
|||
|
|
|||
|
// Update table
|
|||
|
var freeCell = document.getElementById(elements.unallocatedPrefix + data.linodes[index].id);
|
|||
|
var selectCell = document.getElementById(elements.selectPrefix + data.linodes[index].id);
|
|||
|
freeCell.innerHTML = free + " MB";
|
|||
|
if (free >= data.backup.totalSize) {
|
|||
|
var restoreLink = document.createElement("a");
|
|||
|
restoreLink.id = elements.restorePrefix + data.linodes[index].id;
|
|||
|
restoreLink.href = "#";
|
|||
|
restoreLink.innerHTML = "Restore to this Linode";
|
|||
|
restoreLink.addEventListener("click", handleRestore);
|
|||
|
selectCell.appendChild(restoreLink);
|
|||
|
} else {
|
|||
|
selectCell.innerHTML = "--- not enough free space ---";
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// Callback for linodes API call
|
|||
|
var displayLinodes = function(response)
|
|||
|
{
|
|||
|
// Add linodes to array
|
|||
|
data.linodes = data.linodes.concat(response.data);
|
|||
|
|
|||
|
// Request the next page if there are more pages
|
|||
|
if (response.page != response.pages) {
|
|||
|
var filter = {
|
|||
|
"region": data.linode.region
|
|||
|
};
|
|||
|
apiGet("/linode/instances?page=" + (response.page + 1), displayLinodes, filter);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ui.loading.remove();
|
|||
|
for (var i = 0; i < data.linodes.length; i++) {
|
|||
|
// Create row in the table
|
|||
|
ui.destLinodes.appendChild(createLinodeRow(data.linodes[i]));
|
|||
|
// Get the linode's disks
|
|||
|
data.linodes[i].disks = [];
|
|||
|
apiGet("/linode/instances/" + data.linodes[i].id + "/disks", displayDisks, null);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// Callback for linode types API call
|
|||
|
var displayTypes = function(response)
|
|||
|
{
|
|||
|
// Add types to array
|
|||
|
data.types = data.types.concat(response.data);
|
|||
|
|
|||
|
// Request the next page if there are more pages
|
|||
|
if (response.page != response.pages) {
|
|||
|
apiGet("/linode/types?page=" + (response.page + 1), displayTypes, null);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
state.haveTypes = true;
|
|||
|
if (data.backup.id && data.linode.id)
|
|||
|
insertTypes();
|
|||
|
};
|
|||
|
|
|||
|
// Handler for create linode button
|
|||
|
var handleCreate = function(event)
|
|||
|
{
|
|||
|
if (event.currentTarget.disabled)
|
|||
|
return;
|
|||
|
|
|||
|
// Find the selected type
|
|||
|
var index = -1;
|
|||
|
for (var i = 0; i < data.types.length; i++) {
|
|||
|
if (data.types[i].id == ui.destPlan.value) {
|
|||
|
index = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (index == -1)
|
|||
|
return;
|
|||
|
|
|||
|
if (!confirm("Create a new " + data.types[i].label + " from this backup? This new instance will cost $" + data.types[i].price.monthly.toFixed(2) + " per month and backups will be enabled per your global account setting."))
|
|||
|
return;
|
|||
|
|
|||
|
var req = {
|
|||
|
"label": ui.destLabel.value,
|
|||
|
"type": ui.destPlan.value,
|
|||
|
"region": data.linode.region,
|
|||
|
"backup_id": data.backup.id
|
|||
|
};
|
|||
|
var callback = function(response)
|
|||
|
{
|
|||
|
location.href = "/linodes/dashboard?lid=" + response.id;
|
|||
|
};
|
|||
|
apiPost("/linode/instances", req, callback);
|
|||
|
};
|
|||
|
|
|||
|
// Handler for backup restore links
|
|||
|
var handleRestore = function(event)
|
|||
|
{
|
|||
|
// Find the linode associated with this link
|
|||
|
var lid = parseInt(event.currentTarget.id.replace(elements.restorePrefix, ""));
|
|||
|
var index = -1;
|
|||
|
for (var i = 0; i < data.linodes.length; i++) {
|
|||
|
if (data.linodes[i].id == lid) {
|
|||
|
index = i;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (index == -1)
|
|||
|
return;
|
|||
|
if (!confirm("Restore to Linode " + data.linodes[index].label + "?"))
|
|||
|
return;
|
|||
|
|
|||
|
var req = {
|
|||
|
"linode_id": lid,
|
|||
|
"overwrite": false
|
|||
|
};
|
|||
|
|
|||
|
var callback = function(response)
|
|||
|
{
|
|||
|
location.href = "/linodes/dashboard?lid=" + lid;
|
|||
|
};
|
|||
|
apiPost("/linode/instances/" + data.params.lid + "/backups/" + data.backup.id + "/restore", req, callback);
|
|||
|
};
|
|||
|
|
|||
|
// Insert linode types into selector
|
|||
|
var insertTypes = function()
|
|||
|
{
|
|||
|
for (var i = 0; i < data.types.length; i++) {
|
|||
|
if (data.types[i].disk < data.backup.totalSize)
|
|||
|
continue;
|
|||
|
|
|||
|
var option = document.createElement("option");
|
|||
|
option.value = data.types[i].id;
|
|||
|
option.innerHTML = data.types[i].label;
|
|||
|
ui.destPlan.appendChild(option);
|
|||
|
}
|
|||
|
|
|||
|
updatePrice(null);
|
|||
|
ui.newLinode.disabled = false;
|
|||
|
|
|||
|
var filter = {
|
|||
|
"region": data.linode.region
|
|||
|
};
|
|||
|
apiGet("/linode/instances", displayLinodes, filter);
|
|||
|
};
|
|||
|
|
|||
|
// 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;
|
|||
|
}
|
|||
|
|
|||
|
// We also need a backup ID
|
|||
|
if (!data.params.bid) {
|
|||
|
alert("No Backup 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);
|
|||
|
|
|||
|
// Highlight the backups subnav link
|
|||
|
var subnavLinks = document.getElementsByClassName(elements.subnav);
|
|||
|
for (var i = 0; i < subnavLinks.length; i++) {
|
|||
|
if (subnavLinks[i].pathname == "/linodes/backups")
|
|||
|
subnavLinks[i].className += " " + elements.subnavActive;
|
|||
|
}
|
|||
|
|
|||
|
// Get element references
|
|||
|
ui.backupAge = document.getElementById(elements.backupAge);
|
|||
|
ui.backupConfigs = document.getElementById(elements.backupConfigs);
|
|||
|
ui.backupDate = document.getElementById(elements.backupDate);
|
|||
|
ui.backupDisks = document.getElementById(elements.backupDisks);
|
|||
|
ui.backupLabel = document.getElementById(elements.backupLabel);
|
|||
|
ui.backupLocation = document.getElementById(elements.backupLocation);
|
|||
|
ui.backupSize = document.getElementById(elements.backupSize);
|
|||
|
ui.backupType = document.getElementById(elements.backupType);
|
|||
|
ui.destLabel = document.getElementById(elements.destLabel);
|
|||
|
ui.destLinodes = document.getElementById(elements.destLinodes);
|
|||
|
ui.destLocation = document.getElementById(elements.destLocation);
|
|||
|
ui.destPlan = document.getElementById(elements.destPlan);
|
|||
|
ui.destSpace = document.getElementById(elements.destSpace);
|
|||
|
ui.linodeLabel = document.getElementById(elements.linodeLabel);
|
|||
|
ui.linodeTag = document.getElementById(elements.linodeTag);
|
|||
|
ui.linodeTagLink = document.getElementById(elements.linodeTagLink);
|
|||
|
ui.loading = document.getElementById(elements.loading);
|
|||
|
ui.newLinode = document.getElementById(elements.newLinode);
|
|||
|
|
|||
|
// Register event handlers
|
|||
|
ui.destPlan.addEventListener("input", updatePrice);
|
|||
|
ui.newLinode.addEventListener("click", handleCreate);
|
|||
|
|
|||
|
// Get data from API
|
|||
|
apiGet("/linode/instances/" + data.params.lid, displayDetails, null);
|
|||
|
apiGet("/linode/instances/" + data.params.lid + "/backups/" + data.params.bid, displayBackup, null);
|
|||
|
apiGet("/linode/types", displayTypes, null);
|
|||
|
};
|
|||
|
|
|||
|
// Update the given element with the given type's label
|
|||
|
var translatePlan = function(type, el)
|
|||
|
{
|
|||
|
var callback = function(response)
|
|||
|
{
|
|||
|
el.innerHTML = response.label;
|
|||
|
};
|
|||
|
|
|||
|
apiGet("/linode/types/" + type, callback, null);
|
|||
|
};
|
|||
|
|
|||
|
// Update the price display
|
|||
|
var updatePrice = function(event)
|
|||
|
{
|
|||
|
// Find the selected type
|
|||
|
var type = null;
|
|||
|
for (var i = 0; i < data.types.length; i++) {
|
|||
|
if (data.types[i].id == ui.destPlan.value) {
|
|||
|
type = data.types[i];
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!type)
|
|||
|
return;
|
|||
|
|
|||
|
ui.destSpace.innerHTML = type.disk;
|
|||
|
};
|
|||
|
|
|||
|
// Attach onload handler
|
|||
|
window.addEventListener("load", setup);
|
|||
|
})();
|