fix: map PVE failures to typed error records on every cmdlet (#221)

PveApiException carried the status, resource, method and API message an
ErrorRecord needs, but only about 25 of 194 cmdlets built a record. Every
other failing call escaped into PowerShell's default wrapper, so a 403, a
404 and an unreachable server all arrived as ErrorCategory.NotSpecified
with error id PveApiException and a null TargetObject. $_.CategoryInfo,
-ErrorAction filtering and typed catch blocks had nothing to work with.

The classification is a pure function in PSProxmoxVE.Core so it has an
offline test path: PveErrorMapper.Describe returns a kind, a stable error
id and a target derived from the exception. PveCmdletBase translates the
kind to an ErrorCategory and builds the record.

The try/catch is a template method rather than 194 hand-written blocks:
ProcessRecord is sealed and wraps a new abstract ProcessPveRecord in one
catch filtered by PveErrorMapper.IsRecognized, so only the module's own
exception types are mapped and every other exception reaches the engine
exactly as before, including the ones carrying their own error record.
The 192 cmdlets on the base class rename their override; behaviour stays
terminating, as an escaped exception already was.

Closes #155

Co-authored-by: goodolclint-claude[bot] <323206664+goodolclint-claude[bot]@users.noreply.github.com>
This commit is contained in:
goodolclint-claude[bot]
2026-09-03 16:35:52 +00:00
committed by GitHub
parent f973ac92e0
commit 142f18af1d
196 changed files with 598 additions and 195 deletions
@@ -0,0 +1,166 @@
using System;
using System.Globalization;
using System.Net;
using PSProxmoxVE.Core.Exceptions;
namespace PSProxmoxVE.Core.Errors
{
/// <summary>
/// The error classifications this module reports. Every member name is also the name of a
/// <c>System.Management.Automation.ErrorCategory</c> member, so the cmdlet layer can
/// translate a kind without inventing a second vocabulary.
/// </summary>
public enum PveErrorKind
{
/// <summary>The failure was not recognised and carries no classification.</summary>
NotSpecified,
/// <summary>The caller lacks the privilege or the credential the operation needs.</summary>
PermissionDenied,
/// <summary>The credential was rejected because the session is no longer valid.</summary>
AuthenticationError,
/// <summary>The addressed object does not exist on the server.</summary>
ObjectNotFound,
/// <summary>The server rejected the request payload or a parameter value.</summary>
InvalidArgument,
/// <summary>The operation did not complete inside its time budget.</summary>
OperationTimeout,
/// <summary>The server could not be reached, or no session is established.</summary>
ConnectionError,
/// <summary>The server was reached but cannot service the request.</summary>
ResourceUnavailable,
/// <summary>The operation is not valid for the server or object in its current state.</summary>
InvalidOperation,
/// <summary>The operation started and then stopped without completing.</summary>
OperationStopped,
}
/// <summary>The classification and error identifier derived from a failure.</summary>
public sealed class PveErrorDescriptor
{
/// <summary>Initializes a new descriptor.</summary>
/// <param name="kind">The classification of the failure.</param>
/// <param name="errorId">The stable identifier a script can match on.</param>
/// <param name="target">The object the failure is about, or null when none is known.</param>
public PveErrorDescriptor(PveErrorKind kind, string errorId, object? target)
{
Kind = kind;
ErrorId = errorId;
Target = target;
}
/// <summary>The classification of the failure.</summary>
public PveErrorKind Kind { get; }
/// <summary>The stable identifier a script can match on, for example <c>PveApi.404.nodes/pve1/qemu/100</c>.</summary>
public string ErrorId { get; }
/// <summary>The object the failure is about, derived from the exception when the caller supplied none.</summary>
public object? Target { get; }
}
/// <summary>Maps a module exception to the classification and identifier reported to PowerShell.</summary>
public static class PveErrorMapper
{
private const string ApiErrorIdPrefix = "PveApi";
/// <summary>
/// Reports whether <paramref name="exception"/> is one this module classifies. An
/// exception that is not recognised must reach the PowerShell engine untouched: it may
/// carry its own error record, or be a flow-control or fatal exception.
/// </summary>
/// <param name="exception">The failure to test.</param>
/// <returns>True when <see cref="Describe"/> classifies the exception.</returns>
public static bool IsRecognized(Exception exception)
=> exception is PveApiException
or PveNotConnectedException
or PveSessionExpiredException
or PveTaskFailedException
or PveTaskTimeoutException
or PveVersionException;
/// <summary>Classifies <paramref name="exception"/> and derives its error identifier and target.</summary>
/// <param name="exception">The failure to classify.</param>
/// <returns>The classification, error identifier and derived target.</returns>
public static PveErrorDescriptor Describe(Exception exception)
{
if (exception is null) throw new ArgumentNullException(nameof(exception));
switch (exception)
{
case PveApiException api:
return new PveErrorDescriptor(KindForStatus(api.StatusCode), ApiErrorId(api), NullIfBlank(api.Resource));
case PveNotConnectedException:
return new PveErrorDescriptor(PveErrorKind.ConnectionError, "PveNotConnected", null);
case PveSessionExpiredException:
return new PveErrorDescriptor(PveErrorKind.AuthenticationError, "PveSessionExpired", null);
case PveTaskFailedException taskFailed:
return new PveErrorDescriptor(PveErrorKind.OperationStopped, "PveTaskFailed", NullIfBlank(taskFailed.Upid));
case PveTaskTimeoutException taskTimeout:
return new PveErrorDescriptor(PveErrorKind.OperationTimeout, "PveTaskTimeout", NullIfBlank(taskTimeout.Upid));
case PveVersionException:
return new PveErrorDescriptor(PveErrorKind.InvalidOperation, "PveVersionTooOld", null);
default:
return new PveErrorDescriptor(PveErrorKind.NotSpecified, exception.GetType().Name, null);
}
}
private static PveErrorKind KindForStatus(HttpStatusCode status)
{
switch (status)
{
case HttpStatusCode.BadRequest:
return PveErrorKind.InvalidArgument;
// 401 is a rejected or expired ticket, which the module also reports as
// PveSessionExpiredException; 403 is a privilege the ticket does not carry.
case HttpStatusCode.Unauthorized:
return PveErrorKind.AuthenticationError;
case HttpStatusCode.Forbidden:
return PveErrorKind.PermissionDenied;
case HttpStatusCode.NotFound:
return PveErrorKind.ObjectNotFound;
case HttpStatusCode.RequestTimeout:
case HttpStatusCode.GatewayTimeout:
return PveErrorKind.OperationTimeout;
// PveHttpClient reports a failed connection as 503, so 503 is a reachability
// failure here and not the server's own "try again later".
case HttpStatusCode.ServiceUnavailable:
return PveErrorKind.ConnectionError;
}
var code = (int)status;
if (code >= 500 && code <= 599) return PveErrorKind.ResourceUnavailable;
return PveErrorKind.InvalidOperation;
}
private static string ApiErrorId(PveApiException exception)
{
var status = ((int)exception.StatusCode).ToString(CultureInfo.InvariantCulture);
return string.IsNullOrWhiteSpace(exception.Resource)
? $"{ApiErrorIdPrefix}.{status}"
: $"{ApiErrorIdPrefix}.{status}.{exception.Resource}";
}
private static object? NullIfBlank(string? value)
=> string.IsNullOrWhiteSpace(value) ? null : value;
}
}
@@ -15,7 +15,7 @@ namespace PSProxmoxVE.Cmdlets.Backup
[OutputType(typeof(PSObject))]
public sealed class GetPveBackupInfoCmdlet : PveCmdletBase
{
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "Backup compliance info", 7, 0);
@@ -21,7 +21,7 @@ namespace PSProxmoxVE.Cmdlets.Backup
[Parameter(Mandatory = false, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The backup job ID.")]
public string? Id { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new BackupService();
@@ -63,7 +63,7 @@ namespace PSProxmoxVE.Cmdlets.Backup
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 3600).")]
public int Timeout { get; set; } = 3600;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "New-PveBackup"))
return;
@@ -91,7 +91,7 @@ namespace PSProxmoxVE.Cmdlets.Backup
[Parameter(Mandatory = false, HelpMessage = "Prune-backups configuration string.")]
public string? PruneBackups { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess("cluster backup configuration", "New-PveBackupJob"))
return;
@@ -22,7 +22,7 @@ namespace PSProxmoxVE.Cmdlets.Backup
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The backup job ID.")]
public string Id { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"backup job '{Id}'", "Remove-PveBackupJob"))
return;
@@ -96,7 +96,7 @@ namespace PSProxmoxVE.Cmdlets.Backup
[Parameter(Mandatory = false, HelpMessage = "Prune-backups configuration string.")]
public string? PruneBackups { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"backup job '{Id}'", "Set-PveBackupJob"))
return;
@@ -25,7 +25,7 @@ namespace PSProxmoxVE.Cmdlets.CloudInit
[ValidateRange(100, 999999999)]
public int VmId { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "Cloud-Init management", 7, 2);
@@ -30,7 +30,7 @@ namespace PSProxmoxVE.Cmdlets.CloudInit
[Parameter(HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"VM {VmId} on {Node}", "Regenerate PVE Cloud-Init Drive"))
return;
@@ -65,7 +65,7 @@ namespace PSProxmoxVE.Cmdlets.CloudInit
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "Cloud-Init management", 7, 2);
@@ -51,7 +51,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"node '{Node}'", "Add to cluster configuration"))
return;
@@ -61,7 +61,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
private const int ReauthMaxAttempts = 10;
private const int ReauthDelayMs = 3000;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"this node to cluster via '{Hostname}'", "Join cluster"))
return;
@@ -15,7 +15,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[OutputType(typeof(Dictionary<string, object>))]
public sealed class GetPveClusterConfigCmdlet : PveCmdletBase
{
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new ClusterConfigService();
@@ -15,7 +15,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[OutputType(typeof(PveClusterConfigNode))]
public sealed class GetPveClusterConfigNodeCmdlet : PveCmdletBase
{
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new ClusterConfigService();
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[Parameter(Mandatory = false, Position = 0, HelpMessage = "Node to get join info for (defaults to current).")]
public string? Node { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new ClusterConfigService();
@@ -19,7 +19,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[ValidateRange(100, 999999999)]
public int? VmId { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new ClusterConfigService();
@@ -15,7 +15,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[OutputType(typeof(PveClusterOptions))]
public sealed class GetPveClusterOptionCmdlet : PveCmdletBase
{
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new ClusterConfigService();
@@ -29,7 +29,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[Parameter(Mandatory = false, HelpMessage = "Filter results to a specific node name.")]
public string? Node { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new ClusterService();
@@ -15,7 +15,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[OutputType(typeof(PveClusterStatus))]
public sealed class GetPveClusterStatusCmdlet : PveCmdletBase
{
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new ClusterService();
@@ -46,7 +46,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[ValidateRange(1, 3600)]
public int Timeout { get; set; } = 60;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"cluster '{ClusterName}'", "Create new cluster"))
return;
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The cluster node name to remove.")]
public string Node { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"node '{Node}'", "Remove from cluster configuration"))
return;
@@ -65,7 +65,7 @@ namespace PSProxmoxVE.Cmdlets.Cluster
[Parameter(Mandatory = false, HelpMessage = "Comma-separated list of settings to delete/reset.")]
public string? Delete { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess("cluster options", "Set"))
return;
@@ -16,7 +16,7 @@ namespace PSProxmoxVE.Cmdlets.Connection
[Alias("dpve")]
public sealed class DisconnectPveServerCmdlet : PveCmdletBase
{
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
bool explicitSessionSupplied = MyInvocation.BoundParameters.ContainsKey(nameof(Session));
var sessionToDisconnect = explicitSessionSupplied ? Session : ModuleState.ActiveSession;
@@ -70,7 +70,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var target = TargetNode ?? SourceNode;
if (!ShouldProcess($"Container {VmId} on node '{SourceNode}' to new container on node '{target}'", "Copy-PveContainer"))
@@ -52,7 +52,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Filter by tag.")]
public string? Tag { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
@@ -30,7 +30,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[ValidateRange(100, 999999999)]
public int VmId { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var containerService = new ContainerService();
@@ -24,7 +24,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[ValidateRange(100, 999999999)]
public int VmId { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "Container interface listing", 8, 1);
@@ -31,7 +31,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Filter by snapshot name.")]
public string? Name { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
@@ -40,7 +40,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} from node '{Node}' to node '{TargetNode}'", "Move-PveContainer"))
return;
@@ -53,7 +53,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Volume '{Volume}' on container {VmId} (node '{Node}') to storage '{Storage}'", "Move-PveContainerVolume"))
return;
@@ -125,7 +125,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
// Validate -RootFsSize before ShouldProcess so typos like "512M" are rejected
// even with -WhatIf, and so the error is raised regardless of whether
@@ -36,7 +36,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on {Node}", $"Create snapshot '{Name}'"))
return;
@@ -31,7 +31,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[ValidateRange(100, 999999999)]
public int VmId { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "New-PveContainerTemplate"))
return;
@@ -54,7 +54,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Remove-PveContainer"))
return;
@@ -34,7 +34,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
@@ -44,7 +44,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = true, HelpMessage = "New disk size (e.g. 50G or +5G to grow).")]
public string Size { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Disk '{Disk}' on container {VmId} (node '{Node}') to size '{Size}'", "Resize-PveContainerDisk"))
return;
@@ -46,7 +46,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Restart-PveContainer"))
return;
@@ -37,7 +37,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
@@ -39,7 +39,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[ValidateRange(1, 3600)]
public int Timeout { get; set; } = 60;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Resume-PveContainer"))
return;
@@ -99,7 +99,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[Parameter(Mandatory = false, HelpMessage = "Comma-separated config keys to delete.")]
public string? Delete { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Set-PveContainerConfig"))
return;
@@ -41,7 +41,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[ValidateRange(1, 3600)]
public int Timeout { get; set; } = 60;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Start-PveContainer"))
return;
@@ -43,7 +43,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[ValidateRange(1, 3600)]
public int Timeout { get; set; } = 60;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Stop-PveContainer"))
return;
@@ -39,7 +39,7 @@ namespace PSProxmoxVE.Cmdlets.Containers
[ValidateRange(1, 3600)]
public int Timeout { get; set; } = 60;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Container {VmId} on node '{Node}'", "Suspend-PveContainer"))
return;
@@ -24,7 +24,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Optional alias name to filter by.")]
public string? Name { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -12,7 +12,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, Position = 0, HelpMessage = "The security group name. If specified, returns rules within the group.")]
public string? Group { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new FirewallService();
@@ -24,7 +24,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Optional IP set name to filter by.")]
public string? Name { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -22,7 +22,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = true, HelpMessage = "The IP set name.")]
public string Name { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -19,7 +19,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[ValidateRange(100, 999999999)]
public int? VmId { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -22,7 +22,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Optional type filter for references.")]
public string? Type { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -27,7 +27,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Optional rule position to filter by.")]
public int? Position { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, Group, out var scopeErrorId, out var scopeMessage))
@@ -28,7 +28,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Optional comment for the alias.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -15,7 +15,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Optional comment for the security group.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"firewall security group '{Group}'", "Create"))
return;
@@ -25,7 +25,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Optional comment for the IP set.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -31,7 +31,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Optional comment for the entry.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -62,7 +62,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Network interface.")]
public string? Iface { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, Group, out var scopeErrorId, out var scopeMessage))
@@ -22,7 +22,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = true, HelpMessage = "The alias name to remove.")]
public string Name { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -12,7 +12,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The security group name to remove.")]
public string Group { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"firewall security group '{Group}'", "Remove"))
return;
@@ -22,7 +22,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = true, HelpMessage = "The IP set name to remove.")]
public string Name { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -25,7 +25,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = true, HelpMessage = "The CIDR network address to remove.")]
public string Cidr { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -26,7 +26,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = true, HelpMessage = "The rule position to remove.")]
public int Position { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, Group, out var scopeErrorId, out var scopeMessage))
@@ -28,7 +28,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Updated comment for the alias.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -31,7 +31,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Updated comment for the entry.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -49,7 +49,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Enable IP filter.")]
public SwitchParameter IpFilter { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, null, out var scopeErrorId, out var scopeMessage))
@@ -65,7 +65,7 @@ namespace PSProxmoxVE.Cmdlets.Firewall
[Parameter(Mandatory = false, HelpMessage = "Network interface.")]
public string? Iface { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var level = Level;
if (!FirewallScope.TryValidate(level, Node, VmId, Group, out var scopeErrorId, out var scopeMessage))
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.HA
HelpMessage = "HA group name. Omit to list all groups.")]
public string? Group { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new HaService();
@@ -21,7 +21,7 @@ namespace PSProxmoxVE.Cmdlets.HA
HelpMessage = "Service ID (e.g. 'vm:100', 'ct:200'). Omit to list all.")]
public string? Sid { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new HaService();
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.HA
HelpMessage = "HA rule ID. Omit to list all rules.")]
public string? Rule { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "HA Rules", 9, 0);
@@ -15,7 +15,7 @@ namespace PSProxmoxVE.Cmdlets.HA
[OutputType(typeof(PveHaStatus))]
public sealed class GetPveHaStatusCmdlet : PveCmdletBase
{
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
var service = new HaService();
@@ -30,7 +30,7 @@ namespace PSProxmoxVE.Cmdlets.HA
[ValidateSet("Migrate", "Relocate")]
public string Mode { get; set; } = "Migrate";
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"HA resource '{Sid}' to node '{Node}'", $"{Mode}"))
return;
@@ -35,7 +35,7 @@ namespace PSProxmoxVE.Cmdlets.HA
[Parameter(Mandatory = false, HelpMessage = "Description or comment.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"HA group '{Group}'", "Create"))
return;
@@ -42,7 +42,7 @@ namespace PSProxmoxVE.Cmdlets.HA
[Parameter(Mandatory = false, HelpMessage = "Description or comment.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"HA resource '{Sid}'", "Create"))
return;
@@ -31,7 +31,7 @@ namespace PSProxmoxVE.Cmdlets.HA
[Parameter(Mandatory = false, HelpMessage = "Additional rule properties.")]
public System.Collections.Hashtable? Properties { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "HA Rules", 9, 0);
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.HA
HelpMessage = "HA group name to delete.")]
public string Group { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"HA group '{Group}'", "Delete"))
return;
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.HA
HelpMessage = "Service ID (e.g. 'vm:100', 'ct:200').")]
public string Sid { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"HA resource '{Sid}'", "Remove from HA management"))
return;
@@ -19,7 +19,7 @@ namespace PSProxmoxVE.Cmdlets.HA
HelpMessage = "HA rule ID to delete.")]
public string Rule { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "HA Rules", 9, 0);
@@ -37,7 +37,7 @@ namespace PSProxmoxVE.Cmdlets.HA
[Parameter(Mandatory = false, HelpMessage = "Description or comment.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"HA group '{Group}'", "Update"))
return;
@@ -43,7 +43,7 @@ namespace PSProxmoxVE.Cmdlets.HA
[Parameter(Mandatory = false, HelpMessage = "Description or comment.")]
public string? Comment { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"HA resource '{Sid}'", "Update"))
return;
@@ -36,7 +36,7 @@ namespace PSProxmoxVE.Cmdlets.HA
[Parameter(Mandatory = false, HelpMessage = "Additional rule properties.")]
public System.Collections.Hashtable? Properties { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "HA Rules", 9, 0);
@@ -32,7 +32,7 @@ namespace PSProxmoxVE.Cmdlets.Network
"OVSPort", "OVSIntPort", "any_bridge", "any_local_bridge", IgnoreCase = true)]
public string? Type { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, Position = 0, HelpMessage = "Filter by controller identifier.")]
public string? Controller { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "SDN IPAM/DNS/Controller", 6, 2, 8, 1);
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, Position = 0, HelpMessage = "Filter by DNS plugin identifier.")]
public string? Dns { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "SDN IPAM/DNS/Controller", 6, 2, 8, 1);
@@ -20,7 +20,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, Position = 0, HelpMessage = "Filter by IPAM plugin identifier.")]
public string? Ipam { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "SDN IPAM/DNS/Controller", 6, 2, 8, 1);
@@ -23,7 +23,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "Filter by subnet CIDR (e.g. 10.0.0.0/24).")]
public string? Subnet { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "SDN", 6, 2, 8, 0);
@@ -23,7 +23,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "The SDN VNet name.")]
public string? Vnet { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "SDN", 6, 2, 8, 0);
@@ -18,7 +18,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, Position = 0, HelpMessage = "The SDN zone name.")]
public string? Zone { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
var session = GetSession();
RequireVersion(session, "SDN", 6, 2, 8, 0);
@@ -24,7 +24,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
public SwitchParameter Wait { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess(Node, "Apply PVE Network Configuration"))
return;
@@ -14,7 +14,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[OutputType(typeof(void))]
public sealed class InvokePveSdnApplyCmdlet : PveCmdletBase
{
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess("SDN configuration", "Apply pending changes"))
return;
@@ -69,7 +69,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "Comments or notes for this interface.")]
public string? Comments { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"{Iface} on {Node}", "Create PVE Network Iface"))
return;
@@ -36,7 +36,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "The node this controller is configured on.")]
public string? Node { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"SDN Controller '{Controller}'", "Create PVE SDN Controller"))
return;
@@ -40,7 +40,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "The TTL (time-to-live) for DNS records.")]
public int? Ttl { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"SDN DNS '{Dns}'", "Create PVE SDN DNS"))
return;
@@ -36,7 +36,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "The configuration section identifier.")]
public int? Section { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"SDN IPAM '{Ipam}'", "Create PVE SDN IPAM"))
return;
@@ -40,7 +40,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "DHCP range for automatic IP assignment.")]
public string? DhcpRange { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Subnet {Subnet} on VNet {Vnet}", "Create PVE SDN Subnet"))
return;
@@ -36,7 +36,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "Enable VLAN awareness on this VNet.")]
public SwitchParameter VlanAware { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"{Vnet} in zone {Zone}", "Create PVE SDN VNet"))
return;
@@ -52,7 +52,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = false, HelpMessage = "IPAM plugin to use.")]
public string? Ipam { get; set; }
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess(Zone, "Create PVE SDN Zone"))
return;
@@ -22,7 +22,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = true, Position = 1, HelpMessage = "The network interface name.")]
public string Iface { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"{Iface} on {Node}", "Remove PVE Network Iface"))
return;
@@ -18,7 +18,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The controller identifier to remove.")]
public string Controller { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"SDN Controller '{Controller}'", "Remove PVE SDN Controller"))
return;
@@ -18,7 +18,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The DNS plugin identifier to remove.")]
public string Dns { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"SDN DNS '{Dns}'", "Remove PVE SDN DNS"))
return;
@@ -18,7 +18,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The IPAM plugin identifier to remove.")]
public string Ipam { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"SDN IPAM '{Ipam}'", "Remove PVE SDN IPAM"))
return;
@@ -23,7 +23,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[Parameter(Mandatory = true, Position = 1, ValueFromPipelineByPropertyName = true, HelpMessage = "The subnet CIDR to remove.")]
public string Subnet { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess($"Subnet {Subnet} on VNet {Vnet}", "Remove PVE SDN Subnet"))
return;
@@ -18,7 +18,7 @@ namespace PSProxmoxVE.Cmdlets.Network
[ValidatePattern(@"\A[A-Za-z0-9][A-Za-z0-9._-]*\z")]
public string Vnet { get; set; } = string.Empty;
protected override void ProcessRecord()
protected override void ProcessPveRecord()
{
if (!ShouldProcess(Vnet, "Remove PVE SDN VNet"))
return;

Some files were not shown because too many files have changed in this diff Show More