minor optimizations to instance card config and console actions
This commit is contained in:
@@ -53,22 +53,25 @@ type Node struct {
|
|||||||
|
|
||||||
// used in constructing instance cards in index
|
// used in constructing instance cards in index
|
||||||
type InstanceCard struct {
|
type InstanceCard struct {
|
||||||
VMID uint
|
VMID uint
|
||||||
Name string
|
Name string
|
||||||
Type string
|
Type string
|
||||||
Status string
|
Status string
|
||||||
Node string
|
Node string
|
||||||
NodeStatus string
|
NodeStatus string
|
||||||
|
ConfigPath string
|
||||||
|
ConsolePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// used in retriving cluster tasks
|
// used in retriving cluster tasks
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Type string
|
Type string
|
||||||
Node string
|
Node string
|
||||||
User string
|
User string
|
||||||
ID string
|
ID string
|
||||||
VMID uint
|
VMID uint
|
||||||
Status string
|
Status string
|
||||||
|
EndTime uint
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstanceStatus struct {
|
type InstanceStatus struct {
|
||||||
@@ -116,6 +119,12 @@ func GetClusterResources(auth common.Auth) (map[uint]InstanceCard, map[string]No
|
|||||||
for vmid, instance := range instances {
|
for vmid, instance := range instances {
|
||||||
nodestatus := nodes[instance.Node].Status
|
nodestatus := nodes[instance.Node].Status
|
||||||
instance.NodeStatus = nodestatus
|
instance.NodeStatus = nodestatus
|
||||||
|
instance.ConfigPath = fmt.Sprintf("config?node=%s&type=%s&vmid=%d", instance.Node, instance.Type, instance.VMID)
|
||||||
|
if instance.Type == "qemu" {
|
||||||
|
instance.ConsolePath = fmt.Sprintf("%s/?console=kvm&vmid=%d&vmname=%s&node=%s&resize=off&cmd=&novnc=1", common.Global.PVE, instance.VMID, instance.Name, instance.Node)
|
||||||
|
} else if instance.Type == "lxc" {
|
||||||
|
instance.ConsolePath = fmt.Sprintf("%s/?console=lxc&vmid=%d&vmname=%s&node=%s&resize=off&cmd=&xtermjs=1", common.Global.PVE, instance.VMID, instance.Name, instance.Node)
|
||||||
|
}
|
||||||
instances[vmid] = instance
|
instances[vmid] = instance
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +137,9 @@ func GetClusterResources(auth common.Auth) (map[uint]InstanceCard, map[string]No
|
|||||||
return nil, nil, fmt.Errorf("request to /cluster/tasks resulted in %+v", res)
|
return nil, nil, fmt.Errorf("request to /cluster/tasks resulted in %+v", res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
most_recent_task := map[uint]uint{}
|
||||||
|
expected_state := map[uint]string{}
|
||||||
|
|
||||||
for _, v := range ctx.Body["data"].([]any) {
|
for _, v := range ctx.Body["data"].([]any) {
|
||||||
task := Task{}
|
task := Task{}
|
||||||
err := mapstructure.Decode(v, &task)
|
err := mapstructure.Decode(v, &task)
|
||||||
@@ -151,8 +163,21 @@ func GetClusterResources(auth common.Auth) (map[uint]InstanceCard, map[string]No
|
|||||||
} else if !(task.Status == "running" || task.Status == "OK") { // task is not running or finished with status OK
|
} else if !(task.Status == "running" || task.Status == "OK") { // task is not running or finished with status OK
|
||||||
continue
|
continue
|
||||||
} else { // recent task is a start or stop task for user instance which is running or "OK"
|
} else { // recent task is a start or stop task for user instance which is running or "OK"
|
||||||
|
if task.EndTime > most_recent_task[task.VMID] { // if the task's end time is later than the most recent one encountered
|
||||||
|
most_recent_task[task.VMID] = task.EndTime // update the most recent task
|
||||||
|
if task.Type == "qmstart" || task.Type == "vzstart" { // if the task was a start task, update the expected state to running
|
||||||
|
expected_state[task.VMID] = "running"
|
||||||
|
} else if task.Type == "qmstop" || task.Type == "vzstop" { // if the task was a stop task, update the expected state to stopped
|
||||||
|
expected_state[task.VMID] = "stopped"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for vmid, expected_state := range expected_state { // for the expected states from recent tasks
|
||||||
|
if instances[vmid].Status != expected_state { // if the current node's state from /cluster/resources differs from expected state
|
||||||
// get /status/current which is updated faster than /cluster/resources
|
// get /status/current which is updated faster than /cluster/resources
|
||||||
instance := instances[task.VMID]
|
instance := instances[vmid]
|
||||||
path := fmt.Sprintf("/proxmox/nodes/%s/%s/%d/status/current", instance.Node, instance.Type, instance.VMID)
|
path := fmt.Sprintf("/proxmox/nodes/%s/%s/%d/status/current", instance.Node, instance.Type, instance.VMID)
|
||||||
ctx.Body = map[string]any{}
|
ctx.Body = map[string]any{}
|
||||||
res, code, err := common.RequestGetAPI(path, ctx)
|
res, code, err := common.RequestGetAPI(path, ctx)
|
||||||
@@ -167,7 +192,7 @@ func GetClusterResources(auth common.Auth) (map[uint]InstanceCard, map[string]No
|
|||||||
mapstructure.Decode(ctx.Body["data"], &status)
|
mapstructure.Decode(ctx.Body["data"], &status)
|
||||||
|
|
||||||
instance.Status = status.Status
|
instance.Status = status.Status
|
||||||
instances[task.VMID] = instance
|
instances[vmid] = instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
<main>
|
<main>
|
||||||
<section>
|
<section>
|
||||||
<h2><a href="index">Instances</a> / {{.config.Name}}</h2>
|
<h2><a href="index">Instances</a> / {{.config.Name}}</h2>
|
||||||
<form>
|
<form id="config-form">
|
||||||
<fieldset class="w3-card w3-padding">
|
<fieldset class="w3-card w3-padding">
|
||||||
<legend>Resources</legend>
|
<legend>Resources</legend>
|
||||||
<div class="input-grid" id="resources" style="grid-template-columns: auto auto auto 1fr;">
|
<div class="input-grid" id="resources" style="grid-template-columns: auto auto auto 1fr;">
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
{{end}}
|
{{end}}
|
||||||
<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="submit">EXIT</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
@@ -19,7 +19,7 @@ async function init () {
|
|||||||
initNetworks();
|
initNetworks();
|
||||||
initDevices();
|
initDevices();
|
||||||
|
|
||||||
document.querySelector("#exit").addEventListener("click", handleFormExit);
|
document.querySelector("#config-form").addEventListener("submit", handleFormExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
class VolumeAction extends HTMLElement {
|
class VolumeAction extends HTMLElement {
|
||||||
@@ -530,7 +530,8 @@ async function refreshBoot () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleFormExit () {
|
async function handleFormExit (event) {
|
||||||
|
event.preventDefault();
|
||||||
const body = {
|
const body = {
|
||||||
cores: document.querySelector("#cores").value,
|
cores: document.querySelector("#cores").value,
|
||||||
memory: document.querySelector("#ram").value
|
memory: document.querySelector("#ram").value
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { requestPVE, requestAPI, goToPage, setAppearance, getSearchSettings, goToURL, requestDash, setSVGSrc, setSVGAlt } from "./utils.js";
|
import { requestPVE, requestAPI, setAppearance, getSearchSettings, requestDash, setSVGSrc, setSVGAlt } from "./utils.js";
|
||||||
import { alert, dialog } from "./dialog.js";
|
import { alert, dialog } from "./dialog.js";
|
||||||
import { setupClientSync } from "./clientsync.js";
|
import { setupClientSync } from "./clientsync.js";
|
||||||
import wfaInit from "../modules/wfa.js";
|
import wfaInit from "../modules/wfa.js";
|
||||||
@@ -124,17 +124,6 @@ class InstanceCard extends HTMLElement {
|
|||||||
powerButton.onclick = this.handlePowerButton.bind(this);
|
powerButton.onclick = this.handlePowerButton.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const configButton = this.shadowRoot.querySelector("#configure-btn");
|
|
||||||
if (configButton.classList.contains("clickable")) {
|
|
||||||
configButton.onclick = this.handleConfigButton.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
const consoleButton = this.shadowRoot.querySelector("#console-btn");
|
|
||||||
if (consoleButton.classList.contains("clickable")) {
|
|
||||||
consoleButton.classList.add("clickable");
|
|
||||||
consoleButton.onclick = this.handleConsoleButton.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteButton = this.shadowRoot.querySelector("#delete-btn");
|
const deleteButton = this.shadowRoot.querySelector("#delete-btn");
|
||||||
if (deleteButton.classList.contains("clickable")) {
|
if (deleteButton.classList.contains("clickable")) {
|
||||||
deleteButton.onclick = this.handleDeleteButton.bind(this);
|
deleteButton.onclick = this.handleDeleteButton.bind(this);
|
||||||
@@ -186,20 +175,6 @@ class InstanceCard extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConfigButton () {
|
|
||||||
if (!this.actionLock && this.status === "stopped") { // if the action lock is false, and the node is stopped, then navigate to the config page with the node info in the search query
|
|
||||||
goToPage("config", { node: this.node.name, type: this.type, vmid: this.vmid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConsoleButton () {
|
|
||||||
if (!this.actionLock && this.status === "running") {
|
|
||||||
const data = { console: `${this.type === "qemu" ? "kvm" : "lxc"}`, vmid: this.vmid, vmname: this.name, node: this.node.name, resize: "off", cmd: "" };
|
|
||||||
data[`${this.type === "qemu" ? "novnc" : "xtermjs"}`] = 1;
|
|
||||||
goToURL(window.PVE, data, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleDeleteButton () {
|
handleDeleteButton () {
|
||||||
if (!this.actionLock && this.status === "stopped") {
|
if (!this.actionLock && this.status === "stopped") {
|
||||||
const header = `Delete VM ${this.vmid}`;
|
const header = `Delete VM ${this.vmid}`;
|
||||||
|
@@ -114,20 +114,6 @@ export function goToPage (page, data = null) {
|
|||||||
window.location.href = `${page}${data ? "?" : ""}${params}`;
|
window.location.href = `${page}${data ? "?" : ""}${params}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function goToURL (href, data = {}, newwindow = false) {
|
|
||||||
const url = new URL(href);
|
|
||||||
for (const k in data) {
|
|
||||||
url.searchParams.append(k, data[k]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newwindow) {
|
|
||||||
window.open(url, document.title, "height=480,width=848");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
window.location.assign(url.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getURIData () {
|
export function getURIData () {
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
return Object.fromEntries(url.searchParams);
|
return Object.fromEntries(url.searchParams);
|
||||||
|
@@ -41,11 +41,15 @@
|
|||||||
{{if and (eq .NodeStatus "online") (eq .Status "running")}}
|
{{if and (eq .NodeStatus "online") (eq .Status "running")}}
|
||||||
<svg id="power-btn" class="clickable" aria-label="shutdown instance"><use href="images/actions/instance/stop.svg#symb"></svg>
|
<svg id="power-btn" class="clickable" aria-label="shutdown instance"><use href="images/actions/instance/stop.svg#symb"></svg>
|
||||||
<svg id="configure-btn" aria-label=""><use href="images/actions/instance/config-inactive.svg#symb"></svg>
|
<svg id="configure-btn" aria-label=""><use href="images/actions/instance/config-inactive.svg#symb"></svg>
|
||||||
<svg id="console-btn" class="clickable" aria-label="open console"><use href="images/actions/instance/console-active.svg#symb"></svg>
|
<a href="{{.ConsolePath}}" target="_blank">
|
||||||
|
<svg id="console-btn" class="clickable" aria-label="open console"><use href="images/actions/instance/console-active.svg#symb"></svg>
|
||||||
|
</a>
|
||||||
<svg id="delete-btn" aria-label=""><use href="images/actions/instance/delete-inactive.svg#symb"></svg>
|
<svg id="delete-btn" aria-label=""><use href="images/actions/instance/delete-inactive.svg#symb"></svg>
|
||||||
{{else if and (eq .NodeStatus "online") (eq .Status "stopped")}}
|
{{else if and (eq .NodeStatus "online") (eq .Status "stopped")}}
|
||||||
<svg id="power-btn" class="clickable" aria-label="start instance"><use href="images/actions/instance/start.svg#symb"></svg>
|
<svg id="power-btn" class="clickable" aria-label="start instance"><use href="images/actions/instance/start.svg#symb"></svg>
|
||||||
<svg id="configure-btn" class="clickable" aria-label="change configuration"><use href="images/actions/instance/config-active.svg#symb"></svg>
|
<a href="{{.ConfigPath}}">
|
||||||
|
<svg id="configure-btn" class="clickable" aria-label="change configuration"><use href="images/actions/instance/config-active.svg#symb"></svg>
|
||||||
|
</a>
|
||||||
<svg id="console-btn" aria-label=""><use href="images/actions/instance/console-inactive.svg#symb"></svg>
|
<svg id="console-btn" aria-label=""><use href="images/actions/instance/console-inactive.svg#symb"></svg>
|
||||||
<svg id="delete-btn" class="clickable" aria-label="delete instance"><use href="images/actions/instance/delete-active.svg#symb"></svg>
|
<svg id="delete-btn" class="clickable" aria-label="delete instance"><use href="images/actions/instance/delete-active.svg#symb"></svg>
|
||||||
{{else if and (eq .NodeStatus "online") (eq .Status "loading")}}
|
{{else if and (eq .NodeStatus "online") (eq .Status "loading")}}
|
||||||
|
Reference in New Issue
Block a user