/* * 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 . */ 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); })();