diff --git a/src/PSProxmoxVE.Core/Models/Vms/PveVm.cs b/src/PSProxmoxVE.Core/Models/Vms/PveVm.cs
index 88d1f32..dd8df0e 100644
--- a/src/PSProxmoxVE.Core/Models/Vms/PveVm.cs
+++ b/src/PSProxmoxVE.Core/Models/Vms/PveVm.cs
@@ -24,7 +24,10 @@ public class PveVm
public string? Name { get; set; }
///
- /// The current runtime status of the VM (e.g., "running", "stopped").
+ /// The runtime status from the VM list endpoint (e.g., "running", "stopped").
+ /// Note: reports "running" for both running AND paused VMs. For accurate pause
+ /// detection, use or ,
+ /// which are populated when using Get-PveVm -Detailed.
///
[JsonPropertyName("status")]
[JsonProperty("status")]
@@ -94,12 +97,21 @@ public class PveVm
public int? Pid { get; set; }
///
- /// The QMP (QEMU Machine Protocol) status string.
+ /// The QMP (QEMU Machine Protocol) status string (e.g., "running", "paused", "stopped").
+ /// Only populated from the status/current endpoint (Get-PveVm -Detailed), not the list endpoint.
///
[JsonPropertyName("qmpstatus")]
[JsonProperty("qmpstatus")]
public string? QmpStatus { get; set; }
+ ///
+ /// The effective runtime status, preferring QmpStatus over Status for accurate
+ /// pause detection. Returns QmpStatus when available (from -Detailed), otherwise Status.
+ ///
+ [System.Text.Json.Serialization.JsonIgnore]
+ [Newtonsoft.Json.JsonIgnore]
+ public string? EffectiveStatus => QmpStatus ?? Status;
+
///
/// Indicates whether the QEMU guest agent is running (non-zero) inside the VM.
///
@@ -115,7 +127,7 @@ public class PveVm
var uptimeStr = Uptime.HasValue ? TimeSpan.FromSeconds(Uptime.Value).ToString(@"d\.hh\:mm\:ss") : "N/A";
var templateStr = Template is 1 ? " [template]" : string.Empty;
return $"VM {VmId}{templateStr}: {Name ?? "(unnamed)"} | Node: {Node ?? "N/A"} | "
- + $"Status: {Status ?? "N/A"} | CPUs: {CpuCount?.ToString() ?? "N/A"} | "
+ + $"Status: {EffectiveStatus ?? "N/A"} | CPUs: {CpuCount?.ToString() ?? "N/A"} | "
+ $"Mem: {maxMemMb} | Disk: {maxDiskGb} | Uptime: {uptimeStr}";
}
}
diff --git a/src/PSProxmoxVE.Core/Services/VmService.cs b/src/PSProxmoxVE.Core/Services/VmService.cs
index 66734e3..39c8e0c 100644
--- a/src/PSProxmoxVE.Core/Services/VmService.cs
+++ b/src/PSProxmoxVE.Core/Services/VmService.cs
@@ -80,6 +80,34 @@ namespace PSProxmoxVE.Core.Services
return vm;
}
+ ///
+ /// Enriches a VM object with detailed status from the status/current endpoint,
+ /// populating QmpStatus and other fields not available from the list endpoint.
+ ///
+ /// The authenticated PVE session.
+ /// The cluster node name.
+ /// The VM to enrich.
+ public void EnrichVmStatus(PveSession session, string node, PveVm vm)
+ {
+ if (session == null) throw new ArgumentNullException(nameof(session));
+ if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
+ if (vm == null) throw new ArgumentNullException(nameof(vm));
+
+ using var client = new PveHttpClient(session);
+ var response = client.GetAsync($"nodes/{node}/qemu/{vm.VmId}/status/current")
+ .GetAwaiter().GetResult();
+ var data = JObject.Parse(response)["data"];
+ if (data == null) return;
+
+ vm.QmpStatus = data["qmpstatus"]?.ToString();
+ vm.Status = data["status"]?.ToString() ?? vm.Status;
+ vm.Pid = data["pid"]?.ToObject();
+ vm.Uptime = data["uptime"]?.ToObject();
+ vm.CpuCount = data["cpus"]?.ToObject() ?? vm.CpuCount;
+ vm.MaxMem = data["maxmem"]?.ToObject() ?? vm.MaxMem;
+ vm.MaxDisk = data["maxdisk"]?.ToObject() ?? vm.MaxDisk;
+ }
+
///
/// Returns the full configuration of a VM.
///
diff --git a/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs
index b6ecf0d..2cefabb 100644
--- a/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs
+++ b/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs
@@ -58,6 +58,16 @@ namespace PSProxmoxVE.Cmdlets.Vms
[Parameter(Mandatory = false, HelpMessage = "Return only VMs marked as templates.")]
public SwitchParameter TemplatesOnly { get; set; }
+ ///
+ ///
+ /// When specified, queries the status/current endpoint for each VM to populate
+ /// QmpStatus and EffectiveStatus. This provides accurate pause detection but
+ /// requires one additional API call per VM.
+ ///
+ ///
+ [Parameter(Mandatory = false, HelpMessage = "Include detailed status (QmpStatus) via per-VM API call.")]
+ public SwitchParameter Detailed { get; set; }
+
protected override void ProcessRecord()
{
var session = GetSession();
@@ -84,7 +94,26 @@ namespace PSProxmoxVE.Cmdlets.Vms
if (TemplatesOnly.IsPresent)
vms = vms.Where(v => v.Template == 1);
- foreach (var vm in vms)
+ // Materialize before enrichment to avoid multiple enumeration
+ var vmList = vms.ToList();
+
+ if (Detailed.IsPresent)
+ {
+ WriteVerbose($"Enriching {vmList.Count} VM(s) with detailed status...");
+ foreach (var vm in vmList)
+ {
+ try
+ {
+ service.EnrichVmStatus(session, vm.Node ?? Node ?? string.Empty, vm);
+ }
+ catch
+ {
+ // Skip enrichment for inaccessible VMs (e.g. locked, migrating)
+ }
+ }
+ }
+
+ foreach (var vm in vmList)
WriteObject(vm);
}
}
diff --git a/src/PSProxmoxVE/PSProxmoxVE.format.ps1xml b/src/PSProxmoxVE/PSProxmoxVE.format.ps1xml
index 6535d44..90e7cf9 100644
--- a/src/PSProxmoxVE/PSProxmoxVE.format.ps1xml
+++ b/src/PSProxmoxVE/PSProxmoxVE.format.ps1xml
@@ -125,7 +125,7 @@
Name
- Status
+ EffectiveStatus
Node
diff --git a/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1 b/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1
index 14abe90..76c9ea2 100644
--- a/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1
+++ b/tests/PSProxmoxVE.Tests/Integration/Integration.Tests.ps1
@@ -1153,14 +1153,19 @@ Describe 'Integration Tests' -Tag 'Integration' {
if (Skip-IfNoLinuxVm) { return }
# Suspend — -Wait -Timeout polls qmpstatus until 'paused'
- # Note: PVE list endpoint reports Status=running for paused VMs;
- # the -Wait polling uses the status/current endpoint which has qmpstatus.
$suspendTask = Suspend-PveVm -Node $script:Node -VmId $script:LinuxVmId -Wait -Timeout 30
$suspendTask | Should -Not -BeNullOrEmpty
+ # Verify with -Detailed (fetches qmpstatus from status/current endpoint)
+ $vm = Get-PveVm -Node $script:Node -VmId $script:LinuxVmId -Detailed
+ $vm.EffectiveStatus | Should -Be 'paused'
+
# Resume — -Wait -Timeout polls qmpstatus until 'running'
$resumeTask = Resume-PveVm -Node $script:Node -VmId $script:LinuxVmId -Wait -Timeout 30
$resumeTask | Should -Not -BeNullOrEmpty
+
+ $vm = Get-PveVm -Node $script:Node -VmId $script:LinuxVmId -Detailed
+ $vm.EffectiveStatus | Should -Be 'running'
}
It 'Should gracefully restart a VM via ACPI (Restart-PveVm)' {