classes/UnraidClasses.ps1
|
class UnraidAuthException : System.Exception { UnraidAuthException([string]$message) : base($message) {} UnraidAuthException([string]$message, [System.Exception]$innerException) : base($message, $innerException) {} } class UnraidSessionException : System.Exception { UnraidSessionException([string]$message) : base($message) {} UnraidSessionException([string]$message, [System.Exception]$innerException) : base($message, $innerException) {} } class UnraidApiException : System.Exception { UnraidApiException([string]$message) : base($message) {} UnraidApiException([string]$message, [System.Exception]$innerException) : base($message, $innerException) {} } class UnraidSession { [string]$Server [string]$Uri [string]$UserName [string]$CsrfToken [string]$ApiKey [object]$WebSession [datetime]$ConnectedAt [bool]$SkipCertificateCheck [string]$AuthType UnraidSession($Server, $Uri, $UserName, $CsrfToken, $WebSession, $SkipCert) { $this.Server = $Server $this.Uri = $Uri $this.UserName = $UserName $this.CsrfToken = $CsrfToken.ToString() $this.WebSession = $WebSession $this.ConnectedAt = [DateTime]::Now $this.SkipCertificateCheck = $SkipCert $this.AuthType = 'Credential' $this.ApiKey = $null } UnraidSession($Server, $Uri, $ApiKey, $SkipCert) { $this.Server = $Server $this.Uri = $Uri $this.ApiKey = $ApiKey $this.ConnectedAt = [DateTime]::Now $this.SkipCertificateCheck = $SkipCert $this.AuthType = 'ApiKey' $this.UserName = 'API Key' $this.CsrfToken = $null $this.WebSession = $null } [string] ToString() { $authInfo = if ($this.AuthType -eq 'ApiKey') { 'API Key' } else { $this.UserName } return "UnraidSession: [$authInfo @ $($this.Server)]" } } class UnraidSizeFix { static [string] FormatBytes([double]$rawBytes) { if ($rawBytes -eq 0) { return "0 B" } if ($rawBytes -ge 1PB) { return "$([math]::Round($rawBytes / 1PB, 2)) PB" } if ($rawBytes -ge 1TB) { return "$([math]::Round($rawBytes / 1TB, 2)) TB" } if ($rawBytes -ge 1GB) { return "$([math]::Round($rawBytes / 1GB, 2)) GB" } if ($rawBytes -ge 1MB) { return "$([math]::Round($rawBytes / 1MB, 2)) MB" } if ($rawBytes -ge 1KB) { return "$([math]::Round($rawBytes / 1KB, 2)) KB" } return "$rawBytes B" } static [string] FormatTemperature([string]$tempValue) { if (!$tempValue) { return '' } # Strip garbage chars, leave numbers/dots/dashes which sometimes appear $cleanedTemperature = $tempValue -replace '[^\d.-]', '' if ($cleanedTemperature -match '^-?\d+\.?\d*$') { return "${cleanedTemperature}$([char]0x00B0)C" } return "${tempValue}$([char]0x00B0)C" } } class UnraidDataDisk { [string]$Name [string]$Device [string]$Size [string]$Used [string]$Free [string]$FileSystem [string]$Temperature [string]$Status [int]$Errors [bool]$Rotational [long]$NumReads [long]$NumWrites [bool]$Exportable [string]$Transport UnraidDataDisk([object]$diskObject) { $this.Name = $diskObject.name $this.Device = $diskObject.device $this.Size = [UnraidSizeFix]::FormatBytes([double]$diskObject.size * 1KB) $this.FileSystem = $diskObject.fsType $this.Status = $diskObject.status $this.Errors = $diskObject.numErrors $this.Rotational = $diskObject.rotational if ($diskObject.numReads) { $this.NumReads = $diskObject.numReads } if ($diskObject.numWrites) { $this.NumWrites = $diskObject.numWrites } if ($null -ne $diskObject.exportable) { $this.Exportable = [bool]$diskObject.exportable } if ($diskObject.transport) { $this.Transport = $diskObject.transport } if ($diskObject.fsUsed) { $this.Used = [UnraidSizeFix]::FormatBytes([double]$diskObject.fsUsed * 1KB) } if ($diskObject.fsFree) { $this.Free = [UnraidSizeFix]::FormatBytes([double]$diskObject.fsFree * 1KB) } if ($diskObject.temp) { $this.Temperature = [UnraidSizeFix]::FormatTemperature($diskObject.temp) } } } class UnraidParityDisk { [string]$Name [string]$Device [string]$Size [string]$Temperature [string]$Status [int]$Errors [long]$NumReads [long]$NumWrites [bool]$Exportable [string]$Transport UnraidParityDisk([object]$diskObject) { $this.Name = $diskObject.name $this.Device = $diskObject.device $this.Size = [UnraidSizeFix]::FormatBytes([double]$diskObject.size * 1KB) $this.Status = $diskObject.status $this.Errors = $diskObject.numErrors if ($diskObject.numReads) { $this.NumReads = $diskObject.numReads } if ($diskObject.numWrites) { $this.NumWrites = $diskObject.numWrites } if ($null -ne $diskObject.exportable) { $this.Exportable = [bool]$diskObject.exportable } if ($diskObject.transport) { $this.Transport = $diskObject.transport } if ($diskObject.temp) { $this.Temperature = [UnraidSizeFix]::FormatTemperature($diskObject.temp) } } } class UnraidCacheDisk { [string]$Name [string]$Device [string]$Size [string]$Used [string]$Free [string]$Temperature [string]$Status [int]$Errors [bool]$Rotational [long]$NumReads [long]$NumWrites [bool]$Exportable [string]$Transport UnraidCacheDisk([object]$diskObject) { $this.Name = $diskObject.name $this.Device = $diskObject.device $this.Size = [UnraidSizeFix]::FormatBytes([double]$diskObject.size * 1KB) $this.Status = $diskObject.status if ($diskObject.numErrors) { $this.Errors = $diskObject.numErrors } if ($null -ne $diskObject.rotational) { $this.Rotational = $diskObject.rotational } if ($diskObject.numReads) { $this.NumReads = $diskObject.numReads } if ($diskObject.numWrites) { $this.NumWrites = $diskObject.numWrites } if ($null -ne $diskObject.exportable) { $this.Exportable = [bool]$diskObject.exportable } if ($diskObject.transport) { $this.Transport = $diskObject.transport } if ($diskObject.fsUsed) { $this.Used = [UnraidSizeFix]::FormatBytes([double]$diskObject.fsUsed * 1KB) } if ($diskObject.fsFree) { $this.Free = [UnraidSizeFix]::FormatBytes([double]$diskObject.fsFree * 1KB) } if ($diskObject.temp) { $this.Temperature = [UnraidSizeFix]::FormatTemperature($diskObject.temp) } } } class UnraidArray { [string]$State [string]$TotalSize [string]$UsedSpace [string]$FreeSpace [int]$DiskCount [string]$ParityStatus [string]$ParityProgress [string]$ParitySpeed [int]$ParityErrors [int]$ParityCorrecting [datetime]$ParityCheckDate [int]$ParityCheckDuration [UnraidDataDisk[]]$DataDisks [UnraidParityDisk[]]$ParityDisks [UnraidCacheDisk[]]$CacheDisks UnraidArray([object]$arrayData) { $this.State = $arrayData.state if ($arrayData.capacity -and $arrayData.capacity.kilobytes) { $kilobytesTotal = [double]$arrayData.capacity.kilobytes.total $kilobytesUsed = [double]$arrayData.capacity.kilobytes.used $kilobytesFree = [double]$arrayData.capacity.kilobytes.free $this.TotalSize = [UnraidSizeFix]::FormatBytes($kilobytesTotal * 1KB) $this.UsedSpace = [UnraidSizeFix]::FormatBytes($kilobytesUsed * 1KB) $this.FreeSpace = [UnraidSizeFix]::FormatBytes($kilobytesFree * 1KB) # If API says STARTED but we have 0 bytes capacity, the array is NOT actually mounted or # the API is out of sync (which seems to happen somewhat frequently) # It is likely in a 'Starting' limbo or unmounted state. if ($this.State -eq "STARTED" -and $kilobytesTotal -eq 0) { $this.State = "STARTED (UNMOUNTED)" } } if ($arrayData.capacity -and $arrayData.capacity.disks) { $this.DiskCount = $arrayData.capacity.disks.total } if ($arrayData.parityCheckStatus) { $this.ParityStatus = $arrayData.parityCheckStatus.status $this.ParityProgress = "$($arrayData.parityCheckStatus.progress)%" if ($null -ne $arrayData.parityCheckStatus.errors) { $this.ParityErrors = $arrayData.parityCheckStatus.errors } if ($null -ne $arrayData.parityCheckStatus.correcting) { $this.ParityCorrecting = $arrayData.parityCheckStatus.correcting } if ($arrayData.parityCheckStatus.date) { $this.ParityCheckDate = $arrayData.parityCheckStatus.date } if ($arrayData.parityCheckStatus.duration) { $this.ParityCheckDuration = $arrayData.parityCheckStatus.duration } if ($arrayData.parityCheckStatus.speed -and $arrayData.parityCheckStatus.speed -gt 0) { $this.ParitySpeed = [UnraidSizeFix]::FormatBytes($arrayData.parityCheckStatus.speed) + "/s" } else { $this.ParitySpeed = "0 B/s" } } if ($arrayData.disks) { $this.DataDisks = $arrayData.disks | ForEach-Object { [UnraidDataDisk]::new($_) } } if ($arrayData.parities) { $this.ParityDisks = $arrayData.parities | ForEach-Object { [UnraidParityDisk]::new($_) } } if ($arrayData.caches) { $this.CacheDisks = $arrayData.caches | ForEach-Object { [UnraidCacheDisk]::new($_) } } } } class UnraidContainer { [string]$Id [string]$Name [string]$Image [string]$ImageId [string]$Command [string]$Created [long]$SizeRootFs [string]$State [string]$Status [bool]$AutoStart [string]$NetworkMode [string]$IPAddress [string]$WebUI [string]$PortSummary [string[]]$VolumeMappings [object]$Labels UnraidContainer($containerData, $serverIpAddress) { $this.Id = $containerData.id $this.Image = $containerData.image $this.ImageId = $containerData.imageId $this.Command = $containerData.command $this.Created = $containerData.created $this.State = $containerData.state $this.Status = $containerData.status $this.AutoStart = $containerData.autoStart if ($containerData.sizeRootFs) { $this.SizeRootFs = $containerData.sizeRootFs } if ($containerData.names) { $this.Name = $containerData.names[0].TrimStart('/') } if ($containerData.hostConfig) { $this.NetworkMode = $containerData.hostConfig.networkMode } $this.IPAddress = $serverIpAddress if ($this.NetworkMode -notin @("host", "bridge") -and $containerData.networkSettings.Networks) { try { $containerNetworkSettings = $containerData.networkSettings.Networks if ($containerNetworkSettings -is [string]) { $containerNetworkSettings = $containerNetworkSettings | ConvertFrom-Json } foreach ($networkProperty in $containerNetworkSettings.PSObject.Properties) { $networkDetails = $networkProperty.Value if ($networkDetails.IPAddress) { $this.IPAddress = $networkDetails.IPAddress if ($this.NetworkMode -eq $networkProperty.Name) { break } } } } catch { # Parsing failed, default to server IP } } if ($containerData.labels) { $containerLabels = if ($containerData.labels -is [string]) { $containerData.labels | ConvertFrom-Json } else { $containerData.labels } $this.Labels = $containerLabels # Resolve WebUI URL if available $webUiLabel = $containerLabels.'net.unraid.docker.webui' if ($webUiLabel) { $webUiTemplate = $webUiLabel -replace "\[IP\]", $this.IPAddress # Replace [PORT:1234] with actual external ports # We regex match the token, find the mapping, and swap it $portPlaceholderMatch = [regex]::Match($webUiTemplate, '\[PORT:(\d+)\]') if ($portPlaceholderMatch.Success) { $internalPort = $portPlaceholderMatch.Groups[1].Value $mappedExternalPort = $internalPort if ($containerData.ports) { $portMapping = $containerData.ports | Where-Object { "$($_.privatePort)" -eq "$internalPort" } | Select-Object -First 1 if ($portMapping -and $portMapping.publicPort) { $mappedExternalPort = $portMapping.publicPort } } $webUiTemplate = $webUiTemplate -replace "\[PORT:$internalPort\]", $mappedExternalPort } $this.WebUI = $webUiTemplate } } if ($containerData.ports) { $activePorts = $containerData.ports | Where-Object { $_.publicPort } $portStrings = $activePorts | ForEach-Object { "$($_.publicPort):$($_.privatePort)/$($_.type)" } $this.PortSummary = $portStrings -join ", " } if ($containerData.mounts) { $this.VolumeMappings = foreach ($mountPoint in $containerData.mounts) { $sourcePath = if ($mountPoint.Source) { $mountPoint.Source } else { $mountPoint.source } $targetPath = if ($mountPoint.Destination) { $mountPoint.Destination } else { $mountPoint.destination } $accessMode = if ($mountPoint.RW -or $mountPoint.rw -or ($mountPoint.ReadOnly -eq $false)) { "rw" } else { "ro" } if ($sourcePath) { "'$sourcePath' -> '$targetPath' ($accessMode)" } } } } } class UnraidServer { [string]$Name [string]$Description [string]$Version [string]$Kernel [string]$IPAddress [string]$WanIP [string]$TimeZone [string]$Uptime [string]$State [string]$Motherboard [string]$CPU [string]$CpuUsage [string]$RamUsage [string]$ArrayUsage [string]$Guid [object[]]$NetworkDevices [object]$Baseboard [object]$SystemInfo [object[]]$MemoryLayout [string]$Workgroup [string]$Domain UnraidServer($serverData) { $serverInfo = $serverData.server $sysInfo = $serverData.info.system $cpuInfo = $serverData.info.cpu $memoryMetrics = $serverData.metrics.memory $this.Name = $serverInfo.name $this.IPAddress = $serverInfo.lanip $this.Guid = $serverInfo.guid $this.Description = $serverData.vars.comment $this.State = $serverData.array.state if ($serverInfo.wanip) { $this.WanIP = $serverInfo.wanip } if ($serverData.vars.timeZone) { $this.TimeZone = $serverData.vars.timeZone } if ($serverData.info.versions.core.unraid) { $this.Version = $serverData.info.versions.core.unraid } else { $this.Version = "Unknown" } if ($serverData.info.versions.core.kernel) { $this.Kernel = $serverData.info.versions.core.kernel } $uptimeRawString = $serverData.info.os.uptime $this.Uptime = $uptimeRawString -replace "up ", "" $this.Motherboard = "$($sysInfo.manufacturer) - $($sysInfo.model)" if ($cpuInfo.threads -and $cpuInfo.threads -ne $cpuInfo.cores) { $this.CPU = "$($cpuInfo.brand) ($($cpuInfo.cores) Cores / $($cpuInfo.threads) Threads)" } else { $this.CPU = "$($cpuInfo.brand) ($($cpuInfo.cores) Cores)" } $cpuTotalPercent = $serverData.metrics.cpu.percentTotal $this.CpuUsage = "$([math]::Round($cpuTotalPercent, 1))%" $ramUsedFormatted = [UnraidSizeFix]::FormatBytes($memoryMetrics.used) $ramTotalFormatted = [UnraidSizeFix]::FormatBytes($memoryMetrics.total) $ramPercent = [math]::Round($memoryMetrics.percentTotal, 1) $this.RamUsage = "$ramPercent% ($ramUsedFormatted / $ramTotalFormatted)" if ($serverData.array.capacity.kilobytes.total -gt 0) { $arrayUsedBytes = [double]$serverData.array.capacity.kilobytes.used * 1KB $arrayTotalBytes = [double]$serverData.array.capacity.kilobytes.total * 1KB $usagePercent = ($arrayUsedBytes / $arrayTotalBytes) * 100 $usagePercentRounded = [math]::Round($usagePercent, 1) $usedString = [UnraidSizeFix]::FormatBytes($arrayUsedBytes) $totalString = [UnraidSizeFix]::FormatBytes($arrayTotalBytes) $this.ArrayUsage = "$usagePercentRounded% ($usedString / $totalString)" } else { $this.ArrayUsage = "N/A" } if ($serverData.info.devices.network) { $this.NetworkDevices = $serverData.info.devices.network } if ($serverData.info.baseboard) { $this.Baseboard = $serverData.info.baseboard } if ($serverData.info.system) { $this.SystemInfo = $serverData.info.system } if ($serverData.info.memory.layout) { $this.MemoryLayout = $serverData.info.memory.layout } if ($serverData.vars.workgroup) { $this.Workgroup = $serverData.vars.workgroup } if ($serverData.vars.domain) { $this.Domain = $serverData.vars.domain } } } class UnraidMetrics { [double]$CpuTotal [object]$CpuCores [double]$MemTotal [double]$MemUsed [double]$MemFree [double]$MemBuffCache [double]$MemActive [double]$MemAvailable [string]$MemPercent [double]$SwapTotal [double]$SwapUsed [double]$SwapFree [string]$SwapPercent [double]$SwapPercentTotal UnraidMetrics($metricsData) { $this.CpuTotal = [math]::Round($metricsData.metrics.cpu.percentTotal, 1) $memoryInfo = $metricsData.metrics.memory $this.MemTotal = $memoryInfo.total $this.MemUsed = $memoryInfo.used $this.MemFree = $memoryInfo.free $this.MemBuffCache = $memoryInfo.buffcache $this.MemActive = $memoryInfo.active $memoryPercent = [math]::Round($memoryInfo.percentTotal, 1) $this.MemPercent = "$memoryPercent%" $this.SwapTotal = $memoryInfo.swapTotal $this.SwapUsed = $memoryInfo.swapUsed $this.SwapFree = $memoryInfo.swapFree if ($memoryInfo.percentSwapTotal) { $this.SwapPercentTotal = $memoryInfo.percentSwapTotal $this.SwapPercent = "$([math]::Round($memoryInfo.percentSwapTotal, 1))%" } elseif ($this.SwapTotal -gt 0) { $swapPercentCalculated = [math]::Round((($this.SwapUsed / $this.SwapTotal) * 100), 1) $this.SwapPercent = "$swapPercentCalculated%" $this.SwapPercentTotal = $swapPercentCalculated } else { $this.SwapPercent = "0%" $this.SwapPercentTotal = 0 } if ($metricsData.metrics.cpu.cpus) { $this.CpuCores = $metricsData.metrics.cpu.cpus } if ($memoryInfo.available) { $this.MemAvailable = $memoryInfo.available } } [string] FormatBytes([double]$bytesToFormat) { return [UnraidSizeFix]::FormatBytes($bytesToFormat) } } class UnraidVm { [string]$Id [string]$Name [string]$State UnraidVm($vmData) { $this.Id = $vmData.id $this.Name = $vmData.name $this.State = $vmData.state } } class UnraidNotification { [string]$Id [string]$Title [string]$Subject [string]$Description [string]$Importance [string]$Type [string]$Timestamp [string]$FormattedTimestamp [string]$Link UnraidNotification($notificationData) { $this.Id = $notificationData.id $this.Title = $notificationData.title $this.Subject = $notificationData.subject $this.Description = $notificationData.description $this.Importance = $notificationData.importance $this.Type = $notificationData.type $this.Link = $notificationData.link $this.Timestamp = $notificationData.timestamp $this.FormattedTimestamp = $notificationData.formattedTimestamp } } class UnraidShare { [string]$Name [string]$Comment [string]$Free [string]$Used [string]$Size [string]$Allocator [string]$SplitLevel [string]$Floor [bool]$Cache [string[]]$Include [string[]]$Exclude [string]$Cow [string]$LuksStatus UnraidShare($shareData) { $this.Name = $shareData.name $this.Comment = $shareData.comment $this.Free = [UnraidSizeFix]::FormatBytes($shareData.free) $this.Used = [UnraidSizeFix]::FormatBytes($shareData.used) $calculatedSize = $shareData.size if (! $calculatedSize -or $calculatedSize -eq 0) { $calculatedSize = $shareData.used + $shareData.free } $this.Size = [UnraidSizeFix]::FormatBytes($calculatedSize) if ($shareData.allocator) { $this.Allocator = $shareData.allocator } if ($shareData.splitLevel) { $this.SplitLevel = $shareData.splitLevel } if ($shareData.floor) { $this.Floor = $shareData.floor } if ($null -ne $shareData.cache) { $this.Cache = [bool]$shareData.cache } if ($shareData.include) { $this.Include = $shareData.include } if ($shareData.exclude) { $this.Exclude = $shareData.exclude } if ($shareData.cow) { $this.Cow = $shareData.cow } if ($shareData.luksStatus) { $this.LuksStatus = $shareData.luksStatus } } } class UnraidPlugin { [string]$Name [string]$Version [bool]$HasApi [bool]$HasCli UnraidPlugin($pluginData) { $this.Name = $pluginData.name $this.Version = $pluginData.version $this.HasApi = $pluginData.hasApiModule $this.HasCli = $pluginData.hasCliModule } } class UnraidParityHistory { [string]$Status [int]$Progress [string]$Speed [int]$Errors [int]$Correcting [datetime]$Date [int]$DurationSeconds [string]$Duration UnraidParityHistory($parityHistoryData) { $this.Status = $parityHistoryData.status $this.Progress = $parityHistoryData.progress $this.Errors = $parityHistoryData.errors $this.Correcting = $parityHistoryData.correcting $this.Date = $parityHistoryData.date $this.DurationSeconds = $parityHistoryData.duration if ($parityHistoryData.speed -gt 0) { $this.Speed = [UnraidSizeFix]::FormatBytes($parityHistoryData.speed) + "/s" } if ($parityHistoryData.duration) { # Standard HH:mm:ss calc $durationHours = [int][math]::Floor($parityHistoryData.duration / 3600) $durationMinutes = [int][math]::Floor(($parityHistoryData.duration % 3600) / 60) $durationSecondsLocal = [int]($parityHistoryData.duration % 60) $hoursFormatted = $durationHours.ToString('D2') $minutesFormatted = $durationMinutes.ToString('D2') $secondsFormatted = $durationSecondsLocal.ToString('D2') $this.Duration = "$hoursFormatted`:$minutesFormatted`:$secondsFormatted" } } } class UnraidUps { [string]$Name [string]$Model [string]$Status [int]$BatteryCharge [string]$BatteryRuntime [int]$Load [double]$InputVoltage [double]$OutputVoltage UnraidUps($upsData) { $this.Name = $upsData.name $this.Model = $upsData.model $this.Status = $upsData.status if ($upsData.battery) { $this.BatteryCharge = $upsData.battery.chargeLevel $this.BatteryRuntime = $upsData.battery.estimatedRuntime } if ($upsData.power) { $this.Load = $upsData.power.loadPercentage $this.InputVoltage = $upsData.power.inputVoltage $this.OutputVoltage = $upsData.power.outputVoltage } } } |