diff --git a/src/PSProxmoxVE/Cmdlets/Containers/CopyPveContainerCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/CopyPveContainerCmdlet.cs new file mode 100644 index 0000000..f505adb --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/CopyPveContainerCmdlet.cs @@ -0,0 +1,98 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Clones (copies) an LXC container on a Proxmox VE node. + /// + /// Creates a clone of an existing LXC container. 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, "PveContainer", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class CopyPveContainerCmdlet : PveCmdletBase + { + /// + /// The node on which the source container resides. + /// + [Parameter(Mandatory = true)] + public string SourceNode { get; set; } = string.Empty; + + /// + /// The ID of the source container to clone. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// The container ID to assign to the new clone. When omitted, the next available ID is used. + /// + [Parameter(Mandatory = false)] + public int? NewVmId { get; set; } + + /// + /// The hostname 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 container is not a template. + /// + /// + [Parameter(Mandatory = false)] + public SwitchParameter Full { get; set; } + + /// + /// Target storage pool for the full clone root filesystem. + /// + [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($"Container {VmId} on node '{SourceNode}' to new container on node '{target}'", "Copy-PveContainer")) + return; + + var session = GetSession(); + var containerService = new ContainerService(); + + var task = containerService.CloneContainer( + session, + SourceNode, + VmId, + NewVmId ?? 0, + NewName, + TargetNode, + Full.IsPresent); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node ?? SourceNode, task.Upid!, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Containers/GetPveContainerCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/GetPveContainerCmdlet.cs new file mode 100644 index 0000000..9709284 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/GetPveContainerCmdlet.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Containers; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Gets one or more LXC containers from a Proxmox VE server. + /// + /// Retrieves container objects from the Proxmox VE API. Results can be filtered by + /// node, container ID, name, status, or tag. + /// + /// + [Cmdlet(VerbsCommon.Get, "PveContainer")] + [OutputType(typeof(PveContainer))] + public sealed class GetPveContainerCmdlet : PveCmdletBase + { + /// + /// + /// The name of the node to query. Accepts pipeline input from a PveNode object's Name property. + /// When omitted, containers from all nodes are returned. + /// + /// + [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)] + public string? Node { get; set; } + + /// + /// Filter results to the container with this ID. + /// + [Parameter(Mandatory = false)] + public int? VmId { get; set; } + + /// + /// Filter results to containers whose name matches this value (case-insensitive, contains match). + /// + [Parameter(Mandatory = false)] + public string? Name { get; set; } + + /// + /// Filter results to containers in the specified status (e.g., "running", "stopped"). + /// + [Parameter(Mandatory = false)] + public string? Status { get; set; } + + /// + /// Filter results to containers that have the specified tag (substring match against the semicolon-separated tags field). + /// + [Parameter(Mandatory = false)] + public string? Tag { get; set; } + + protected override void ProcessRecord() + { + var session = GetSession(); + var service = new ContainerService(); + + IEnumerable containers = service.GetContainers(session, Node); + + if (VmId.HasValue) + containers = containers.Where(c => c.VmId == VmId.Value); + + if (!string.IsNullOrEmpty(Name)) + containers = containers.Where(c => c.Name != null && + c.Name.IndexOf(Name, System.StringComparison.OrdinalIgnoreCase) >= 0); + + if (!string.IsNullOrEmpty(Status)) + containers = containers.Where(c => string.Equals(c.Status, Status, System.StringComparison.OrdinalIgnoreCase)); + + if (!string.IsNullOrEmpty(Tag)) + containers = containers.Where(c => c.Tags != null && + c.Tags.IndexOf(Tag, System.StringComparison.OrdinalIgnoreCase) >= 0); + + foreach (var container in containers) + WriteObject(container); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Containers/GetPveContainerConfigCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/GetPveContainerConfigCmdlet.cs new file mode 100644 index 0000000..5cafd67 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/GetPveContainerConfigCmdlet.cs @@ -0,0 +1,41 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Containers; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Gets the configuration of an LXC container. + /// + /// Retrieves the full configuration of the specified LXC container from the Proxmox VE API, + /// including CPU, memory, storage, network, and metadata settings. + /// + /// + [Cmdlet(VerbsCommon.Get, "PveContainerConfig")] + [OutputType(typeof(PveContainerConfig))] + public sealed class GetPveContainerConfigCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the container 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 container 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 containerService = new ContainerService(); + + var config = containerService.GetContainerConfig(session, Node, VmId); + WriteObject(config); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Containers/NewPveContainerCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/NewPveContainerCmdlet.cs new file mode 100644 index 0000000..6d87b8d --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/NewPveContainerCmdlet.cs @@ -0,0 +1,192 @@ +using System.Collections.Generic; +using System.Management.Automation; +using System.Net; +using System.Runtime.InteropServices; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Creates a new LXC container on a Proxmox VE node. + /// + /// Creates a new Linux container using the Proxmox VE API. An OS template is required. + /// The container runs in unprivileged mode by default. Use -Wait to block until the + /// creation task completes. + /// + /// + [Cmdlet(VerbsCommon.New, "PveContainer", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class NewPveContainerCmdlet : PveCmdletBase + { + /// + /// The node on which to create the container. + /// + [Parameter(Mandatory = true, Position = 0)] + public string Node { get; set; } = string.Empty; + + /// + /// The container ID to assign. When omitted, the next available ID is used. + /// + [Parameter(Mandatory = false)] + public int? VmId { get; set; } + + /// + /// The hostname to assign to the container. + /// + [Parameter(Mandatory = false)] + public string? Hostname { get; set; } + + /// + /// Memory limit in MiB. + /// + [Parameter(Mandatory = false)] + public int? Memory { get; set; } + + /// + /// Swap size in MiB. + /// + [Parameter(Mandatory = false)] + public int? Swap { get; set; } + + /// + /// Number of CPU cores to allocate to the container. + /// + [Parameter(Mandatory = false)] + public int? Cores { get; set; } + + /// + /// Size of the root filesystem (e.g., "8G"). + /// + [Parameter(Mandatory = false)] + public string? RootFsSize { get; set; } + + /// + /// Storage pool for the root filesystem (e.g., "local-lvm"). + /// + [Parameter(Mandatory = false)] + public string? RootFsStorage { get; set; } + + /// + /// + /// The OS template to use (e.g., "local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst"). + /// + /// + [Parameter(Mandatory = false)] + public string? OsTemplate { get; set; } + + /// + /// Root password for the container. + /// + [Parameter(Mandatory = false)] + public System.Security.SecureString? Password { get; set; } + + /// + /// SSH public keys to inject for the root user (newline-separated). + /// + [Parameter(Mandatory = false)] + public string? SshPublicKeys { get; set; } + + /// + /// + /// When specified (the default), the container runs in unprivileged mode. + /// To create a privileged container, explicitly set this to $false. + /// + /// + [Parameter(Mandatory = false)] + public SwitchParameter Unprivileged { get; set; } = new SwitchParameter(true); + + /// + /// Network interface model (e.g., "eth0"). + /// + [Parameter(Mandatory = false)] + public string? Network { get; set; } + + /// + /// Network bridge to attach to (e.g., "vmbr0"). + /// + [Parameter(Mandatory = false)] + public string? Bridge { get; set; } + + /// + /// When specified, starts the container 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($"Container on node '{Node}'", "New-PveContainer")) + return; + + var session = GetSession(); + var containerService = new ContainerService(); + + var config = new Dictionary(); + + if (VmId.HasValue) + config["vmid"] = VmId.Value.ToString(); + if (!string.IsNullOrEmpty(Hostname)) + config["hostname"] = Hostname!; + if (Memory.HasValue) + config["memory"] = Memory.Value.ToString(); + if (Swap.HasValue) + config["swap"] = Swap.Value.ToString(); + if (Cores.HasValue) + config["cores"] = Cores.Value.ToString(); + if (!string.IsNullOrEmpty(OsTemplate)) + config["ostemplate"] = OsTemplate!; + + if (!string.IsNullOrEmpty(RootFsStorage)) + { + var rootFsValue = RootFsStorage!; + if (!string.IsNullOrEmpty(RootFsSize)) + rootFsValue += $":{RootFsSize}"; + config["rootfs"] = rootFsValue; + } + + if (Password != null) + { + var ptr = Marshal.SecureStringToGlobalAllocUnicode(Password); + try + { + config["password"] = Marshal.PtrToStringUni(ptr) ?? string.Empty; + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(ptr); + } + } + + if (!string.IsNullOrEmpty(SshPublicKeys)) + config["ssh-public-keys"] = SshPublicKeys!; + + config["unprivileged"] = Unprivileged.IsPresent ? "1" : "0"; + + if (!string.IsNullOrEmpty(Bridge)) + { + var ifName = string.IsNullOrEmpty(Network) ? "eth0" : Network!; + config["net0"] = $"name={ifName},bridge={Bridge}"; + } + + if (Start.IsPresent) + config["start"] = "1"; + + var task = containerService.CreateContainer(session, Node, config); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node ?? Node, task.Upid!, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Containers/RemovePveContainerCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/RemovePveContainerCmdlet.cs new file mode 100644 index 0000000..f7b38a7 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/RemovePveContainerCmdlet.cs @@ -0,0 +1,75 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Removes an LXC container from a Proxmox VE node. + /// + /// Deletes an LXC container and, optionally, all associated storage. + /// This operation is destructive and requires confirmation unless -Force is specified. + /// + /// + [Cmdlet(VerbsCommon.Remove, "PveContainer", + SupportsShouldProcess = true, + ConfirmImpact = ConfirmImpact.High)] + [OutputType(typeof(PveTask))] + public sealed class RemovePveContainerCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the container 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 container to remove. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// + /// When specified, also removes the container 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 container. + /// + /// + [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($"Container {VmId} on node '{Node}'", "Remove-PveContainer")) + return; + + var session = GetSession(); + var containerService = new ContainerService(); + + var task = containerService.RemoveContainer(session, Node, VmId, Purge.IsPresent); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node ?? Node, task.Upid!, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Containers/RestartPveContainerCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/RestartPveContainerCmdlet.cs new file mode 100644 index 0000000..c7e3986 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/RestartPveContainerCmdlet.cs @@ -0,0 +1,71 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Gracefully restarts an LXC container on a Proxmox VE node. + /// + /// Performs a graceful shutdown of the container followed by a start via the Proxmox VE API. + /// A configurable timeout controls how long to wait for the container to shut down cleanly. + /// Use -Wait to block until both tasks complete. + /// + /// + [Cmdlet(VerbsLifecycle.Restart, "PveContainer", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class RestartPveContainerCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the container 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 container 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($"Container {VmId} on node '{Node}'", "Restart-PveContainer")) + return; + + var session = GetSession(); + var containerService = new ContainerService(); + var taskService = new TaskService(); + + // Graceful shutdown + var shutdownTask = containerService.ShutdownContainer(session, Node, VmId, Timeout); + + if (Wait.IsPresent) + taskService.WaitForTask(session, shutdownTask.Node ?? Node, shutdownTask.Upid!, null, null, null); + + // Start + var startTask = containerService.StartContainer(session, Node, VmId); + + if (Wait.IsPresent) + startTask = taskService.WaitForTask(session, startTask.Node ?? Node, startTask.Upid!, null, null, null); + + WriteObject(startTask); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Containers/SetPveContainerConfigCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/SetPveContainerConfigCmdlet.cs new file mode 100644 index 0000000..bb1fc2a --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/SetPveContainerConfigCmdlet.cs @@ -0,0 +1,110 @@ +using System.Collections.Generic; +using System.Management.Automation; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Updates the configuration of an LXC container. + /// + /// Modifies one or more configuration settings of the specified LXC container via the + /// Proxmox VE API. Only the parameters explicitly provided are changed; all other settings + /// are left untouched. + /// + /// + [Cmdlet(VerbsCommon.Set, "PveContainerConfig", SupportsShouldProcess = true)] + public sealed class SetPveContainerConfigCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the container 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 container to configure. Accepts pipeline input. + /// + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + public int VmId { get; set; } + + /// + /// The hostname to assign to the container. + /// + [Parameter(Mandatory = false)] + public string? Hostname { get; set; } + + /// + /// Number of CPU cores to allocate to the container. + /// + [Parameter(Mandatory = false)] + public int? Cores { get; set; } + + /// + /// Memory limit in MiB. + /// + [Parameter(Mandatory = false)] + public int? Memory { get; set; } + + /// + /// Swap size in MiB. + /// + [Parameter(Mandatory = false)] + public int? Swap { get; set; } + + /// + /// Human-readable description / notes for the container. + /// + [Parameter(Mandatory = false)] + public string? Description { get; set; } + + /// + /// Semicolon-separated list of tags to assign to the container. + /// + [Parameter(Mandatory = false)] + public string? Tags { get; set; } + + /// + /// DNS nameservers (space-separated). + /// + [Parameter(Mandatory = false)] + public string? Nameserver { get; set; } + + /// + /// DNS search domain. + /// + [Parameter(Mandatory = false)] + public string? SearchDomain { get; set; } + + protected override void ProcessRecord() + { + if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Set-PveContainerConfig")) + return; + + var session = GetSession(); + var containerService = new ContainerService(); + + var config = new Dictionary(); + + if (!string.IsNullOrEmpty(Hostname)) + config["hostname"] = Hostname!; + if (Cores.HasValue) + config["cores"] = Cores.Value.ToString(); + if (Memory.HasValue) + config["memory"] = Memory.Value.ToString(); + if (Swap.HasValue) + config["swap"] = Swap.Value.ToString(); + if (!string.IsNullOrEmpty(Description)) + config["description"] = Description!; + if (!string.IsNullOrEmpty(Tags)) + config["tags"] = Tags!; + if (!string.IsNullOrEmpty(Nameserver)) + config["nameserver"] = Nameserver!; + if (!string.IsNullOrEmpty(SearchDomain)) + config["searchdomain"] = SearchDomain!; + + containerService.SetContainerConfig(session, Node, VmId, config); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Containers/StartPveContainerCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/StartPveContainerCmdlet.cs new file mode 100644 index 0000000..6cceea7 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/StartPveContainerCmdlet.cs @@ -0,0 +1,57 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Starts an LXC container on a Proxmox VE node. + /// + /// Sends a start command to the specified LXC container via the Proxmox VE API. + /// Use -Wait to block until the start task completes. + /// + /// + [Cmdlet(VerbsLifecycle.Start, "PveContainer", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class StartPveContainerCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the container 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 container 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($"Container {VmId} on node '{Node}'", "Start-PveContainer")) + return; + + var session = GetSession(); + var containerService = new ContainerService(); + + var task = containerService.StartContainer(session, Node, VmId); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node ?? Node, task.Upid!, null, null, null); + } + + WriteObject(task); + } + } +} diff --git a/src/PSProxmoxVE/Cmdlets/Containers/StopPveContainerCmdlet.cs b/src/PSProxmoxVE/Cmdlets/Containers/StopPveContainerCmdlet.cs new file mode 100644 index 0000000..70c2129 --- /dev/null +++ b/src/PSProxmoxVE/Cmdlets/Containers/StopPveContainerCmdlet.cs @@ -0,0 +1,59 @@ +using System.Management.Automation; +using PSProxmoxVE.Core.Models.Vms; +using PSProxmoxVE.Core.Services; + +namespace PSProxmoxVE.Cmdlets.Containers +{ + /// + /// Stops (powers off) an LXC container on a Proxmox VE node. + /// + /// Sends a stop command to the specified LXC container via the Proxmox VE API. + /// This immediately terminates the container without a graceful shutdown. + /// Use Restart-PveContainer or a guest-initiated shutdown for graceful stops. + /// Use -Wait to block until the stop task completes. + /// + /// + [Cmdlet(VerbsLifecycle.Stop, "PveContainer", SupportsShouldProcess = true)] + [OutputType(typeof(PveTask))] + public sealed class StopPveContainerCmdlet : PveCmdletBase + { + /// + /// + /// The node on which the container 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 container 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($"Container {VmId} on node '{Node}'", "Stop-PveContainer")) + return; + + var session = GetSession(); + var containerService = new ContainerService(); + + var task = containerService.StopContainer(session, Node, VmId); + + if (Wait.IsPresent) + { + var taskService = new TaskService(); + task = taskService.WaitForTask(session, task.Node ?? Node, task.Upid!, null, null, null); + } + + WriteObject(task); + } + } +}