diff --git a/agent/pve.go b/agent/pve.go index a7d6660d..bc4b1212 100644 --- a/agent/pve.go +++ b/agent/pve.go @@ -123,6 +123,9 @@ func (pm *pveManager) getPVEStats() ([]*container.PveNodeStats, error) { resourceStats.MaxCPU = resource.MaxCPU resourceStats.MaxMem = resource.MaxMem resourceStats.Uptime = resource.Uptime + resourceStats.DiskRead = resource.DiskRead + resourceStats.DiskWrite = resource.DiskWrite + resourceStats.Disk = resource.MaxDisk // prevent first run from sending all prev sent/recv bytes total_sent := resource.NetOut @@ -143,6 +146,8 @@ func (pm *pveManager) getPVEStats() ([]*container.PveNodeStats, error) { resourceStats.Cpu = twoDecimals(100.0 * resource.CPU * float64(resource.MaxCPU) / float64(pm.cpuCount)) resourceStats.Mem = bytesToMegabytes(float64(resource.Mem)) resourceStats.Bandwidth = [2]uint64{uint64(sent_delta), uint64(recv_delta)} + resourceStats.NetOut = total_sent + resourceStats.NetIn = total_recv stats = append(stats, resourceStats) } diff --git a/internal/entities/container/container.go b/internal/entities/container/container.go index 97c10338..454c7341 100644 --- a/internal/entities/container/container.go +++ b/internal/entities/container/container.go @@ -157,8 +157,13 @@ type PveNodeStats struct { // fields used for pve_vms table - MaxCPU uint64 `json:"-" cbor:"10,keyasint,omitzero"` // PVE: max vCPU count - MaxMem uint64 `json:"-" cbor:"11,keyasint,omitzero"` // PVE: max memory bytes - Uptime uint64 `json:"-" cbor:"12,keyasint,omitzero"` // PVE: uptime in seconds - Type string `json:"-" cbor:"13,keyasint,omitzero"` // PVE: resource type (e.g. "qemu" or "lxc") + MaxCPU uint64 `json:"-" cbor:"10,keyasint,omitzero"` // PVE: max vCPU count + MaxMem uint64 `json:"-" cbor:"11,keyasint,omitzero"` // PVE: max memory bytes + Uptime uint64 `json:"-" cbor:"12,keyasint,omitzero"` // PVE: uptime in seconds + Type string `json:"-" cbor:"13,keyasint,omitzero"` // PVE: resource type (e.g. "qemu" or "lxc") + DiskRead uint64 `json:"-" cbor:"14,keyasint,omitzero"` // PVE: cumulative disk read bytes + DiskWrite uint64 `json:"-" cbor:"15,keyasint,omitzero"` // PVE: cumulative disk write bytes + Disk uint64 `json:"-" cbor:"16,keyasint,omitzero"` // PVE: allocated disk size in bytes + NetOut uint64 `json:"-" cbor:"17,keyasint,omitzero"` // PVE: cumulative bytes sent by VM + NetIn uint64 `json:"-" cbor:"18,keyasint,omitzero"` // PVE: cumulative bytes received by VM } diff --git a/internal/hub/systems/system.go b/internal/hub/systems/system.go index 2630cfbf..3ae44348 100644 --- a/internal/hub/systems/system.go +++ b/internal/hub/systems/system.go @@ -367,7 +367,7 @@ func createPVEVMRecords(app core.App, data []*container.PveNodeStats, systemId s valueStrings := make([]string, 0, len(data)) for i, vm := range data { suffix := fmt.Sprintf("%d", i) - valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:type%[1]s}, {:cpu%[1]s}, {:mem%[1]s}, {:net%[1]s}, {:maxcpu%[1]s}, {:maxmem%[1]s}, {:uptime%[1]s}, {:updated})", suffix)) + valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:type%[1]s}, {:cpu%[1]s}, {:mem%[1]s}, {:netout%[1]s}, {:netin%[1]s}, {:maxcpu%[1]s}, {:maxmem%[1]s}, {:uptime%[1]s}, {:diskread%[1]s}, {:diskwrite%[1]s}, {:disk%[1]s}, {:updated})", suffix)) params["id"+suffix] = makeStableHashId(systemId, vm.Id) params["name"+suffix] = vm.Name params["type"+suffix] = vm.Type // "qemu" or "lxc" @@ -376,11 +376,14 @@ func createPVEVMRecords(app core.App, data []*container.PveNodeStats, systemId s params["maxcpu"+suffix] = vm.MaxCPU params["maxmem"+suffix] = vm.MaxMem params["uptime"+suffix] = vm.Uptime - netBytes := vm.Bandwidth[0] + vm.Bandwidth[1] - params["net"+suffix] = netBytes + params["diskread"+suffix] = vm.DiskRead + params["diskwrite"+suffix] = vm.DiskWrite + params["disk"+suffix] = vm.Disk + params["netout"+suffix] = vm.NetOut // cumulative bytes sent by VM + params["netin"+suffix] = vm.NetIn // cumulative bytes received by VM } queryString := fmt.Sprintf( - "INSERT INTO pve_vms (id, system, name, type, cpu, mem, net, maxcpu, maxmem, uptime, updated) VALUES %s ON CONFLICT(id) DO UPDATE SET system=excluded.system, name=excluded.name, type=excluded.type, cpu=excluded.cpu, mem=excluded.mem, net=excluded.net, maxcpu=excluded.maxcpu, maxmem=excluded.maxmem, uptime=excluded.uptime, updated=excluded.updated", + "INSERT INTO pve_vms (id, system, name, type, cpu, mem, netout, netin, maxcpu, maxmem, uptime, diskread, diskwrite, disk, updated) VALUES %s ON CONFLICT(id) DO UPDATE SET system=excluded.system, name=excluded.name, type=excluded.type, cpu=excluded.cpu, mem=excluded.mem, netout=excluded.netout, netin=excluded.netin, maxcpu=excluded.maxcpu, maxmem=excluded.maxmem, uptime=excluded.uptime, diskread=excluded.diskread, diskwrite=excluded.diskwrite, disk=excluded.disk, updated=excluded.updated", strings.Join(valueStrings, ","), ) _, err := app.DB().NewQuery(queryString).Bind(params).Execute() diff --git a/internal/migrations/0_collections_snapshot_0_19_0_dev_2.go b/internal/migrations/0_collections_snapshot_0_19_0_dev_3.go similarity index 97% rename from internal/migrations/0_collections_snapshot_0_19_0_dev_2.go rename to internal/migrations/0_collections_snapshot_0_19_0_dev_3.go index 1d0db753..73612e97 100644 --- a/internal/migrations/0_collections_snapshot_0_19_0_dev_2.go +++ b/internal/migrations/0_collections_snapshot_0_19_0_dev_3.go @@ -1847,30 +1847,6 @@ func init() { "system": false, "type": "number" }, - { - "hidden": false, - "id": "pve_vms_mem001", - "max": null, - "min": 0, - "name": "mem", - "onlyInt": false, - "presentable": false, - "required": false, - "system": false, - "type": "number" - }, - { - "hidden": false, - "id": "pve_vms_net001", - "max": null, - "min": null, - "name": "net", - "onlyInt": false, - "presentable": false, - "required": false, - "system": false, - "type": "number" - }, { "hidden": false, "id": "number1253106325", @@ -1883,6 +1859,18 @@ func init() { "system": false, "type": "number" }, + { + "hidden": false, + "id": "pve_vms_mem001", + "max": null, + "min": 0, + "name": "mem", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, { "hidden": false, "id": "number1693954525", @@ -1895,6 +1883,66 @@ func init() { "system": false, "type": "number" }, + { + "hidden": false, + "id": "number208985346", + "max": null, + "min": null, + "name": "disk", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number4125810518", + "max": null, + "min": null, + "name": "diskread", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number752404475", + "max": null, + "min": null, + "name": "diskwrite", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number1880667380", + "max": null, + "min": null, + "name": "netout", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number2702533949", + "max": null, + "min": null, + "name": "netin", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, { "hidden": false, "id": "number1563400775", @@ -1932,7 +1980,6 @@ func init() { "updateRule": null, "viewRule": null } - ]` err := app.ImportCollectionsByMarshaledJSON([]byte(jsonData), false) diff --git a/internal/site/src/components/pve-table/pve-table-columns.tsx b/internal/site/src/components/pve-table/pve-table-columns.tsx index 598e942f..fc4c1147 100644 --- a/internal/site/src/components/pve-table/pve-table-columns.tsx +++ b/internal/site/src/components/pve-table/pve-table-columns.tsx @@ -3,9 +3,9 @@ import { Button } from "@/components/ui/button" import { cn, decimalString, formatBytes, hourWithSeconds, toFixedFloat } from "@/lib/utils" import type { PveVmRecord } from "@/types" import { - ArrowUpDownIcon, ClockIcon, CpuIcon, + HardDriveIcon, MemoryStickIcon, MonitorIcon, ServerIcon, @@ -42,7 +42,7 @@ export const pveVmCols: ColumnDef[] = [ accessorFn: (record) => record.name, header: ({ column }) => , cell: ({ getValue }) => { - return {getValue() as string} + return {getValue() as string} }, }, { @@ -57,7 +57,7 @@ export const pveVmCols: ColumnDef[] = [ header: ({ column }) => , cell: ({ getValue }) => { const allSystems = useStore($allSystemsById) - return {allSystems[getValue() as string]?.name ?? ""} + return {allSystems[getValue() as string]?.name ?? ""} }, }, { @@ -68,7 +68,7 @@ export const pveVmCols: ColumnDef[] = [ cell: ({ getValue }) => { const type = getValue() as string return ( - + {type} ) @@ -81,7 +81,7 @@ export const pveVmCols: ColumnDef[] = [ header: ({ column }) => , cell: ({ getValue }) => { const val = getValue() as number - return {`${decimalString(val, val >= 10 ? 1 : 2)}%`} + return {`${decimalString(val, val >= 10 ? 1 : 2)}%`} }, }, { @@ -93,41 +93,86 @@ export const pveVmCols: ColumnDef[] = [ const val = getValue() as number const formatted = formatBytes(val, false, undefined, true) return ( - {`${decimalString(formatted.value, formatted.value >= 10 ? 1 : 2)} ${formatted.unit}`} + {`${decimalString(formatted.value, formatted.value >= 10 ? 1 : 2)} ${formatted.unit}`} ) }, }, { - id: "net", - accessorFn: (record) => record.net, + id: "maxmem", + accessorFn: (record) => record.maxmem, + header: ({ column }) => , invertSorting: true, - header: ({ column }) => , + cell: ({ getValue }) => { + // maxmem is stored in bytes; convert to MB for formatBytes + const formatted = formatBytes(getValue() as number, false, undefined, false) + return {`${toFixedFloat(formatted.value, 2)} ${formatted.unit}`} + }, + }, + { + id: "disk", + accessorFn: (record) => record.disk, + invertSorting: true, + header: ({ column }) => , + cell: ({ getValue }) => { + const formatted = formatBytes(getValue() as number, false, undefined, false) + return {`${toFixedFloat(formatted.value, 2)} ${formatted.unit}`} + }, + }, + { + id: "diskread", + accessorFn: (record) => record.diskread, + invertSorting: true, + header: ({ column }) => , cell: ({ getValue }) => { const val = getValue() as number - const formatted = formatBytes(val, true, undefined, false) + const formatted = formatBytes(val, false, undefined, false) + return {`${toFixedFloat(formatted.value, 2)} ${formatted.unit}`} + }, + }, + { + id: "diskwrite", + accessorFn: (record) => record.diskwrite, + invertSorting: true, + header: ({ column }) => , + cell: ({ getValue }) => { + const val = getValue() as number + const formatted = formatBytes(val, false, undefined, false) + return {`${toFixedFloat(formatted.value, 2)} ${formatted.unit}`} + }, + }, + { + id: "netin", + accessorFn: (record) => record.netin, + invertSorting: true, + header: ({ column }) => , + cell: ({ getValue }) => { + const val = getValue() as number + const formatted = formatBytes(val, false, undefined, false) return ( - {`${decimalString(formatted.value, formatted.value >= 10 ? 1 : 2)} ${formatted.unit}`} + {`${decimalString(formatted.value, formatted.value >= 10 ? 1 : 2)} ${formatted.unit}`} + ) + }, + }, + { + id: "netout", + accessorFn: (record) => record.netout, + invertSorting: true, + header: ({ column }) => , + cell: ({ getValue }) => { + const val = getValue() as number + const formatted = formatBytes(val, false, undefined, false) + return ( + {`${decimalString(formatted.value, formatted.value >= 10 ? 1 : 2)} ${formatted.unit}`} ) }, }, { id: "maxcpu", accessorFn: (record) => record.maxcpu, - header: ({ column }) => , + header: ({ column }) => , invertSorting: true, cell: ({ getValue }) => { - return {getValue() as number} - }, - }, - { - id: "maxmem", - accessorFn: (record) => record.maxmem, - header: ({ column }) => , - invertSorting: true, - cell: ({ getValue }) => { - // maxmem is stored in bytes; convert to MB for formatBytes - const formatted = formatBytes(getValue() as number, false, undefined, false) - return {`${toFixedFloat(formatted.value, 2)} ${formatted.unit}`} + return {getValue() as number} }, }, { @@ -136,7 +181,7 @@ export const pveVmCols: ColumnDef[] = [ invertSorting: true, header: ({ column }) => , cell: ({ getValue }) => { - return {formatUptime(getValue() as number)} + return {formatUptime(getValue() as number)} }, }, { @@ -146,7 +191,7 @@ export const pveVmCols: ColumnDef[] = [ header: ({ column }) => , cell: ({ getValue }) => { const timestamp = getValue() as number - return {hourWithSeconds(new Date(timestamp).toISOString())} + return {hourWithSeconds(new Date(timestamp).toISOString())} }, }, ] @@ -164,7 +209,7 @@ function HeaderButton({ column, name, Icon }: { column: Column; nam > {Icon && } {name} - + {/* */} ) } diff --git a/internal/site/src/components/pve-table/pve-table.tsx b/internal/site/src/components/pve-table/pve-table.tsx index 3c82c88f..cd68430a 100644 --- a/internal/site/src/components/pve-table/pve-table.tsx +++ b/internal/site/src/components/pve-table/pve-table.tsx @@ -46,7 +46,6 @@ export default function PveTable({ systemId }: { systemId?: string }) { function fetchData(systemId?: string) { pb.collection("pve_vms") .getList(0, 2000, { - fields: "id,name,type,cpu,mem,net,maxcpu,maxmem,uptime,system,updated", filter: systemId ? pb.filter("system={:system}", { system: systemId }) : undefined, }) .then(({ items }) => { @@ -145,7 +144,7 @@ export default function PveTable({ systemId }: { systemId?: string }) {
- All Proxmox VMs + Proxmox Resources CPU is percent of overall host CPU usage. @@ -259,7 +258,11 @@ function PveVmSheet({ const memFormatted = formatBytes(vm.mem, false, undefined, true) const maxMemFormatted = formatBytes(vm.maxmem, false, undefined, false) - const netFormatted = formatBytes(vm.net, true, undefined, false) + const netoutFormatted = formatBytes(vm.netout, false, undefined, false) + const netinFormatted = formatBytes(vm.netin, false, undefined, false) + const diskReadFormatted = formatBytes(vm.diskread, false, undefined, false) + const diskWriteFormatted = formatBytes(vm.diskwrite, false, undefined, false) + const diskFormatted = formatBytes(vm.disk, false, undefined, false) return ( @@ -294,9 +297,14 @@ function PveVmSheet({
{`${decimalString(memFormatted.value, memFormatted.value >= 10 ? 1 : 2)} ${memFormatted.unit}`}
- Network + Upload
-
{`${decimalString(netFormatted.value, netFormatted.value >= 10 ? 1 : 2)} ${netFormatted.unit}`}
+
{`${decimalString(netoutFormatted.value, netoutFormatted.value >= 10 ? 1 : 2)} ${netoutFormatted.unit}`}
+ +
+ Download +
+
{`${decimalString(netinFormatted.value, netinFormatted.value >= 10 ? 1 : 2)} ${netinFormatted.unit}`}
vCPUs @@ -308,6 +316,21 @@ function PveVmSheet({
{`${decimalString(maxMemFormatted.value, maxMemFormatted.value >= 10 ? 1 : 2)} ${maxMemFormatted.unit}`}
+
+ Disk Read +
+
{`${decimalString(diskReadFormatted.value, diskReadFormatted.value >= 10 ? 1 : 2)} ${diskReadFormatted.unit}`}
+ +
+ Disk Write +
+
{`${decimalString(diskWriteFormatted.value, diskWriteFormatted.value >= 10 ? 1 : 2)} ${diskWriteFormatted.unit}`}
+ +
+ Disk Size +
+
{`${decimalString(diskFormatted.value, diskFormatted.value >= 10 ? 1 : 2)} ${diskFormatted.unit}`}
+
Uptime
diff --git a/internal/site/src/types.d.ts b/internal/site/src/types.d.ts index 94389af8..f1566fb5 100644 --- a/internal/site/src/types.d.ts +++ b/internal/site/src/types.d.ts @@ -285,14 +285,22 @@ export interface PveVmRecord extends RecordModel { cpu: number /** Memory used (MB) */ mem: number - /** Network bandwidth (bytes/s, combined send+recv) */ - net: number + /** Total upload (bytes, sent by VM) */ + netout: number + /** Total download (bytes, received by VM) */ + netin: number /** Max vCPU count */ maxcpu: number /** Max memory (bytes) */ maxmem: number /** Uptime (seconds) */ uptime: number + /** Cumulative disk read (bytes) */ + diskread: number + /** Cumulative disk write (bytes) */ + diskwrite: number + /** Allocated disk size (bytes) */ + disk: number /** Unix timestamp (ms) */ updated: number } @@ -447,116 +455,116 @@ export interface SystemdRecord extends RecordModel { } export interface SystemdServiceDetails { - AccessSELinuxContext: string; - ActivationDetails: any[]; - ActiveEnterTimestamp: number; - ActiveEnterTimestampMonotonic: number; - ActiveExitTimestamp: number; - ActiveExitTimestampMonotonic: number; - ActiveState: string; - After: string[]; - AllowIsolate: boolean; - AssertResult: boolean; - AssertTimestamp: number; - AssertTimestampMonotonic: number; - Asserts: any[]; - Before: string[]; - BindsTo: any[]; - BoundBy: any[]; - CPUUsageNSec: number; - CanClean: any[]; - CanFreeze: boolean; - CanIsolate: boolean; - CanLiveMount: boolean; - CanReload: boolean; - CanStart: boolean; - CanStop: boolean; - CollectMode: string; - ConditionResult: boolean; - ConditionTimestamp: number; - ConditionTimestampMonotonic: number; - Conditions: any[]; - ConflictedBy: any[]; - Conflicts: string[]; - ConsistsOf: any[]; - DebugInvocation: boolean; - DefaultDependencies: boolean; - Description: string; - Documentation: string[]; - DropInPaths: any[]; - ExecMainPID: number; - FailureAction: string; - FailureActionExitStatus: number; - Following: string; - FragmentPath: string; - FreezerState: string; - Id: string; - IgnoreOnIsolate: boolean; - InactiveEnterTimestamp: number; - InactiveEnterTimestampMonotonic: number; - InactiveExitTimestamp: number; - InactiveExitTimestampMonotonic: number; - InvocationID: string; - Job: Array; - JobRunningTimeoutUSec: number; - JobTimeoutAction: string; - JobTimeoutRebootArgument: string; - JobTimeoutUSec: number; - JoinsNamespaceOf: any[]; - LoadError: string[]; - LoadState: string; - MainPID: number; - Markers: any[]; - MemoryCurrent: number; - MemoryLimit: number; - MemoryPeak: number; - NRestarts: number; - Names: string[]; - NeedDaemonReload: boolean; - OnFailure: any[]; - OnFailureJobMode: string; - OnFailureOf: any[]; - OnSuccess: any[]; - OnSuccessJobMode: string; - OnSuccessOf: any[]; - PartOf: any[]; - Perpetual: boolean; - PropagatesReloadTo: any[]; - PropagatesStopTo: any[]; - RebootArgument: string; - Refs: any[]; - RefuseManualStart: boolean; - RefuseManualStop: boolean; - ReloadPropagatedFrom: any[]; - RequiredBy: any[]; - Requires: string[]; - RequiresMountsFor: any[]; - Requisite: any[]; - RequisiteOf: any[]; - Result: string; - SliceOf: any[]; - SourcePath: string; - StartLimitAction: string; - StartLimitBurst: number; - StartLimitIntervalUSec: number; - StateChangeTimestamp: number; - StateChangeTimestampMonotonic: number; - StopPropagatedFrom: any[]; - StopWhenUnneeded: boolean; - SubState: string; - SuccessAction: string; - SuccessActionExitStatus: number; - SurviveFinalKillSignal: boolean; - TasksCurrent: number; - TasksMax: number; - Transient: boolean; - TriggeredBy: string[]; - Triggers: any[]; - UnitFilePreset: string; - UnitFileState: string; - UpheldBy: any[]; - Upholds: any[]; - WantedBy: any[]; - Wants: string[]; - WantsMountsFor: any[]; -} \ No newline at end of file + AccessSELinuxContext: string + ActivationDetails: any[] + ActiveEnterTimestamp: number + ActiveEnterTimestampMonotonic: number + ActiveExitTimestamp: number + ActiveExitTimestampMonotonic: number + ActiveState: string + After: string[] + AllowIsolate: boolean + AssertResult: boolean + AssertTimestamp: number + AssertTimestampMonotonic: number + Asserts: any[] + Before: string[] + BindsTo: any[] + BoundBy: any[] + CPUUsageNSec: number + CanClean: any[] + CanFreeze: boolean + CanIsolate: boolean + CanLiveMount: boolean + CanReload: boolean + CanStart: boolean + CanStop: boolean + CollectMode: string + ConditionResult: boolean + ConditionTimestamp: number + ConditionTimestampMonotonic: number + Conditions: any[] + ConflictedBy: any[] + Conflicts: string[] + ConsistsOf: any[] + DebugInvocation: boolean + DefaultDependencies: boolean + Description: string + Documentation: string[] + DropInPaths: any[] + ExecMainPID: number + FailureAction: string + FailureActionExitStatus: number + Following: string + FragmentPath: string + FreezerState: string + Id: string + IgnoreOnIsolate: boolean + InactiveEnterTimestamp: number + InactiveEnterTimestampMonotonic: number + InactiveExitTimestamp: number + InactiveExitTimestampMonotonic: number + InvocationID: string + Job: Array + JobRunningTimeoutUSec: number + JobTimeoutAction: string + JobTimeoutRebootArgument: string + JobTimeoutUSec: number + JoinsNamespaceOf: any[] + LoadError: string[] + LoadState: string + MainPID: number + Markers: any[] + MemoryCurrent: number + MemoryLimit: number + MemoryPeak: number + NRestarts: number + Names: string[] + NeedDaemonReload: boolean + OnFailure: any[] + OnFailureJobMode: string + OnFailureOf: any[] + OnSuccess: any[] + OnSuccessJobMode: string + OnSuccessOf: any[] + PartOf: any[] + Perpetual: boolean + PropagatesReloadTo: any[] + PropagatesStopTo: any[] + RebootArgument: string + Refs: any[] + RefuseManualStart: boolean + RefuseManualStop: boolean + ReloadPropagatedFrom: any[] + RequiredBy: any[] + Requires: string[] + RequiresMountsFor: any[] + Requisite: any[] + RequisiteOf: any[] + Result: string + SliceOf: any[] + SourcePath: string + StartLimitAction: string + StartLimitBurst: number + StartLimitIntervalUSec: number + StateChangeTimestamp: number + StateChangeTimestampMonotonic: number + StopPropagatedFrom: any[] + StopWhenUnneeded: boolean + SubState: string + SuccessAction: string + SuccessActionExitStatus: number + SurviveFinalKillSignal: boolean + TasksCurrent: number + TasksMax: number + Transient: boolean + TriggeredBy: string[] + Triggers: any[] + UnitFilePreset: string + UnitFileState: string + UpheldBy: any[] + Upholds: any[] + WantedBy: any[] + Wants: string[] + WantsMountsFor: any[] +}