/* * 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, apiGet, apiPost, countryContinents, migrateETA, parseParams, regionNames, setupHeader, translateKernel } from "/global.js"; (function() { // Element names specific to this page elements.cloneButton = "clone-button"; elements.configCloneTable = "config-clone-table"; elements.configDiskRow = "config-disk-row"; elements.configsNone = "configs-none"; elements.configTable = "config-table-body"; elements.dcOther = "dc-other"; elements.destBackups = "dest-backups"; elements.destBackupsPrice = "dest-backups-price"; elements.destLabel = "dest-label"; elements.destLinode = "dest-linode"; elements.destLocation = "dest-location"; elements.destPlan = "dest-plan"; elements.destPlanPrice = "dest-plan-price"; elements.diskCloneTable = "disk-clone-table"; elements.disksNone = "disks-none"; elements.diskTable = "disk-table-body"; elements.etaLocal = "eta-local"; elements.etaRemote = "eta-remote"; elements.etaRow = "eta-row"; elements.linodeLabel = "linode-label"; elements.linodeTag = "linode-tag"; elements.linodeTagLink = "linode-tag-link"; elements.lmcRow = "lmc-tr1"; elements.lmcRowAlt = "lmc-tr2"; elements.lmcRowThree = "lmc-tr3"; elements.loadingConfigs = "loading-configs"; elements.loadingDisks = "loading-disks"; elements.newLinode = "new-linode"; elements.sourceLocation = "source-location"; // Data recieved from API calls var data = {}; data.configs = []; data.disks = []; data.linode = {}; data.regions = []; data.types = []; // Static references to UI elements var ui = {}; ui.cloneButton = {}; ui.configCloneTable = {}; ui.configsNone = {}; ui.configTable = {}; ui.dcOther = {}; ui.destBackups = {}; ui.destBackupsPrice = {}; ui.destLabel = {}; ui.destLinode = {}; ui.destLocation = {}; ui.destPlan = {}; ui.destPlanPrice = {}; ui.diskCloneTable = {}; ui.disksNone = {}; ui.diskTable = {}; ui.etaLocal = {}; ui.etaRemote = {}; ui.etaRow = {}; ui.linodeLabel = {}; ui.linodeTag = {}; ui.linodeTagLink = {}; ui.loadingConfigs = {}; ui.loadingDisks = {}; ui.newLinode = []; ui.sourceLocation = {}; // Temporary state var state = {}; state.configs = []; state.disks = []; // Generate a config profile table row for clone section var createCloneConfigRow = function(config) { var row = document.createElement("tr"); row.className = elements.lmcRowThree + " " + elements.configDiskRow; var label = document.createElement("td"); label.colSpan = ui.configsNone.children[0].colSpan; label.innerHTML = config.label; row.appendChild(label); return row; }; // Create a disk table row for clone section var createCloneDiskRow = function(disk) { var row = document.createElement("tr"); row.className = elements.lmcRowThree + " " + elements.configDiskRow; // Label var label = document.createElement("td"); label.innerHTML = disk.label; row.appendChild(label); // Size var size = document.createElement("td"); size.innerHTML = disk.size + " MB"; row.appendChild(size); // Local copy ETA var local = document.createElement("td"); local.innerHTML = migrateETA(disk.size, true); row.appendChild(local); // Remote copy ETA var remote = document.createElement("td"); remote.innerHTML = migrateETA(disk.size, false); row.appendChild(remote); return row; }; // Generate a config profile table row var createConfigRow = function(config, alt) { var row = document.createElement("tr"); if (alt) row.className = elements.lmcRowAlt; else row.className = elements.lmcRow; // Select checkbox var selectCell = document.createElement("td"); var select = document.createElement("input"); select.id = "config-" + config.id; select.type = "checkbox"; select.addEventListener("input", handleSelect); selectCell.appendChild(select); row.appendChild(selectCell); // Config profile name var nameCell = document.createElement("td"); nameCell.innerHTML = config.label; row.appendChild(nameCell); // Kernel var kernel = document.createElement("td"); translateKernel(config.kernel, kernel); row.appendChild(kernel); // Number of disks var disks = document.createElement("td"); var numDisks = 0; for (var disk in config.devices) { if (config.devices[disk] && config.devices[disk].disk_id) numDisks++; } disks.innerHTML = numDisks; row.appendChild(disks); return row; }; // Generate a disk table row var createDiskRow = function(disk, alt) { var row = document.createElement("tr"); if (alt) row.className = elements.lmcRowAlt; else row.className = elements.lmcRow; // Select checkbox var selectCell = document.createElement("td"); var select = document.createElement("input"); select.id = "disk-" + disk.id; select.type = "checkbox"; select.addEventListener("input", handleSelect); selectCell.appendChild(select); row.appendChild(selectCell); // Disk name var nameCell = document.createElement("td"); nameCell.innerHTML = disk.label; row.appendChild(nameCell); // Disk type var typeCell = document.createElement("td"); typeCell.innerHTML = disk.filesystem; row.appendChild(typeCell); // Disk size var sizeCell = document.createElement("td"); sizeCell.innerHTML = disk.size + " MB"; row.appendChild(sizeCell); return row; }; // Callback for config profiles 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 if (response.page != response.pages) { apiGet("/linode/instances/" + data.params.lid + "/configs?page=" + (response.page + 1), displayConfigs, null); return; } // Remove loading row ui.loadingConfigs.remove(); // Insert config profile rows into table for (var i = 0; i < data.configs.length; i++) ui.configTable.appendChild(createConfigRow(data.configs[i], i % 2)); }; // Callback for linode details API call var displayDetails = function(response) { data.linode = response; // Set page title and header stuff document.title += " // " + data.linode.label; ui.linodeLabel.innerHTML = data.linode.label; if (data.linode.tags.length == 1) { ui.linodeTagLink.href = "/linodes?tag=" + data.linode.tags[0]; ui.linodeTagLink.innerHTML = "(" + data.linode.tags[0] + ")"; ui.linodeTag.style.display = "inline"; } // Display the source location var region = null; for (var i = 0; i < data.regions.length; i++) { if (data.regions[i].id == data.linode.region) { region = data.regions[i]; break; } } if (region && region.label && region.label.length) ui.sourceLocation.innerHTML = region.label; else if (regionNames[data.linode.region]) ui.sourceLocation.innerHTML = regionNames[data.linode.region]; else ui.sourceLocation.innerHTML = data.linode.region; }; // Callback for linode disks API call var displayDisks = function(response) { // Add disks to array data.disks = data.disks.concat(response.data); // Request the next page if there are more pages if (response.page != response.pages) { apiGet("/linode/instances/" + data.params.lid + "/disks?page=" + (response.page + 1), displayDisks, null); return; } // Remove loading row ui.loadingDisks.remove(); // Insert disk rows into table for (var i = 0; i < data.disks.length; i++) ui.diskTable.appendChild(createDiskRow(data.disks[i], i % 2)); }; // Callback for linodes API call var displayLinodes = function(response) { // Add linodes to selector for (var i = 0; i < response.data.length; i++) { if (response.data[i].id == data.params.lid) continue; var linode = document.createElement("option"); linode.value = response.data[i].id; linode.innerHTML = response.data[i].label; var optgroup = document.getElementById(response.data[i].region); optgroup.appendChild(linode); optgroup.style.display = "initial"; } // Request the next page if there are more pages if (response.page != response.pages) apiGet("/linode/instances?page=" + (response.page + 1), displayLinodes, null); }; // 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), displayRegions, null); return; } for (var i = 0; i < data.regions.length; i++) { // Add regions to selector var dc = document.createElement("option"); dc.value = data.regions[i].id; if (data.regions[i].label && data.regions[i].label.length) dc.innerHTML = data.regions[i].label; else if (regionNames[data.regions[i].id]) dc.innerHTML = regionNames[data.regions[i].id]; else dc.innerHTML = data.regions[i].id; var group = null; if (countryContinents[data.regions[i].country]) group = document.getElementById(countryContinents[data.regions[i].country]); if (!group) group = ui.dcOther; group.style.display = "initial"; group.appendChild(dc); // Add optgroups to linode selector var optgroup = document.createElement("optgroup"); optgroup.id = data.regions[i].id; if (data.regions[i].label && data.regions[i].label.length) optgroup.label = data.regions[i].label; else if (regionNames[data.regions[i].id]) optgroup.label = regionNames[data.regions[i].id]; else optgroup.label = data.regions[i].label; ui.destLinode.appendChild(optgroup); } apiGet("/linode/instances/" + data.params.lid, displayDetails, null); apiGet("/linode/instances", displayLinodes, null); }; // Callback for linode types API call var displayTypes = function(response) { // Add types to array data.types = data.types.concat(response.data); // Request the next page if there are more pages if (response.page != response.pages) { apiGet("/linode/types?page=" + (response.page + 1), displayTypes, null); return; } // Insert types into selector for (var i = 0; i < data.types.length; i++) { var type = document.createElement("option"); type.value = data.types[i].id; type.innerHTML = data.types[i].label; ui.destPlan.appendChild(type); } updatePrices(null); }; // Clone button handler var handleClone = function(event) { if (event.currentTarget.disabled) return; var req = { "configs": state.configs, "disks": state.disks }; var destLinode = parseInt(ui.destLinode.value); if (destLinode == 0) { req.region = ui.destLocation.value; req.type = ui.destPlan.value; if (ui.destLabel.value.length) req.label = ui.destLabel.value; req.backups_enabled = ui.destBackups.checked; } else { req.linode_id = destLinode; } apiPost("/linode/instances/" + data.params.lid + "/clone", req, function(response) { location.href = "/linodes/dashboard?lid=" + data.params.lid; }); }; // Handler for checkbox selections var handleSelect = function(event) { // Determine whether we're working with configs or disks var arr = state.configs; if (event.currentTarget.id.split("-")[0] == "disk") arr = state.disks; // Get the ID of the thing var id = parseInt(event.currentTarget.id.split("-")[1]); var index = arr.indexOf(id); // If the box is checked, add the ID, otherwise remove it if (event.currentTarget.checked && index == -1) arr.push(id); else if (index != -1) arr.splice(index, 1); updateClone(); }; // Initial setup var setup = function() { // Parse URL parameters data.params = parseParams(); // We need a Linode ID, so die if we don't have it if (!data.params.lid) { alert("No Linode ID supplied!"); return; } setupHeader(); // Update links on page to include proper Linode ID var anchors = document.getElementsByTagName("a"); for (var i = 0; i < anchors.length; i++) anchors[i].href = anchors[i].href.replace("lid=0", "lid=" + data.params.lid); // Get element references ui.cloneButton = document.getElementById(elements.cloneButton); ui.configCloneTable = document.getElementById(elements.configCloneTable); ui.configsNone = document.getElementById(elements.configsNone); ui.configTable = document.getElementById(elements.configTable); ui.dcOther = document.getElementById(elements.dcOther); ui.destBackups = document.getElementById(elements.destBackups); ui.destBackupsPrice = document.getElementById(elements.destBackupsPrice); ui.destLabel = document.getElementById(elements.destLabel); ui.destLinode = document.getElementById(elements.destLinode); ui.destLocation = document.getElementById(elements.destLocation); ui.destPlan = document.getElementById(elements.destPlan); ui.destPlanPrice = document.getElementById(elements.destPlanPrice); ui.diskCloneTable = document.getElementById(elements.diskCloneTable); ui.disksNone = document.getElementById(elements.disksNone); ui.diskTable = document.getElementById(elements.diskTable); ui.etaLocal = document.getElementById(elements.etaLocal); ui.etaRemote = document.getElementById(elements.etaRemote); ui.etaRow = document.getElementById(elements.etaRow); ui.linodeLabel = document.getElementById(elements.linodeLabel); ui.linodeTag = document.getElementById(elements.linodeTag); ui.linodeTagLink = document.getElementById(elements.linodeTagLink); ui.loadingConfigs = document.getElementById(elements.loadingConfigs); ui.loadingDisks = document.getElementById(elements.loadingDisks); ui.newLinode = document.getElementsByClassName(elements.newLinode); ui.sourceLocation = document.getElementById(elements.sourceLocation); // Register event handlers ui.cloneButton.addEventListener("click", handleClone); ui.destLinode.addEventListener("input", updateDestination); ui.destPlan.addEventListener("input", updatePrices); // Get data from API apiGet("/linode/instances/" + data.params.lid + "/configs", displayConfigs, null); apiGet("/linode/instances/" + data.params.lid + "/disks", displayDisks, null); apiGet("/linode/types", displayTypes, null); apiGet("/regions", displayRegions, null); }; // Update clone config and disk info var updateClone = function() { // Remove all config and disk rows var configDiskRows = document.getElementsByClassName(elements.configDiskRow); for (var i = configDiskRows.length - 1; i >= 0; i--) configDiskRows[i].remove(); var allDisks = []; // Loop through configs and create rows for (var i = 0; i < state.configs.length; i++) { // Find config var config = null; for (var j = 0; j < data.configs.length; j++) { if (state.configs[i] == data.configs[j].id) { config = data.configs[j]; break; } } if (!config) continue; // Add config to table ui.configCloneTable.appendChild(createCloneConfigRow(config)); // Loop through config's disks and add them to array for (var disk in config.devices) { if (config.devices[disk] && config.devices[disk].disk_id && allDisks.indexOf(config.devices[disk].disk_id) == -1) allDisks.push(config.devices[disk].disk_id); } } // Add independent disks to array for (var i = 0; i < state.disks.length; i++) { if (allDisks.indexOf(state.disks[i]) == -1) allDisks.push(state.disks[i]); } // Loop through disks and create rows var totalSize = 0; for (var i = 0; i < allDisks.length; i++) { // Find disk var disk = null; for (var j = 0; j < data.disks.length; j++) { if (allDisks[i] == data.disks[j].id) { disk = data.disks[j]; break; } } if (!disk) continue; totalSize += disk.size; // Add disk to table ui.diskCloneTable.insertBefore(createCloneDiskRow(disk), ui.etaRow); } // Show/hide the "none" rows if (state.configs.length > 0) ui.configsNone.style.display = "none"; else ui.configsNone.style.display = "table-row"; if (allDisks.length > 0) ui.disksNone.style.display = "none"; else ui.disksNone.style.display = "table-row"; // Compute total ETAs ui.etaLocal.innerHTML = migrateETA(totalSize, true); ui.etaRemote.innerHTML = migrateETA(totalSize, false); }; // Update destination form var updateDestination = function(event) { var display = "none"; if (ui.destLinode.value == "0") display = "table-row"; for (var i = 0; i < ui.newLinode.length; i++) ui.newLinode[i].style.display = display; }; // Update price labels var updatePrices = function(event) { // Find the selected type var type = null; for (var i = 0; i < data.types.length; i++) { if (data.types[i].id == ui.destPlan.value) { type = data.types[i]; break; } } if (!type) return; ui.destPlanPrice.innerHTML = type.price.monthly; ui.destBackupsPrice.innerHTML = type.addons.backups.price.monthly; }; // Attach onload handler window.addEventListener("load", setup); })();