-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
a31c6a9
commit 3ed46ae
Showing
22 changed files
with
1,323 additions
and
424 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 15 additions & 6 deletions
21
packages/cron-jobs/src/jobs/integrations/health-monitoring.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,23 @@ | ||
import { EVERY_5_SECONDS } from "@homarr/cron-jobs-core/expressions"; | ||
import { systemInfoRequestHandler } from "@homarr/request-handler/health-monitoring"; | ||
import { clusterInfoRequestHandler, systemInfoRequestHandler } from "@homarr/request-handler/health-monitoring"; | ||
import { createRequestIntegrationJobHandler } from "@homarr/request-handler/lib/cached-request-integration-job-handler"; | ||
|
||
import { createCronJob } from "../../lib"; | ||
|
||
export const healthMonitoringJob = createCronJob("healthMonitoring", EVERY_5_SECONDS).withCallback( | ||
createRequestIntegrationJobHandler(systemInfoRequestHandler.handler, { | ||
widgetKinds: ["healthMonitoring"], | ||
getInput: { | ||
healthMonitoring: () => ({}), | ||
createRequestIntegrationJobHandler( | ||
(integration, itemOptions: Record<string, never>) => { | ||
const { kind } = integration; | ||
if (kind !== "proxmox") { | ||
return systemInfoRequestHandler.handler({ ...integration, kind }, itemOptions); | ||
} | ||
return clusterInfoRequestHandler.handler({ ...integration, kind }, itemOptions); | ||
}, | ||
}), | ||
{ | ||
widgetKinds: ["healthMonitoring"], | ||
getInput: { | ||
healthMonitoring: () => ({}), | ||
}, | ||
}, | ||
), | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
125 changes: 125 additions & 0 deletions
125
packages/integrations/src/proxmox/proxmox-integration.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.