Public/Storage/Get-VergevSANStatus.ps1

function Get-VergevSANStatus {
    <#
    .SYNOPSIS
        Retrieves vSAN health status and statistics from VergeOS.

    .DESCRIPTION
        Get-VergevSANStatus retrieves the health status, capacity information,
        and operational statistics of the VergeOS vSAN storage system.

    .PARAMETER Cluster
        Filter by cluster name. If not specified, returns status for all clusters.

    .PARAMETER IncludeTierStatus
        Include detailed per-tier status information.

    .PARAMETER Server
        The VergeOS connection to use. Defaults to the current default connection.

    .EXAMPLE
        Get-VergevSANStatus

        Returns vSAN status for all clusters.

    .EXAMPLE
        Get-VergevSANStatus -IncludeTierStatus

        Returns vSAN status with detailed tier information.

    .EXAMPLE
        Get-VergevSANStatus | Where-Object { $_.State -ne 'Online' }

        Lists any clusters not in online state.

    .OUTPUTS
        Verge.vSANStatus objects containing:
        - ClusterName: The cluster name
        - Status: Overall status (Online, Offline, Error, etc.)
        - State: Simplified state (Online, Warning, Error)
        - TotalNodes: Total number of nodes
        - OnlineNodes: Number of online nodes
        - TotalRAMGB: Total cluster RAM in GB
        - OnlineRAMGB: Online RAM in GB
        - UsedRAMGB: Used RAM in GB
        - TotalCores: Total CPU cores
        - OnlineCores: Online CPU cores
        - UsedCores: Used CPU cores
        - Tiers: Array of tier status (if -IncludeTierStatus)

    .NOTES
        The vSAN is the distributed storage system in VergeOS that provides
        redundancy and high availability for VM storage.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Position = 0)]
        [string]$Cluster,

        [Parameter()]
        [switch]$IncludeTierStatus,

        [Parameter()]
        [object]$Server
    )

    begin {
        # Resolve connection
        if (-not $Server) {
            $Server = $script:DefaultConnection
        }
        if (-not $Server) {
            throw [System.InvalidOperationException]::new(
                'Not connected to VergeOS. Use Connect-VergeOS to establish a connection.'
            )
        }
    }

    process {
        try {
            # Build query parameters for clusters with status
            $queryParams = @{}

            if ($IncludeTierStatus) {
                $queryParams['fields'] = @(
                    '$key'
                    'name'
                    'description'
                    'enabled'
                    'storage'
                    'compute'
                    'status#status as status'
                    'status#state as state'
                    'status#status_info as status_info'
                    'status#total_nodes as total_nodes'
                    'status#online_nodes as online_nodes'
                    'status#running_machines as running_machines'
                    'status#total_ram as total_ram'
                    'status#online_ram as online_ram'
                    'status#used_ram as used_ram'
                    'status#total_cores as total_cores'
                    'status#online_cores as online_cores'
                    'status#used_cores as used_cores'
                    'status#last_update as last_update'
                    'tiers[$key,tier,status#status as status,status#used as used,status#capacity as capacity,stats#rops as read_ops,stats#wops as write_ops,stats#rbps as read_bps,stats#wbps as write_bps]'
                ) -join ','
            }
            else {
                $queryParams['fields'] = @(
                    '$key'
                    'name'
                    'description'
                    'enabled'
                    'storage'
                    'compute'
                    'status#status as status'
                    'status#state as state'
                    'status#status_info as status_info'
                    'status#total_nodes as total_nodes'
                    'status#online_nodes as online_nodes'
                    'status#running_machines as running_machines'
                    'status#total_ram as total_ram'
                    'status#online_ram as online_ram'
                    'status#used_ram as used_ram'
                    'status#total_cores as total_cores'
                    'status#online_cores as online_cores'
                    'status#used_cores as used_cores'
                    'status#last_update as last_update'
                ) -join ','
            }

            # Filter by cluster name if specified
            if ($Cluster) {
                $queryParams['filter'] = "name eq '$Cluster'"
            }

            Write-Verbose "Querying cluster vSAN status"
            $response = Invoke-VergeAPI -Method GET -Endpoint 'clusters' -Query $queryParams -Connection $Server

            $clusters = if ($response -is [array]) { $response } else { @($response) }

            foreach ($clusterData in $clusters) {
                if (-not $clusterData -or -not $clusterData.name) {
                    continue
                }

                # Map status to display
                $statusDisplay = switch ($clusterData.status) {
                    'online' { 'Online' }
                    'offline' { 'Offline' }
                    'maintenance' { 'Maintenance' }
                    'reduced' { 'Reduced Capacity' }
                    'noredundant' { 'No Redundancy' }
                    'error' { 'Error' }
                    'updating' { 'Updating' }
                    'shutdown' { 'Shutting Down' }
                    'insufficient' { 'Insufficient Nodes' }
                    default { $clusterData.status }
                }

                $stateDisplay = switch ($clusterData.state) {
                    'online' { 'Online' }
                    'offline' { 'Offline' }
                    'warning' { 'Warning' }
                    'error' { 'Error' }
                    default { $clusterData.state }
                }

                # Convert RAM from MB to GB
                $totalRAMGB = if ($clusterData.total_ram) { [math]::Round($clusterData.total_ram / 1024, 2) } else { 0 }
                $onlineRAMGB = if ($clusterData.online_ram) { [math]::Round($clusterData.online_ram / 1024, 2) } else { 0 }
                $usedRAMGB = if ($clusterData.used_ram) { [math]::Round($clusterData.used_ram / 1024, 2) } else { 0 }

                # Calculate RAM usage percentage
                $ramUsedPercent = if ($onlineRAMGB -gt 0) {
                    [math]::Round(($usedRAMGB / $onlineRAMGB) * 100, 1)
                } else { 0 }

                # Calculate core usage percentage
                $coreUsedPercent = if ($clusterData.online_cores -gt 0) {
                    [math]::Round(($clusterData.used_cores / $clusterData.online_cores) * 100, 1)
                } else { 0 }

                # Convert last update timestamp
                $lastUpdate = $null
                if ($clusterData.last_update) {
                    $lastUpdate = [DateTimeOffset]::FromUnixTimeSeconds($clusterData.last_update).LocalDateTime
                }

                # Process tier data if included
                $tierData = $null
                if ($IncludeTierStatus -and $clusterData.tiers) {
                    $tierData = foreach ($tier in $clusterData.tiers) {
                        $usedGB = if ($tier.used) { [math]::Round($tier.used / 1073741824, 2) } else { 0 }
                        $capacityGB = if ($tier.capacity) { [math]::Round($tier.capacity / 1073741824, 2) } else { 0 }
                        $usedPct = if ($capacityGB -gt 0) { [math]::Round(($usedGB / $capacityGB) * 100, 1) } else { 0 }

                        [PSCustomObject]@{
                            Tier           = $tier.tier
                            Status         = $tier.status
                            UsedGB         = $usedGB
                            CapacityGB     = $capacityGB
                            UsedPercent    = $usedPct
                            ReadOps        = $tier.read_ops
                            WriteOps       = $tier.write_ops
                            ReadBytesPerSec  = $tier.read_bps
                            WriteBytesPerSec = $tier.write_bps
                        }
                    }
                }

                # Determine health status
                $healthStatus = switch ($clusterData.state) {
                    'online' { 'Healthy' }
                    'warning' { 'Degraded' }
                    'error' { 'Critical' }
                    'offline' { 'Offline' }
                    default { 'Unknown' }
                }

                $output = [PSCustomObject]@{
                    PSTypeName        = 'Verge.vSANStatus'
                    Key               = $clusterData.'$key'
                    ClusterName       = $clusterData.name
                    Description       = $clusterData.description
                    Enabled           = [bool]$clusterData.enabled
                    IsStorage         = [bool]$clusterData.storage
                    IsCompute         = [bool]$clusterData.compute
                    Status            = $statusDisplay
                    StatusRaw         = $clusterData.status
                    State             = $stateDisplay
                    StateRaw          = $clusterData.state
                    HealthStatus      = $healthStatus
                    StatusInfo        = $clusterData.status_info
                    TotalNodes        = $clusterData.total_nodes
                    OnlineNodes       = $clusterData.online_nodes
                    RunningMachines   = $clusterData.running_machines
                    TotalRAMGB        = $totalRAMGB
                    OnlineRAMGB       = $onlineRAMGB
                    UsedRAMGB         = $usedRAMGB
                    RAMUsedPercent    = $ramUsedPercent
                    TotalCores        = $clusterData.total_cores
                    OnlineCores       = $clusterData.online_cores
                    UsedCores         = $clusterData.used_cores
                    CoreUsedPercent   = $coreUsedPercent
                    LastUpdate        = $lastUpdate
                }

                if ($IncludeTierStatus) {
                    $output | Add-Member -MemberType NoteProperty -Name 'Tiers' -Value $tierData
                }

                Write-Output $output
            }
        }
        catch {
            Write-Error -Message "Failed to get vSAN status: $($_.Exception.Message)" -ErrorId 'GetvSANStatusFailed'
        }
    }
}