Implemented plan upgrades

This commit is contained in:
L. Bradley LaBoon 2020-01-22 21:01:47 -05:00
parent b4bf0aad4b
commit e8aaa6d416
7 changed files with 458 additions and 0 deletions

BIN
img/lv-upgrade.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -194,6 +194,18 @@ h3 {
float: right;
}
#upgrade {
background-color: #F0F0F0;
display: none;
margin: 0 15px 20px;
padding: 5px;
text-align: center;
}
#upgrade p {
margin: 0;
}
#uptime {
display: none;
}

View File

@ -67,6 +67,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
elements.transferMonthly = "transfer-monthly";
elements.transferOverage = "transfer-overage";
elements.transferUsed = "transfer-used";
elements.upgrade = "upgrade";
elements.volumeTable = "volume-table";
// Data recieved from API calls
@ -78,6 +79,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
data.linode = {};
data.linodeTransfer = {};
data.notifications = [];
data.plan = {};
data.volumes = [];
// Static references to UI elements
@ -111,6 +113,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
ui.transferMonthly = {};
ui.transferOverage = {};
ui.transferUsed = {};
ui.upgrade = {};
ui.volumeTable = {};
// Temporary state
@ -524,6 +527,10 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
backups.appendChild(backupLink);
}
// Get plan info
if (data.linode.type)
apiGet("/linode/types/" + data.linode.type, displayPlan, null);
state.linodeRefresh = false;
};
@ -636,6 +643,16 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
}
};
// Callback for linode plan API call
var displayPlan = function(response)
{
data.plan = response;
// Display the upgrade banner if one is available
if (data.plan.successor)
ui.upgrade.style.display = "block";
};
// Show storage totals
var displayStorage = function()
{
@ -789,6 +806,7 @@ import { settings, elements, apiDelete, apiGet, apiPost, eventTitles, parseParam
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);
// Attach button handlers

View File

@ -28,6 +28,11 @@ along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
<!--#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="upgrade">
<img src="/img/lv-upgrade.png" alt="upgrade" />
<h2>A free upgrade is available for this Linode!</h2>
<p><a href="/linodes/mutate?lid=0">Click here for more information!</a></p>
</div>
<div id="notifications"></div>
<div id="linode-details">
<table class="lmc-table">

View File

@ -0,0 +1,76 @@
<!--
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 - Pending Upgrades!</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" type="text/css" href="mutate.css" />
<script src="mutate.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="mutate">
<table class="lmc-table">
<thead>
<tr>
<td colspan="2">Pending upgrades!</td>
</tr>
</thead>
<tbody>
<tr class="lmc-tr3">
<td>
<h2>This Linode has pending upgrades:</h2>
Resources affected:
<ul>
<li id="ram"><strong>RAM</strong> goes from <span id="ram-current"></span> GB → <strong><span id="ram-new"></span> GB</strong></li>
<li id="storage"><strong>Storage</strong> goes from <span id="storage-current"></span> GB → <strong><span id="storage-new"></span> GB</strong></li>
<li id="cpu"><strong>vCPUs</strong> go from <span id="cpu-current"></span> vCPUs → <strong><span id="cpu-new"></span> vCPUs</strong></li>
<li id="gpu"><strong>GPUs</strong> go from <span id="gpu-current"></span> GPUs → <strong><span id="gpu-new"></span> GPUs</strong></li>
<li id="mbit"><strong>Outbound Mbits</strong> goes from <span id="mbit-current"></span> Mbits → <strong><span id="mbit-new"></span> Mbits</strong></li>
<li id="xfer"><strong>Outbound transfer</strong> goes from <span id="xfer-current"></span> MB → <strong><span id="xfer-new"></span> MB</strong></li>
</ul>
<span id="resize"><input disabled id="resize-box" type="checkbox" /><label for="resize-box">Automatically resize disk "<span id="disk-name"></span>"</label></span><br />
<button disabled id="upgrade-button" type="button">Enter the Upgrade Queue NOW</button>
</td>
<td>
<h2>How it works</h2>
<ol>
<li><strong>Click button</strong></li>
<li><strong>Wait turn in upgrade queue</strong></li>
<li>
<strong>Linode is shut down and its disk images are migrated</strong><br />
You will experience downtime while your Linode is migrated. It will take around <span id="eta"></span> to migrate your Linode, but that may vary based on host and network load.
</li>
<li><strong>Linode upgraded and booted</strong><span id="tiny"> (if it was running)</span></li>
<li>
<strong>Enjoyment</strong><br />
After the migration completes, you can take advantage of the new resources by resizing your disk images.
</li>
</ol>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>

78
linodes/mutate/mutate.css Normal file
View File

@ -0,0 +1,78 @@
/*
* 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');
button {
margin-top: 10px;
}
#eta {
color: #333;
font-weight: bold;
}
h2 {
margin-top: 0;
}
.lmc-tr3 {
border-bottom: none;
}
#mutate {
padding: 0px 15px 15px;
}
ol {
font-weight: bold;
}
ol li {
font-weight: normal;
margin-bottom: 10px;
}
#resize {
display: none;
}
tbody td {
color: #666;
font-size: 14px;
vertical-align: top;
}
tbody td:last-child {
width: 50%;
}
tbody td h2 {
color: #333;
}
tbody td strong {
color: #333;
}
#tiny {
font-size: 12px;
}
ul li {
display: none;
}

269
linodes/mutate/mutate.js Normal file
View File

@ -0,0 +1,269 @@
/*
* 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, migrateETA, parseParams, setupHeader } from "/global.js";
(function()
{
// Element names specific to this page
elements.cpu = "cpu";
elements.cpuCurrent = "cpu-current";
elements.cpuNew = "cpu-new";
elements.diskName = "disk-name";
elements.eta = "eta";
elements.gpu = "gpu";
elements.gpuCurrent = "gpu-current";
elements.gpuNew = "gpu-new";
elements.linodeLabel = "linode-label";
elements.linodeTag = "linode-tag";
elements.linodeTagLink = "linode-tag-link";
elements.mbit = "mbit";
elements.mbitCurrent = "mbit-current";
elements.mbitNew = "mbit-new";
elements.ram = "ram";
elements.ramCurrent = "ram-current";
elements.ramNew = "ram-new";
elements.resize = "resize";
elements.resizeBox = "resize-box";
elements.storage = "storage";
elements.storageCurrent = "storage-current";
elements.storageNew = "storage-new";
elements.upgradeButton = "upgrade-button";
elements.xfer = "xfer";
elements.xferCurrent = "xfer-current";
elements.xferNew = "xfer-new";
// Data recieved from API calls
var data = {};
data.disks = [];
data.linode = {};
data.plan = {};
data.successor = {};
// Static references to UI elements
var ui = {};
ui.cpu = {};
ui.cpuCurrent = {};
ui.cpuNew = {};
ui.diskName = {};
ui.eta = {};
ui.gpu = {};
ui.gpuCurrent = {};
ui.gpuNew = {};
ui.linodeLabel = {};
ui.linodeTag = {};
ui.linodeTagLink = {};
ui.mbit = {};
ui.mbitCurrent = {};
ui.mbitNew = {};
ui.ram = {};
ui.ramCurrent = {};
ui.ramNew = {};
ui.resize = {};
ui.resizeBox = {};
ui.storage = {};
ui.storageCurrent = {};
ui.storageNew = {};
ui.upgradeButton = {};
ui.xfer = {};
ui.xferCurrent = {};
ui.xferNew = {};
// 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";
}
// Get plan info
apiGet("/linode/types/" + data.linode.type, displayPlan, null);
};
// Callback for linode 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 pages
if (response.page != response.pages) {
apiGet("/linode/instances/" + data.params.lid + "/disks?page=" + (response.page + 1), displayDisks, null);
return;
}
// Compute total size and types of disks
var size = 0;
var numExt = 0;
var numSwap = 0;
for (var i = 0; i < data.disks.length; i++) {
size += data.disks[i].size;
if (data.disks[i].filesystem.match(/ext3|ext4/)) {
numExt++;
ui.diskName.innerHTML = data.disks[i].label;
}
else if (data.disks[i].filesystem == "swap")
numSwap++;
}
// Compute estimated resize time
ui.eta.innerHTML = migrateETA(size, true);
// Show auto-resize option if there is only a single ext disk
if ((data.disks.length == 1 && numExt == 1) || (data.disks.length == 2 && numExt == 1 && numSwap == 1)) {
ui.resizeBox.disabled = false;
ui.resizeBox.checked = true;
ui.resize.style.display = "initial";
}
};
// Callback for plan details API call
var displayPlan = function(response)
{
data.plan = response;
// Redirect to dashboard if there is no upgrade
if (!data.plan.successor) {
location.href = "/linodes/dashboard?lid=" + data.params.lid;
return;
}
// Get successor
apiGet("/linode/types/" + data.plan.successor, displaySuccessor, null);
};
// Callback for successor details API call
var displaySuccessor = function(response)
{
data.successor = response;
// Set UI stuff
if (data.plan.memory != data.successor.memory) {
ui.ramCurrent.innerHTML = data.plan.memory / 1024;
ui.ramNew.innerHTML = data.successor.memory / 1024;
ui.ram.style.display = "list-item";
}
if (data.plan.disk != data.successor.disk) {
ui.storageCurrent.innerHTML = data.plan.disk / 1024;
ui.storageNew.innerHTML = data.successor.disk / 1024;
ui.storage.style.display = "list-item";
}
if (data.plan.vcpus != data.successor.vcpus) {
ui.cpuCurrent.innerHTML = data.plan.vcpus;
ui.cpuNew.innerHTML = data.successor.vcpus;
ui.cpu.style.display = "list-item";
}
if (data.plan.gpus != data.successor.gpus) {
ui.gpuCurrent.innerHTML = data.plan.gpus;
ui.gpuNew.innerHTML = data.successor.gpus;
ui.gpu.style.display = "list-item";
}
if (data.plan.network_out != data.successor.network_out) {
ui.mbitCurrent.innerHTML = data.plan.network_out;
ui.mbitNew.innerHTML = data.successor.network_out;
ui.mbit.style.display = "list-item";
}
if (data.plan.transfer != data.successor.transfer) {
ui.xferCurrent.innerHTML = data.plan.transfer;
ui.xferNew.innerHTML = data.successor.transfer;
ui.xfer.style.display = "list-item";
}
ui.upgradeButton.disabled = false;
};
// Click handler for upgrade button
var handleUpgrade = function(event)
{
if (event.currentTarget.disabled)
return;
var req = {
"allow_auto_disk_resize": (!ui.resizeBox.disabled && ui.resizeBox.checked)
};
apiPost("/linode/instances/" + data.params.lid + "/mutate", req, function(response)
{
location.href = "/linodes/dashboard?lid=" + data.params.lid;
});
};
// 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.cpu = document.getElementById(elements.cpu);
ui.cpuCurrent = document.getElementById(elements.cpuCurrent);
ui.cpuNew = document.getElementById(elements.cpuNew);
ui.diskName = document.getElementById(elements.diskName);
ui.eta = document.getElementById(elements.eta);
ui.gpu = document.getElementById(elements.gpu);
ui.gpuCurrent = document.getElementById(elements.gpuCurrent);
ui.gpuNew = document.getElementById(elements.gpuNew);
ui.linodeLabel = document.getElementById(elements.linodeLabel);
ui.linodeTag = document.getElementById(elements.linodeTag);
ui.linodeTagLink = document.getElementById(elements.linodeTagLink);
ui.mbit = document.getElementById(elements.mbit);
ui.mbitCurrent = document.getElementById(elements.mbitCurrent);
ui.mbitNew = document.getElementById(elements.mbitNew);
ui.ram = document.getElementById(elements.ram);
ui.ramCurrent = document.getElementById(elements.ramCurrent);
ui.ramNew = document.getElementById(elements.ramNew);
ui.resize = document.getElementById(elements.resize);
ui.resizeBox = document.getElementById(elements.resizeBox);
ui.storage = document.getElementById(elements.storage);
ui.storageCurrent = document.getElementById(elements.storageCurrent);
ui.storageNew = document.getElementById(elements.storageNew);
ui.upgradeButton = document.getElementById(elements.upgradeButton);
ui.xfer = document.getElementById(elements.xfer);
ui.xferCurrent = document.getElementById(elements.xferCurrent);
ui.xferNew = document.getElementById(elements.xferNew);
// Attach event handlers
ui.upgradeButton.addEventListener("click", handleUpgrade);
// Get data from API
apiGet("/linode/instances/" + data.params.lid, displayDetails, null);
apiGet("/linode/instances/" + data.params.lid + "/disks", displayDisks, null);
};
// Attach onload handler
window.addEventListener("load", setup);
})();