Implement NodeBalancers
This commit is contained in:
72
nodebalancers/add/add.css
Normal file
72
nodebalancers/add/add.css
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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');
|
||||
|
||||
#add {
|
||||
padding: 0px 15px 15px;
|
||||
}
|
||||
|
||||
#add-button {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin: 0 auto;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#datacenters {
|
||||
font-size: 18px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#location {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
optgroup {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#submit {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
tbody td {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
tbody td:nth-of-type(2) {
|
||||
color: green;
|
||||
}
|
||||
|
||||
td {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td:first-of-type {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
thead {
|
||||
background: linear-gradient(#00EE00, #00BF00);
|
||||
}
|
147
nodebalancers/add/add.js
Normal file
147
nodebalancers/add/add.js
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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, countryContinents, parseParams, regionNames, setupHeader } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
// Element names specific to this page
|
||||
elements.addButton = "add-button";
|
||||
elements.datacenters = "datacenters";
|
||||
elements.dcOther = "dc-other";
|
||||
elements.hourly = "hourly";
|
||||
elements.monthly = "monthly";
|
||||
|
||||
// Data recieved from API calls
|
||||
var data = {};
|
||||
data.linodes = [];
|
||||
data.regions = [];
|
||||
|
||||
// Static references to UI elements
|
||||
var ui = {};
|
||||
ui.addButton = {};
|
||||
ui.datacenters = {};
|
||||
ui.dcOther = {};
|
||||
ui.hourly = {};
|
||||
ui.monthly = {};
|
||||
|
||||
// Callback for regions API call
|
||||
var displayRegions = function(response)
|
||||
{
|
||||
// Add regions that support block storage to array
|
||||
for (var i = 0; i < response.data.length; i++) {
|
||||
if (response.data[i].capabilities.includes("NodeBalancers"))
|
||||
data.regions.push(response.data[i]);
|
||||
}
|
||||
|
||||
// Request the next page if there are more pages
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/regions?page=" + (response.page + 1), displayRegions, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add regions to selector
|
||||
for (var i = 0; i < data.regions.length; i++) {
|
||||
// Regional pricing
|
||||
if (data.regions[i].id == "id-cgk") {
|
||||
data.regions[i].hourly = "$0.018/hr";
|
||||
data.regions[i].monthly = "$12.00/mo";
|
||||
} else if (data.regions[i].id == "br-gru") {
|
||||
data.regions[i].hourly = "$0.021/hr";
|
||||
data.regions[i].monthly = "$14.00/mo";
|
||||
} else {
|
||||
data.regions[i].hourly = "$0.015/hr";
|
||||
data.regions[i].monthly = "$10.00/mo";
|
||||
}
|
||||
|
||||
var loc = document.createElement("option");
|
||||
loc.value = data.regions[i].id;
|
||||
if (data.regions[i].label && data.regions[i].label.length)
|
||||
loc.innerHTML = data.regions[i].label;
|
||||
else if (regionNames[data.regions[i].id])
|
||||
loc.innerHTML = regionNames[data.regions[i].id];
|
||||
else
|
||||
loc.innerHTML = data.regions[i].id;
|
||||
var optgroup = null;
|
||||
if (countryContinents[data.regions[i].country])
|
||||
optgroup = document.getElementById(countryContinents[data.regions[i].country]);
|
||||
if (!optgroup)
|
||||
optgroup = ui.dcOther;
|
||||
optgroup.style.display = "initial";
|
||||
optgroup.appendChild(loc);
|
||||
}
|
||||
|
||||
ui.addButton.disabled = false;
|
||||
};
|
||||
|
||||
// Click handler for add button
|
||||
var handleAdd = function(event)
|
||||
{
|
||||
if (event.currentTarget.disabled)
|
||||
return;
|
||||
|
||||
var req = {
|
||||
"region": ui.datacenters.value
|
||||
};
|
||||
if (data.params.tag)
|
||||
req.tags = [data.params.tag];
|
||||
|
||||
apiPost("/nodebalancers", req, function(response)
|
||||
{
|
||||
location.href = "/nodebalancers/balancer?nbid=" + response.id;
|
||||
});
|
||||
};
|
||||
|
||||
// Location select handler
|
||||
var handleLocation = function(event)
|
||||
{
|
||||
// Update prices based on location
|
||||
for (var i = 0; i < data.regions.length; i++) {
|
||||
if (data.regions[i].id == ui.datacenters.value) {
|
||||
ui.hourly.innerHTML = data.regions[i].hourly;
|
||||
ui.monthly.innerHTML = data.regions[i].monthly;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
var setup = function()
|
||||
{
|
||||
// Parse URL parameters
|
||||
data.params = parseParams();
|
||||
|
||||
setupHeader();
|
||||
|
||||
// Get element references
|
||||
ui.addButton = document.getElementById(elements.addButton);
|
||||
ui.datacenters = document.getElementById(elements.datacenters);
|
||||
ui.dcOther = document.getElementById(elements.dcOther);
|
||||
ui.hourly = document.getElementById(elements.hourly);
|
||||
ui.monthly = document.getElementById(elements.monthly);
|
||||
|
||||
// Attach event handlers
|
||||
ui.addButton.addEventListener("click", handleAdd);
|
||||
ui.datacenters.addEventListener("input", handleLocation);
|
||||
|
||||
// Get data from API
|
||||
apiGet("/regions", displayRegions, null);
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
66
nodebalancers/add/index.shtml
Normal file
66
nodebalancers/add/index.shtml
Normal file
@ -0,0 +1,66 @@
|
||||
<!--
|
||||
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 - Add a NodeBalancer</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="add.css" />
|
||||
<script src="add.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--#include virtual="/include/header.html"-->
|
||||
<div id="main-content" class="wrapper">
|
||||
<div id="top-links"><a href="/nodebalancers">NodeBalancers</a> » <span class="top-links-title">Add a NodeBalancer</span></div>
|
||||
<div id="add">
|
||||
<table class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Plan</td>
|
||||
<td>Hourly</td>
|
||||
<td>Monthly</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="lmc-tr3">
|
||||
<td><input checked type="radio" name="plan" /></td>
|
||||
<td>NodeBalancer</td>
|
||||
<td id="hourly">$0.015/hr</td>
|
||||
<td id="monthly">$10.00/mo</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="location">
|
||||
<h2>Location</h2>
|
||||
<select id="datacenters">
|
||||
<optgroup id="na" label="North America"></optgroup>
|
||||
<optgroup id="eu" label="Europe"></optgroup>
|
||||
<optgroup id="ap" label="Asia/Pacific"></optgroup>
|
||||
<optgroup id="sa" label="South America"></optgroup>
|
||||
<optgroup id="af" label="Africa"></optgroup>
|
||||
<optgroup id="dc-other" label="Other"></optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div id="submit">
|
||||
<button disabled id="add-button" type="button">Add this NodeBalancer!</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
62
nodebalancers/balancer/balancer.css
Normal file
62
nodebalancers/balancer/balancer.css
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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');
|
||||
|
||||
canvas {
|
||||
height: 323px;
|
||||
}
|
||||
|
||||
.center-cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#cxn-color {
|
||||
background-color: #906;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#nodebalancer-details {
|
||||
padding: 0px 15px 20px;
|
||||
}
|
||||
|
||||
#settings-table tr td:first-of-type {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#settings-table tr td:not(:last-of-type) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#traffic-in-color {
|
||||
background-color: #03C;
|
||||
}
|
||||
|
||||
#traffic-out-color {
|
||||
background-color: #32CD32;
|
||||
}
|
411
nodebalancers/balancer/balancer.js
Normal file
411
nodebalancers/balancer/balancer.js
Normal file
@ -0,0 +1,411 @@
|
||||
/*
|
||||
* 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, apiPut, countSI, drawSeries, parseParams, regionNames, setupHeader } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
// Element names specific to this page
|
||||
elements.centerCell = "center-cell";
|
||||
elements.configTable = "config-table";
|
||||
elements.cxnAvg = "cxn-avg";
|
||||
elements.cxnGraph = "cxn-graph";
|
||||
elements.cxnLast = "cxn-last";
|
||||
elements.cxnMax = "cxn-max";
|
||||
elements.hostname = "hostname";
|
||||
elements.inputLabel = "input-label";
|
||||
elements.ipv4 = "ipv4";
|
||||
elements.ipv6 = "ipv6";
|
||||
elements.lmcRow = "lmc-tr1";
|
||||
elements.lmcRowAlt = "lmc-tr2";
|
||||
elements.loadingConfigs = "loading-configs";
|
||||
elements.location = "location";
|
||||
elements.nodebalancerLabel = "nodebalancer-label";
|
||||
elements.nodebalancerTag = "nodebalancer-tag";
|
||||
elements.nodebalancerTagLink = "nodebalancer-tag-link";
|
||||
elements.removePrefix = "remove-config-";
|
||||
elements.saveButton = "save-button";
|
||||
elements.tableLabel = "table-label";
|
||||
elements.tags = "tags";
|
||||
elements.throttle = "throttle";
|
||||
elements.trafficGraph = "traffic-graph";
|
||||
elements.trafficInAvg = "traffic-in-avg";
|
||||
elements.trafficInLast = "traffic-in-last";
|
||||
elements.trafficInMax = "traffic-in-max";
|
||||
elements.trafficOutAvg = "traffic-out-avg";
|
||||
elements.trafficOutLast = "traffic-out-last";
|
||||
elements.trafficOutMax = "traffic-out-max";
|
||||
elements.transferred = "transferred";
|
||||
|
||||
// Data recieved from API calls
|
||||
var data = {};
|
||||
data.configs = [];
|
||||
data.nodebalancer = {};
|
||||
data.params = {};
|
||||
data.regions = [];
|
||||
data.stats = {};
|
||||
|
||||
// Static references to UI elements
|
||||
var ui = {};
|
||||
ui.configTable = {};
|
||||
ui.cxnAvg = {};
|
||||
ui.cxnGraph = {};
|
||||
ui.cxnLast = {};
|
||||
ui.cxnMax = {};
|
||||
ui.hostname = {};
|
||||
ui.inputLabel = {};
|
||||
ui.ipv4 = {};
|
||||
ui.ipv6 = {};
|
||||
ui.loadingConfigs = {};
|
||||
ui.location = {};
|
||||
ui.nodebalancerLabel = {};
|
||||
ui.nodebalancerTag = {};
|
||||
ui.nodebalancerTagLink = {};
|
||||
ui.saveButton = {};
|
||||
ui.tableLabel = {};
|
||||
ui.tags = {};
|
||||
ui.throttle = {};
|
||||
ui.trafficGraph = {};
|
||||
ui.trafficInAvg = {};
|
||||
ui.trafficInLast = {};
|
||||
ui.trafficInMax = {};
|
||||
ui.trafficOutAvg = {};
|
||||
ui.trafficOutLast = {};
|
||||
ui.trafficOutMax = {};
|
||||
ui.transferred = {};
|
||||
|
||||
// Temporary state
|
||||
var state = {};
|
||||
state.haveNodebalancer = false;
|
||||
state.haveRegions = false;
|
||||
|
||||
// Generate a configuration table row
|
||||
var createConfigRow = function(config, alt)
|
||||
{
|
||||
var row = document.createElement("tr");
|
||||
if (alt)
|
||||
row.className = elements.lmcRowAlt;
|
||||
else
|
||||
row.className = elements.lmcRow;
|
||||
|
||||
var port = document.createElement("td");
|
||||
var portLink = document.createElement("a");
|
||||
portLink.href = "/nodebalancers/config?nbid=" + data.params.nbid + "&nbcid=" + config.id;
|
||||
portLink.innerHTML = "Port " + config.port;
|
||||
port.appendChild(portLink);
|
||||
row.appendChild(port);
|
||||
|
||||
var protocol = document.createElement("td");
|
||||
protocol.innerHTML = config.protocol.toUpperCase();
|
||||
row.appendChild(protocol);
|
||||
|
||||
var algorithmStrings = {
|
||||
"roundrobin": "Round Robin",
|
||||
"leastconn": "Least Connections",
|
||||
"source": "Source IP"
|
||||
};
|
||||
var algorithm = document.createElement("td");
|
||||
if (algorithmStrings[config.algorithm])
|
||||
algorithm.innerHTML = algorithmStrings[config.algorithm];
|
||||
else
|
||||
algorithm.innerHTML = config.algorithm;
|
||||
row.appendChild(algorithm);
|
||||
|
||||
var stickinessStrings = {
|
||||
"none": "None",
|
||||
"table": "Table",
|
||||
"http_cookie": "HTTP Cookie"
|
||||
};
|
||||
var stickiness = document.createElement("td");
|
||||
if (stickinessStrings[config.stickiness])
|
||||
stickiness.innerHTML = stickinessStrings[config.stickiness];
|
||||
else
|
||||
stickiness.innerHTML = config.stickiness;
|
||||
row.appendChild(stickiness);
|
||||
|
||||
var healthStrings = {
|
||||
"none": "None",
|
||||
"connection": "TCP Connection",
|
||||
"http": "HTTP Valid Status",
|
||||
"http_body": "HTTP Body Regex"
|
||||
};
|
||||
var healthCheck = document.createElement("td");
|
||||
if (healthStrings[config.check])
|
||||
healthCheck.innerHTML = healthStrings[config.check];
|
||||
else
|
||||
healthCheck.innerHTML = config.check;
|
||||
row.appendChild(healthCheck);
|
||||
|
||||
var status = document.createElement("td");
|
||||
status.innerHTML = config.nodes_status.up + " up, " + config.nodes_status.down + " down";
|
||||
row.appendChild(status);
|
||||
|
||||
var options = document.createElement("td");
|
||||
options.className = elements.centerCell;
|
||||
var editLink = document.createElement("a");
|
||||
editLink.href = "/nodebalancers/config?nbid=" + data.params.nbid + "&nbcid=" + config.id;
|
||||
editLink.innerHTML = "Edit";
|
||||
var separator = document.createElement("span");
|
||||
separator.innerHTML = " | ";
|
||||
var removeLink = document.createElement("a");
|
||||
removeLink.href = "#";
|
||||
removeLink.id = elements.removePrefix + config.id;
|
||||
removeLink.innerHTML = "Remove";
|
||||
removeLink.addEventListener("click", handleConfigRemove);
|
||||
options.appendChild(editLink);
|
||||
options.appendChild(separator);
|
||||
options.appendChild(removeLink);
|
||||
row.appendChild(options);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
// Callback for nodebalancer configs 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 pages
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/nodebalancers/" + data.params.nbid + "/configs?page=" + (response.page + 1), displayConfigs, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove loading row
|
||||
ui.loadingConfigs.remove();
|
||||
|
||||
// Insert configuration rows into table
|
||||
for (var i = 0; i < data.configs.length; i++)
|
||||
ui.configTable.appendChild(createConfigRow(data.configs[i], i % 2));
|
||||
};
|
||||
|
||||
// Callback for nodebalancer API call
|
||||
var displayNodebalancer = function(response)
|
||||
{
|
||||
data.nodebalancer = response;
|
||||
|
||||
// Set page title and header stuff
|
||||
if (document.title.indexOf("//") == -1)
|
||||
document.title += " // Edit " + data.nodebalancer.label;
|
||||
ui.nodebalancerLabel.innerHTML = data.nodebalancer.label;
|
||||
if (data.nodebalancer.tags.length == 1) {
|
||||
ui.nodebalancerTagLink.href = "/nodebalancers?tag=" + data.nodebalancer.tags[0];
|
||||
ui.nodebalancerTagLink.innerHTML = "(" + data.nodebalancer.tags[0] + ")";
|
||||
ui.nodebalancerTag.style.display = "inline";
|
||||
} else {
|
||||
ui.nodebalancerTag.style.display = "none";
|
||||
}
|
||||
|
||||
// Populate info
|
||||
ui.tableLabel.innerHTML = data.nodebalancer.label;
|
||||
ui.hostname.innerHTML = data.nodebalancer.hostname;
|
||||
ui.ipv4.innerHTML = data.nodebalancer.ipv4;
|
||||
ui.ipv6.innerHTML = data.nodebalancer.ipv6;
|
||||
ui.transferred.innerHTML = countSI(data.nodebalancer.transfer.in * 1048576) + "B in - " + countSI(data.nodebalancer.transfer.out * 1048576) + "B out (" + countSI(data.nodebalancer.transfer.total * 1048576) + "B total)";
|
||||
ui.inputLabel.value = data.nodebalancer.label;
|
||||
ui.throttle.value = data.nodebalancer.client_conn_throttle;
|
||||
ui.tags.value = data.nodebalancer.tags.join(",");
|
||||
|
||||
ui.saveButton.disabled = false;
|
||||
|
||||
state.haveNodebalancer = true;
|
||||
if (state.haveRegions)
|
||||
insertRegion();
|
||||
};
|
||||
|
||||
// Callback for regions API call
|
||||
var displayRegions = function(response)
|
||||
{
|
||||
// Add regions to array
|
||||
data.regions = data.regions.concat(response.data);
|
||||
|
||||
// Request the next page if there are more pages
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/regions?page=" + (response.page + 1), getRegions, null);
|
||||
return;
|
||||
}
|
||||
|
||||
state.haveRegions = true;
|
||||
if (state.haveNodebalancer)
|
||||
insertRegion();
|
||||
};
|
||||
|
||||
// Callback for nodebalancer stats API call
|
||||
var displayStats = function(response)
|
||||
{
|
||||
// Insert dummy points in case of blank data
|
||||
if (!response.data.connections.length)
|
||||
response.data.connections = [[0,0]];
|
||||
if (!response.data.traffic.in.length)
|
||||
response.data.traffic.in = [[0,0]];
|
||||
if (!response.data.traffic.out.length)
|
||||
reponse.data.traffic.out = [[0,0]];
|
||||
|
||||
data.stats.cxn = [{
|
||||
"color": "#906",
|
||||
"fill": true,
|
||||
"points": response.data.connections
|
||||
}];
|
||||
data.stats.traffic = [
|
||||
{
|
||||
"color": "#32CD32",
|
||||
"fill": true,
|
||||
"points": response.data.traffic.out
|
||||
},
|
||||
{
|
||||
"color": "#03C",
|
||||
"fill": false,
|
||||
"points": response.data.traffic.in
|
||||
}
|
||||
];
|
||||
|
||||
// Draw graphs
|
||||
drawSeries(data.stats.cxn, ui.cxnGraph);
|
||||
drawSeries(data.stats.traffic, ui.trafficGraph);
|
||||
|
||||
// Update tables
|
||||
ui.cxnMax.innerHTML = data.stats.cxn[0].max;
|
||||
ui.cxnAvg.innerHTML = data.stats.cxn[0].avg.toFixed(2);
|
||||
ui.cxnLast.innerHTML = data.stats.cxn[0].points[data.stats.cxn[0].points.length - 1][1];
|
||||
ui.trafficOutMax.innerHTML = countSI(data.stats.traffic[1].max) + "b/s";
|
||||
ui.trafficOutAvg.innerHTML = countSI(data.stats.traffic[1].avg) + "b/s";
|
||||
ui.trafficOutLast.innerHTML = countSI(data.stats.traffic[1].points[data.stats.traffic[1].points.length - 1][1]) + "b/s";
|
||||
ui.trafficInMax.innerHTML = countSI(data.stats.traffic[0].max) + "b/s";
|
||||
ui.trafficInAvg.innerHTML = countSI(data.stats.traffic[0].avg) + "b/s";
|
||||
ui.trafficInLast.innerHTML = countSI(data.stats.traffic[0].points[data.stats.traffic[0].points.length - 1][1]) + "b/s";
|
||||
};
|
||||
|
||||
// Click handler for config remove link
|
||||
var handleConfigRemove = function(event)
|
||||
{
|
||||
if (!confirm("Are you sure you want to delete this config?"))
|
||||
return;
|
||||
|
||||
var nbcid = event.currentTarget.id.substring(elements.removePrefix.length);
|
||||
apiDelete("/nodebalancers/" + data.params.nbid + "/configs/" + nbcid, function()
|
||||
{
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
// Click handler for save button
|
||||
var handleSave = function(event)
|
||||
{
|
||||
if (event.currentTarget.disabled)
|
||||
return;
|
||||
|
||||
var req = {
|
||||
"label": ui.inputLabel.value,
|
||||
"client_conn_throttle": parseInt(ui.throttle.value),
|
||||
"tags": []
|
||||
};
|
||||
|
||||
if (ui.tags.value.length)
|
||||
req.tags = ui.tags.value.split(",");
|
||||
|
||||
apiPut("/nodebalancers/" + data.params.nbid, req, function(response)
|
||||
{
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
// Display region info
|
||||
var insertRegion = function()
|
||||
{
|
||||
var regionData = null;
|
||||
for (var i = 0; i < data.regions.length; i++) {
|
||||
if (data.regions[i].id == data.nodebalancer.region) {
|
||||
regionData = data.regions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (regionData && regionData.label && regionData.label.length)
|
||||
ui.location.innerHTML = regionData.label;
|
||||
else if (regionNames[data.nodebalancer.region])
|
||||
ui.location.innerHTML = regionNames[data.nodebalancer.region];
|
||||
else
|
||||
ui.location.innerHTML = data.nodebalancer.region;
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
var setup = function()
|
||||
{
|
||||
// Parse URL parameters
|
||||
data.params = parseParams();
|
||||
|
||||
// We need a NodeBalancer ID, so die if we don't have it
|
||||
if (!data.params.nbid) {
|
||||
alert("No NodeBalancer 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("nbid=0", "nbid=" + data.params.nbid);
|
||||
|
||||
// Get element references
|
||||
ui.configTable = document.getElementById(elements.configTable);
|
||||
ui.cxnAvg = document.getElementById(elements.cxnAvg);
|
||||
ui.cxnGraph = document.getElementById(elements.cxnGraph);
|
||||
ui.cxnLast = document.getElementById(elements.cxnLast);
|
||||
ui.cxnMax = document.getElementById(elements.cxnMax);
|
||||
ui.hostname = document.getElementById(elements.hostname);
|
||||
ui.inputLabel = document.getElementById(elements.inputLabel);
|
||||
ui.ipv4 = document.getElementById(elements.ipv4);
|
||||
ui.ipv6 = document.getElementById(elements.ipv6);
|
||||
ui.loadingConfigs = document.getElementById(elements.loadingConfigs);
|
||||
ui.location = document.getElementById(elements.location);
|
||||
ui.nodebalancerLabel = document.getElementById(elements.nodebalancerLabel);
|
||||
ui.nodebalancerTag = document.getElementById(elements.nodebalancerTag);
|
||||
ui.nodebalancerTagLink = document.getElementById(elements.nodebalancerTagLink);
|
||||
ui.saveButton = document.getElementById(elements.saveButton);
|
||||
ui.tableLabel = document.getElementById(elements.tableLabel);
|
||||
ui.tags = document.getElementById(elements.tags);
|
||||
ui.throttle = document.getElementById(elements.throttle);
|
||||
ui.trafficGraph = document.getElementById(elements.trafficGraph);
|
||||
ui.trafficInAvg = document.getElementById(elements.trafficInAvg);
|
||||
ui.trafficInLast = document.getElementById(elements.trafficInLast);
|
||||
ui.trafficInMax = document.getElementById(elements.trafficInMax);
|
||||
ui.trafficOutAvg = document.getElementById(elements.trafficOutAvg);
|
||||
ui.trafficOutLast = document.getElementById(elements.trafficOutLast);
|
||||
ui.trafficOutMax = document.getElementById(elements.trafficOutMax);
|
||||
ui.transferred = document.getElementById(elements.transferred);
|
||||
|
||||
// Attach event handlers
|
||||
ui.saveButton.addEventListener("click", handleSave);
|
||||
|
||||
// Set graph resolutions
|
||||
ui.cxnGraph.height = ui.cxnGraph.clientHeight;
|
||||
ui.cxnGraph.width = ui.cxnGraph.clientWidth;
|
||||
ui.trafficGraph.height = ui.trafficGraph.clientHeight;
|
||||
ui.trafficGraph.width = ui.trafficGraph.clientWidth;
|
||||
|
||||
// Get data from the API
|
||||
apiGet("/nodebalancers/" + data.params.nbid, displayNodebalancer, null);
|
||||
apiGet("/regions", displayRegions, null);
|
||||
apiGet("/nodebalancers/" + data.params.nbid + "/configs", displayConfigs, null);
|
||||
apiGet("/nodebalancers/" + data.params.nbid + "/stats", displayStats, null);
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
162
nodebalancers/balancer/index.shtml
Normal file
162
nodebalancers/balancer/index.shtml
Normal file
@ -0,0 +1,162 @@
|
||||
<!--
|
||||
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 - NodeBalancers</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="balancer.css" />
|
||||
<script src="balancer.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--#include virtual="/include/header.html"-->
|
||||
<div id="main-content" class="wrapper">
|
||||
<div id="top-links"><a href="/nodebalancers">NodeBalancers</a> » <span id="nodebalancer-tag"><a id="nodebalancer-tag-link" href=""></a> » </span><span id="nodebalancer-label" class="top-links-title"></span></div>
|
||||
<div id="nodebalancer-details">
|
||||
<table class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="7">Configurations</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Port</td>
|
||||
<td>Protocol</td>
|
||||
<td>Algorithm</td>
|
||||
<td>Session Stickiness</td>
|
||||
<td>Health Check Method</td>
|
||||
<td>Node Status</td>
|
||||
<td class="center-cell">Options</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="config-table">
|
||||
<tr id="loading-configs" class="lmc-tr3">
|
||||
<td colspan="7">Loading configurations...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="sub-links"><a href="/nodebalancers/config?nbid=0&nbcid=0">Create Configuration</a></p>
|
||||
<table class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td id="table-label" colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">NodeBalancer Settings</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="settings-table">
|
||||
<tr class="lmc-tr3">
|
||||
<td>Hostname</td>
|
||||
<td id="hostname"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>IPv4 Address</td>
|
||||
<td id="ipv4"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>IPv6 Address</td>
|
||||
<td id="ipv6"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Location</td>
|
||||
<td id="location"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Transferred this Month</td>
|
||||
<td id="transferred"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>NodeBalancer Label</td>
|
||||
<td><input id="input-label" type="text" value="" size="24" /></td>
|
||||
<td class="info">Rename your NodeBalancer</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Client Connection Throttle</td>
|
||||
<td><input id="throttle" type="number" min="0" max="20" value="0" size="4" /></td>
|
||||
<td class="info">To help mitigate abuse, throttle connections from a single client IP to this number per second. 0 to disable.</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Tags</td>
|
||||
<td><input id="tags" type="text" size="24" /> (comma-separated)</td>
|
||||
<td class="info">Group NodeBalancers together on the NodeBalancers tab using tags!</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td></td>
|
||||
<td><button disabled id="save-button" type="button">Save Changes</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Graphs</h3>
|
||||
<div class="lmc-graph">
|
||||
<h4>Connections (CXN/s) - Last 24 Hours</h4>
|
||||
<canvas id="cxn-graph"></canvas>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Max</td>
|
||||
<td>Avg</td>
|
||||
<td>Last</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div id="cxn-color" class="lmc-graph-color"></div> Connections</td>
|
||||
<td id="cxn-max"></td>
|
||||
<td id="cxn-avg"></td>
|
||||
<td id="cxn-last"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4>Traffic (bits/s) - Last 24 Hours</h4>
|
||||
<canvas id="traffic-graph"></canvas>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>Max</td>
|
||||
<td>Avg</td>
|
||||
<td>Last</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div id="traffic-out-color" class="lmc-graph-color"></div> Outgoing</td>
|
||||
<td id="traffic-out-max"></td>
|
||||
<td id="traffic-out-avg"></td>
|
||||
<td id="traffic-out-last"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="traffic-in-color" class="lmc-graph-color"></div> Incoming</td>
|
||||
<td id="traffic-in-max"></td>
|
||||
<td id="traffic-in-avg"></td>
|
||||
<td id="traffic-in-last"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
43
nodebalancers/config/config.css
Normal file
43
nodebalancers/config/config.css
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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');
|
||||
|
||||
.center-cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.check-show {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#config {
|
||||
padding: 0px 15px 15px;
|
||||
}
|
||||
|
||||
#config-table tbody:not(.lmc-tbody-head) tr td:first-of-type {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#config-table tbody:not(.lmc-tbody-head) tr td:not(:last-of-type) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.protocol-show {
|
||||
display: none;
|
||||
}
|
394
nodebalancers/config/config.js
Normal file
394
nodebalancers/config/config.js
Normal file
@ -0,0 +1,394 @@
|
||||
/*
|
||||
* 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, apiPut, parseParams, setupHeader } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
// Element names specific to this page
|
||||
elements.algorithm = "algorithm";
|
||||
elements.centerCell = "center-cell";
|
||||
elements.checkAttempts = "check-attempts";
|
||||
elements.checkBody = "check-body";
|
||||
elements.checkInterval = "check-interval";
|
||||
elements.checkPath = "check-path";
|
||||
elements.checkShow = "check-show";
|
||||
elements.checkTimeout = "check-timeout";
|
||||
elements.checkType = "check-type";
|
||||
elements.cipherSuite = "cipher-suite";
|
||||
elements.commonName = "common-name";
|
||||
elements.configLabel = "config-label";
|
||||
elements.fingerprint = "fingerprint";
|
||||
elements.lmcRow = "lmc-tr1";
|
||||
elements.lmcRowAlt = "lmc-tr2";
|
||||
elements.nodebalancerLabel = "nodebalancer-label";
|
||||
elements.nodebalancerTag = "nodebalancer-tag";
|
||||
elements.nodebalancerTagLink = "nodebalancer-tag-link";
|
||||
elements.nodesTable = "nodes-table";
|
||||
elements.nonzero = "nonzero";
|
||||
elements.passive = "passive";
|
||||
elements.port = "port";
|
||||
elements.protocol = "protocol";
|
||||
elements.protocolShow = "protocol-show";
|
||||
elements.proxyProtocol = "proxy-protocol";
|
||||
elements.removePrefix = "remove-node-";
|
||||
elements.replaceCert = "replace-cert";
|
||||
elements.replaceCertOnly = "replace-cert-only";
|
||||
elements.replaceLink = "replace-link";
|
||||
elements.saveButton = "save-button";
|
||||
elements.sslCert = "ssl-cert";
|
||||
elements.sslKey = "ssl-key";
|
||||
elements.stickiness = "stickiness";
|
||||
|
||||
// Data recieved from API calls
|
||||
var data = {};
|
||||
data.config = {};
|
||||
data.nodebalancer = {};
|
||||
data.nodes = [];
|
||||
|
||||
// Static references to UI elements
|
||||
var ui = {};
|
||||
ui.algorithm = {};
|
||||
ui.checkAttempts = {};
|
||||
ui.checkBody = {};
|
||||
ui.checkInterval = {};
|
||||
ui.checkPath = {};
|
||||
ui.checkShow = [];
|
||||
ui.checkTimeout = {};
|
||||
ui.checkType = {};
|
||||
ui.cipherSuite = {};
|
||||
ui.commonName = {};
|
||||
ui.configLabel = {};
|
||||
ui.fingerprint = {};
|
||||
ui.nodebalancerLabel = {};
|
||||
ui.nodebalancerTag = {};
|
||||
ui.nodebalancerTagLink = {};
|
||||
ui.nodesTable = {};
|
||||
ui.passive = {};
|
||||
ui.port = {};
|
||||
ui.protocol = {};
|
||||
ui.protocolShow = [];
|
||||
ui.proxyProtocol = {};
|
||||
ui.saveButton = {};
|
||||
ui.replaceLink = {};
|
||||
ui.sslCert = {};
|
||||
ui.sslKey = {};
|
||||
ui.stickiness = {};
|
||||
|
||||
// Temporary State
|
||||
var state = {};
|
||||
state.replaceCert = true;
|
||||
|
||||
// Create a row for the nodes table
|
||||
var createNodeRow = function(node, alt)
|
||||
{
|
||||
var row = document.createElement("tr");
|
||||
if (alt)
|
||||
row.className = elements.lmcRowAlt;
|
||||
else
|
||||
row.className = elements.lmcRow;
|
||||
|
||||
var label = document.createElement("td");
|
||||
label.innerHTML = node.label;
|
||||
row.appendChild(label);
|
||||
|
||||
var address = document.createElement("td");
|
||||
var lastColon = node.address.lastIndexOf(":");
|
||||
address.innerHTML = node.address.slice(0, lastColon);
|
||||
row.appendChild(address);
|
||||
|
||||
var port = document.createElement("td");
|
||||
port.innerHTML = node.address.slice(lastColon + 1);
|
||||
row.appendChild(port);
|
||||
|
||||
var weight = document.createElement("td");
|
||||
weight.innerHTML = node.weight;
|
||||
row.appendChild(weight);
|
||||
|
||||
var mode = document.createElement("td");
|
||||
mode.innerHTML = node.mode.charAt(0).toUpperCase() + node.mode.slice(1);
|
||||
row.appendChild(mode);
|
||||
|
||||
var status = document.createElement("td");
|
||||
status.innerHTML = node.status;
|
||||
row.appendChild(status);
|
||||
|
||||
var options = document.createElement("td");
|
||||
options.className = elements.centerCell;
|
||||
var editLink = document.createElement("a");
|
||||
editLink.href = "/nodebalancers/node?nbid=" + data.params.nbid + "&nbcid=" + data.params.nbcid + "&nbnid=" + node.id;
|
||||
editLink.innerHTML = "Edit";
|
||||
var separator = document.createElement("span");
|
||||
separator.innerHTML = " | ";
|
||||
var removeLink = document.createElement("a");
|
||||
removeLink.id = elements.removePrefix + node.id;
|
||||
removeLink.href = "#";
|
||||
removeLink.innerHTML = "Remove";
|
||||
removeLink.addEventListener("click", handleRemoveNode);
|
||||
options.appendChild(editLink);
|
||||
options.appendChild(separator);
|
||||
options.appendChild(removeLink);
|
||||
row.appendChild(options);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
// Callback for config API call
|
||||
var displayConfig = function(response)
|
||||
{
|
||||
data.config = response;
|
||||
|
||||
ui.configLabel.innerHTML = "Port " + data.config.port;
|
||||
ui.port.value = data.config.port;
|
||||
ui.protocol.value = data.config.protocol;
|
||||
ui.proxyProtocol.value = data.config.proxy_protocol;
|
||||
ui.algorithm.value = data.config.algorithm;
|
||||
ui.stickiness.value = data.config.stickiness;
|
||||
ui.commonName.innerHTML = data.config.ssl_commonname;
|
||||
ui.fingerprint.innerHTML = data.config.ssl_fingerprint;
|
||||
ui.cipherSuite.value = data.config.cipher_suite;
|
||||
ui.checkType.value = data.config.check;
|
||||
ui.checkInterval.value = data.config.check_interval;
|
||||
ui.checkTimeout.value = data.config.check_timeout;
|
||||
ui.checkAttempts.value = data.config.check_attempts;
|
||||
ui.checkPath.value = data.config.check_path;
|
||||
ui.checkBody.value = data.config.check_body;
|
||||
ui.passive.checked = data.config.check_passive;
|
||||
state.replaceCert = (!data.config.ssl_key || data.config.ssl_key.indexOf("<REDACTED>") == -1);
|
||||
|
||||
ui.saveButton.disabled = false;
|
||||
|
||||
showHideChecks();
|
||||
showHideProtocol();
|
||||
};
|
||||
|
||||
// Callback for nodebalancer API call
|
||||
var displayNodebalancer = function(response)
|
||||
{
|
||||
data.nodebalancer = response;
|
||||
|
||||
// Set page title and header stuff
|
||||
if (document.title.indexOf("//") == -1)
|
||||
document.title += " // Edit " + data.nodebalancer.label;
|
||||
ui.nodebalancerLabel.innerHTML = data.nodebalancer.label;
|
||||
if (data.nodebalancer.tags.length == 1) {
|
||||
ui.nodebalancerTagLink.href = "/nodebalancers?tag=" + data.nodebalancer.tags[0];
|
||||
ui.nodebalancerTagLink.innerHTML = "(" + data.nodebalancer.tags[0] + ")";
|
||||
ui.nodebalancerTag.style.display = "inline";
|
||||
} else {
|
||||
ui.nodebalancerTag.style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
// Callback for nodes API call
|
||||
var displayNodes = function(response)
|
||||
{
|
||||
data.nodes = data.nodes.concat(response.data);
|
||||
|
||||
// Request the next page if there are more pages
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid + "/nodes?page=" + (response.page + 1), displayNodes, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert nodes into table
|
||||
for (var i = 0; i < data.nodes.length; i++)
|
||||
ui.nodesTable.appendChild(createNodeRow(data.nodes[i], i % 2));
|
||||
};
|
||||
|
||||
// Remove node handler
|
||||
var handleRemoveNode = function(event)
|
||||
{
|
||||
if (!confirm("Are you sure you want to remove this node?"))
|
||||
return;
|
||||
|
||||
var nbnid = event.currentTarget.id.substring(elements.removePrefix.length);
|
||||
apiDelete("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid + "/nodes/" + nbnid, function()
|
||||
{
|
||||
location.reload();
|
||||
});
|
||||
};
|
||||
|
||||
// Replace link handler
|
||||
var handleReplace = function(event)
|
||||
{
|
||||
state.replaceCert = true;
|
||||
showHideProtocol();
|
||||
};
|
||||
|
||||
// Save button handler
|
||||
var handleSave = function(event)
|
||||
{
|
||||
if (event.currentTarget.disabled)
|
||||
return;
|
||||
|
||||
var req = {
|
||||
"port": parseInt(ui.port.value),
|
||||
"protocol": ui.protocol.value,
|
||||
"algorithm": ui.algorithm.value,
|
||||
"stickiness": ui.stickiness.value,
|
||||
"check": ui.checkType.value,
|
||||
"check_passive": ui.passive.checked
|
||||
};
|
||||
|
||||
if (ui.protocol.value == "tcp") {
|
||||
req.proxy_protocol = ui.proxyProtocol.value;
|
||||
} else if (ui.protocol.value == "https") {
|
||||
req.cipher_suite = ui.cipherSuite.value;
|
||||
if (state.replaceCert) {
|
||||
req.ssl_cert = ui.sslCert.value;
|
||||
req.ssl_key = ui.sslKey.value;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ui.checkType.value) {
|
||||
case "http_body":
|
||||
req.check_body = ui.checkBody.value;
|
||||
case "http":
|
||||
req.check_path = ui.checkPath.value;
|
||||
case "connection":
|
||||
req.check_interval = parseInt(ui.checkInterval.value);
|
||||
req.check_timeout = parseInt(ui.checkTimeout.value);
|
||||
req.check_attempts = parseInt(ui.checkAttempts.value);
|
||||
}
|
||||
|
||||
if (data.params.nbcid == 0) {
|
||||
apiPost("/nodebalancers/" + data.params.nbid + "/configs", req, function(response)
|
||||
{
|
||||
location.href = "/nodebalancers/config?nbid=" + data.params.nbid + "&nbcid=" + response.id;
|
||||
});
|
||||
} else {
|
||||
apiPut("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid, req, function(response)
|
||||
{
|
||||
location.href = "/nodebalancers/balancer?nbid=" + data.params.nbid;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
var setup = function()
|
||||
{
|
||||
// Parse URL parameters
|
||||
data.params = parseParams();
|
||||
|
||||
// We need a NodeBalancer ID, so die if we don't have it
|
||||
if (!data.params.nbid) {
|
||||
alert("No NodeBalancer ID supplied!");
|
||||
return;
|
||||
}
|
||||
|
||||
// We also need a config ID
|
||||
if (!data.params.nbcid) {
|
||||
alert("No config 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("nbid=0", "nbid=" + data.params.nbid);
|
||||
anchors[i].href = anchors[i].href.replace("nbcid=0", "nbcid=" + data.params.nbcid);
|
||||
}
|
||||
|
||||
// Get element references
|
||||
ui.algorithm = document.getElementById(elements.algorithm);
|
||||
ui.checkAttempts = document.getElementById(elements.checkAttempts);
|
||||
ui.checkBody = document.getElementById(elements.checkBody);
|
||||
ui.checkInterval = document.getElementById(elements.checkInterval);
|
||||
ui.checkPath = document.getElementById(elements.checkPath);
|
||||
ui.checkShow = document.getElementsByClassName(elements.checkShow);
|
||||
ui.checkTimeout = document.getElementById(elements.checkTimeout);
|
||||
ui.checkType = document.getElementById(elements.checkType);
|
||||
ui.cipherSuite = document.getElementById(elements.cipherSuite);
|
||||
ui.commonName = document.getElementById(elements.commonName);
|
||||
ui.configLabel = document.getElementById(elements.configLabel);
|
||||
ui.fingerprint = document.getElementById(elements.fingerprint);
|
||||
ui.nodebalancerLabel = document.getElementById(elements.nodebalancerLabel);
|
||||
ui.nodebalancerTag = document.getElementById(elements.nodebalancerTag);
|
||||
ui.nodebalancerTagLink = document.getElementById(elements.nodebalancerTagLink);
|
||||
ui.nodesTable = document.getElementById(elements.nodesTable);
|
||||
ui.passive = document.getElementById(elements.passive);
|
||||
ui.port = document.getElementById(elements.port);
|
||||
ui.protocol = document.getElementById(elements.protocol);
|
||||
ui.protocolShow = document.getElementsByClassName(elements.protocolShow);
|
||||
ui.proxyProtocol = document.getElementById(elements.proxyProtocol);
|
||||
ui.replaceLink = document.getElementById(elements.replaceLink);
|
||||
ui.saveButton = document.getElementById(elements.saveButton);
|
||||
ui.sslCert = document.getElementById(elements.sslCert);
|
||||
ui.sslKey = document.getElementById(elements.sslKey);
|
||||
ui.stickiness = document.getElementById(elements.stickiness);
|
||||
|
||||
// Register event handlers
|
||||
ui.checkType.addEventListener("input", showHideChecks);
|
||||
ui.protocol.addEventListener("input", showHideProtocol);
|
||||
ui.replaceLink.addEventListener("click", handleReplace);
|
||||
ui.saveButton.addEventListener("click", handleSave);
|
||||
|
||||
// Get data from API
|
||||
apiGet("/nodebalancers/" + data.params.nbid, displayNodebalancer, null);
|
||||
if (parseInt(data.params.nbcid)) {
|
||||
apiGet("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid, displayConfig, null);
|
||||
apiGet("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid + "/nodes", displayNodes, null);
|
||||
} else {
|
||||
ui.configLabel.innerHTML = "Create Configuration";
|
||||
ui.saveButton.disabled = false;
|
||||
var hideElements = document.getElementsByClassName(elements.nonzero);
|
||||
for (var i = 0; i < hideElements.length; i++)
|
||||
hideElements[i].style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
// Show/hide check rows
|
||||
var showHideChecks = function(event)
|
||||
{
|
||||
for (var i = 0; i < ui.checkShow.length; i++) {
|
||||
if (ui.checkShow[i].classList.contains(elements.checkShow + "-" + ui.checkType.value))
|
||||
ui.checkShow[i].style.display = "table-row";
|
||||
else
|
||||
ui.checkShow[i].style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
// Show/hide protocol stuff
|
||||
var showHideProtocol = function(event)
|
||||
{
|
||||
for (var i = 0; i < ui.protocolShow.length; i++) {
|
||||
if (ui.protocolShow[i].classList.contains(elements.protocolShow + "-" + ui.protocol.value)) {
|
||||
if (ui.protocolShow[i].classList.contains(elements.replaceCert)) {
|
||||
if (ui.protocolShow[i].classList.contains(elements.replaceCertOnly) == state.replaceCert)
|
||||
ui.protocolShow[i].style.display = "table-row";
|
||||
else
|
||||
ui.protocolShow[i].style.display = "none";
|
||||
} else {
|
||||
ui.protocolShow[i].style.display = "table-row";
|
||||
}
|
||||
} else {
|
||||
ui.protocolShow[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
if (data.params.nbcid == 0 && ui.protocol.value == "http")
|
||||
ui.port.value = 80;
|
||||
if (data.params.nbcid == 0 && ui.protocol.value == "https")
|
||||
ui.port.value = 443;
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
222
nodebalancers/config/index.shtml
Normal file
222
nodebalancers/config/index.shtml
Normal file
@ -0,0 +1,222 @@
|
||||
<!--
|
||||
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 - NodeBalancers</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="config.css" />
|
||||
<script src="config.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--#include virtual="/include/header.html"-->
|
||||
<div id="main-content" class="wrapper">
|
||||
<div id="top-links"><a href="/nodebalancers">NodeBalancers</a> » <span id="nodebalancer-tag"><a id="nodebalancer-tag-link" href=""></a> » </span><a id="nodebalancer-label" href="/nodebalancers/balancer?nbid=0"></a> » <span id="config-label" class="top-links-title"></span></div>
|
||||
<div id="config">
|
||||
<table class="lmc-table nonzero">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="7">Nodes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Label</td>
|
||||
<td>Address</td>
|
||||
<td>Port</td>
|
||||
<td>Weight</td>
|
||||
<td>Mode</td>
|
||||
<td>Status</td>
|
||||
<td class="center-cell">Options</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="nodes-table"></tbody>
|
||||
</table>
|
||||
<p class="sub-links nonzero"><a href="/nodebalancers/node?nbid=0&nbcid=0&nbnid=0">Add Node</a></p>
|
||||
<table id="config-table" class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="3">Edit Configuration</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">Configuration Settings</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Port</td>
|
||||
<td><input id="port" type="number" min="1" max="65535" value="80" size="4" /></td>
|
||||
<td class="info">Listen on this port</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Protocol</td>
|
||||
<td>
|
||||
<select id="protocol">
|
||||
<option value="tcp">TCP</option>
|
||||
<option selected value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
</select>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 protocol-show protocol-show-tcp">
|
||||
<td>Proxy Protocol</td>
|
||||
<td>
|
||||
<select id="proxy-protocol">
|
||||
<option selected value="none">None</option>
|
||||
<option value="v1">v1</option>
|
||||
<option value="v2">v2</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="info">Proxy protocol preserves initial TCP connection information. Consult the <a target="_blank" href="https://www.linode.com/docs/products/networking/nodebalancers/guides/proxy-protocol/">Proxy Protocol guide</a> for information on the differences between each option.</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Algorithm</td>
|
||||
<td>
|
||||
<select id="algorithm">
|
||||
<option selected value="roundrobin">Round Robin</option>
|
||||
<option value="leastconn">Least Connections</option>
|
||||
<option value="source">Source IP</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="info">Roundrobin assigns connections to each backend sequentially. Least connections choose the backend with the least number of current connections. Source uses the client's IPv4 address</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Session Stickiness</td>
|
||||
<td>
|
||||
<select id="stickiness">
|
||||
<option value="none">None</option>
|
||||
<option selected value="table">Table</option>
|
||||
<option value="http_cookie">HTTP Cookie</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="info">Route subsequent requests from a client to the same backend</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody class="lmc-tbody-head">
|
||||
<tr class="noshow">
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr class="protocol-show protocol-show-https">
|
||||
<td colspan="3">SSL Settings</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr class="lmc-tr3 protocol-show protocol-show-https replace-cert">
|
||||
<td>Common Name</td>
|
||||
<td id="common-name"></td>
|
||||
<td class="info">Need help with SSL? - <a target="_blank" href="https://www.linode.com/docs/products/networking/nodebalancers/guides/configure/">NodeBalancer Reference Guide</a></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 protocol-show protocol-show-https replace-cert">
|
||||
<td>Fingerprint</td>
|
||||
<td id="fingerprint" colspan="2"></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 protocol-show protocol-show-https">
|
||||
<td>SSL Cipher Suite</td>
|
||||
<td>
|
||||
<select id="cipher-suite">
|
||||
<option selected value="recommended">Recommended</option>
|
||||
<option value="legacy">Legacy</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="info">Select 'recommended' to use only strong encryption. Select 'legacy' to enable weak encryption that supports older browsers. You really want to select 'recommended'. <a target="_blank" href="https://www.linode.com/docs/products/networking/nodebalancers/guides/configure/">NodeBalancer Reference Guide</a></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 protocol-show protocol-show-https replace-cert">
|
||||
<td></td>
|
||||
<td><a id="replace-link" href="#">Replace Certificate</a></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 protocol-show protocol-show-https replace-cert replace-cert-only">
|
||||
<td>SSL Certificate</td>
|
||||
<td colspan="2"><textarea id="ssl-cert" rows="6" cols="64" placeholder="Please provide your SSL certificate (including chained intermediate certificates if needed)"></textarea></td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 protocol-show protocol-show-https replace-cert replace-cert-only">
|
||||
<td>Private Key</td>
|
||||
<td colspan="2"><textarea id="ssl-key" rows="6" cols="64" placeholder="Please provide your unpassphrased SSL private key"></textarea></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody class="lmc-tbody-head">
|
||||
<tr class="noshow">
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">Active Health Check</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Health Check Type</td>
|
||||
<td>
|
||||
<select id="check-type">
|
||||
<option selected value="none">None</option>
|
||||
<option value="connection">TCP Connection</option>
|
||||
<option value="http">HTTP Valid Status</option>
|
||||
<option value="http_body">HTTP Body Regex</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="info">Active health checks proactively check the health of the backend nodes. 'TCP Connection' requires a successful TCP handshake. 'HTTP Valid Status' requires a 2xx or 3xx response from the backend node. 'HTTP Body Regex' uses a regex to match against an expected result body.</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 check-show check-show-connection check-show-http check-show-http_body">
|
||||
<td>Check Interval</td>
|
||||
<td><input id="check-interval" type="number" min="1" value="5" size="4" /></td>
|
||||
<td class="info">Seconds between health check probes</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 check-show check-show-connection check-show-http check-show-http_body">
|
||||
<td>Check Timeout</td>
|
||||
<td><input id="check-timeout" type="number" min="1" max="30" value="3" size="4" /></td>
|
||||
<td class="info">Seconds to wait before considering the probe a failure. 1-30. Must be less than check interval.</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 check-show check-show-connection check-show-http check-show-http_body">
|
||||
<td>Check Attempts</td>
|
||||
<td><input id="check-attempts" type="number" min="1" max="30" value="2" size="4" /></td>
|
||||
<td class="info">Number of failed probes before taking a node out of rotation. 1-30</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 check-show check-show-http check-show-http_body">
|
||||
<td>Check HTTP Path</td>
|
||||
<td><input id="check-path" type="text" value="/" size="24" /></td>
|
||||
<td class="info">When check=http, the path to request</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3 check-show check-show-http_body">
|
||||
<td>Expected HTTP Body</td>
|
||||
<td><textarea id="check-body" rows="4" cols="40"></textarea></td>
|
||||
<td class="info">When check=http, a regex to match within the first 16KB of the response body</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody class="lmc-tbody-head">
|
||||
<tr class="noshow">
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">Passive Checks</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Enabled</td>
|
||||
<td><input checked id="passive" type="checkbox" /></td>
|
||||
<td class="info">Enable passive checks based on observing communication with backend nodes.</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td></td>
|
||||
<td><button disabled id="save-button" type="button">Save Changes</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
41
nodebalancers/index.shtml
Normal file
41
nodebalancers/index.shtml
Normal file
@ -0,0 +1,41 @@
|
||||
<!--
|
||||
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 - NodeBalancers</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="nodebalancers.css" />
|
||||
<script src="nodebalancers.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--#include virtual="/include/header.html"-->
|
||||
<div id="main-content" class="wrapper">
|
||||
<div id="nodebalancers">
|
||||
<span id="loading">Loading...</span>
|
||||
</div>
|
||||
<div id="transfer-pool">
|
||||
<p id="transfer-header">This Month's Network Transfer Pool</p>
|
||||
<div id="transfer-bar">
|
||||
<div id="bar-used"></div><div id="bar-remaining"></div>
|
||||
</div>
|
||||
<p id="transfer-details"><span id="transfer-used"></span> Used, <span id="transfer-remaining"></span> Remaining, <span id="transfer-quota"></span> Quota</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
87
nodebalancers/node/index.shtml
Normal file
87
nodebalancers/node/index.shtml
Normal file
@ -0,0 +1,87 @@
|
||||
<!--
|
||||
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 - NodeBalancers</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="node.css" />
|
||||
<script src="node.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--#include virtual="/include/header.html"-->
|
||||
<div id="main-content" class="wrapper">
|
||||
<div id="top-links"><a href="/nodebalancers">NodeBalancers</a> » <span id="nodebalancer-tag"><a id="nodebalancer-tag-link" href=""></a> » </span><a id="nodebalancer-label" href="/nodebalancers/balancer?nbid=0"></a> » <a id="config-label" href="/nodebalancers/config?nbid=0&nbcid=0">Port</a> » <span class="top-links-title">Edit Node</span></div>
|
||||
<div id="node">
|
||||
<table class="lmc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td colspan="3">Edit Node</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">Node Settings</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Label</td>
|
||||
<td><input id="label" type="text" size="30" /></td>
|
||||
<td class="info">This backend node's label</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Address</td>
|
||||
<td>
|
||||
<select id="address">
|
||||
<option selected disabled value="0">Select A Linode</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="info">The private IP address of the backend Linode instance</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Port</td>
|
||||
<td><input id="port" type="number" min="1" max="65535" size="4" /></td>
|
||||
<td class="info">The backend port for this node</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Weight</td>
|
||||
<td><input id="weight" type="number" min="1" max="255" value="100" size="4" /></td>
|
||||
<td class="info">Load balancing weight, 1-255. Higher means more connections.</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td>Mode</td>
|
||||
<td>
|
||||
<select id="mode">
|
||||
<option value="accept">Accept</option>
|
||||
<option value="reject">Reject</option>
|
||||
<option value="backup">Backup</option>
|
||||
<option value="drain">Drain</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="info">The connections mode for this node</td>
|
||||
</tr>
|
||||
<tr class="lmc-tr3">
|
||||
<td></td>
|
||||
<td><button disabled id="save-button" type="button">Save Changes</button></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
27
nodebalancers/node/node.css
Normal file
27
nodebalancers/node/node.css
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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');
|
||||
|
||||
#node {
|
||||
padding: 0px 15px 15px;
|
||||
}
|
||||
|
||||
tbody tr td:first-of-type {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
255
nodebalancers/node/node.js
Normal file
255
nodebalancers/node/node.js
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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, apiPut, parseParams, setupHeader } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
// Element names specific to this page
|
||||
elements.address = "address";
|
||||
elements.configLabel = "config-label";
|
||||
elements.label = "label";
|
||||
elements.mode = "mode";
|
||||
elements.nodebalancerLabel = "nodebalancer-label";
|
||||
elements.nodebalancerTag = "nodebalancer-tag";
|
||||
elements.nodebalancerTagLink = "nodebalancer-tag-link";
|
||||
elements.port = "port";
|
||||
elements.saveButton = "save-button";
|
||||
elements.weight = "weight";
|
||||
|
||||
// Data recieved from API calls
|
||||
var data = {};
|
||||
data.config = {};
|
||||
data.linodeFilters = {
|
||||
"ipv4": {
|
||||
"+contains": "192.168"
|
||||
}
|
||||
};
|
||||
data.linodes = [];
|
||||
data.nodebalancer = {};
|
||||
data.node = {};
|
||||
|
||||
// Static references to UI elements
|
||||
var ui = {};
|
||||
ui.address = {};
|
||||
ui.configLabel = {};
|
||||
ui.label = {};
|
||||
ui.mode = {};
|
||||
ui.nodebalancerLabel = {};
|
||||
ui.nodebalancerTag = {};
|
||||
ui.nodebalancerTagLink = {};
|
||||
ui.port = {};
|
||||
ui.saveButton = {};
|
||||
ui.weight = {};
|
||||
|
||||
// Callback for config API call
|
||||
var displayConfig = function(response)
|
||||
{
|
||||
data.config = response;
|
||||
|
||||
ui.configLabel.innerHTML = "Port " + data.config.port;
|
||||
if (!parseInt(data.params.nbnid))
|
||||
ui.port.value = data.config.port;
|
||||
};
|
||||
|
||||
// Callback for linodes API call
|
||||
var displayLinodes = function(response)
|
||||
{
|
||||
data.linodes = data.linodes.concat(response.data);
|
||||
|
||||
// Request the next page if there are more pages
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/linode/instances?page=" + (response.page + 1), displayLinodes, data.linodeFilters);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add linodes to address selector
|
||||
for (var i = 0; i < data.linodes.length; i++) {
|
||||
var ipIndex = -1;
|
||||
for (var j = 0; j < data.linodes[i].ipv4.length; j++) {
|
||||
if (data.linodes[i].ipv4[j].indexOf("192.168.") == 0) {
|
||||
ipIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ipIndex == -1)
|
||||
continue;
|
||||
|
||||
var option = document.createElement("option");
|
||||
option.value = data.linodes[i].ipv4[j];
|
||||
option.innerHTML = data.linodes[i].ipv4[j] + " (" + data.linodes[i].label + ")";
|
||||
ui.address.appendChild(option);
|
||||
}
|
||||
|
||||
if (parseInt(data.params.nbnid))
|
||||
apiGet("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid + "/nodes/" + data.params.nbnid, displayNode, null);
|
||||
else
|
||||
ui.saveButton.disabled = false;
|
||||
};
|
||||
|
||||
// Callback for nodebalancer API call
|
||||
var displayNodebalancer = function(response)
|
||||
{
|
||||
data.nodebalancer = response;
|
||||
|
||||
// Set page title and header stuff
|
||||
if (document.title.indexOf("//") == -1)
|
||||
document.title += " // Edit " + data.nodebalancer.label;
|
||||
ui.nodebalancerLabel.innerHTML = data.nodebalancer.label;
|
||||
if (data.nodebalancer.tags.length == 1) {
|
||||
ui.nodebalancerTagLink.href = "/nodebalancers?tag=" + data.nodebalancer.tags[0];
|
||||
ui.nodebalancerTagLink.innerHTML = "(" + data.nodebalancer.tags[0] + ")";
|
||||
ui.nodebalancerTag.style.display = "inline";
|
||||
} else {
|
||||
ui.nodebalancerTag.style.display = "none";
|
||||
}
|
||||
|
||||
// Get linodes
|
||||
data.linodeFilters.region = data.nodebalancer.region;
|
||||
apiGet("/linode/instances", displayLinodes, data.linodeFilters);
|
||||
};
|
||||
|
||||
// Callback for node API call
|
||||
var displayNode = function(response)
|
||||
{
|
||||
data.node = response;
|
||||
|
||||
ui.label.value = data.node.label;
|
||||
var lastColon = data.node.address.lastIndexOf(":");
|
||||
ui.address.value = data.node.address.slice(0, lastColon);
|
||||
ui.port.value = data.node.address.slice(lastColon + 1);
|
||||
ui.weight.value = data.node.weight;
|
||||
ui.mode.value = data.node.mode;
|
||||
|
||||
ui.saveButton.disabled = false;
|
||||
};
|
||||
|
||||
// Save button handler
|
||||
var handleSave = function(event)
|
||||
{
|
||||
if (event.currentTarget.disabled)
|
||||
return;
|
||||
|
||||
if (ui.address.value == "0")
|
||||
return;
|
||||
|
||||
var req = {
|
||||
"label": ui.label.value,
|
||||
"address": ui.address.value + ":" + ui.port.value,
|
||||
"weight": parseInt(ui.weight.value),
|
||||
"mode": ui.mode.value
|
||||
};
|
||||
|
||||
if (data.params.nbnid == 0) {
|
||||
apiPost("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid + "/nodes", req, function(response)
|
||||
{
|
||||
location.href = "/nodebalancers/config?nbid=" + data.params.nbid + "&nbcid=" + data.params.nbcid;
|
||||
});
|
||||
} else {
|
||||
apiPut("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid + "/nodes/" + data.params.nbnid, req, function(response)
|
||||
{
|
||||
location.href = "/nodebalancers/config?nbid=" + data.params.nbid + "&nbcid=" + data.params.nbcid;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
var setup = function()
|
||||
{
|
||||
// Parse URL parameters
|
||||
data.params = parseParams();
|
||||
|
||||
// We need a NodeBalancer ID, so die if we don't have it
|
||||
if (!data.params.nbid) {
|
||||
alert("No NodeBalancer ID supplied!");
|
||||
return;
|
||||
}
|
||||
|
||||
// We also need a config ID
|
||||
if (!data.params.nbcid) {
|
||||
alert("No config ID supplied!");
|
||||
return;
|
||||
}
|
||||
|
||||
// We also need a node ID
|
||||
if (!data.params.nbnid) {
|
||||
alert("No node 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("nbid=0", "nbid=" + data.params.nbid);
|
||||
anchors[i].href = anchors[i].href.replace("nbcid=0", "nbcid=" + data.params.nbcid);
|
||||
}
|
||||
|
||||
// Get element references
|
||||
ui.address = document.getElementById(elements.address);
|
||||
ui.configLabel = document.getElementById(elements.configLabel);
|
||||
ui.label = document.getElementById(elements.label);
|
||||
ui.mode = document.getElementById(elements.mode);
|
||||
ui.nodebalancerLabel = document.getElementById(elements.nodebalancerLabel);
|
||||
ui.nodebalancerTag = document.getElementById(elements.nodebalancerTag);
|
||||
ui.nodebalancerTagLink = document.getElementById(elements.nodebalancerTagLink);
|
||||
ui.port = document.getElementById(elements.port);
|
||||
ui.saveButton = document.getElementById(elements.saveButton);
|
||||
ui.weight = document.getElementById(elements.weight);
|
||||
|
||||
// Register event handlers
|
||||
ui.saveButton.addEventListener("click", handleSave);
|
||||
|
||||
// Get data from API
|
||||
apiGet("/nodebalancers/" + data.params.nbid, displayNodebalancer, null);
|
||||
apiGet("/nodebalancers/" + data.params.nbid + "/configs/" + data.params.nbcid, displayConfig, null);
|
||||
};
|
||||
|
||||
// Show/hide check rows
|
||||
var showHideChecks = function(event)
|
||||
{
|
||||
for (var i = 0; i < ui.checkShow.length; i++) {
|
||||
if (ui.checkShow[i].classList.contains(elements.checkShow + "-" + ui.checkType.value))
|
||||
ui.checkShow[i].style.display = "table-row";
|
||||
else
|
||||
ui.checkShow[i].style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
// Show/hide protocol stuff
|
||||
var showHideProtocol = function(event)
|
||||
{
|
||||
for (var i = 0; i < ui.protocolShow.length; i++) {
|
||||
if (ui.protocolShow[i].classList.contains(elements.protocolShow + "-" + ui.protocol.value)) {
|
||||
if (ui.protocolShow[i].classList.contains(elements.replaceCert)) {
|
||||
if (ui.protocolShow[i].classList.contains(elements.replaceCertOnly) == state.replaceCert)
|
||||
ui.protocolShow[i].style.display = "table-row";
|
||||
else
|
||||
ui.protocolShow[i].style.display = "none";
|
||||
} else {
|
||||
ui.protocolShow[i].style.display = "table-row";
|
||||
}
|
||||
} else {
|
||||
ui.protocolShow[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
64
nodebalancers/nodebalancers.css
Normal file
64
nodebalancers/nodebalancers.css
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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');
|
||||
|
||||
#transfer-bar {
|
||||
border: 3px solid #999;
|
||||
margin: 0 auto;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
#bar-remaining {
|
||||
background-color: #EFEFEF;
|
||||
border: 2px solid #EFEFEF;
|
||||
display: none;
|
||||
font-size: 16px;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#bar-used {
|
||||
background-color: #ADD370;
|
||||
border: 2px dashed #008000;
|
||||
display: none;
|
||||
font-size: 16px;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.center-cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#nodebalancers {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
#transfer-details {
|
||||
margin: 5px 0px;
|
||||
padding-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#transfer-header {
|
||||
font-weight: bold;
|
||||
margin: 50px 0px 5px;
|
||||
text-align: center;
|
||||
}
|
366
nodebalancers/nodebalancers.js
Normal file
366
nodebalancers/nodebalancers.js
Normal file
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* 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, regionNames, apiGet, countSI, parseParams, setupHeader } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
// Element names specific to this page
|
||||
elements.barRemaining = "bar-remaining";
|
||||
elements.barUsed = "bar-used";
|
||||
elements.centerCell = "center-cell";
|
||||
elements.info = "info";
|
||||
elements.lmcRow = "lmc-tr1";
|
||||
elements.lmcRowAlt = "lmc-tr2";
|
||||
elements.lmcTable = "lmc-table";
|
||||
elements.loading = "loading";
|
||||
elements.nodebalancerPortsPrefix = "nodebalancer-ports-";
|
||||
elements.nodebalancers = "nodebalancers";
|
||||
elements.nodebalancerStatusPrefix = "nodebalancer-nodestatus-";
|
||||
elements.nodebalancerTagPrefix = "nodebalancer-tag-";
|
||||
elements.subLinks = "sub-links";
|
||||
elements.transferQuota = "transfer-quota";
|
||||
elements.transferRemaining = "transfer-remaining";
|
||||
elements.transferUsed = "transfer-used";
|
||||
|
||||
// Data recieved from API calls
|
||||
var data = {};
|
||||
data.params = {};
|
||||
data.nodebalancers = [];
|
||||
data.nodebalancerTags = [];
|
||||
data.noTag = false;
|
||||
data.regions = [];
|
||||
|
||||
// Static references to UI elements
|
||||
var ui = {};
|
||||
ui.barRemaining = {};
|
||||
ui.barUsed = {};
|
||||
ui.loading = {};
|
||||
ui.nodebalancers = {};
|
||||
ui.nodebalancerTables = {};
|
||||
ui.transferQuota = {};
|
||||
ui.transferRemaining = {};
|
||||
ui.transferUsed = {};
|
||||
|
||||
// Temporary state
|
||||
var state = {};
|
||||
state.haveNodebalancers = false;
|
||||
state.haveRegions = false;
|
||||
|
||||
var createNodebalancerRow = function(nodebalancer, alt)
|
||||
{
|
||||
var row = document.createElement("tr");
|
||||
if (alt)
|
||||
row.className = elements.lmcRowAlt;
|
||||
else
|
||||
row.className = elements.lmcRow;
|
||||
var name = document.createElement("td");
|
||||
var nameLink = document.createElement("a");
|
||||
nameLink.href = "/nodebalancers/balancer?nbid=" + nodebalancer.id;
|
||||
nameLink.innerHTML = nodebalancer.label;
|
||||
name.appendChild(nameLink);
|
||||
var regionData = null;
|
||||
for (var i = 0; i < data.regions.length; i++) {
|
||||
if (data.regions[i].id == nodebalancer.region) {
|
||||
regionData = data.regions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
var region = document.createElement("td");
|
||||
if (regionData && regionData.label && regionData.label.length)
|
||||
region.innerHTML = regionData.label;
|
||||
else if (regionNames[nodebalancer.region])
|
||||
region.innerHTML = regionNames[nodebalancer.region];
|
||||
else
|
||||
region.innerHTML = nodebalancer.region;
|
||||
var ip = document.createElement("td");
|
||||
ip.innerHTML = nodebalancer.ipv4;
|
||||
var ports = document.createElement("td");
|
||||
ports.id = elements.nodebalancerPortsPrefix + nodebalancer.id;
|
||||
var nodeStatus = document.createElement("td");
|
||||
nodeStatus.id = elements.nodebalancerStatusPrefix + nodebalancer.id;
|
||||
var transferred = document.createElement("td");
|
||||
transferred.innerHTML = countSI(nodebalancer.transfer.total * 1048576) + "B";
|
||||
var options = document.createElement("td");
|
||||
options.className = elements.centerCell;
|
||||
var editLink = document.createElement("a");
|
||||
editLink.href = "/nodebalancers/balancer?nbid=" + nodebalancer.id;
|
||||
editLink.innerHTML = "Edit";
|
||||
var optionsSeparator = document.createElement("span");
|
||||
optionsSeparator.innerHTML = " | ";
|
||||
var removeLink = document.createElement("a");
|
||||
removeLink.href = "/nodebalancers/remove?nbid=" + nodebalancer.id;
|
||||
removeLink.innerHTML = "Remove";
|
||||
options.appendChild(editLink);
|
||||
options.appendChild(optionsSeparator);
|
||||
options.appendChild(removeLink);
|
||||
row.appendChild(name);
|
||||
row.appendChild(region);
|
||||
row.appendChild(ip);
|
||||
row.appendChild(ports);
|
||||
row.appendChild(nodeStatus);
|
||||
row.appendChild(transferred);
|
||||
row.appendChild(options);
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
var createNodebalancerTable = function(tag)
|
||||
{
|
||||
var table = document.createElement("table");
|
||||
table.id = elements.nodebalancerTagPrefix + tag;
|
||||
table.className = elements.lmcTable;
|
||||
var thead = document.createElement("thead");
|
||||
var headRow1 = document.createElement("tr");
|
||||
var title = document.createElement("td");
|
||||
if (tag.length == 0)
|
||||
title.innerHTML = "NodeBalancers";
|
||||
else
|
||||
title.innerHTML = tag;
|
||||
headRow1.appendChild(title);
|
||||
var headRow2 = document.createElement("tr");
|
||||
var cells = ["Label", "Location", "IP", "Ports", "Node Status", "Transferred", "Options"];
|
||||
title.colSpan = cells.length;
|
||||
for (var i = 0; i < cells.length; i++) {
|
||||
var cell = document.createElement("td");
|
||||
if (cells[i] == "Options")
|
||||
cell.className = elements.centerCell;
|
||||
cell.innerHTML = cells[i];
|
||||
headRow2.appendChild(cell);
|
||||
}
|
||||
thead.appendChild(headRow1);
|
||||
thead.appendChild(headRow2);
|
||||
var tbody = document.createElement("tbody");
|
||||
table.appendChild(thead);
|
||||
table.appendChild(tbody);
|
||||
ui.nodebalancerTables[tag] = tbody;
|
||||
|
||||
var subLinks = document.createElement("p");
|
||||
subLinks.className = elements.subLinks;
|
||||
var addNodebalancer = document.createElement("a");
|
||||
addNodebalancer.href = "/nodebalancers/add";
|
||||
if (tag.length > 0)
|
||||
addNodebalancer.href += "?tag=" + tag;
|
||||
addNodebalancer.innerHTML = "Add a NodeBalancer";
|
||||
subLinks.appendChild(addNodebalancer);
|
||||
|
||||
ui.nodebalancers.appendChild(table);
|
||||
ui.nodebalancers.appendChild(subLinks);
|
||||
};
|
||||
|
||||
// Callback for NB configs API call
|
||||
var displayConfigs = function(response)
|
||||
{
|
||||
// Find the index of this NB in the array
|
||||
var nbid = parseInt(response['_endpoint'].split("/")[2]);
|
||||
var nbindex = -1;
|
||||
for (var i = 0; i < data.nodebalancers.length; i++) {
|
||||
if (data.nodebalancers[i].id == nbid) {
|
||||
nbindex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nbindex == -1)
|
||||
return;
|
||||
|
||||
// Add configs to object
|
||||
data.nodebalancers[nbindex].configs = data.nodebalancers[nbindex].configs.concat(response.data);
|
||||
|
||||
// Request the next page if there are more pages
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/nodebalancers/" + nbid + "/configs?page=" + (response.page + 1), displayConfigs, null);
|
||||
return;
|
||||
}
|
||||
|
||||
var ports = document.getElementById(elements.nodebalancerPortsPrefix + nbid);
|
||||
var status = document.getElementById(elements.nodebalancerStatusPrefix + nbid);
|
||||
var upTotal = 0;
|
||||
var downTotal = 0;
|
||||
|
||||
// Count the backend totals and insert port/config links
|
||||
if (!data.nodebalancers[nbindex].configs.length) {
|
||||
var addLink = document.createElement("a");
|
||||
addLink.href = "/nodebalancers/config?nbid=" + nbid + "&nbcid=0";
|
||||
addLink.innerHTML = "Add...";
|
||||
ports.appendChild(addLink);
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.nodebalancers[nbindex].configs.length; i++) {
|
||||
upTotal += data.nodebalancers[nbindex].configs[i].nodes_status.up;
|
||||
downTotal += data.nodebalancers[nbindex].configs[i].nodes_status.down;
|
||||
|
||||
if (i > 0) {
|
||||
var separator = document.createElement("span");
|
||||
separator.innerHTML = ", ";
|
||||
ports.appendChild(separator);
|
||||
}
|
||||
var port = document.createElement("a");
|
||||
port.href = "/nodebalancers/config?nbid=" + nbid + "&nbcid=" + data.nodebalancers[nbindex].configs[i].id;
|
||||
port.innerHTML = data.nodebalancers[nbindex].configs[i].port;
|
||||
ports.appendChild(port);
|
||||
}
|
||||
|
||||
status.innerHTML = upTotal + " up, " + downTotal + " down";
|
||||
};
|
||||
|
||||
var displayNodebalancers = function(response)
|
||||
{
|
||||
// Add linodes to array
|
||||
data.nodebalancers = data.nodebalancers.concat(response.data);
|
||||
|
||||
// Add new tags to array
|
||||
for (var i = 0; i < response.data.length; i++) {
|
||||
if (response.data[i].tags.length == 0)
|
||||
data.noTag = true;
|
||||
|
||||
for (var j = 0; j < response.data[i].tags.length; j++) {
|
||||
if (!data.nodebalancerTags.includes(response.data[i].tags[j]))
|
||||
data.nodebalancerTags.push(response.data[i].tags[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Request the next page if there are more pages
|
||||
if (response.page != response.pages) {
|
||||
var progress = (response.page / response.pages) * 100;
|
||||
progress = progress.toFixed(0);
|
||||
ui.loading.innerHTML = "Loading " + progress + "%...";
|
||||
var filters = null;
|
||||
if (data.params.tag)
|
||||
filters = {
|
||||
"tags": data.params.tag
|
||||
};
|
||||
apiGet("/nodebalancers?page=" + (response.page + 1), displayNodebalancers, filters);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove tag filter if there are no results, otherwise redirect to add page
|
||||
if (!data.nodebalancers.length) {
|
||||
if (data.params.tag)
|
||||
location.href = "/nodebalancers";
|
||||
else
|
||||
location.href = "/nodebalancers/add";
|
||||
}
|
||||
|
||||
// Sort
|
||||
data.nodebalancerTags.sort();
|
||||
data.nodebalancers.sort(function(a, b)
|
||||
{
|
||||
return a.label.toLowerCase().localeCompare(b.label.toLowerCase());
|
||||
});
|
||||
|
||||
// Create tables
|
||||
ui.loading.remove();
|
||||
if (data.noTag)
|
||||
createNodebalancerTable("");
|
||||
for (var i = 0; i < data.nodebalancerTags.length; i++)
|
||||
createNodebalancerTable(data.nodebalancerTags[i]);
|
||||
|
||||
state.haveNodebalancers = true;
|
||||
|
||||
// Insert linodes
|
||||
if (state.haveRegions)
|
||||
insertNodebalancers();
|
||||
};
|
||||
|
||||
var displayRegions = function(response)
|
||||
{
|
||||
// Add regions to array
|
||||
data.regions = data.regions.concat(response.data);
|
||||
|
||||
// Request the next page if there are more pages
|
||||
if (response.page != response.pages) {
|
||||
apiGet("/regions?page=" + (response.page + 1), getRegions, null);
|
||||
return;
|
||||
}
|
||||
|
||||
state.haveRegions = true;
|
||||
if (state.haveNodebalancers)
|
||||
insertNodebalancers();
|
||||
};
|
||||
|
||||
var displayTransfer = function(response)
|
||||
{
|
||||
// Get border width of bar segments from CSS sheet
|
||||
var remainingBorderWidth = 0;
|
||||
var usedBorderWidth = 0;
|
||||
for (var i = 0; i < document.styleSheets[0].cssRules.length; i++) {
|
||||
if (document.styleSheets[0].cssRules[i].selectorText == "#" + elements.barRemaining)
|
||||
remainingBorderWidth = Number.parseInt(document.styleSheets[0].cssRules[i].style.borderWidth) * 2;
|
||||
else if (document.styleSheets[0].cssRules[i].selectorText == "#" + elements.barUsed)
|
||||
usedBorderWidth = Number.parseInt(document.styleSheets[0].cssRules[i].style.borderWidth) * 2;
|
||||
}
|
||||
|
||||
var usage = (response.used / response.quota) * 100;
|
||||
usage = usage.toFixed(0);
|
||||
|
||||
if (usage != 0) {
|
||||
ui.barUsed.style = "display: inline-block; width: calc(" + usage + "% - " + usedBorderWidth + "px);";
|
||||
ui.barUsed.innerHTML = usage + "% Used";
|
||||
}
|
||||
if (usage != 100) {
|
||||
ui.barRemaining.style = "display: inline-block; width: calc(" + (100 - usage) + "% - " + remainingBorderWidth + "px);";
|
||||
ui.barRemaining.innerHTML = (100 - usage) + "% Remaining";
|
||||
}
|
||||
|
||||
ui.transferUsed.innerHTML = response.used + "GB";
|
||||
ui.transferRemaining.innerHTML = (response.quota - response.used) + "GB";
|
||||
ui.transferQuota.innerHTML = response.quota + "GB";
|
||||
};
|
||||
|
||||
var insertNodebalancers = function()
|
||||
{
|
||||
// Insert linodes into tables
|
||||
for (var i = 0; i < data.nodebalancers.length; i++) {
|
||||
if (data.nodebalancers[i].tags.length == 0)
|
||||
ui.nodebalancerTables[""].appendChild(createNodebalancerRow(data.nodebalancers[i], ui.nodebalancerTables[""].children.length % 2));
|
||||
for (var j = 0; j < data.nodebalancers[i].tags.length; j++)
|
||||
ui.nodebalancerTables[data.nodebalancers[i].tags[j]].appendChild(createNodebalancerRow(data.nodebalancers[i], ui.nodebalancerTables[data.nodebalancers[i].tags[j]].children.length % 2));
|
||||
|
||||
data.nodebalancers[i].configs = [];
|
||||
apiGet("/nodebalancers/" + data.nodebalancers[i].id + "/configs", displayConfigs, null);
|
||||
}
|
||||
};
|
||||
|
||||
var setup = function()
|
||||
{
|
||||
// Parse URL parameters
|
||||
data.params = parseParams();
|
||||
|
||||
ui.barRemaining = document.getElementById(elements.barRemaining);
|
||||
ui.barUsed = document.getElementById(elements.barUsed);
|
||||
ui.loading = document.getElementById(elements.loading);
|
||||
ui.nodebalancers = document.getElementById(elements.nodebalancers);
|
||||
ui.transferQuota = document.getElementById(elements.transferQuota);
|
||||
ui.transferRemaining = document.getElementById(elements.transferRemaining);
|
||||
ui.transferUsed = document.getElementById(elements.transferUsed);
|
||||
|
||||
setupHeader();
|
||||
|
||||
// Get linode and transfer info
|
||||
apiGet("/regions", displayRegions, null);
|
||||
apiGet("/account/transfer", displayTransfer, null);
|
||||
var filters = null;
|
||||
if (data.params.tag)
|
||||
filters = {
|
||||
"tags": data.params.tag
|
||||
};
|
||||
apiGet("/nodebalancers", displayNodebalancers, filters);
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
36
nodebalancers/remove/index.shtml
Normal file
36
nodebalancers/remove/index.shtml
Normal file
@ -0,0 +1,36 @@
|
||||
<!--
|
||||
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 - Remove NodeBalancer</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" type="text/css" href="remove.css" />
|
||||
<script src="remove.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!--#include virtual="/include/header.html"-->
|
||||
<div id="main-content" class="wrapper">
|
||||
<div id="top-links"><a href="/nodebalancers">NodeBalancers</a> » <a id="toplink-label" href="/nodebalancers/balancer?nbid=0"></a> » <span class="top-links-title">Remove</span></div>
|
||||
<div id="remove">
|
||||
<p>Are you sure you want to delete the "<span id="label"></span>" NodeBalancer?</p>
|
||||
<button id="delete-button" type="button">Yes, delete this sucker</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
22
nodebalancers/remove/remove.css
Normal file
22
nodebalancers/remove/remove.css
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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');
|
||||
|
||||
#remove {
|
||||
padding: 0px 15px 15px;
|
||||
}
|
85
nodebalancers/remove/remove.js
Normal file
85
nodebalancers/remove/remove.js
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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, apiDelete, parseParams, setupHeader } from "/global.js";
|
||||
|
||||
(function()
|
||||
{
|
||||
// Element names specific to this page
|
||||
elements.deleteButton = "delete-button";
|
||||
elements.label = "label";
|
||||
elements.toplinkLabel = "toplink-label";
|
||||
|
||||
// Data recieved from API calls
|
||||
var data = {};
|
||||
|
||||
// Static references to UI elements
|
||||
var ui = {};
|
||||
ui.deleteButton = {};
|
||||
ui.label = {};
|
||||
ui.toplinkLabel = {};
|
||||
|
||||
// Callback for image API call
|
||||
var displayNodebalancer = function(response)
|
||||
{
|
||||
ui.toplinkLabel.innerHTML = response.label;
|
||||
ui.label.innerHTML = response.label;
|
||||
};
|
||||
|
||||
// Handler for delete button
|
||||
var handleDelete = function(event)
|
||||
{
|
||||
apiDelete("/nodebalancers/" + data.params.nbid, function(response)
|
||||
{
|
||||
location.href = "/nodebalancers";
|
||||
});
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
var setup = function()
|
||||
{
|
||||
// Parse URL parameters
|
||||
data.params = parseParams();
|
||||
|
||||
// We need an image ID, so die if we don't have it
|
||||
if (!data.params.nbid) {
|
||||
alert("No NodeBalancer ID supplied!");
|
||||
return;
|
||||
}
|
||||
|
||||
setupHeader();
|
||||
|
||||
// Update links on page to include proper volume ID
|
||||
var anchors = document.getElementsByTagName("a");
|
||||
for (var i = 0; i < anchors.length; i++)
|
||||
anchors[i].href = anchors[i].href.replace("nbid=0", "nbid=" + data.params.nbid);
|
||||
|
||||
// Get element references
|
||||
ui.deleteButton = document.getElementById(elements.deleteButton);
|
||||
ui.label = document.getElementById(elements.label);
|
||||
ui.toplinkLabel = document.getElementById(elements.toplinkLabel);
|
||||
|
||||
// Attach event handlers
|
||||
ui.deleteButton.addEventListener("click", handleDelete);
|
||||
|
||||
// Get data from API
|
||||
apiGet("/nodebalancers/" + data.params.nbid, displayNodebalancer, null);
|
||||
};
|
||||
|
||||
// Attach onload handler
|
||||
window.addEventListener("load", setup);
|
||||
})();
|
Reference in New Issue
Block a user