From cc4d08070d5ee034ce32054708eebd2eb09a546d Mon Sep 17 00:00:00 2001 From: Clint Branham Date: Tue, 17 Mar 2026 15:45:57 -0500 Subject: [PATCH] feat(cmdlets): implement VM lifecycle cmdlets Add Get-PveVm, New-PveVm, Remove-PveVm, Start/Stop/Suspend/Resume/ Reset/Restart-PveVm, Copy-PveVm, Move-PveVm, Get-PveVmConfig, Set-PveVmConfig, and Resize-PveVmDisk. All support pipeline input, -WhatIf/-Confirm on state changes, and -Wait for async tasks. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Cmdlets/Vms/CopyPveVmCmdlet.cs | 92 +++++++++ src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs | 87 +++++++++ .../Cmdlets/Vms/GetPveVmConfigCmdlet.cs | 41 ++++ .../Cmdlets/Vms/MovePveVmCmdlet.cs | 73 +++++++ src/PSProxmoxVE/Cmdlets/Vms/NewPveVmCmdlet.cs | 178 ++++++++++++++++++ .../Cmdlets/Vms/RemovePveVmCmdlet.cs | 75 ++++++++ .../Cmdlets/Vms/ResetPveVmCmdlet.cs | 59 ++++++ .../Cmdlets/Vms/ResizePveVmDiskCmdlet.cs | 77 ++++++++ .../Cmdlets/Vms/RestartPveVmCmdlet.cs | 71 +++++++ .../Cmdlets/Vms/ResumePveVmCmdlet.cs | 57 ++++++ .../Cmdlets/Vms/SetPveVmConfigCmdlet.cs | 118 ++++++++++++ .../Cmdlets/Vms/StartPveVmCmdlet.cs | 57 ++++++ .../Cmdlets/Vms/StopPveVmCmdlet.cs | 59 ++++++ .../Cmdlets/Vms/SuspendPveVmCmdlet.cs | 58 ++++++ 14 files changed, 1102 insertions(+) create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/CopyPveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/GetPveVmConfigCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/MovePveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/NewPveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/RemovePveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/ResetPveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/ResizePveVmDiskCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/RestartPveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/ResumePveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/SetPveVmConfigCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/StartPveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/StopPveVmCmdlet.cs create mode 100644 src/PSProxmoxVE/Cmdlets/Vms/SuspendPveVmCmdlet.cs diff --git a/src/PSProxmoxVE/Cmdlets/Vms/CopyPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/CopyPveVmCmdlet.cs new file mode 100644 index 0000000..4ebc5a2 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/CopyPveVmCmdlet.cs @@ -0,0 +1,92 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Clones (copies) a QEMU/KVM virtual machine on a Proxmox VE node. + /// + /// Creates a clone of an existing VM. By default, a linked clone is created when the source + /// is a template. Use -Full to create a full independent copy. Optionally specify a target + /// node to create the clone on a different cluster node. + /// Use -Wait to block until the clone task completes. + /// + /// + [Cmdlet(VerbsCommon.Copy, "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class CopyPveVmCmdlet : PveCmdletBase + { + /// + /// The node on which the source VM resides. + /// + [Parameter(Mandatory = true)] + public string SourceNode { get; set; } = string.Empty; + + /// + /// The ID of the source VM to clone. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// The VM ID to assign to the new clone. When omitted, the next available ID is used. + /// + [Parameter(Mandatory = false)] + public int? NewVmId { get; set; } + + /// + /// The display name for the new clone. + /// + [Parameter(Mandatory = false)] + public string? NewName { get; set; } + + /// + /// The target node for the clone. Defaults to the source node. + /// + [Parameter(Mandatory = false)] + public string? TargetNode { get; set; } + + /// + /// + /// When specified, creates a full independent clone instead of a linked clone. + /// A full clone is required when the source VM is not a template. + /// + /// + [Parameter(Mandatory = false)] + public SwitchParameter Full { get; set; } + + /// + /// Target storage pool for the full clone disks. + /// + [Parameter(Mandatory = false)] + public string? Storage { get; set; } + + /// + /// When specified, waits for the clone task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + var target = TargetNode ?? SourceNode; + if (!ShouldProcess($"VM {VmId} on node '{SourceNode}' to new VM on node '{target}'", "Copy-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var newid = NewVmId ?? 0; + var task = vmService.CloneVm(session, SourceNode, VmId, newid, NewName, TargetNode, Full.IsPresent); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs new file mode 100644 index 0000000..a3532c0 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmCmdlet.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Gets one or more QEMU/KVM virtual machines from a Proxmox VE server. + /// + /// Retrieves virtual machine objects from the Proxmox VE API. Results can be filtered by + /// node, VM ID, name, status, tag, or limited to template VMs only. + /// + /// + [Cmdlet(VerbsCommon.Get, "PveVm")] + [OutputType(typeof(PveVm))] + public sealed class GetPveVmCmdlet : PveCmdletBase + { + /// + /// + /// The name of the node to query. Accepts pipeline input from a PveNode object's Name property. + /// When omitted, VMs from all nodes are returned. + /// + /// + [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)] + public string? Node { get; set; } + + /// + /// Filter results to the VM with this ID. + /// + [Parameter(Mandatory = false)] + public int? VmId { get; set; } + + /// + /// Filter results to VMs whose name matches this value (case-insensitive, contains match). + /// + [Parameter(Mandatory = false)] + public string? Name { get; set; } + + /// + /// Filter results to VMs in the specified status (e.g., "running", "stopped"). + /// + [Parameter(Mandatory = false)] + public string? Status { get; set; } + + /// + /// Filter results to VMs that have the specified tag (substring match against the semicolon-separated tags field). + /// + [Parameter(Mandatory = false)] + public string? Tag { get; set; } + + /// + /// When specified, returns only VMs that are marked as templates. + /// + [Parameter(Mandatory = false)] + public SwitchParameter TemplatesOnly { get; set; } + + protected override void ProcessRecord() + { + var session = GetSession(); + var service = new VmService(); + + IEnumerable vms = service.GetVms(session, Node); + + if (VmId.HasValue) + vms = vms.Where(v => v.VmId == VmId.Value); + + if (!string.IsNullOrEmpty(Name)) + vms = vms.Where(v => v.Name != null && + v.Name.IndexOf(Name, System.StringComparison.OrdinalIgnoreCase) >= 0); + + if (!string.IsNullOrEmpty(Status)) + vms = vms.Where(v => string.Equals(v.Status, Status, System.StringComparison.OrdinalIgnoreCase)); + + if (!string.IsNullOrEmpty(Tag)) + vms = vms.Where(v => v.Tags != null && + v.Tags.IndexOf(Tag, System.StringComparison.OrdinalIgnoreCase) >= 0); + + if (TemplatesOnly.IsPresent) + vms = vms.Where(v => v.Template == 1); + + foreach (var vm in vms) + WriteObject(vm); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmConfigCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmConfigCmdlet.cs new file mode 100644 index 0000000..f4b8696 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/GetPveVmConfigCmdlet.cs @@ -0,0 +1,41 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Gets the configuration of a QEMU/KVM virtual machine. + /// + /// Retrieves the full hardware and metadata configuration of the specified virtual machine + /// from the Proxmox VE API, including CPU, memory, disk, network, and cloud-init settings. + /// + /// + [Cmdlet(VerbsCommon.Get, "PveVmConfig")] + [OutputType(typeof(PveVmConfig))] + public sealed class GetPveVmConfigCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM whose configuration to retrieve. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + protected override void ProcessRecord() + { + var session = GetSession(); + var vmService = new VmService(); + + var config = vmService.GetVmConfig(session, Node, VmId); + WriteObject(config); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/MovePveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/MovePveVmCmdlet.cs new file mode 100644 index 0000000..38539f4 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/MovePveVmCmdlet.cs @@ -0,0 +1,73 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Migrates a QEMU/KVM virtual machine to a different Proxmox VE node. + /// + /// Performs a live or offline migration of the specified virtual machine to the target node. + /// Use -Online for live migration (VM remains running). Use -Wait to block until the + /// migration task completes. + /// + /// + [Cmdlet(VerbsCommon.Move, "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class MovePveVmCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM currently resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to migrate. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// The destination node to migrate the VM to. + /// + [Parameter(Mandatory = true)] + public string TargetNode { get; set; } = string.Empty; + + /// + /// + /// When specified, performs a live migration so the VM remains running during migration. + /// Requires shared storage between the source and target nodes. + /// + /// + [Parameter(Mandatory = false)] + public SwitchParameter Online { get; set; } + + /// + /// When specified, waits for the migration task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} from node '{Node}' to node '{TargetNode}'", "Move-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var task = vmService.MigrateVm(session, Node, VmId, TargetNode, Online.IsPresent); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/NewPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/NewPveVmCmdlet.cs new file mode 100644 index 0000000..eed3d2a --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/NewPveVmCmdlet.cs @@ -0,0 +1,178 @@ +using System.Collections.Generic; +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Creates a new QEMU/KVM virtual machine on a Proxmox VE node. + /// + /// Creates a new virtual machine using the Proxmox VE API. Supports common hardware + /// configuration options. Use -Wait to block until the creation task completes. + /// + /// + [Cmdlet(VerbsCommon.New, "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class NewPveVmCmdlet : PveCmdletBase + { + /// + /// The node on which to create the VM. + /// + [Parameter(Mandatory = true, Position = 0)] + public string Node { get; set; } = string.Empty; + + /// + /// The VM ID to assign. When omitted, the next available ID is used. + /// + [Parameter(Mandatory = false)] + public int? VmId { get; set; } + + /// + /// The display name of the VM. + /// + [Parameter(Mandatory = false)] + public string? Name { get; set; } + + /// + /// Memory size in MiB. + /// + [Parameter(Mandatory = false)] + public int? Memory { get; set; } + + /// + /// Number of CPU cores per socket. + /// + [Parameter(Mandatory = false)] + public int? Cores { get; set; } + + /// + /// Number of CPU sockets. + /// + [Parameter(Mandatory = false)] + public int? Sockets { get; set; } + + /// + /// Emulated CPU type (e.g., "host", "x86-64-v2-AES"). + /// + [Parameter(Mandatory = false)] + public string? CpuType { get; set; } + + /// + /// BIOS type: "seabios" (default) or "ovmf" (UEFI). + /// + [Parameter(Mandatory = false)] + public string? Bios { get; set; } + + /// + /// Emulated machine type (e.g., "q35", "i440fx"). + /// + [Parameter(Mandatory = false)] + public string? Machine { get; set; } + + /// + /// Size of the primary disk (e.g., "32G"). + /// + [Parameter(Mandatory = false)] + public string? DiskSize { get; set; } + + /// + /// Storage pool for the primary disk (e.g., "local-lvm"). + /// + [Parameter(Mandatory = false)] + public string? DiskStorage { get; set; } + + /// + /// Disk format (e.g., "raw", "qcow2"). + /// + [Parameter(Mandatory = false)] + public string? DiskFormat { get; set; } + + /// + /// Network interface model (e.g., "virtio", "e1000"). + /// + [Parameter(Mandatory = false)] + public string? Network { get; set; } + + /// + /// Network bridge to attach to (e.g., "vmbr0"). + /// + [Parameter(Mandatory = false)] + public string? Bridge { get; set; } + + /// + /// Guest OS type hint (e.g., "l26" for Linux 2.6+, "win10"). + /// + [Parameter(Mandatory = false)] + public string? OsType { get; set; } + + /// + /// When specified, starts the VM after creation. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Start { get; set; } + + /// + /// When specified, waits for the creation task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM on node '{Node}'", "New-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var config = new Dictionary(); + + if (VmId.HasValue) + config["vmid"] = VmId.Value; + if (!string.IsNullOrEmpty(Name)) + config["name"] = Name!; + if (Memory.HasValue) + config["memory"] = Memory.Value; + if (Cores.HasValue) + config["cores"] = Cores.Value; + if (Sockets.HasValue) + config["sockets"] = Sockets.Value; + if (!string.IsNullOrEmpty(CpuType)) + config["cpu"] = CpuType!; + if (!string.IsNullOrEmpty(Bios)) + config["bios"] = Bios!; + if (!string.IsNullOrEmpty(Machine)) + config["machine"] = Machine!; + if (!string.IsNullOrEmpty(OsType)) + config["ostype"] = OsType!; + + if (!string.IsNullOrEmpty(DiskStorage) && !string.IsNullOrEmpty(DiskSize)) + { + var diskValue = $"{DiskStorage}:{DiskSize}"; + if (!string.IsNullOrEmpty(DiskFormat)) + diskValue += $",format={DiskFormat}"; + config["virtio0"] = diskValue; + } + + if (!string.IsNullOrEmpty(Bridge)) + { + var netModel = string.IsNullOrEmpty(Network) ? "virtio" : Network!; + config["net0"] = $"{netModel},bridge={Bridge}"; + } + + if (Start.IsPresent) + config["start"] = "1"; + + var task = vmService.CreateVm(session, Node, config); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/RemovePveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/RemovePveVmCmdlet.cs new file mode 100644 index 0000000..bd3813d --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/RemovePveVmCmdlet.cs @@ -0,0 +1,75 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Removes a QEMU/KVM virtual machine from a Proxmox VE node. + /// + /// Deletes a virtual machine and, optionally, all associated disk images. + /// This operation is destructive and requires confirmation unless -Force is specified. + /// + /// + [Cmdlet(VerbsCommon.Remove, "PveVm", + SupportsShouldProcess = true, + ConfirmImpact = ConfirmImpact.High)] + [OutputType(typeof(PveTask))] + public sealed class RemovePveVmCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to remove. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// + /// When specified, also removes the VM from HA resource configuration and replication jobs. + /// + /// + [Parameter(Mandatory = false)] + public SwitchParameter Purge { get; set; } + + /// + /// + /// When specified, bypasses locks and forces removal even if a lock is set on the VM. + /// + /// + [Parameter(Mandatory = false)] + public SwitchParameter Force { get; set; } + + /// + /// When specified, waits for the removal task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Remove-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var task = vmService.RemoveVm(session, Node, VmId, Purge.IsPresent); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/ResetPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/ResetPveVmCmdlet.cs new file mode 100644 index 0000000..0d9e44e --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/ResetPveVmCmdlet.cs @@ -0,0 +1,59 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Resets (hard-resets) a QEMU/KVM virtual machine on a Proxmox VE node. + /// + /// Sends a hard-reset signal to the specified virtual machine via the Proxmox VE API. + /// This is equivalent to pressing the physical reset button on a machine — no graceful + /// shutdown occurs. Use Restart-PveVm for a graceful reboot. + /// Use -Wait to block until the reset task completes. + /// + /// + [Cmdlet("Reset", "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class ResetPveVmCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to reset. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// When specified, waits for the reset task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Reset-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var task = vmService.ResetVm(session, Node, VmId); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/ResizePveVmDiskCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/ResizePveVmDiskCmdlet.cs new file mode 100644 index 0000000..abaec99 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/ResizePveVmDiskCmdlet.cs @@ -0,0 +1,77 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Resizes a disk attached to a QEMU/KVM virtual machine. + /// + /// Resizes the specified disk on a virtual machine via the Proxmox VE API. + /// Use an absolute size (e.g., "50G") to set a fixed size, or a relative size + /// prefixed with "+" (e.g., "+10G") to grow the disk by that amount. + /// Disk shrinking is not supported by Proxmox VE. + /// Use -Wait to block until the resize task completes. + /// + /// + [Cmdlet(VerbsCommon.Resize, "PveVmDisk", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class ResizePveVmDiskCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM whose disk should be resized. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// + /// The disk slot to resize (e.g., "virtio0", "scsi0", "ide0", "sata0"). + /// + /// + [Parameter(Mandatory = true)] + public string Disk { get; set; } = string.Empty; + + /// + /// + /// The new size for the disk. Use an absolute value (e.g., "50G") to set a specific size, + /// or a "+" prefix (e.g., "+10G") to grow the disk by the specified amount. + /// + /// + [Parameter(Mandatory = true)] + public string Size { get; set; } = string.Empty; + + /// + /// When specified, waits for the resize task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"Disk '{Disk}' on VM {VmId} (node '{Node}') to size '{Size}'", "Resize-PveVmDisk")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var task = vmService.ResizeDisk(session, Node, VmId, Disk, Size); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/RestartPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/RestartPveVmCmdlet.cs new file mode 100644 index 0000000..ca33251 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/RestartPveVmCmdlet.cs @@ -0,0 +1,71 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Gracefully restarts a QEMU/KVM virtual machine on a Proxmox VE node. + /// + /// 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. + /// + /// + [Cmdlet(VerbsLifecycle.Restart, "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class RestartPveVmCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to restart. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// + /// Timeout in seconds for the graceful shutdown phase. Defaults to 60 seconds. + /// + /// + [Parameter(Mandatory = false)] + public int Timeout { get; set; } = 60; + + /// + /// When specified, waits for both shutdown and start tasks to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Restart-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + var taskService = new TaskService(); + + // Graceful shutdown + var shutdownTask = vmService.ShutdownVm(session, Node, VmId, Timeout); + + if (Wait.IsPresent) + taskService.WaitForTask(session, shutdownTask.Node, shutdownTask.Upid, null, null, null); + + // Start + var startTask = vmService.StartVm(session, Node, VmId); + + if (Wait.IsPresent) + startTask = taskService.WaitForTask(session, startTask.Node, startTask.Upid, null, null, null); + + WriteObject(startTask); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/ResumePveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/ResumePveVmCmdlet.cs new file mode 100644 index 0000000..54ba77c --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/ResumePveVmCmdlet.cs @@ -0,0 +1,57 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Resumes a suspended QEMU/KVM virtual machine on a Proxmox VE node. + /// + /// Resumes execution of a previously suspended virtual machine via the Proxmox VE API. + /// Use -Wait to block until the resume task completes. + /// + /// + [Cmdlet(VerbsLifecycle.Resume, "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class ResumePveVmCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to resume. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// When specified, waits for the resume task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Resume-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var task = vmService.ResumeVm(session, Node, VmId); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/SetPveVmConfigCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/SetPveVmConfigCmdlet.cs new file mode 100644 index 0000000..1a16aa5 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/SetPveVmConfigCmdlet.cs @@ -0,0 +1,118 @@ +using System.Collections.Generic; +using System.Management.Automation; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Updates the configuration of a QEMU/KVM virtual machine. + /// + /// Modifies one or more configuration settings of the specified virtual machine via the + /// Proxmox VE API. Only the parameters explicitly provided are changed; all other settings + /// are left untouched. The operation is synchronous and produces no output. + /// + /// + [Cmdlet(VerbsCommon.Set, "PveVmConfig", SupportsShouldProcess = true)] + public sealed class SetPveVmConfigCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to configure. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// Number of CPU cores per socket. + /// + [Parameter(Mandatory = false)] + public int? Cores { get; set; } + + /// + /// Number of CPU sockets. + /// + [Parameter(Mandatory = false)] + public int? Sockets { get; set; } + + /// + /// Memory size in MiB. + /// + [Parameter(Mandatory = false)] + public int? Memory { get; set; } + + /// + /// Emulated CPU type (e.g., "host", "x86-64-v2-AES"). + /// + [Parameter(Mandatory = false)] + public string? CpuType { get; set; } + + /// + /// Human-readable description / notes for the VM. + /// + [Parameter(Mandatory = false)] + public string? Description { get; set; } + + /// + /// Semicolon-separated list of tags to assign to the VM. + /// + [Parameter(Mandatory = false)] + public string? Tags { get; set; } + + /// + /// BIOS type: "seabios" or "ovmf". + /// + [Parameter(Mandatory = false)] + public string? Bios { get; set; } + + /// + /// Emulated machine type (e.g., "q35", "i440fx"). + /// + [Parameter(Mandatory = false)] + public string? Machine { get; set; } + + /// + /// Guest OS type hint (e.g., "l26", "win10"). + /// + [Parameter(Mandatory = false)] + public string? OsType { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Set-PveVmConfig")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var config = new Dictionary(); + + if (Cores.HasValue) + config["cores"] = Cores.Value; + if (Sockets.HasValue) + config["sockets"] = Sockets.Value; + if (Memory.HasValue) + config["memory"] = Memory.Value; + if (!string.IsNullOrEmpty(CpuType)) + config["cpu"] = CpuType!; + if (!string.IsNullOrEmpty(Description)) + config["description"] = Description!; + if (!string.IsNullOrEmpty(Tags)) + config["tags"] = Tags!; + if (!string.IsNullOrEmpty(Bios)) + config["bios"] = Bios!; + if (!string.IsNullOrEmpty(Machine)) + config["machine"] = Machine!; + if (!string.IsNullOrEmpty(OsType)) + config["ostype"] = OsType!; + + vmService.SetVmConfig(session, Node, VmId, config); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/StartPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/StartPveVmCmdlet.cs new file mode 100644 index 0000000..9dd632c --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/StartPveVmCmdlet.cs @@ -0,0 +1,57 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Starts a QEMU/KVM virtual machine on a Proxmox VE node. + /// + /// Sends a start command to the specified virtual machine via the Proxmox VE API. + /// Use -Wait to block until the start task completes. + /// + /// + [Cmdlet(VerbsLifecycle.Start, "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class StartPveVmCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to start. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// When specified, waits for the start task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Start-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var task = vmService.StartVm(session, Node, VmId); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/StopPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/StopPveVmCmdlet.cs new file mode 100644 index 0000000..f897a97 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/StopPveVmCmdlet.cs @@ -0,0 +1,59 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Stops (powers off) a QEMU/KVM virtual machine on a Proxmox VE node. + /// + /// Sends a stop command to the specified virtual machine via the Proxmox VE API. + /// This is equivalent to pulling the power plug — the VM is stopped immediately without + /// a graceful shutdown. Use Restart-PveVm or a guest-initiated shutdown for graceful stops. + /// Use -Wait to block until the stop task completes. + /// + /// + [Cmdlet(VerbsLifecycle.Stop, "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class StopPveVmCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to stop. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// When specified, waits for the stop task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Stop-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var task = vmService.StopVm(session, Node, VmId); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Vms/SuspendPveVmCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Vms/SuspendPveVmCmdlet.cs new file mode 100644 index 0000000..d89c010 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Vms/SuspendPveVmCmdlet.cs @@ -0,0 +1,58 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Vms +{ + /// + /// Suspends a QEMU/KVM virtual machine on a Proxmox VE node. + /// + /// Suspends (pauses) the specified virtual machine via the Proxmox VE API. + /// The VM state is preserved in memory. Use Resume-PveVm to resume execution. + /// Use -Wait to block until the suspend task completes. + /// + /// + [Cmdlet(VerbsLifecycle.Suspend, "PveVm", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class SuspendPveVmCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the VM resides. Accepts pipeline input from a PveNode object's Name property. + /// + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public string Node { get; set; } = string.Empty; + + /// + /// The ID of the VM to suspend. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// When specified, waits for the suspend task to complete before returning. + /// + [Parameter(Mandatory = false)] + public SwitchParameter Wait { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"VM {VmId} on node '{Node}'", "Suspend-PveVm")) + return; + + var session = GetSession(); + var vmService = new VmService(); + + var task = vmService.SuspendVm(session, Node, VmId); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node, task.Upid, null, null, null); + } + + WriteObject(task); + } + } +}