Show VM status
This commit is contained in:
parent
3c00c23205
commit
7ef5afb978
@ -6,6 +6,17 @@
|
||||
|
||||
import { APIClient } from "./ApiClient";
|
||||
|
||||
export type VMState =
|
||||
| "NoState"
|
||||
| "Running"
|
||||
| "Blocked"
|
||||
| "Paused"
|
||||
| "Shutdown"
|
||||
| "Shutoff"
|
||||
| "Crashed"
|
||||
| "PowerManagementSuspended"
|
||||
| "Other";
|
||||
|
||||
interface VMInfoInterface {
|
||||
name: string;
|
||||
uuid?: string;
|
||||
@ -40,6 +51,10 @@ export class VMInfo implements VMInfoInterface {
|
||||
this.memory = int.memory;
|
||||
this.vnc_access = int.vnc_access;
|
||||
}
|
||||
|
||||
get ViewURL(): string {
|
||||
return `/api/vm/${this.uuid}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class VMApi {
|
||||
@ -55,6 +70,90 @@ export class VMApi {
|
||||
).data.map((i: VMInfoInterface) => new VMInfo(i));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of a VM
|
||||
*/
|
||||
static async GetState(vm: VMInfo): Promise<VMState> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/vm/${vm.uuid}/state`,
|
||||
method: "GET",
|
||||
})
|
||||
).data.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the VM
|
||||
*/
|
||||
static async StartVM(vm: VMInfo): Promise<void> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/vm/${vm.uuid}/start`,
|
||||
method: "GET",
|
||||
})
|
||||
).data.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the VM
|
||||
*/
|
||||
static async ShutdownVM(vm: VMInfo): Promise<void> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/vm/${vm.uuid}/shutdown`,
|
||||
method: "GET",
|
||||
})
|
||||
).data.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restt the VM
|
||||
*/
|
||||
static async ResetVM(vm: VMInfo): Promise<void> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/vm/${vm.uuid}/reset`,
|
||||
method: "GET",
|
||||
})
|
||||
).data.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill the VM
|
||||
*/
|
||||
static async KillVM(vm: VMInfo): Promise<void> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/vm/${vm.uuid}/kill`,
|
||||
method: "GET",
|
||||
})
|
||||
).data.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspend the VM
|
||||
*/
|
||||
static async SuspendVM(vm: VMInfo): Promise<void> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/vm/${vm.uuid}/suspend`,
|
||||
method: "GET",
|
||||
})
|
||||
).data.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume the VM
|
||||
*/
|
||||
static async ResumeVM(vm: VMInfo): Promise<void> {
|
||||
return (
|
||||
await APIClient.exec({
|
||||
uri: `/vm/${vm.uuid}/resume`,
|
||||
method: "GET",
|
||||
})
|
||||
).data.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a virtual machine
|
||||
*/
|
||||
|
@ -123,9 +123,11 @@ function VMListWidget(p: {
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Tooltip title="View this VM">
|
||||
<IconButton>
|
||||
<VisibilityIcon />
|
||||
</IconButton>
|
||||
<RouterLink to={row.ViewURL}>
|
||||
<IconButton>
|
||||
<VisibilityIcon />
|
||||
</IconButton>
|
||||
</RouterLink>
|
||||
</Tooltip>
|
||||
<Tooltip title="Delete this VM">
|
||||
<IconButton onClick={() => deleteVM(row)}>
|
||||
|
@ -1,8 +1,154 @@
|
||||
import { VMInfo } from "../../api/VMApi";
|
||||
import PauseIcon from "@mui/icons-material/Pause";
|
||||
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
||||
import PowerSettingsNewIcon from "@mui/icons-material/PowerSettingsNew";
|
||||
import ReplayIcon from "@mui/icons-material/Replay";
|
||||
import StopIcon from "@mui/icons-material/Stop";
|
||||
import { CircularProgress, IconButton, Tooltip } from "@mui/material";
|
||||
import React from "react";
|
||||
import { VMApi, VMInfo, VMState } from "../../api/VMApi";
|
||||
import { useAlert } from "../../hooks/providers/AlertDialogProvider";
|
||||
import { useConfirm } from "../../hooks/providers/ConfirmDialogProvider";
|
||||
import { useSnackbar } from "../../hooks/providers/SnackbarProvider";
|
||||
|
||||
export function VMStatusWidget(p: {
|
||||
d: VMInfo;
|
||||
onChange?: () => void;
|
||||
onChange?: (s: VMState) => void;
|
||||
}): React.ReactElement {
|
||||
return <>TODO</>;
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
const [state, setState] = React.useState<undefined | VMState>(undefined);
|
||||
|
||||
const refresh = async () => {
|
||||
try {
|
||||
const s = await VMApi.GetState(p.d);
|
||||
if (s !== state) p.onChange?.(s);
|
||||
setState(s);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
snackbar("Failed to refresh VM status!");
|
||||
}
|
||||
};
|
||||
|
||||
const changedAction = () => setState(undefined);
|
||||
|
||||
React.useEffect(() => {
|
||||
const i = setInterval(() => refresh(), 3000);
|
||||
|
||||
return () => clearInterval(i);
|
||||
});
|
||||
|
||||
if (state === undefined)
|
||||
return (
|
||||
<>
|
||||
<CircularProgress size={"1rem"} />
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ display: "inline-flex" }}>
|
||||
{state}
|
||||
|
||||
{/* Start VM */}
|
||||
<ActionButton
|
||||
currState={state}
|
||||
cond={["Shutdown", "Shutoff", "Crashed"]}
|
||||
icon={<PlayArrowIcon />}
|
||||
tooltip="Start the Virtual Machine"
|
||||
performAction={() => VMApi.StartVM(p.d)}
|
||||
onExecuted={changedAction}
|
||||
/>
|
||||
|
||||
{/* Resume VM */}
|
||||
<ActionButton
|
||||
currState={state}
|
||||
cond={["Paused", "PowerManagementSuspended"]}
|
||||
icon={<PlayArrowIcon />}
|
||||
tooltip="Resume the Virtual Machine"
|
||||
performAction={() => VMApi.ResumeVM(p.d)}
|
||||
onExecuted={changedAction}
|
||||
/>
|
||||
|
||||
{/* Suspend VM */}
|
||||
<ActionButton
|
||||
currState={state}
|
||||
cond={["Running"]}
|
||||
icon={<PauseIcon />}
|
||||
tooltip="Suspend the Virtual Machine"
|
||||
confirmMessage="Do you really want to supsend this VM?"
|
||||
performAction={() => VMApi.SuspendVM(p.d)}
|
||||
onExecuted={changedAction}
|
||||
/>
|
||||
|
||||
{/* Shutdown VM */}
|
||||
<ActionButton
|
||||
currState={state}
|
||||
cond={["Running"]}
|
||||
icon={<PowerSettingsNewIcon />}
|
||||
tooltip="Shutdown the Virtual Machine"
|
||||
confirmMessage="Do you really want to shutdown this VM?"
|
||||
performAction={() => VMApi.ShutdownVM(p.d)}
|
||||
onExecuted={changedAction}
|
||||
/>
|
||||
|
||||
{/* Kill VM */}
|
||||
<ActionButton
|
||||
currState={state}
|
||||
cond={["Running", "Paused", "PowerManagementSuspended", "Blocked"]}
|
||||
icon={<StopIcon />}
|
||||
tooltip="Kill the Virtual Machine"
|
||||
confirmMessage="Do you really want to kill this VM? This could lead to data loss / corruption!"
|
||||
performAction={() => VMApi.KillVM(p.d)}
|
||||
onExecuted={changedAction}
|
||||
/>
|
||||
|
||||
{/* Reset VM */}
|
||||
<ActionButton
|
||||
currState={state}
|
||||
cond={["Running", "Paused", "PowerManagementSuspended", "Blocked"]}
|
||||
icon={<ReplayIcon />}
|
||||
tooltip="Reset the Virtual Machine"
|
||||
confirmMessage="Do you really want to reset this VM?"
|
||||
performAction={() => VMApi.ResetVM(p.d)}
|
||||
onExecuted={changedAction}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ActionButton(p: {
|
||||
currState: VMState;
|
||||
cond: VMState[];
|
||||
icon: React.ReactElement;
|
||||
tooltip: string;
|
||||
confirmMessage?: string;
|
||||
performAction: () => Promise<void>;
|
||||
onExecuted: () => void;
|
||||
}): React.ReactElement {
|
||||
const confirm = useConfirm();
|
||||
const alert = useAlert();
|
||||
|
||||
if (!p.cond.includes(p.currState)) return <></>;
|
||||
|
||||
const performAction = async () => {
|
||||
try {
|
||||
if (p.confirmMessage && !(await confirm(p.confirmMessage))) return;
|
||||
await p.performAction();
|
||||
p.onExecuted();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert("Failed to perform action! " + e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Tooltip title={p.tooltip}>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={performAction}
|
||||
style={{ paddingBottom: "0px", paddingTop: "0px" }}
|
||||
>
|
||||
{p.icon}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user