Private/Invoke-PVEPowerAction.ps1
|
function Invoke-PVEPowerAction { <# .SYNOPSIS Shared engine for every VM and container power cmdlet. .DESCRIPTION 1. Resolves the collected targets (IDs or names) with Resolve-PVEGuestTarget. Unknown IDs or exact names and guests of the other type are reported as errors there; templates are errors here. 2. Reads each guest's live status. A guest already in the requested state is skipped with a warning and written to the output unchanged, so pipelines keep flowing. 3. Asks ShouldProcess on the calling cmdlet (so -WhatIf and -Confirm apply), then POSTs /nodes/{node}/{type}/{vmid}/status/{action}. Each call returns a task UPID. 4. Complete-PVEOperation: with -RunAsync, writes the tasks. Otherwise waits for all of them together (Wait-PVETask), then writes each guest whose task succeeded, with its live status. Failed tasks are errors. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Calls ShouldProcess on the calling public cmdlet, passed as -Cmdlet, so its -WhatIf and -Confirm apply.')] [CmdletBinding()] param( [Parameter(Mandatory)] [System.Management.Automation.PSCmdlet]$Cmdlet, [Parameter(Mandatory)] [ValidateSet('qemu', 'lxc')] [string]$Type, [Parameter(Mandatory)] [ValidateSet('start', 'stop', 'shutdown', 'reset', 'reboot', 'suspend', 'resume')] [string]$Action, # Text for ShouldProcess and messages, for example 'Power off (hard stop)'. [Parameter(Mandatory)] [string]$Description, [Parameter(Mandatory)] [AllowEmptyCollection()] [System.Collections.Generic.List[psobject]]$Target, # Status the guest must be in for the action to make sense; others are skipped with a warning. [Parameter(Mandatory)] [ValidateSet('running', 'stopped', 'any')] [string]$RequiredStatus, [hashtable]$Parameters, [switch]$RunAsync ) $operations = [System.Collections.Generic.List[psobject]]::new() foreach ($guest in @(Resolve-PVEGuestTarget -Cmdlet $Cmdlet -Type $Type -Target $Target -IncludeTemplate)) { $label = Get-PVEGuestLabel -Guest $guest if ($guest.IsTemplate) { $Cmdlet.WriteError([System.Management.Automation.ErrorRecord]::new( [System.InvalidOperationException]::new("$label is a template; templates cannot be powered on or off."), 'PVEGuestIsTemplate', [System.Management.Automation.ErrorCategory]::InvalidOperation, $guest)) continue } try { $null = Update-PVEGuestStatus -Guest $guest } catch { $Cmdlet.WriteError($_) continue } if ($RequiredStatus -eq 'running' -and $guest.Status -ne 'running') { Write-Warning "$label is $($guest.Status); '$Description' skipped." $guest continue } if ($RequiredStatus -eq 'stopped' -and $guest.Status -eq 'running') { Write-Warning "$label is already running; '$Description' skipped." $guest continue } if (-not $Cmdlet.ShouldProcess($label, $Description)) { continue } $path = Format-PVEPath '/nodes/{node}/{type}/{vmid}/status/{action}' @{ node = $guest.Node type = $Type vmid = $guest.VMId action = $Action } try { $upid = [string](Invoke-PVERequest -Connection $guest.Connection -Method POST -Path $path -Parameters $Parameters) } catch { $Cmdlet.WriteError($_) continue } Write-Verbose "$Description $label`: task $upid" $operations.Add([pscustomobject]@{ Upid = $upid; Connection = $guest.Connection; Guest = $guest }) } Complete-PVEOperation -Cmdlet $Cmdlet -Operation $operations -RunAsync:$RunAsync } |