mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-08-09 13:59:24 +00:00
feat: add Move-PveContainer cmdlet for LXC migration
- New Move-PveContainer cmdlet with -Online and -Wait support - Uses existing ContainerService.MigrateContainer - Pester unit tests added to ContainerLifecycle.Tests.ps1 - README cmdlet reference updated Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -238,6 +238,7 @@ SDN management requires Proxmox VE 8.0 or later. Connected server is version 7.4
|
||||
| `Stop-PveContainer` | Stop a container |
|
||||
| `Restart-PveContainer` | Restart a container |
|
||||
| `Copy-PveContainer` | Clone a container |
|
||||
| `Move-PveContainer` | Migrate a container to another node |
|
||||
| `Get-PveContainerConfig` | Get container configuration |
|
||||
| `Set-PveContainerConfig` | Modify container configuration |
|
||||
| `Get-PveContainerSnapshot` | List container snapshots |
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Vms;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Migrates an LXC container to a different Proxmox VE node.</para>
|
||||
/// <para type="description">
|
||||
/// Performs a live or offline migration of the specified container to the target node.
|
||||
/// Use -Online for live migration (container remains running). Use -Wait to block until the
|
||||
/// migration task completes.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Move, "PveContainer", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(PveTask))]
|
||||
public sealed class MovePveContainerCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>The node on which the container currently resides.</summary>
|
||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The PVE node name.")]
|
||||
public string Node { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The ID of the container to migrate. Accepts pipeline input.</summary>
|
||||
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The container identifier.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
/// <summary>The destination node to migrate the container to.</summary>
|
||||
[Parameter(Mandatory = true, HelpMessage = "The destination node for migration.")]
|
||||
public string TargetNode { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// When specified, performs a live migration so the container remains running during migration.
|
||||
/// Requires shared storage between the source and target nodes.
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Perform live migration (container stays running).")]
|
||||
public SwitchParameter Online { get; set; }
|
||||
|
||||
/// <summary>When specified, waits for the migration task to complete before returning.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"Container {VmId} from node '{Node}' to node '{TargetNode}'", "Move-PveContainer"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new ContainerService();
|
||||
|
||||
WriteVerbose($"Migrating container {VmId} from '{Node}' to '{TargetNode}'...");
|
||||
var task = service.MigrateContainer(session, Node, VmId, TargetNode, Online.IsPresent);
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, Node, task.Upid, null, null, null);
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ BeforeAll {
|
||||
'Start-PveContainer', 'Stop-PveContainer',
|
||||
'Restart-PveContainer',
|
||||
'Get-PveContainerConfig', 'Set-PveContainerConfig',
|
||||
'Copy-PveContainer')) {
|
||||
'Copy-PveContainer', 'Move-PveContainer')) {
|
||||
$script:Availability[$name] = $null -ne (Get-Command $name -ErrorAction SilentlyContinue)
|
||||
}
|
||||
|
||||
@@ -293,3 +293,64 @@ Describe 'Restart-PveContainer' {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Move-PveContainer
|
||||
# ---------------------------------------------------------------------------
|
||||
Describe 'Move-PveContainer' {
|
||||
|
||||
BeforeAll { $script:Cmd = Get-Command 'Move-PveContainer' -ErrorAction SilentlyContinue }
|
||||
|
||||
Context 'Command existence' {
|
||||
It 'Should be available after module import' {
|
||||
Skip-IfMissing 'Move-PveContainer'
|
||||
$script:Cmd | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Parameter metadata' {
|
||||
It 'Node should be Mandatory' {
|
||||
Skip-IfMissing 'Move-PveContainer'
|
||||
$isMandatory = $script:Cmd.Parameters['Node'].ParameterSets.Values |
|
||||
Where-Object { $_.IsMandatory }
|
||||
$isMandatory | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'VmId should be Mandatory' {
|
||||
Skip-IfMissing 'Move-PveContainer'
|
||||
$isMandatory = $script:Cmd.Parameters['VmId'].ParameterSets.Values |
|
||||
Where-Object { $_.IsMandatory }
|
||||
$isMandatory | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'TargetNode should be Mandatory' {
|
||||
Skip-IfMissing 'Move-PveContainer'
|
||||
$isMandatory = $script:Cmd.Parameters['TargetNode'].ParameterSets.Values |
|
||||
Where-Object { $_.IsMandatory }
|
||||
$isMandatory | Should -Not -BeNullOrEmpty
|
||||
}
|
||||
|
||||
It 'Should have Online switch parameter' {
|
||||
Skip-IfMissing 'Move-PveContainer'
|
||||
$script:Cmd.Parameters.ContainsKey('Online') | Should -BeTrue
|
||||
}
|
||||
|
||||
It 'Should have Wait switch parameter' {
|
||||
Skip-IfMissing 'Move-PveContainer'
|
||||
$script:Cmd.Parameters.ContainsKey('Wait') | Should -BeTrue
|
||||
}
|
||||
|
||||
It 'Should support ShouldProcess' {
|
||||
Skip-IfMissing 'Move-PveContainer'
|
||||
$script:Cmd.Parameters.ContainsKey('WhatIf') | Should -BeTrue
|
||||
}
|
||||
}
|
||||
|
||||
Context 'Without active session' {
|
||||
It 'Should throw when no session is active' {
|
||||
Skip-IfMissing 'Move-PveContainer'
|
||||
{ Move-PveContainer -Node 'pve-node1' -VmId 200 -TargetNode 'pve-node2' -ErrorAction Stop } |
|
||||
Should -Throw '*No active Proxmox VE session*'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user