feat(widget): add proxmox integration (#1969)
* feat(widget): add proxmox integration * fix: broken lock file * fix: ci issues * fix: ci issues * fix: ci issues * chore: debug temporary * fix: name is not used correctly for nodes and storage in proxmox * fix: remove temporary debu logs * fix: job runs for both cluster and system health and throws error * fix: ts-expect-error is unnecessary * fix: remove unused import
This commit is contained in:
@@ -23,6 +23,7 @@ import { OverseerrIntegration } from "../overseerr/overseerr-integration";
|
||||
import { PiHoleIntegration } from "../pi-hole/pi-hole-integration";
|
||||
import { PlexIntegration } from "../plex/plex-integration";
|
||||
import { ProwlarrIntegration } from "../prowlarr/prowlarr-integration";
|
||||
import { ProxmoxIntegration } from "../proxmox/proxmox-integration";
|
||||
import type { Integration, IntegrationInput } from "./integration";
|
||||
|
||||
export const integrationCreator = <TKind extends keyof typeof integrationCreators>(
|
||||
@@ -72,4 +73,5 @@ export const integrationCreators = {
|
||||
readarr: ReadarrIntegration,
|
||||
dashDot: DashDotIntegration,
|
||||
tdarr: TdarrIntegration,
|
||||
proxmox: ProxmoxIntegration,
|
||||
} satisfies Record<IntegrationKind, new (integration: IntegrationInput) => Integration>;
|
||||
|
||||
125
packages/integrations/src/proxmox/proxmox-integration.ts
Normal file
125
packages/integrations/src/proxmox/proxmox-integration.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import type { Proxmox } from "proxmox-api";
|
||||
import proxmoxApi from "proxmox-api";
|
||||
|
||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/certificates/server";
|
||||
import { extractErrorMessage } from "@homarr/common";
|
||||
import { logger } from "@homarr/log";
|
||||
|
||||
import { Integration } from "../base/integration";
|
||||
import { IntegrationTestConnectionError } from "../base/test-connection-error";
|
||||
import type {
|
||||
ComputeResourceBase,
|
||||
LxcResource,
|
||||
NodeResource,
|
||||
QemuResource,
|
||||
Resource,
|
||||
StorageResource,
|
||||
} from "./proxmox-types";
|
||||
|
||||
export class ProxmoxIntegration extends Integration {
|
||||
public async testConnectionAsync(): Promise<void> {
|
||||
const proxmox = this.getPromoxApi();
|
||||
await proxmox.nodes.$get().catch((error) => {
|
||||
throw new IntegrationTestConnectionError("internalServerError", extractErrorMessage(error));
|
||||
});
|
||||
}
|
||||
|
||||
public async getClusterInfoAsync() {
|
||||
const proxmox = this.getPromoxApi();
|
||||
const resources = await proxmox.cluster.resources.$get();
|
||||
|
||||
logger.info(
|
||||
`Found ${resources.length} resources in Proxmox cluster node=${resources.filter((resource) => resource.type === "node").length} lxc=${resources.filter((resource) => resource.type === "lxc").length} qemu=${resources.filter((resource) => resource.type === "qemu").length} storage=${resources.filter((resource) => resource.type === "storage").length}`,
|
||||
);
|
||||
|
||||
const mappedResources = resources.map(mapResource).filter((resource) => resource !== null);
|
||||
return {
|
||||
nodes: mappedResources.filter((resource): resource is NodeResource => resource.type === "node"),
|
||||
lxcs: mappedResources.filter((resource): resource is LxcResource => resource.type === "lxc"),
|
||||
vms: mappedResources.filter((resource): resource is QemuResource => resource.type === "qemu"),
|
||||
storages: mappedResources.filter((resource): resource is StorageResource => resource.type === "storage"),
|
||||
};
|
||||
}
|
||||
|
||||
private getPromoxApi() {
|
||||
return proxmoxApi({
|
||||
host: this.url("/").host,
|
||||
tokenID: `${this.getSecretValue("username")}@${this.getSecretValue("realm")}!${this.getSecretValue("tokenId")}`,
|
||||
tokenSecret: this.getSecretValue("apiKey"),
|
||||
fetch: fetchWithTrustedCertificatesAsync,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const mapResource = (resource: Proxmox.clusterResourcesResources): Resource | null => {
|
||||
switch (resource.type) {
|
||||
case "node":
|
||||
return mapNodeResource(resource);
|
||||
case "lxc":
|
||||
case "qemu":
|
||||
return mapVmResource(resource);
|
||||
case "storage":
|
||||
return mapStorageResource(resource);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const mapComputeResource = (resource: Proxmox.clusterResourcesResources): Omit<ComputeResourceBase<string>, "type"> => {
|
||||
return {
|
||||
cpu: {
|
||||
utilization: resource.cpu ?? 0,
|
||||
cores: resource.maxcpu ?? 0,
|
||||
},
|
||||
memory: {
|
||||
used: resource.mem ?? 0,
|
||||
total: resource.maxmem ?? 0,
|
||||
},
|
||||
storage: {
|
||||
used: resource.disk ?? 0,
|
||||
total: resource.maxdisk ?? 0,
|
||||
read: (resource.diskread as number | null) ?? null,
|
||||
write: (resource.diskwrite as number | null) ?? null,
|
||||
},
|
||||
network: {
|
||||
in: (resource.netin as number | null) ?? null,
|
||||
out: (resource.netout as number | null) ?? null,
|
||||
},
|
||||
haState: resource.hastate ?? null,
|
||||
isRunning: resource.status === "running" || resource.status === "online",
|
||||
name: resource.name ?? "",
|
||||
node: resource.node ?? "",
|
||||
status: resource.status ?? (resource.type === "node" ? "offline" : "stopped"),
|
||||
uptime: resource.uptime ?? 0,
|
||||
};
|
||||
};
|
||||
|
||||
const mapNodeResource = (resource: Proxmox.clusterResourcesResources): NodeResource => {
|
||||
return {
|
||||
type: "node",
|
||||
...mapComputeResource(resource),
|
||||
name: resource.node ?? "",
|
||||
};
|
||||
};
|
||||
|
||||
const mapVmResource = (resource: Proxmox.clusterResourcesResources): LxcResource | QemuResource => {
|
||||
return {
|
||||
type: resource.type as "lxc" | "qemu",
|
||||
vmId: resource.vmid ?? 0,
|
||||
...mapComputeResource(resource),
|
||||
};
|
||||
};
|
||||
|
||||
const mapStorageResource = (resource: Proxmox.clusterResourcesResources): StorageResource => {
|
||||
return {
|
||||
type: "storage",
|
||||
name: resource.storage ?? "",
|
||||
node: resource.node ?? "",
|
||||
isRunning: resource.status === "available",
|
||||
status: resource.status ?? "offline",
|
||||
storagePlugin: resource.storage ?? "",
|
||||
total: resource.maxdisk ?? 0,
|
||||
used: resource.disk ?? 0,
|
||||
isShared: resource.shared === 1,
|
||||
};
|
||||
};
|
||||
57
packages/integrations/src/proxmox/proxmox-types.ts
Normal file
57
packages/integrations/src/proxmox/proxmox-types.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
interface ResourceBase<TType extends string> {
|
||||
type: TType;
|
||||
name: string;
|
||||
node: string;
|
||||
isRunning: boolean;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface ComputeResourceBase<TType extends string> extends ResourceBase<TType> {
|
||||
cpu: {
|
||||
utilization: number; // previously cpu (0-1)
|
||||
cores: number; // previously cpuCores
|
||||
};
|
||||
memory: {
|
||||
used: number; // previously mem
|
||||
total: number; // previously maxMem
|
||||
};
|
||||
storage: {
|
||||
used: number; // previously disk
|
||||
total: number; // previously maxDisk
|
||||
read: number | null; // previously diskRead
|
||||
write: number | null; // previously diskWrite
|
||||
};
|
||||
network: {
|
||||
in: number | null; // previously netIn
|
||||
out: number | null; // previously netOut
|
||||
};
|
||||
uptime: number; // expressed in seconds
|
||||
haState: string | null; // HA service status (for HA managed VMs).
|
||||
}
|
||||
|
||||
export type NodeResource = ComputeResourceBase<"node">;
|
||||
|
||||
export interface LxcResource extends ComputeResourceBase<"lxc"> {
|
||||
vmId: number;
|
||||
}
|
||||
|
||||
export interface QemuResource extends ComputeResourceBase<"qemu"> {
|
||||
vmId: number;
|
||||
}
|
||||
|
||||
export interface StorageResource extends ResourceBase<"storage"> {
|
||||
storagePlugin: string;
|
||||
used: number; // previously disk
|
||||
total: number; // previously maxDisk
|
||||
isShared: boolean; // previously storageShared
|
||||
}
|
||||
|
||||
export type ComputeResource = NodeResource | LxcResource | QemuResource;
|
||||
export type Resource = ComputeResource | StorageResource;
|
||||
|
||||
export interface ProxmoxClusterInfo {
|
||||
nodes: NodeResource[];
|
||||
lxcs: LxcResource[];
|
||||
vms: QemuResource[];
|
||||
storages: StorageResource[];
|
||||
}
|
||||
@@ -6,3 +6,4 @@ export * from "./interfaces/media-requests/media-request";
|
||||
export * from "./pi-hole/pi-hole-types";
|
||||
export * from "./base/searchable-integration";
|
||||
export * from "./homeassistant/homeassistant-types";
|
||||
export * from "./proxmox/proxmox-types";
|
||||
|
||||
Reference in New Issue
Block a user