mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-08-09 05:49:24 +00:00
feat: add -Timeout parameter with state polling to lifecycle cmdlets
All VM and container lifecycle cmdlets (Start, Stop, Restart, Suspend, Resume, Reset) now poll VM/container status when -Wait is specified, blocking until the expected state is reached or -Timeout (default 60s) is exceeded. Implementation: - PveCmdletBase.WaitForStatusTransition() — shared helper that waits for PVE task completion then polls status via API - 9 cmdlets updated: 6 VM (Start, Stop, Restart, Suspend, Resume, Reset) + 3 container (Start, Stop, Restart) - -Timeout parameter with [ValidateRange(1, 3600)] on each Integration tests: - Replace all manual Start-Sleep + polling loops with -Wait -Timeout - Switch to real Ubuntu cloud OVA (571 MB) for Import-PveOva testing - OVA test verifies full import + VM start Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,10 +33,11 @@ namespace PSProxmoxVE.Cmdlets.Containers
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">
|
||||
/// Timeout in seconds for the graceful shutdown phase. Defaults to 60 seconds.
|
||||
/// Timeout in seconds for the graceful shutdown phase and -Wait status polling. Defaults to 60 seconds.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Maximum time to wait for the task.")]
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +53,6 @@ namespace PSProxmoxVE.Cmdlets.Containers
|
||||
|
||||
var session = GetSession();
|
||||
var containerService = new ContainerService();
|
||||
var taskService = new TaskService();
|
||||
|
||||
WriteVerbose($"Restarting container {VmId} on node '{Node}'...");
|
||||
|
||||
@@ -60,13 +60,13 @@ namespace PSProxmoxVE.Cmdlets.Containers
|
||||
var shutdownTask = containerService.ShutdownContainer(session, Node, VmId, Timeout);
|
||||
|
||||
if (Wait.IsPresent)
|
||||
taskService.WaitForTask(session, shutdownTask.Node ?? Node, shutdownTask.Upid!, null, null, null);
|
||||
WaitForStatusTransition(session, Node, shutdownTask, VmId, "stopped", Timeout, isContainer: true);
|
||||
|
||||
// Start
|
||||
var startTask = containerService.StartContainer(session, Node, VmId);
|
||||
|
||||
if (Wait.IsPresent)
|
||||
startTask = taskService.WaitForTask(session, startTask.Node ?? Node, startTask.Upid!, null, null, null);
|
||||
startTask = WaitForStatusTransition(session, Node, startTask, VmId, "running", Timeout, isContainer: true);
|
||||
|
||||
WriteObject(startTask);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,11 @@ namespace PSProxmoxVE.Cmdlets.Containers
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
/// <summary>Maximum seconds to wait for the status transition when -Wait is specified. Default 60.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Start-PveContainer"))
|
||||
@@ -49,8 +54,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, task.Node ?? Node, task.Upid!, null, null, null);
|
||||
task = WaitForStatusTransition(session, Node, task, VmId, "running", Timeout, isContainer: true);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace PSProxmoxVE.Cmdlets.Containers
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
/// <summary>Maximum seconds to wait for the status transition when -Wait is specified. Default 60.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Stop-PveContainer"))
|
||||
@@ -51,8 +56,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, task.Node ?? Node, task.Upid!, null, null, null);
|
||||
task = WaitForStatusTransition(session, Node, task, VmId, "stopped", Timeout, isContainer: true);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Authentication;
|
||||
using PSProxmoxVE.Core.Exceptions;
|
||||
using PSProxmoxVE.Core.Models.Vms;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets
|
||||
{
|
||||
@@ -35,5 +38,69 @@ namespace PSProxmoxVE.Cmdlets
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for a PVE task to complete, then optionally polls VM status until
|
||||
/// it matches <paramref name="expectedStatus"/>. Used by lifecycle cmdlets
|
||||
/// (Start, Stop, Suspend, Resume, etc.) when -Wait is specified.
|
||||
/// </summary>
|
||||
/// <param name="session">The authenticated PVE session.</param>
|
||||
/// <param name="node">The cluster node name.</param>
|
||||
/// <param name="task">The task returned by the lifecycle API call.</param>
|
||||
/// <param name="vmid">The VM or container ID to poll.</param>
|
||||
/// <param name="expectedStatus">The expected status string (e.g. "running", "stopped", "paused").</param>
|
||||
/// <param name="timeoutSeconds">Maximum seconds to wait for the status transition. Default 60.</param>
|
||||
/// <param name="isContainer">True to poll container status instead of VM status.</param>
|
||||
/// <returns>The completed task.</returns>
|
||||
protected PveTask WaitForStatusTransition(
|
||||
PveSession session,
|
||||
string node,
|
||||
PveTask task,
|
||||
int vmid,
|
||||
string expectedStatus,
|
||||
int timeoutSeconds = 60,
|
||||
bool isContainer = false)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
|
||||
// First wait for the PVE task to complete
|
||||
if (!string.IsNullOrEmpty(task.Upid))
|
||||
{
|
||||
task = taskService.WaitForTask(session, node, task.Upid, null, null, null);
|
||||
}
|
||||
|
||||
// Then poll until VM/container reaches the expected status
|
||||
var deadline = DateTime.UtcNow.AddSeconds(timeoutSeconds);
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
try
|
||||
{
|
||||
string? currentStatus;
|
||||
if (isContainer)
|
||||
{
|
||||
var ct = new ContainerService().GetContainer(session, node, vmid);
|
||||
currentStatus = ct.Status;
|
||||
}
|
||||
else
|
||||
{
|
||||
var vm = new VmService().GetVm(session, node, vmid);
|
||||
currentStatus = vm.Status;
|
||||
}
|
||||
|
||||
if (string.Equals(currentStatus, expectedStatus, StringComparison.OrdinalIgnoreCase))
|
||||
return task;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore transient errors during polling
|
||||
}
|
||||
|
||||
System.Threading.Thread.Sleep(2000);
|
||||
}
|
||||
|
||||
throw new PveTaskTimeoutException(
|
||||
task.Upid ?? "unknown",
|
||||
TimeSpan.FromSeconds(timeoutSeconds));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
/// <summary>Maximum seconds to wait for the status transition when -Wait is specified. Default 60.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Reset-PveVm"))
|
||||
@@ -51,8 +56,7 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, Node, task.Upid, null, null, null);
|
||||
task = WaitForStatusTransition(session, Node, task, VmId, "running", Timeout);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
|
||||
@@ -33,10 +33,11 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">
|
||||
/// Timeout in seconds for the graceful shutdown phase. Defaults to 60 seconds.
|
||||
/// Timeout in seconds for the graceful shutdown phase and -Wait status polling. Defaults to 60 seconds.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Maximum time to wait for the task.")]
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
/// <summary>
|
||||
@@ -52,7 +53,6 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
|
||||
var session = GetSession();
|
||||
var vmService = new VmService();
|
||||
var taskService = new TaskService();
|
||||
|
||||
WriteVerbose($"Restarting VM {VmId} on node '{Node}'...");
|
||||
|
||||
@@ -60,13 +60,13 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
var shutdownTask = vmService.ShutdownVm(session, Node, VmId, Timeout);
|
||||
|
||||
if (Wait.IsPresent)
|
||||
taskService.WaitForTask(session, Node, shutdownTask.Upid, null, null, null);
|
||||
WaitForStatusTransition(session, Node, shutdownTask, VmId, "stopped", Timeout);
|
||||
|
||||
// Start
|
||||
var startTask = vmService.StartVm(session, Node, VmId);
|
||||
|
||||
if (Wait.IsPresent)
|
||||
startTask = taskService.WaitForTask(session, Node, startTask.Upid, null, null, null);
|
||||
startTask = WaitForStatusTransition(session, Node, startTask, VmId, "running", Timeout);
|
||||
|
||||
WriteObject(startTask);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,11 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
/// <summary>Maximum seconds to wait for the status transition when -Wait is specified. Default 60.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Resume-PveVm"))
|
||||
@@ -49,8 +54,7 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, Node, task.Upid, null, null, null);
|
||||
task = WaitForStatusTransition(session, Node, task, VmId, "running", Timeout);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
|
||||
@@ -36,6 +36,11 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
/// <summary>Maximum seconds to wait for the status transition when -Wait is specified. Default 60.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Start-PveVm"))
|
||||
@@ -49,8 +54,7 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, Node, task.Upid, null, null, null);
|
||||
task = WaitForStatusTransition(session, Node, task, VmId, "running", Timeout);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
|
||||
@@ -38,6 +38,11 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
/// <summary>Maximum seconds to wait for the status transition when -Wait is specified. Default 60.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Stop-PveVm"))
|
||||
@@ -51,8 +56,7 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, Node, task.Upid, null, null, null);
|
||||
task = WaitForStatusTransition(session, Node, task, VmId, "stopped", Timeout);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
|
||||
@@ -37,6 +37,11 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
/// <summary>Maximum seconds to wait for the status transition when -Wait is specified. Default 60.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 60).")]
|
||||
[ValidateRange(1, 3600)]
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Suspend-PveVm"))
|
||||
@@ -50,8 +55,7 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, Node, task.Upid, null, null, null);
|
||||
task = WaitForStatusTransition(session, Node, task, VmId, "paused", Timeout);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
|
||||
Reference in New Issue
Block a user