add network interface to config,

improve w3-card styling

Signed-off-by: Arthur Lu <learthurgo@gmail.com>
This commit is contained in:
Arthur Lu 2023-05-15 21:22:12 +00:00
parent bbc67fa6fb
commit 6530240677
8 changed files with 86 additions and 14 deletions

View File

@ -31,7 +31,7 @@
<p id="vmid">VMID Range:</p> <p id="vmid">VMID Range:</p>
<p id="nodes">Nodes:</p> <p id="nodes">Nodes:</p>
</div> </div>
<div class="w3-card w3-padding w3-margin-top"> <div class="w3-card w3-padding">
<h3>Cluster Resources</h3> <h3>Cluster Resources</h3>
<table id="resource-table" class="w3-table w3-table-all w3-mobile" style="overflow-x: auto; margin-bottom: 1em;"> <table id="resource-table" class="w3-table w3-table-all w3-mobile" style="overflow-x: auto; margin-bottom: 1em;">
<thead> <thead>

View File

@ -28,9 +28,9 @@
<form> <form>
<fieldset class="w3-card w3-padding"> <fieldset class="w3-card w3-padding">
<span><legend>Resources</legend></span> <span><legend>Resources</legend></span>
<div class="input-grid" id="resources" style="grid-template-columns: repeat(3, auto) 1fr;"></div> <div class="input-grid" id="resources" style="grid-template-columns: auto auto auto 1fr;"></div>
</fieldset> </fieldset>
<fieldset class="w3-card w3-padding" style="margin-top: 16px;"> <fieldset class="w3-card w3-padding">
<span><legend>Disks</legend></span> <span><legend>Disks</legend></span>
<div class="input-grid" id="disks" style="grid-template-columns: auto auto auto 1fr;"></div> <div class="input-grid" id="disks" style="grid-template-columns: auto auto auto 1fr;"></div>
<div class="w3-container w3-center"> <div class="w3-container w3-center">
@ -38,6 +38,10 @@
<img id="cd-add" src="images/actions/disk/add-cd.svg" class="clickable none" alt="Add New CDROM" title="Add New CDROM"> <img id="cd-add" src="images/actions/disk/add-cd.svg" class="clickable none" alt="Add New CDROM" title="Add New CDROM">
</div> </div>
</fieldset> </fieldset>
<fieldset class="w3-card w3-padding">
<span><legend>Network Interface</legend></span>
<div class="input-grid" id="networks" style="grid-template-columns: auto auto auto 1fr;"></div>
</fieldset>
<div class="w3-container w3-center" id="form-actions"> <div class="w3-container w3-center" id="form-actions">
<button class="w3-button w3-margin" id="exit" type="button">EXIT</button> <button class="w3-button w3-margin" id="exit" type="button">EXIT</button>
</div> </div>

View File

@ -47,6 +47,10 @@ main, dialog {
box-shadow: var(--main-card-box-shadow); box-shadow: var(--main-card-box-shadow);
} }
.w3-card + .w3-card {
margin-top: 16px;
}
th { th {
background-color: var(--main-table-header-bg-color); background-color: var(--main-table-header-bg-color);
} }

View File

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 229.034 229.034"><style>:root{color:#fff}@media (prefers-color-scheme:light){:root{color:#000}}</style><path d="M218.411 167.068l-70.305-70.301 9.398-35.073a7.504 7.504 0 00-1.94-7.245L103.311 2.197A7.499 7.499 0 0096.066.256L56.812 10.774a7.502 7.502 0 00-3.362 12.548l39.259 39.262-6.364 23.756-23.751 6.363-39.263-39.26a7.498 7.498 0 00-7.245-1.94 7.498 7.498 0 00-5.303 5.303L.266 96.059a7.5 7.5 0 001.941 7.244l52.249 52.255a7.5 7.5 0 007.245 1.941l35.076-9.4 70.302 70.306c6.854 6.854 15.968 10.628 25.662 10.629h.001c9.695 0 18.81-3.776 25.665-10.631 14.153-14.151 14.156-37.178.004-51.335zM207.8 207.795a21.15 21.15 0 01-15.058 6.239h-.002a21.153 21.153 0 01-15.056-6.236l-73.363-73.367a7.5 7.5 0 00-7.245-1.942L62 141.889 15.875 95.758l6.035-22.523 33.139 33.137a7.499 7.499 0 007.244 1.941l32.116-8.604a7.5 7.5 0 005.304-5.304l8.606-32.121a7.5 7.5 0 00-1.941-7.244L73.242 21.901l22.524-6.036 46.128 46.129-9.398 35.073a7.5 7.5 0 001.941 7.245l73.365 73.361c8.305 8.307 8.304 21.819-.002 30.122z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><style>:root{color:#fff}@media (prefers-color-scheme:light){:root{color:#000}}</style><g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12H2M5.45 5.11L2 12v6a2 2 0 002 2h16a2 2 0 002-2v-6l-3.45-6.89A2 2 0 0016.76 4H7.24a2 2 0 00-1.79 1.11zM6 16h0M10 16h0"/></g></svg> <svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><style>:root{color:#fff}@media (prefers-color-scheme:light){:root{color:#000}}</style><path d="M22 12H2m3.45-6.89L2 12v6a2 2 0 002 2h16a2 2 0 002-2v-6l-3.45-6.89A2 2 0 0016.76 4H7.24a2 2 0 00-1.79 1.11zM6 16h0m4 0h0" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 405 B

View File

@ -0,0 +1 @@
<svg width="800" height="800" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><style>:root{color:#fff}@media (prefers-color-scheme:light){:root{color:#000}}</style><path d="M9 7V5H7v2H5v4h6V7H9zm-9 9h16V0H0v16zm2-2V2h12v12H2z" fill="currentColor" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 281 B

View File

@ -3,7 +3,8 @@ import {alert, dialog} from "./dialog.js";
window.addEventListener("DOMContentLoaded", init); // do the dumb thing where the disk config refreshes every second window.addEventListener("DOMContentLoaded", init); // do the dumb thing where the disk config refreshes every second
let diskMetaData = resources.disk; let diskMetaData = resources_config.disk;
let networkMetaData = resources_config.network;
let node; let node;
let type; let type;
@ -25,10 +26,16 @@ async function init () {
populateResources(); populateResources();
populateDisk(); populateDisk();
populateNetworks();
document.querySelector("#exit").addEventListener("click", handleFormExit); document.querySelector("#exit").addEventListener("click", handleFormExit);
} }
function getOrdered(keys){
let ordered_keys = Object.keys(keys).sort((a,b) => {parseInt(a) - parseInt(b)}); // ordered integer list
return ordered_keys;
}
async function getConfig () { async function getConfig () {
config = await requestPVE(`/nodes/${node}/${type}/${vmid}/config`, "GET"); config = await requestPVE(`/nodes/${node}/${type}/${vmid}/config`, "GET");
} }
@ -36,11 +43,11 @@ async function getConfig () {
function populateResources () { function populateResources () {
let name = type === "qemu" ? "name" : "hostname"; let name = type === "qemu" ? "name" : "hostname";
document.querySelector("#name").innerHTML = document.querySelector("#name").innerHTML.replace("%{vmname}", config.data[name]); document.querySelector("#name").innerHTML = document.querySelector("#name").innerHTML.replace("%{vmname}", config.data[name]);
addResourceLine("resources", "images/resources/cpu.svg", "Cores", {type: "number", value: config.data.cores, min: 1, max: 8192}, "Threads"); // TODO add max from quota API addResourceLine("resources", "images/resources/cpu.svg", "Cores", {type: "number", value: config.data.cores, min: 1, max: 8192}, "Threads");
addResourceLine("resources", "images/resources/ram.svg", "Memory", {type: "number", value: config.data.memory, min: 16, step: 1}, "MiB"); // TODO add max from quota API addResourceLine("resources", "images/resources/ram.svg", "Memory", {type: "number", value: config.data.memory, min: 16, step: 1}, "MiB");
if (type === "lxc") { if (type === "lxc") {
addResourceLine("resources", "images/resources/swap.svg", "Swap", {type: "number", value: config.data.swap, min: 0, step: 1}, "MiB"); // TODO add max from quota API addResourceLine("resources", "images/resources/swap.svg", "Swap", {type: "number", value: config.data.swap, min: 0, step: 1}, "MiB");
} }
} }
@ -86,7 +93,7 @@ function populateDisk () {
disks[element.replace(prefix, "")] = config.data[element]; disks[element.replace(prefix, "")] = config.data[element];
} }
}); });
let ordered_keys = getOrderedUsed(disks); let ordered_keys = getOrdered(disks);
ordered_keys.forEach(element => { ordered_keys.forEach(element => {
let disk = disks[element]; let disk = disks[element];
addDiskLine("disks", prefix, busName, element, disk); addDiskLine("disks", prefix, busName, element, disk);
@ -100,11 +107,6 @@ function populateDisk () {
} }
} }
function getOrderedUsed(disks){
let ordered_keys = Object.keys(disks).sort((a,b) => {parseInt(a) - parseInt(b)}); // ordered integer list
return ordered_keys;
}
function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) { function addDiskLine (fieldset, busPrefix, busName, device, diskDetails) {
let field = document.querySelector(`#${fieldset}`); let field = document.querySelector(`#${fieldset}`);
@ -428,6 +430,63 @@ async function handleCDAdd () {
}); });
} }
function populateNetworks () {
document.querySelector("#networks").innerHTML = "";
let networks = {};
let prefix = networkMetaData.prefix;
Object.keys(config.data).forEach(element => {
if (element.startsWith(prefix)) {
networks[element.replace(prefix, "")] = config.data[element];
}
});
let ordered_keys = getOrdered(networks);
ordered_keys.forEach(element => {
addNetworkLine("networks", `${prefix}${element}`, networks[element]);
});
}
function addNetworkLine (fieldset, netID, netDetails) {
let field = document.querySelector(`#${fieldset}`);
let icon = document.createElement("img");
icon.src = "images/resources/network.svg";
icon.alt = netID;
icon.dataset.network = netID;
field.appendChild(icon);
let netLabel = document.createElement("label");
netLabel.innerText = netID;
netLabel.dataset.network = netID;
field.append(netLabel);
let netDesc = document.createElement("p");
netDesc.innerText = netDetails;
netDesc.dataset.network = netID;
field.append(netDesc);
let actionDiv = document.createElement("div");
actionDiv.classList.add("last-item");
let action = document.createElement("img");
action.classList.add("clickable");
action.src = `images/actions/network/config.svg`;
action.title = "Config Network";
action.addEventListener("click", handleNetworkConfig);
action.dataset.network = netID;
actionDiv.appendChild(action);
field.append(actionDiv);
}
async function handleNetworkConfig () {
let netID = this.dataset.network;
let header = `Edit ${netID}`;
let body = ``;
dialog(header, body, async (result, form) => {
if (result === "confirm") {
}
});
}
async function handleFormExit () { async function handleFormExit () {
let body = { let body = {
node: node, node: node,

View File

@ -22,6 +22,9 @@ export const resources_config = {
sata: {name: "SATA", icon: "images/resources/drive.svg", actions: ["detach", "move", "reassign", "resize"]}, sata: {name: "SATA", icon: "images/resources/drive.svg", actions: ["detach", "move", "reassign", "resize"]},
unused: {name: "UNUSED", icon: "images/resources/drive.svg", actions: ["attach", "delete", "reassign"]} unused: {name: "UNUSED", icon: "images/resources/drive.svg", actions: ["attach", "delete", "reassign"]}
} }
},
network: {
prefix: "net"
} }
} }