From 177fa97b76b23512301a43e4ea494cd480914cbb Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 5 Oct 2026 14:51:48 +0200 Subject: [PATCH] feat: support Unraid storage pools (#7038) --- apps/docs/docs/integrations/unraid/index.mdx | 2 + .../src/unraid/unraid-integration.ts | 42 ++++++++++++++----- .../integrations/src/unraid/unraid-types.ts | 28 ++++++++----- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/apps/docs/docs/integrations/unraid/index.mdx b/apps/docs/docs/integrations/unraid/index.mdx index 60c97d9a2..aedc0da9d 100644 --- a/apps/docs/docs/integrations/unraid/index.mdx +++ b/apps/docs/docs/integrations/unraid/index.mdx @@ -25,6 +25,8 @@ import imgSelectApikeyPreset from "./imgs/unraid-select-api-key-preset.png"; CPU usage is averaged across logical processors, including SMT threads. +Storage includes array data disks and storage pools, including pool-only setups. Pool capacity is shown for the member reporting filesystem statistics; members with missing statistics and zero-size filesystems are omitted. Parity and boot devices are excluded. + ### Adding the integration diff --git a/packages/integrations/src/unraid/unraid-integration.ts b/packages/integrations/src/unraid/unraid-integration.ts index 0c26b8134..25b26d0ba 100644 --- a/packages/integrations/src/unraid/unraid-integration.ts +++ b/packages/integrations/src/unraid/unraid-integration.ts @@ -46,6 +46,21 @@ export class UnraidIntegration extends Integration implements ISystemHealthMonit const totalMemory = systemInfo.metrics.memory.total; const usedMemory = Math.max(totalMemory - systemInfo.metrics.memory.available, 0); const uptime = dayjs(systemInfo.info.os.uptime); + const disks = [...systemInfo.array.disks, ...systemInfo.array.caches]; + const fileSystem = disks.flatMap((disk) => { + // Only the pool member reporting filesystem statistics represents its capacity. + if (disk.fsSize === null || disk.fsFree === null || disk.fsUsed === null) return []; + if (disk.fsSize === 0) return []; + + return [ + { + deviceName: disk.name ?? disk.device ?? disk.id, + used: `${disk.fsUsed * 1024}`, // API filesystem sizes are in KiB. + available: `${disk.fsFree * 1024}`, + percentage: (disk.fsUsed / disk.fsSize) * 100, + }, + ]; + }); return { version: systemInfo.info.os.release, @@ -59,16 +74,11 @@ export class UnraidIntegration extends Integration implements ISystemHealthMonit rebootRequired: false, availablePkgUpdates: 0, cpuTemp: undefined, // Not implemented, see https://github.com/unraid/api/issues/1597 - fileSystem: systemInfo.array.disks.map((disk) => ({ - deviceName: disk.name, - used: `${disk.fsUsed * 1024}`, // API is in KiB (kibibytes), convert to bytes - available: `${(disk.size - disk.fsUsed) * 1024}`, // free space left on the disk, API is in KiB (kibibytes) - percentage: (disk.fsUsed / disk.size) * 100, // The units are the same, therefore the actual unit is irrelevant - })), - smart: systemInfo.array.disks.map((disk) => ({ - deviceName: disk.name, + fileSystem, + smart: disks.map((disk) => ({ + deviceName: disk.name ?? disk.device ?? disk.id, temperature: disk.temp ?? null, - overallStatus: disk.status, + overallStatus: disk.status ?? "UNKNOWN", // See ArrayDiskStatus from https://studio.apollographql.com/public/Unraid-API/variant/current/explorer healthy: disk.status === "DISK_OK", })), @@ -111,8 +121,20 @@ export class UnraidIntegration extends Integration implements ISystemHealthMonit } } disks { + id name - size + device + fsSize + fsFree + fsUsed + status + temp + } + caches { + id + name + device + fsSize fsFree fsUsed status diff --git a/packages/integrations/src/unraid/unraid-types.ts b/packages/integrations/src/unraid/unraid-types.ts index 877c364ff..119fd8202 100644 --- a/packages/integrations/src/unraid/unraid-types.ts +++ b/packages/integrations/src/unraid/unraid-types.ts @@ -1,5 +1,21 @@ import z from "zod"; +const filesystemSizeSchema = z + .union([z.number(), z.string().regex(/^\d+$/).transform(Number)]) + .pipe(z.number().int().min(0).max(Number.MAX_SAFE_INTEGER)) + .nullable(); + +const unraidDiskSchema = z.object({ + id: z.string(), + name: z.string().nullable(), + device: z.string().nullable(), + fsSize: filesystemSizeSchema, + fsFree: filesystemSizeSchema, + fsUsed: filesystemSizeSchema, + status: z.string().nullable(), + temp: z.number().nullish(), +}); + export const unraidSystemInfoSchema = z.object({ metrics: z.object({ cpu: z.object({ @@ -27,16 +43,8 @@ export const unraidSystemInfoSchema = z.object({ used: z.coerce.number(), }), }), - disks: z.array( - z.object({ - name: z.string(), - size: z.number(), - fsFree: z.number(), - fsUsed: z.number(), - status: z.string(), - temp: z.number().nullish(), - }), - ), + disks: z.array(unraidDiskSchema), + caches: z.array(unraidDiskSchema), }), info: z.object({ devices: z.object({