fix: use qmpstatus for suspend state detection, fix SDN subnet ID format

Suspend/Resume:
- Poll status/current endpoint (not VM list) to get qmpstatus
- PVE reports status=running but qmpstatus=paused for suspended VMs
- Verified locally: Suspend-PveVm -Wait -Timeout 30 now works

SDN:
- Subnet IDs use zone prefix, not vnet: {zone}-{ip}-{prefix}
- Add 5s propagation delay before VNet deletion after subnet removal
- Switch to real Ubuntu 24.04 OVA for Import-PveOva testing

Integration tests:
- All VM/container lifecycle calls use -Wait -Timeout
- Remove manual Start-Sleep polling loops

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-20 15:37:38 -05:00
parent 2b772ebc65
commit a06cd3f2e5
2 changed files with 21 additions and 15 deletions
+19 -13
View File
@@ -1,6 +1,8 @@
using System;
using System.Management.Automation;
using Newtonsoft.Json.Linq;
using PSProxmoxVE.Core.Authentication;
using PSProxmoxVE.Core.Client;
using PSProxmoxVE.Core.Exceptions;
using PSProxmoxVE.Core.Models.Vms;
using PSProxmoxVE.Core.Services;
@@ -69,25 +71,29 @@ namespace PSProxmoxVE.Cmdlets
task = taskService.WaitForTask(session, node, task.Upid, null, null, null);
}
// Then poll until VM/container reaches the expected status
// Then poll status/current until VM/container reaches the expected status.
// We query the status/current endpoint directly instead of the list endpoint
// because it returns qmpstatus (needed for paused state detection — PVE reports
// status=running but qmpstatus=paused for suspended VMs).
var statusResource = isContainer
? $"nodes/{node}/lxc/{vmid}/status/current"
: $"nodes/{node}/qemu/{vmid}/status/current";
var deadline = DateTime.UtcNow.AddSeconds(timeoutSeconds);
using var pollClient = new PveHttpClient(session);
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;
}
var json = pollClient.GetAsync(statusResource).GetAwaiter().GetResult();
var data = JObject.Parse(json)["data"];
var status = data?["status"]?.ToString();
var qmpStatus = data?["qmpstatus"]?.ToString();
if (string.Equals(currentStatus, expectedStatus, StringComparison.OrdinalIgnoreCase))
// Use qmpstatus when available (more accurate for VM pause state)
var effectiveStatus = qmpStatus ?? status;
if (string.Equals(effectiveStatus, expectedStatus, StringComparison.OrdinalIgnoreCase))
return task;
}
catch
@@ -773,8 +773,8 @@ Describe 'Integration Tests' -Tag 'Integration' {
if (Skip-IfNoTarget) { return }
if ($script:SkipSdn) { Set-ItResult -Skipped -Because $script:SkipSdn; return }
# PVE stores subnet IDs in the format: {vnet}-{ip}-{prefix}
{ Remove-PveSdnSubnet -Vnet 'pestervn' -Subnet 'pestervn-10.99.0.0-24' `
# PVE stores subnet IDs in the format: {zone}-{ip}-{prefix}
{ Remove-PveSdnSubnet -Vnet 'pestervn' -Subnet 'pesterz-10.99.0.0-24' `
-Confirm:$false -ErrorAction Stop } | Should -Not -Throw
}