Der Test liest Version, Cluster, Nodes, Storages, Bridges, VNets und die effektiven Rechte des Tokens und leitet daraus ab, welcher Vorgang per API, per SSH oder gar nicht geht – je PVE-Version: Template-Bau per API erst ab 8.4 mit import-Storage, Snippets und LXC-Exec nur per SSH. Fehlende Rechte erscheinen einzeln mit Pfad und Zweck; die Rollenliste in docs/proxmox-rechte.md prüft ein Test gegen den Code. SSH-Host-Keys werden wie Zertifikate erst nach Bestätigung genutzt, und bestätigt werden kann nur ein Schlüssel, den der letzte Test gesehen hat. Statussymbole in der GUI sind jetzt gezeichnet: ◐ und ○ fehlen im Web-Subset von JetBrains Mono. Refs #003 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
import { benoetigteRechte, versionLesen } from "@/engine/proxmox/faehigkeiten";
|
|
import type { ProxmoxMock } from "./mock";
|
|
|
|
/**
|
|
* Typische Proxmox-Zustände für Tests. Die Antworten folgen dem Format der echten API
|
|
* (geprüft gegen das API-Schema von PVE 9.2).
|
|
*/
|
|
export type Szenario = {
|
|
version: string;
|
|
cluster?: { name: string; nodes: Array<{ name: string; ip: string; online?: boolean }> };
|
|
/** Storage `local` mit Inhaltstyp import? (ab 8.4 nötig für Template-Bau per API) */
|
|
importStorage?: boolean;
|
|
/** Rechte, die dem Token fehlen sollen. */
|
|
ohneRechte?: string[];
|
|
/** Ohne Sys.Audit auf / antwortet /cluster/status mit 403. */
|
|
ohneClusterStatus?: boolean;
|
|
};
|
|
|
|
export function szenarioEinrichten(mock: ProxmoxMock, szenario: Szenario): void {
|
|
const version = versionLesen(szenario.version)!;
|
|
const nodes = szenario.cluster?.nodes ?? [{ name: "pve01", ip: "127.0.0.1", online: true }];
|
|
const ohne = new Set(szenario.ohneRechte ?? []);
|
|
const alle = benoetigteRechte(version).map((r) => r.recht);
|
|
const rechte = Object.fromEntries(alle.filter((r) => !ohne.has(r)).map((r) => [r, 1]));
|
|
|
|
mock.route("GET", "/version", () => ({
|
|
data: { version: szenario.version, release: `${version.major}.${version.minor}`, repoid: "abcdef" },
|
|
}));
|
|
mock.route("GET", "/cluster/status", () =>
|
|
szenario.ohneClusterStatus
|
|
? { status: 403, statusText: "Permission check failed (/, Sys.Audit)" }
|
|
: {
|
|
data: [
|
|
...(szenario.cluster
|
|
? [
|
|
{
|
|
type: "cluster",
|
|
name: szenario.cluster.name,
|
|
quorate: 1,
|
|
nodes: nodes.length,
|
|
version: 3,
|
|
},
|
|
]
|
|
: []),
|
|
...nodes.map((n, i) => ({
|
|
type: "node",
|
|
name: n.name,
|
|
online: n.online === false ? 0 : 1,
|
|
ip: n.ip,
|
|
local: i === 0 ? 1 : 0,
|
|
nodeid: i + 1,
|
|
})),
|
|
],
|
|
},
|
|
);
|
|
mock.route("GET", "/nodes", () => ({
|
|
data: nodes.map((n) => ({
|
|
node: n.name,
|
|
status: n.online === false ? "offline" : "online",
|
|
cpu: 0.05,
|
|
maxcpu: 16,
|
|
mem: 8 * 1024 ** 3,
|
|
maxmem: 64 * 1024 ** 3,
|
|
uptime: 3600,
|
|
})),
|
|
}));
|
|
mock.route("GET", /^\/nodes\/([^/]+)\/version$/, () => ({
|
|
data: { version: szenario.version, release: `${version.major}.${version.minor}`, repoid: "abcdef" },
|
|
}));
|
|
mock.route("GET", /^\/nodes\/([^/]+)\/storage$/, () => ({
|
|
data: [
|
|
{
|
|
storage: "local",
|
|
type: "dir",
|
|
content: ["iso", "vztmpl", "backup", "snippets", ...(szenario.importStorage ? ["import"] : [])].join(
|
|
",",
|
|
),
|
|
active: 1,
|
|
enabled: 1,
|
|
shared: 0,
|
|
avail: 80 * 1024 ** 3,
|
|
total: 100 * 1024 ** 3,
|
|
},
|
|
{
|
|
storage: "local-zfs",
|
|
type: "zfspool",
|
|
content: "images,rootdir",
|
|
active: 1,
|
|
enabled: 1,
|
|
shared: 0,
|
|
avail: 800 * 1024 ** 3,
|
|
total: 1000 * 1024 ** 3,
|
|
},
|
|
],
|
|
}));
|
|
mock.route("GET", /^\/nodes\/([^/]+)\/network$/, () => ({
|
|
data: [
|
|
{ iface: "eno1", type: "eth", active: 1 },
|
|
{
|
|
iface: "vmbr0",
|
|
type: "bridge",
|
|
active: 1,
|
|
bridge_vlan_aware: 1,
|
|
cidr: "10.0.10.2/24",
|
|
comments: "LAN\n",
|
|
},
|
|
{ iface: "vmbr1", type: "bridge", active: 1 },
|
|
],
|
|
}));
|
|
mock.route("GET", "/cluster/sdn/vnets", () =>
|
|
ohne.has("SDN.Audit")
|
|
? { status: 403, statusText: "Permission check failed (/sdn, SDN.Audit)" }
|
|
: { data: [{ vnet: "dmz", zone: "zone1", tag: 30, alias: "DMZ" }] },
|
|
);
|
|
mock.route("GET", "/access/permissions", (anfrage) => {
|
|
const p = anfrage.abfrage.get("path") ?? "/";
|
|
return { data: { [p]: rechte } };
|
|
});
|
|
}
|