Public/System/Get-VergeNodeDevice.ps1

function Get-VergeNodeDevice {
    <#
    .SYNOPSIS
        Retrieves hardware devices from VergeOS nodes.

    .DESCRIPTION
        Get-VergeNodeDevice retrieves PCI, USB, and GPU devices from VergeOS nodes.
        This includes network controllers, display controllers, storage controllers,
        USB devices, and other hardware attached to the nodes.

    .PARAMETER Node
        The name of the node to retrieve devices for. Supports wildcards (* and ?).
        If not specified, devices for all nodes are returned.

    .PARAMETER NodeObject
        A node object from Get-VergeNode to retrieve devices for.

    .PARAMETER DeviceType
        Filter by device type: PCI, USB, or GPU.

    .PARAMETER DeviceClass
        Filter PCI devices by class. Common values include:
        - 'Network controller'
        - 'Display controller'
        - 'Mass storage controller'
        - 'Serial bus controller'

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

    .EXAMPLE
        Get-VergeNodeDevice

        Retrieves all devices from all nodes.

    .EXAMPLE
        Get-VergeNodeDevice -Node "node1"

        Retrieves all devices from node1.

    .EXAMPLE
        Get-VergeNodeDevice -DeviceType PCI

        Retrieves all PCI devices from all nodes.

    .EXAMPLE
        Get-VergeNodeDevice -DeviceType PCI -DeviceClass "Display controller"

        Retrieves all GPUs/display controllers.

    .EXAMPLE
        Get-VergeNode -Name "node1" | Get-VergeNodeDevice -DeviceType USB

        Retrieves USB devices from node1 using pipeline input.

    .OUTPUTS
        PSCustomObject with PSTypeName 'Verge.NodeDevice'

    .NOTES
        PCI devices can be used for GPU passthrough when IOMMU is enabled.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Filter')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Position = 0, ParameterSetName = 'Filter')]
        [SupportsWildcards()]
        [string]$Node,

        [Parameter(Mandatory, ParameterSetName = 'ByNode', ValueFromPipeline)]
        [PSTypeName('Verge.Node')]
        [PSCustomObject]$NodeObject,

        [Parameter()]
        [ValidateSet('PCI', 'USB', 'GPU')]
        [string]$DeviceType,

        [Parameter()]
        [string]$DeviceClass,

        [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 {
            Write-Verbose "Querying node devices from $($Server.Server)"

            # Resolve node key if needed
            $nodeKey = $null
            if ($PSCmdlet.ParameterSetName -eq 'ByNode') {
                $nodeKey = $NodeObject.Key
                if ($NodeObject._Connection) {
                    $Server = $NodeObject._Connection
                }
            }
            elseif ($Node) {
                $nodeObj = Get-VergeNode -Name $Node -Server $Server | Select-Object -First 1
                if ($nodeObj) {
                    $nodeKey = $nodeObj.Key
                }
                else {
                    Write-Warning "Node '$Node' not found"
                    return
                }
            }

            # Determine which endpoints to query based on DeviceType
            $endpoints = @()
            if (-not $DeviceType -or $DeviceType -eq 'PCI') {
                $endpoints += 'node_pci_devices'
            }
            if (-not $DeviceType -or $DeviceType -eq 'USB') {
                $endpoints += 'node_usb_devices'
            }
            if ($DeviceType -eq 'GPU') {
                # GPUs are a subset of PCI devices - use display controllers
                $endpoints = @('node_pci_devices')
            }

            foreach ($endpoint in $endpoints) {
                $queryParams = @{}
                $filters = [System.Collections.Generic.List[string]]::new()

                # Filter by node
                if ($nodeKey) {
                    $filters.Add("node eq $nodeKey")
                }

                # Filter by device class (PCI only)
                if ($DeviceClass -and $endpoint -eq 'node_pci_devices') {
                    $filters.Add("class ct '$DeviceClass'")
                }

                # Filter for GPUs specifically
                if ($DeviceType -eq 'GPU' -and $endpoint -eq 'node_pci_devices') {
                    $filters.Add("device_type eq '03'")  # 03 = Display controller
                }

                # Apply filters
                if ($filters.Count -gt 0) {
                    $queryParams['filter'] = $filters -join ' and '
                }

                # Set fields based on endpoint type
                if ($endpoint -eq 'node_pci_devices') {
                    $queryParams['fields'] = @(
                        '$key'
                        'node'
                        'node#name as node_name'
                        'name'
                        'slot'
                        'class'
                        'class_hex'
                        'device_type'
                        'vendor'
                        'device'
                        'vendor_device_hex'
                        'svendor'
                        'subsystem_device'
                        'physical_slot'
                        'driver'
                        'module'
                        'numa'
                        'iommu_group'
                        'sriov_totalvfs'
                        'sriov_numvfs'
                    ) -join ','
                }
                else {
                    # USB devices
                    $queryParams['fields'] = @(
                        '$key'
                        'node'
                        'node#name as node_name'
                        'bus'
                        'device'
                        'path'
                        'devpath'
                        'vendor'
                        'vendor_id'
                        'model'
                        'model_id'
                        'serial'
                        'usb_version'
                        'speed'
                        'interface_drivers'
                    ) -join ','
                }

                $response = Invoke-VergeAPI -Method GET -Endpoint $endpoint -Query $queryParams -Connection $Server

                # Handle both single object and array responses
                $devices = if ($response -is [array]) { $response } else { @($response) }

                foreach ($device in $devices) {
                    # Skip null entries
                    if (-not $device) {
                        continue
                    }

                    # Determine device type for output
                    $type = if ($endpoint -eq 'node_pci_devices') { 'PCI' } else { 'USB' }

                    # Build common output object
                    $output = [PSCustomObject]@{
                        PSTypeName     = 'Verge.NodeDevice'
                        Key            = [int]$device.'$key'
                        Node           = $device.node_name
                        NodeKey        = $device.node
                        Type           = $type
                    }

                    if ($type -eq 'PCI') {
                        # Add PCI-specific properties
                        $output | Add-Member -MemberType NoteProperty -Name 'Name' -Value $device.name
                        $output | Add-Member -MemberType NoteProperty -Name 'Slot' -Value $device.slot
                        $output | Add-Member -MemberType NoteProperty -Name 'Class' -Value $device.class
                        $output | Add-Member -MemberType NoteProperty -Name 'ClassHex' -Value $device.class_hex
                        $output | Add-Member -MemberType NoteProperty -Name 'DeviceTypeCode' -Value $device.device_type
                        $output | Add-Member -MemberType NoteProperty -Name 'Vendor' -Value $device.vendor
                        $output | Add-Member -MemberType NoteProperty -Name 'Device' -Value $device.device
                        $output | Add-Member -MemberType NoteProperty -Name 'VendorDeviceHex' -Value $device.vendor_device_hex
                        $output | Add-Member -MemberType NoteProperty -Name 'SubsystemVendor' -Value $device.svendor
                        $output | Add-Member -MemberType NoteProperty -Name 'SubsystemDevice' -Value $device.subsystem_device
                        $output | Add-Member -MemberType NoteProperty -Name 'PhysicalSlot' -Value $device.physical_slot
                        $output | Add-Member -MemberType NoteProperty -Name 'Driver' -Value $device.driver
                        $output | Add-Member -MemberType NoteProperty -Name 'Module' -Value $device.module
                        $output | Add-Member -MemberType NoteProperty -Name 'NUMA' -Value $device.numa
                        $output | Add-Member -MemberType NoteProperty -Name 'IOMMUGroup' -Value $device.iommu_group
                        $output | Add-Member -MemberType NoteProperty -Name 'SRIOVTotalVFs' -Value ([int]$device.sriov_totalvfs)
                        $output | Add-Member -MemberType NoteProperty -Name 'SRIOVNumVFs' -Value ([int]$device.sriov_numvfs)
                    }
                    else {
                        # Add USB-specific properties
                        $output | Add-Member -MemberType NoteProperty -Name 'Name' -Value $device.model
                        $output | Add-Member -MemberType NoteProperty -Name 'Bus' -Value $device.bus
                        $output | Add-Member -MemberType NoteProperty -Name 'DeviceNum' -Value $device.device
                        $output | Add-Member -MemberType NoteProperty -Name 'Path' -Value $device.path
                        $output | Add-Member -MemberType NoteProperty -Name 'DevPath' -Value $device.devpath
                        $output | Add-Member -MemberType NoteProperty -Name 'Vendor' -Value $device.vendor
                        $output | Add-Member -MemberType NoteProperty -Name 'VendorID' -Value $device.vendor_id
                        $output | Add-Member -MemberType NoteProperty -Name 'Model' -Value $device.model
                        $output | Add-Member -MemberType NoteProperty -Name 'ModelID' -Value $device.model_id
                        $output | Add-Member -MemberType NoteProperty -Name 'Serial' -Value $device.serial
                        $output | Add-Member -MemberType NoteProperty -Name 'USBVersion' -Value $device.usb_version
                        $output | Add-Member -MemberType NoteProperty -Name 'Speed' -Value $device.speed
                        $output | Add-Member -MemberType NoteProperty -Name 'InterfaceDrivers' -Value $device.interface_drivers
                    }

                    # Add hidden properties for pipeline support
                    $output | Add-Member -MemberType NoteProperty -Name '_Connection' -Value $Server -Force

                    Write-Output $output
                }
            }
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
}