Public/System/New-VergeCluster.ps1

function New-VergeCluster {
    <#
    .SYNOPSIS
        Creates a new cluster in VergeOS.

    .DESCRIPTION
        New-VergeCluster creates a new cluster with the specified configuration.
        Clusters group nodes for compute and storage resources.

    .PARAMETER Name
        The name of the new cluster. Must be unique and 1-128 characters.

    .PARAMETER Description
        An optional description for the cluster.

    .PARAMETER Enabled
        Enable the cluster after creation. Default is true.

    .PARAMETER Compute
        Enable compute workloads on this cluster.

    .PARAMETER NestedVirtualization
        Enable nested virtualization (running VMs inside VMs).

    .PARAMETER AllowNestedVirtMigration
        Allow live migration of VMs with nested virtualization enabled.

    .PARAMETER AllowVGPUMigration
        Allow live migration of VMs with vGPU devices (experimental).

    .PARAMETER DefaultCPUType
        The default CPU type for VMs in this cluster.

    .PARAMETER DisableCPUSecurityMitigations
        Disable CPU security mitigations. Only use if you trust all guests.

    .PARAMETER DisableSMT
        Disable Simultaneous Multi-Threading (hyper-threading).

    .PARAMETER EnableSplitLockDetection
        Enable split lock detection. May impact VM performance.

    .PARAMETER EnergyPerfPolicy
        CPU energy-performance policy: Performance, BalancePerformance, BalancePower, Normal, Power.

    .PARAMETER ScalingGovernor
        CPU scaling governor: Performance, OnDemand, PowerSave.

    .PARAMETER RAMPerUnit
        RAM per billing unit in MB.

    .PARAMETER CoresPerUnit
        CPU cores per billing unit.

    .PARAMETER CostPerUnit
        Cost per billing unit.

    .PARAMETER PricePerUnit
        Price per billing unit.

    .PARAMETER MaxRAMPerVM
        Maximum RAM allowed per VM in MB.

    .PARAMETER MaxCoresPerVM
        Maximum CPU cores allowed per VM.

    .PARAMETER TargetRAMPercent
        Target maximum RAM utilization percentage (0-100).

    .PARAMETER RAMOvercommitPercent
        Percentage of reserve RAM to use for machines (0-100).

    .PARAMETER StorageCachePerNode
        Storage cache per node in MB.

    .PARAMETER StorageBufferPerNode
        Storage buffer per node in MB.

    .PARAMETER StorageHugepages
        Allocate hugepages for storage.

    .PARAMETER EnableNVMePowerManagement
        Enable NVMe power management. Some drives have issues with this.

    .PARAMETER SwapTier
        Storage tier used for swap (-1 to disable, 0-5 for tier).

    .PARAMETER SwapPerDrive
        Swap space per drive in MB.

    .PARAMETER MaxCoreTemp
        Maximum core temperature in Celsius.

    .PARAMETER CriticalCoreTemp
        Critical core temperature in Celsius.

    .PARAMETER MaxCoreTempWarnPercent
        Temperature warning threshold percentage.

    .PARAMETER DisableSleep
        Disable CPU sleep states. Increases power usage and heat.

    .PARAMETER LogFilter
        System log filter expression.

    .PARAMETER PassThru
        Return the created cluster object.

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

    .EXAMPLE
        New-VergeCluster -Name "Development"

        Creates a basic cluster named "Development".

    .EXAMPLE
        New-VergeCluster -Name "Production" -Description "Production workloads" -Compute -MaxRAMPerVM 65536 -MaxCoresPerVM 32

        Creates a production cluster with compute enabled and VM resource limits.

    .EXAMPLE
        New-VergeCluster -Name "GPU-Cluster" -NestedVirtualization -AllowVGPUMigration -PassThru

        Creates a cluster with nested virtualization and vGPU support.

    .OUTPUTS
        None by default. Verge.Cluster when -PassThru is specified.

    .NOTES
        After creating a cluster, add nodes using Set-VergeNode to assign them.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [ValidateLength(1, 128)]
        [string]$Name,

        [Parameter()]
        [ValidateLength(0, 2048)]
        [string]$Description,

        [Parameter()]
        [bool]$Enabled = $true,

        [Parameter()]
        [switch]$Compute,

        [Parameter()]
        [switch]$NestedVirtualization,

        [Parameter()]
        [bool]$AllowNestedVirtMigration = $true,

        [Parameter()]
        [switch]$AllowVGPUMigration,

        [Parameter()]
        [ValidateSet(
            'qemu64', 'kvm64', 'host',
            'Broadwell', 'Cascadelake-Server', 'Conroe', 'Cooperlake',
            'core2duo', 'coreduo', 'Denverton',
            'EPYC', 'EPYC-Genoa', 'EPYC-Milan', 'EPYC-Rome',
            'GraniteRapids', 'Haswell', 'Icelake-Server', 'IvyBridge',
            'KnightsMill', 'n270', 'Nehalem',
            'Opteron_G1', 'Opteron_G2', 'Opteron_G3', 'Opteron_G4', 'Opteron_G5',
            'Penryn', 'phenom', 'SandyBridge', 'SapphireRapids',
            'Skylake-Client', 'Skylake-Server', 'Snowridge', 'Westmere'
        )]
        [string]$DefaultCPUType,

        [Parameter()]
        [switch]$DisableCPUSecurityMitigations,

        [Parameter()]
        [switch]$DisableSMT,

        [Parameter()]
        [switch]$EnableSplitLockDetection,

        [Parameter()]
        [ValidateSet('Performance', 'BalancePerformance', 'BalancePower', 'Normal', 'Power')]
        [string]$EnergyPerfPolicy = 'Performance',

        [Parameter()]
        [ValidateSet('Performance', 'OnDemand', 'PowerSave')]
        [string]$ScalingGovernor = 'Performance',

        [Parameter()]
        [ValidateRange(0, 1048576)]
        [int]$RAMPerUnit = 4096,

        [Parameter()]
        [ValidateRange(0, 1024)]
        [int]$CoresPerUnit = 1,

        [Parameter()]
        [ValidateRange(0, [double]::MaxValue)]
        [double]$CostPerUnit = 0,

        [Parameter()]
        [ValidateRange(0, [double]::MaxValue)]
        [double]$PricePerUnit = 0,

        [Parameter()]
        [ValidateRange(0, 1048576)]
        [int]$MaxRAMPerVM = 65536,

        [Parameter()]
        [ValidateRange(0, 1024)]
        [int]$MaxCoresPerVM = 16,

        [Parameter()]
        [ValidateRange(0, 100)]
        [double]$TargetRAMPercent = 80,

        [Parameter()]
        [ValidateRange(0, 100)]
        [double]$RAMOvercommitPercent = 0,

        [Parameter()]
        [ValidateRange(0, 5000000)]
        [int]$StorageCachePerNode,

        [Parameter()]
        [ValidateRange(0, 5000000)]
        [int]$StorageBufferPerNode,

        [Parameter()]
        [bool]$StorageHugepages = $true,

        [Parameter()]
        [switch]$EnableNVMePowerManagement,

        [Parameter()]
        [ValidateRange(-1, 5)]
        [int]$SwapTier = -1,

        [Parameter()]
        [ValidateRange(0, 1000000)]
        [int]$SwapPerDrive,

        [Parameter()]
        [ValidateRange(0, 150)]
        [int]$MaxCoreTemp,

        [Parameter()]
        [ValidateRange(0, 150)]
        [int]$CriticalCoreTemp,

        [Parameter()]
        [ValidateRange(-1, 100)]
        [int]$MaxCoreTempWarnPercent = 10,

        [Parameter()]
        [switch]$DisableSleep,

        [Parameter()]
        [string]$LogFilter,

        [Parameter()]
        [switch]$PassThru,

        [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.'
            )
        }

        # Map friendly names to API values
        $energyPerfMap = @{
            'Performance'        = 'performance'
            'BalancePerformance' = 'balance-performance'
            'BalancePower'       = 'balance-power'
            'Normal'             = 'normal'
            'Power'              = 'power'
        }

        $scalingGovMap = @{
            'Performance' = 'performance'
            'OnDemand'    = 'ondemand'
            'PowerSave'   = 'powersave'
        }
    }

    process {
        # Build request body
        $body = @{
            name                           = $Name
            enabled                        = $Enabled
            compute                        = [bool]$Compute
            kvm_nested                     = [bool]$NestedVirtualization
            allow_nested_virt_migration    = $AllowNestedVirtMigration
            allow_vgpu_migration           = [bool]$AllowVGPUMigration
            disable_cpu_security_mitigations = [bool]$DisableCPUSecurityMitigations
            disable_smt                    = [bool]$DisableSMT
            enable_split_lock_detection    = [bool]$EnableSplitLockDetection
            x86_energy_perf_policy         = $energyPerfMap[$EnergyPerfPolicy]
            scaling_governor               = $scalingGovMap[$ScalingGovernor]
            ram_per_unit                   = $RAMPerUnit
            cores_per_unit                 = $CoresPerUnit
            cost_per_unit                  = $CostPerUnit
            price_per_unit                 = $PricePerUnit
            max_ram_per_vm                 = $MaxRAMPerVM
            max_cores_per_vm               = $MaxCoresPerVM
            target_ram_pct                 = $TargetRAMPercent
            ram_overcommit_pct             = $RAMOvercommitPercent
            storage_hugepages              = $StorageHugepages
            enable_nvme_power_management   = [bool]$EnableNVMePowerManagement
            swap_tier                      = $SwapTier
            disable_sleep                  = [bool]$DisableSleep
            max_core_temp_warn_perc        = $MaxCoreTempWarnPercent
        }

        # Add optional parameters
        if ($Description) {
            $body['description'] = $Description
        }

        if ($DefaultCPUType) {
            $body['default_cpu'] = $DefaultCPUType
        }

        if ($PSBoundParameters.ContainsKey('StorageCachePerNode')) {
            $body['storage_cachesize'] = $StorageCachePerNode
        }

        if ($PSBoundParameters.ContainsKey('StorageBufferPerNode')) {
            $body['storage_buffersize'] = $StorageBufferPerNode
        }

        if ($PSBoundParameters.ContainsKey('SwapPerDrive')) {
            $body['swap_per_drive'] = $SwapPerDrive
        }

        if ($PSBoundParameters.ContainsKey('MaxCoreTemp')) {
            $body['max_core_temp'] = $MaxCoreTemp
        }

        if ($PSBoundParameters.ContainsKey('CriticalCoreTemp')) {
            $body['critical_core_temp'] = $CriticalCoreTemp
        }

        if ($LogFilter) {
            $body['log_filter'] = $LogFilter
        }

        if ($PSCmdlet.ShouldProcess($Name, 'Create Cluster')) {
            try {
                Write-Verbose "Creating cluster '$Name'"
                $response = Invoke-VergeAPI -Method POST -Endpoint 'clusters' -Body $body -Connection $Server

                # Get the created cluster key
                $clusterKey = $response.'$key'
                if (-not $clusterKey -and $response.key) {
                    $clusterKey = $response.key
                }

                Write-Verbose "Cluster '$Name' created with Key: $clusterKey"

                if ($PassThru -and $clusterKey) {
                    # Return the created cluster
                    Start-Sleep -Milliseconds 500
                    Get-VergeCluster -Key $clusterKey -Server $Server
                }
            }
            catch {
                $errorMessage = $_.Exception.Message
                if ($errorMessage -match 'already in use' -or $errorMessage -match 'unique') {
                    throw "A cluster with the name '$Name' already exists."
                }
                throw "Failed to create cluster '$Name': $errorMessage"
            }
        }
    }
}