lmc/nodebalancers/balancer/balancer.js

412 lines
13 KiB
JavaScript

/*
* This file is part of Linode Manager Classic.
*
* Linode Manager Classic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Linode Manager Classic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Linode Manager Classic. If not, see <https://www.gnu.org/licenses/>.
*/
import { settings, elements, 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);
})();