diff --git a/src/PSProxmoxVE/Cmdlets/PveCmdletBase.cs b/src/PSProxmoxVE/Cmdlets/PveCmdletBase.cs index 467e57d..ab10e0b 100644 --- a/src/PSProxmoxVE/Cmdlets/PveCmdletBase.cs +++ b/src/PSProxmoxVE/Cmdlets/PveCmdletBase.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; 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; +using PSProxmoxVE.Core.Utilities; namespace PSProxmoxVE.Cmdlets { @@ -131,21 +131,26 @@ namespace PSProxmoxVE.Cmdlets : $"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/status/current"; var deadline = DateTime.UtcNow.AddSeconds(timeoutSeconds); + var statusReached = false; using var pollClient = new PveHttpClient(session); while (DateTime.UtcNow < deadline) { try { var json = pollClient.GetAsync(statusResource).GetAwaiter().GetResult(); - var data = JObject.Parse(json)["data"]; - var status = data?["status"]?.ToString(); - var qmpStatus = data?["qmpstatus"]?.ToString(); + var snapshot = GuestStatusSnapshot.Evaluate(json, expectedStatus); - // Use qmpstatus when available (more accurate for VM pause state) - var effectiveStatus = qmpStatus ?? status; + if (snapshot.StatusMatched) + { + statusReached = true; - if (string.Equals(effectiveStatus, expectedStatus, StringComparison.OrdinalIgnoreCase)) - return task; + // PVE reports the target status before the operation releases the + // config lock. A caller that issues its next request inside that + // window gets "can't lock file '/var/lock/qemu-server/lock-.conf' + // - got timeout" from its own API call. + if (!snapshot.Locked) + return task; + } } catch (Exception ex) when (ex is not OutOfMemoryException and not StackOverflowException) { @@ -155,6 +160,10 @@ namespace PSProxmoxVE.Cmdlets System.Threading.Thread.Sleep(2000); } + // Status reached and only the lock outlasted the deadline. + if (statusReached) + return task; + throw new PveTaskTimeoutException( task.Upid ?? "unknown", TimeSpan.FromSeconds(timeoutSeconds));