mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-09-03 18:55:33 +00:00
fix: Restart-PveVm uses PVE's native reboot endpoint (cmdlet + tests)
This commit is contained in:
committed by
GitHub
parent
2846395662
commit
c5e73f4fc9
@@ -7,9 +7,10 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Gracefully restarts a QEMU/KVM virtual machine on a Proxmox VE node.</para>
|
||||
/// <para type="description">
|
||||
/// Performs a graceful shutdown of the VM followed by a start, via the Proxmox VE API.
|
||||
/// A configurable timeout controls how long to wait for the guest to shut down cleanly
|
||||
/// before the operation is considered failed. Use -Wait to block until both tasks complete.
|
||||
/// Reboots the VM through Proxmox VE's native reboot endpoint, which shuts the guest down
|
||||
/// and starts it again as a single server-side operation. A configurable timeout controls
|
||||
/// how long to wait for the guest to shut down cleanly. Use -Wait to block until the VM is
|
||||
/// running again.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Restart, "PveVm", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
@@ -41,7 +42,7 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
public int Timeout { get; set; } = 60;
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">When specified, waits for both shutdown and start tasks to complete before returning.</para>
|
||||
/// <para type="description">When specified, waits until the VM is running again before returning.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
@@ -56,19 +57,12 @@ namespace PSProxmoxVE.Cmdlets.Vms
|
||||
|
||||
WriteVerbose($"Restarting VM {VmId} on node '{Node}'...");
|
||||
|
||||
// Graceful shutdown
|
||||
var shutdownTask = vmService.ShutdownVm(session, Node, VmId, Timeout);
|
||||
var task = vmService.RebootVm(session, Node, VmId, Timeout);
|
||||
|
||||
if (Wait.IsPresent)
|
||||
WaitForStatusTransition(session, Node, shutdownTask, VmId, "stopped", Timeout);
|
||||
task = WaitForStatusTransition(session, Node, task, VmId, "running", Timeout);
|
||||
|
||||
// Start
|
||||
var startTask = vmService.StartVm(session, Node, VmId);
|
||||
|
||||
if (Wait.IsPresent)
|
||||
startTask = WaitForStatusTransition(session, Node, startTask, VmId, "running", Timeout);
|
||||
|
||||
WriteObject(startTask);
|
||||
WriteObject(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,5 +109,61 @@ namespace PSProxmoxVE.Core.Tests.Services
|
||||
"cmd.exe", new[] { "/c", null!, "echo" }));
|
||||
Assert.Equal("args", ex.ParamName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RebootVm_PostsToTheNativeRebootEndpoint()
|
||||
{
|
||||
string? resource = null;
|
||||
var mockClient = new Mock<IPveHttpClient>();
|
||||
mockClient
|
||||
.Setup(c => c.PostAsync(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>()))
|
||||
.Callback<string, Dictionary<string, string>>((r, _) => resource = r)
|
||||
.ReturnsAsync("{\"data\":\"UPID:pve1:00001234:00005678:6A970AAB:qmreboot:100:root@pam:\"}");
|
||||
|
||||
var service = new VmService(mockClient.Object);
|
||||
var task = service.RebootVm(CreateSession(), TestNode, TestVmId);
|
||||
|
||||
// Composing a reboot as shutdown + start races PVE's post-stop cleanup for the
|
||||
// config lock; the native endpoint keeps the whole restart server-side.
|
||||
Assert.Equal($"nodes/{TestNode}/qemu/{TestVmId}/status/reboot", resource);
|
||||
Assert.Contains("qmreboot", task.Upid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RebootVm_SendsTimeoutWhenSupplied()
|
||||
{
|
||||
List<KeyValuePair<string, string>>? captured = null;
|
||||
var mockClient = new Mock<IPveHttpClient>();
|
||||
mockClient
|
||||
.Setup(c => c.PostAsync(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>()))
|
||||
.Callback<string, Dictionary<string, string>>((_, data) => captured = data.ToList())
|
||||
.ReturnsAsync("{\"data\":\"UPID:pve1:00001234:00005678:6A970AAB:qmreboot:100:root@pam:\"}");
|
||||
|
||||
var service = new VmService(mockClient.Object);
|
||||
service.RebootVm(CreateSession(), TestNode, TestVmId, 45);
|
||||
|
||||
Assert.NotNull(captured);
|
||||
Assert.Single(captured!);
|
||||
Assert.Equal("timeout", captured![0].Key);
|
||||
Assert.Equal("45", captured![0].Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RebootVm_OmitsTimeoutWhenNotSupplied()
|
||||
{
|
||||
List<KeyValuePair<string, string>>? captured = null;
|
||||
var mockClient = new Mock<IPveHttpClient>();
|
||||
mockClient
|
||||
.Setup(c => c.PostAsync(It.IsAny<string>(), It.IsAny<Dictionary<string, string>>()))
|
||||
.Callback<string, Dictionary<string, string>>((_, data) => captured = data.ToList())
|
||||
.ReturnsAsync("{\"data\":\"UPID:pve1:00001234:00005678:6A970AAB:qmreboot:100:root@pam:\"}");
|
||||
|
||||
var service = new VmService(mockClient.Object);
|
||||
service.RebootVm(CreateSession(), TestNode, TestVmId);
|
||||
|
||||
Assert.NotNull(captured);
|
||||
Assert.Empty(captured!);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user