mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-08-20 09:52:17 +00:00
feat: add firewall, backup, and SDN IPAM/DNS/controller cmdlets
Add 35 new cmdlets bringing the total to 118: - Firewall (21): rules, groups, aliases, IP sets, options at cluster/node/VM/container levels - Backup (5): ad-hoc vzdump and scheduled backup job CRUD - SDN IPAM/DNS/Controller (9): plugin management for SDN subsystem Also includes: - Fix: Remove-PveRole now has ConfirmImpact.High - Fix: URL-encode snapshot names in API paths - Refactor: extract auth header strings to constants in PveHttpClient - Add PSGallery version badge to README - Full test coverage: 11 JSON fixtures, xUnit model tests, Pester unit tests, and integration tests for firewall/backup/OVA import - Updated manifest, format file, CHANGELOG, README, API coverage docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,10 @@ namespace PSProxmoxVE.Core.Client
|
||||
private readonly HttpClient _httpClient;
|
||||
private bool _disposed;
|
||||
|
||||
private const string ApiTokenPrefix = "PVEAPIToken=";
|
||||
private const string AuthCookieName = "PVEAuthCookie=";
|
||||
private const string CsrfHeaderName = "CSRFPreventionToken";
|
||||
|
||||
/// <summary>
|
||||
/// Creates an HTTP client authenticated with the specified PVE session.
|
||||
/// </summary>
|
||||
@@ -313,14 +317,14 @@ namespace PSProxmoxVE.Core.Client
|
||||
|
||||
if (_session.AuthMode == PveAuthMode.ApiToken)
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation("Authorization", $"PVEAPIToken={_session.ApiToken}");
|
||||
request.Headers.TryAddWithoutValidation("Authorization", $"{ApiTokenPrefix}{_session.ApiToken}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ticket auth
|
||||
request.Headers.Add("Cookie", $"PVEAuthCookie={_session.Ticket}");
|
||||
request.Headers.Add("Cookie", $"{AuthCookieName}{_session.Ticket}");
|
||||
if (mutating && !string.IsNullOrEmpty(_session.CsrfToken))
|
||||
request.Headers.Add("CSRFPreventionToken", _session.CsrfToken);
|
||||
request.Headers.Add(CsrfHeaderName, _session.CsrfToken);
|
||||
}
|
||||
|
||||
return request;
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Proxmox VE scheduled backup job as returned by
|
||||
/// the /cluster/backup endpoint.
|
||||
/// </summary>
|
||||
public class PveBackupJob
|
||||
{
|
||||
/// <summary>
|
||||
/// The backup job identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("id")]
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The job type (typically "vzdump").
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
[JsonProperty("type")]
|
||||
public string? Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the job is enabled (1) or disabled (0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("enabled")]
|
||||
[JsonProperty("enabled")]
|
||||
public int? Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The cron-style schedule (e.g. "0 2 * * *").
|
||||
/// </summary>
|
||||
[JsonPropertyName("schedule")]
|
||||
[JsonProperty("schedule")]
|
||||
public string? Schedule { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The target storage for backup files.
|
||||
/// </summary>
|
||||
[JsonPropertyName("storage")]
|
||||
[JsonProperty("storage")]
|
||||
public string? Storage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Backup mode: "snapshot", "suspend", or "stop".
|
||||
/// </summary>
|
||||
[JsonPropertyName("mode")]
|
||||
[JsonProperty("mode")]
|
||||
public string? Mode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Comma-separated list of VM/container IDs to back up, or empty for all.
|
||||
/// </summary>
|
||||
[JsonPropertyName("vmid")]
|
||||
[JsonProperty("vmid")]
|
||||
public string? VmId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to back up all VMs/containers (1 = all).
|
||||
/// </summary>
|
||||
[JsonPropertyName("all")]
|
||||
[JsonProperty("all")]
|
||||
public int? All { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Compression algorithm (e.g. "zstd", "lzo", "gzip").
|
||||
/// </summary>
|
||||
[JsonPropertyName("compress")]
|
||||
[JsonProperty("compress")]
|
||||
public string? Compress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Deprecated — maximum number of backup files to keep. Use prune-backups instead.
|
||||
/// </summary>
|
||||
[JsonPropertyName("maxfiles")]
|
||||
[JsonProperty("maxfiles")]
|
||||
public int? MaxFiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Retention policy string (e.g. "keep-daily=7,keep-weekly=4").
|
||||
/// </summary>
|
||||
[JsonPropertyName("prune-backups")]
|
||||
[JsonProperty("prune-backups")]
|
||||
public string? PruneBackups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Template for backup notes.
|
||||
/// </summary>
|
||||
[JsonPropertyName("notes-template")]
|
||||
[JsonProperty("notes-template")]
|
||||
public string? NotesTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional comment describing the backup job.
|
||||
/// </summary>
|
||||
[JsonPropertyName("comment")]
|
||||
[JsonProperty("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Mail notification setting (e.g. "always", "failure").
|
||||
/// </summary>
|
||||
[JsonPropertyName("mailnotification")]
|
||||
[JsonProperty("mailnotification")]
|
||||
public string? MailNotification { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Email address to send notifications to.
|
||||
/// </summary>
|
||||
[JsonPropertyName("mailto")]
|
||||
[JsonProperty("mailto")]
|
||||
public string? MailTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specific node to run the backup on (empty = any node).
|
||||
/// </summary>
|
||||
[JsonPropertyName("node")]
|
||||
[JsonProperty("node")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Comma-separated list of excluded VM/container IDs.
|
||||
/// </summary>
|
||||
[JsonPropertyName("exclude")]
|
||||
[JsonProperty("exclude")]
|
||||
public string? Exclude { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
var state = Enabled == 1 ? "Enabled" : "Disabled";
|
||||
return $"Backup Job: {Id} | {state} | Schedule: {Schedule ?? "N/A"} | "
|
||||
+ $"Storage: {Storage ?? "N/A"} | Mode: {Mode ?? "N/A"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Firewall;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Proxmox VE firewall IP alias as returned by
|
||||
/// the firewall/aliases endpoints.
|
||||
/// </summary>
|
||||
public class PveFirewallAlias
|
||||
{
|
||||
/// <summary>
|
||||
/// The alias name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The CIDR network or IP address (e.g. "10.0.0.0/8" or "192.168.1.1").
|
||||
/// </summary>
|
||||
[JsonPropertyName("cidr")]
|
||||
[JsonProperty("cidr")]
|
||||
public string Cidr { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Optional comment describing the alias.
|
||||
/// </summary>
|
||||
[JsonPropertyName("comment")]
|
||||
[JsonProperty("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Alias: {Name} → {Cidr}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Firewall;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Proxmox VE firewall security group as returned by
|
||||
/// the /cluster/firewall/groups endpoint.
|
||||
/// </summary>
|
||||
public class PveFirewallGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// The security group name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("group")]
|
||||
[JsonProperty("group")]
|
||||
public string Group { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Optional comment describing the group.
|
||||
/// </summary>
|
||||
[JsonPropertyName("comment")]
|
||||
[JsonProperty("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration digest for detecting concurrent modifications.
|
||||
/// </summary>
|
||||
[JsonPropertyName("digest")]
|
||||
[JsonProperty("digest")]
|
||||
public string? Digest { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Firewall Group: {Group} | Comment: {Comment ?? "N/A"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Firewall;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Proxmox VE firewall IP set as returned by
|
||||
/// the firewall/ipset endpoints.
|
||||
/// </summary>
|
||||
public class PveFirewallIpSet
|
||||
{
|
||||
/// <summary>
|
||||
/// The IP set name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Optional comment describing the IP set.
|
||||
/// </summary>
|
||||
[JsonPropertyName("comment")]
|
||||
[JsonProperty("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration digest for detecting concurrent modifications.
|
||||
/// </summary>
|
||||
[JsonPropertyName("digest")]
|
||||
[JsonProperty("digest")]
|
||||
public string? Digest { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"IP Set: {Name} | Comment: {Comment ?? "N/A"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Firewall;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entry within a Proxmox VE firewall IP set as returned by
|
||||
/// the firewall/ipset/{name} endpoints.
|
||||
/// </summary>
|
||||
public class PveFirewallIpSetEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The CIDR network or IP address (e.g. "192.168.1.0/24" or "10.0.0.1").
|
||||
/// </summary>
|
||||
[JsonPropertyName("cidr")]
|
||||
[JsonProperty("cidr")]
|
||||
public string Cidr { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this is an exclusion entry (1 = exclude from set).
|
||||
/// </summary>
|
||||
[JsonPropertyName("nomatch")]
|
||||
[JsonProperty("nomatch")]
|
||||
public int? NoMatch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional comment describing the entry.
|
||||
/// </summary>
|
||||
[JsonPropertyName("comment")]
|
||||
[JsonProperty("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
var prefix = NoMatch == 1 ? "!" : "";
|
||||
return $"{prefix}{Cidr}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Firewall;
|
||||
|
||||
/// <summary>
|
||||
/// Represents Proxmox VE firewall options as returned by
|
||||
/// the firewall/options endpoints at cluster, node, VM, or container level.
|
||||
/// </summary>
|
||||
public class PveFirewallOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the firewall is enabled (1) or disabled (0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("enable")]
|
||||
[JsonProperty("enable")]
|
||||
public int? Enable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default policy for incoming traffic (ACCEPT, DROP, REJECT).
|
||||
/// </summary>
|
||||
[JsonPropertyName("policy_in")]
|
||||
[JsonProperty("policy_in")]
|
||||
public string? PolicyIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default policy for outgoing traffic (ACCEPT, DROP, REJECT).
|
||||
/// </summary>
|
||||
[JsonPropertyName("policy_out")]
|
||||
[JsonProperty("policy_out")]
|
||||
public string? PolicyOut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Log level for incoming traffic (nolog, emerg, alert, crit, err, warning, notice, info, debug).
|
||||
/// </summary>
|
||||
[JsonPropertyName("log_level_in")]
|
||||
[JsonProperty("log_level_in")]
|
||||
public string? LogLevelIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Log level for outgoing traffic.
|
||||
/// </summary>
|
||||
[JsonPropertyName("log_level_out")]
|
||||
[JsonProperty("log_level_out")]
|
||||
public string? LogLevelOut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to allow DHCP traffic (VM/container level).
|
||||
/// </summary>
|
||||
[JsonPropertyName("dhcp")]
|
||||
[JsonProperty("dhcp")]
|
||||
public int? Dhcp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to allow NDP (IPv6 Neighbor Discovery Protocol).
|
||||
/// </summary>
|
||||
[JsonPropertyName("ndp")]
|
||||
[JsonProperty("ndp")]
|
||||
public int? Ndp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to allow Router Advertisement.
|
||||
/// </summary>
|
||||
[JsonPropertyName("radv")]
|
||||
[JsonProperty("radv")]
|
||||
public int? Radv { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to enable MAC address filter (VM/container level).
|
||||
/// </summary>
|
||||
[JsonPropertyName("macfilter")]
|
||||
[JsonProperty("macfilter")]
|
||||
public int? MacFilter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to enable IP filter (VM/container level).
|
||||
/// </summary>
|
||||
[JsonPropertyName("ipfilter")]
|
||||
[JsonProperty("ipfilter")]
|
||||
public int? IpFilter { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
var state = Enable == 1 ? "Enabled" : "Disabled";
|
||||
return $"Firewall: {state} | In: {PolicyIn ?? "N/A"} | Out: {PolicyOut ?? "N/A"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Firewall;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Proxmox VE firewall reference item (alias, ipset, etc.)
|
||||
/// as returned by the firewall/refs endpoints.
|
||||
/// </summary>
|
||||
public class PveFirewallRef
|
||||
{
|
||||
/// <summary>
|
||||
/// The reference type (e.g. "alias", "ipset").
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The reference name.
|
||||
/// </summary>
|
||||
[JsonPropertyName("name")]
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Optional comment.
|
||||
/// </summary>
|
||||
[JsonPropertyName("comment")]
|
||||
[JsonProperty("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type}: {Name}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Firewall;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Proxmox VE firewall rule as returned by
|
||||
/// the firewall/rules endpoints at cluster, node, VM, or container level.
|
||||
/// </summary>
|
||||
public class PveFirewallRule
|
||||
{
|
||||
/// <summary>
|
||||
/// Rule position (used for ordering and as identifier for updates/deletes).
|
||||
/// </summary>
|
||||
[JsonPropertyName("pos")]
|
||||
[JsonProperty("pos")]
|
||||
public int? Pos { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rule type: "in", "out", or "group".
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Action to take: ACCEPT, DROP, or REJECT.
|
||||
/// </summary>
|
||||
[JsonPropertyName("action")]
|
||||
[JsonProperty("action")]
|
||||
public string Action { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the rule is enabled (1) or disabled (0).
|
||||
/// </summary>
|
||||
[JsonPropertyName("enable")]
|
||||
[JsonProperty("enable")]
|
||||
public int? Enable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Source address or alias.
|
||||
/// </summary>
|
||||
[JsonPropertyName("source")]
|
||||
[JsonProperty("source")]
|
||||
public string? Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Destination address or alias.
|
||||
/// </summary>
|
||||
[JsonPropertyName("dest")]
|
||||
[JsonProperty("dest")]
|
||||
public string? Dest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Protocol (e.g. tcp, udp, icmp).
|
||||
/// </summary>
|
||||
[JsonPropertyName("proto")]
|
||||
[JsonProperty("proto")]
|
||||
public string? Proto { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Destination port or port range.
|
||||
/// </summary>
|
||||
[JsonPropertyName("dport")]
|
||||
[JsonProperty("dport")]
|
||||
public string? Dport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Source port or port range.
|
||||
/// </summary>
|
||||
[JsonPropertyName("sport")]
|
||||
[JsonProperty("sport")]
|
||||
public string? Sport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional comment describing the rule.
|
||||
/// </summary>
|
||||
[JsonPropertyName("comment")]
|
||||
[JsonProperty("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Predefined macro name (e.g. "SSH", "HTTP", "DNS").
|
||||
/// </summary>
|
||||
[JsonPropertyName("macro")]
|
||||
[JsonProperty("macro")]
|
||||
public string? Macro { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Log level for matched packets (e.g. "nolog", "info", "warning").
|
||||
/// </summary>
|
||||
[JsonPropertyName("log")]
|
||||
[JsonProperty("log")]
|
||||
public string? Log { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Network interface to match (e.g. "net0").
|
||||
/// </summary>
|
||||
[JsonPropertyName("iface")]
|
||||
[JsonProperty("iface")]
|
||||
public string? Iface { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
var action = Macro != null ? $"{Action} ({Macro})" : Action;
|
||||
return $"Rule {Pos}: {Type} {action} | "
|
||||
+ $"Src: {Source ?? "any"} → Dst: {Dest ?? "any"} | "
|
||||
+ $"Proto: {Proto ?? "any"} DPort: {Dport ?? "any"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Network;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Software-Defined Networking (SDN) controller as returned by
|
||||
/// the /cluster/sdn/controllers endpoint. Requires Proxmox VE 8.0+.
|
||||
/// </summary>
|
||||
public class PveSdnController
|
||||
{
|
||||
/// <summary>
|
||||
/// The controller identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("controller")]
|
||||
[JsonProperty("controller")]
|
||||
public string Controller { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The controller type (e.g. "evpn", "bgp").
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The Autonomous System Number for BGP/EVPN.
|
||||
/// </summary>
|
||||
[JsonPropertyName("asn")]
|
||||
[JsonProperty("asn")]
|
||||
public int? Asn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Comma-separated list of BGP peer addresses.
|
||||
/// </summary>
|
||||
[JsonPropertyName("peers")]
|
||||
[JsonProperty("peers")]
|
||||
public string? Peers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The node this controller is configured on.
|
||||
/// </summary>
|
||||
[JsonPropertyName("node")]
|
||||
[JsonProperty("node")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"SDN Controller: {Controller} | Type: {Type} | ASN: {Asn?.ToString() ?? "N/A"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Network;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Software-Defined Networking (SDN) DNS plugin as returned by
|
||||
/// the /cluster/sdn/dns endpoint. Requires Proxmox VE 8.0+.
|
||||
/// </summary>
|
||||
public class PveSdnDns
|
||||
{
|
||||
/// <summary>
|
||||
/// The DNS plugin identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("dns")]
|
||||
[JsonProperty("dns")]
|
||||
public string Dns { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The DNS plugin type (e.g. "powerdns").
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the DNS service API.
|
||||
/// </summary>
|
||||
[JsonPropertyName("url")]
|
||||
[JsonProperty("url")]
|
||||
public string? Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The API key for the DNS service.
|
||||
/// </summary>
|
||||
[JsonPropertyName("key")]
|
||||
[JsonProperty("key")]
|
||||
public string? Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The IPv6 reverse zone mask length.
|
||||
/// </summary>
|
||||
[JsonPropertyName("reversemaskv6")]
|
||||
[JsonProperty("reversemaskv6")]
|
||||
public int? ReverseMaskV6 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The TTL (time-to-live) for DNS records.
|
||||
/// </summary>
|
||||
[JsonPropertyName("ttl")]
|
||||
[JsonProperty("ttl")]
|
||||
public int? Ttl { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"SDN DNS: {Dns} | Type: {Type} | URL: {Url ?? "N/A"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PSProxmoxVE.Core.Models.Network;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Software-Defined Networking (SDN) IPAM plugin as returned by
|
||||
/// the /cluster/sdn/ipams endpoint. Requires Proxmox VE 8.0+.
|
||||
/// </summary>
|
||||
public class PveSdnIpam
|
||||
{
|
||||
/// <summary>
|
||||
/// The IPAM plugin identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("ipam")]
|
||||
[JsonProperty("ipam")]
|
||||
public string Ipam { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The IPAM type (e.g. "pve", "netbox", "phpipam").
|
||||
/// </summary>
|
||||
[JsonPropertyName("type")]
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the external IPAM service (for netbox/phpipam types).
|
||||
/// </summary>
|
||||
[JsonPropertyName("url")]
|
||||
[JsonProperty("url")]
|
||||
public string? Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The API token for the external IPAM service.
|
||||
/// </summary>
|
||||
[JsonPropertyName("token")]
|
||||
[JsonProperty("token")]
|
||||
public string? Token { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The configuration section identifier.
|
||||
/// </summary>
|
||||
[JsonPropertyName("section")]
|
||||
[JsonProperty("section")]
|
||||
public int? Section { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"SDN IPAM: {Ipam} | Type: {Type}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PSProxmoxVE.Core.Authentication;
|
||||
using PSProxmoxVE.Core.Client;
|
||||
using PSProxmoxVE.Core.Models.Backup;
|
||||
using PSProxmoxVE.Core.Models.Vms;
|
||||
|
||||
namespace PSProxmoxVE.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for Proxmox VE Backup (vzdump) and backup job API operations.
|
||||
/// </summary>
|
||||
public class BackupService
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// Ad-hoc backup (vzdump)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Creates an ad-hoc backup via vzdump. Returns the task UPID.
|
||||
/// </summary>
|
||||
/// <param name="session">The authenticated PVE session.</param>
|
||||
/// <param name="node">The node to run the backup on.</param>
|
||||
/// <param name="config">Backup configuration parameters.</param>
|
||||
public PveTask CreateBackup(PveSession session, string node, Dictionary<string, string> config)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/vzdump", config)
|
||||
.GetAwaiter().GetResult();
|
||||
return ParseTask(response, node);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Backup jobs (scheduled)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns all scheduled backup jobs.
|
||||
/// </summary>
|
||||
public PveBackupJob[] GetBackupJobs(PveSession session)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync("cluster/backup").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveBackupJob[]>() ?? Array.Empty<PveBackupJob>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a single scheduled backup job by ID.
|
||||
/// </summary>
|
||||
public PveBackupJob? GetBackupJob(PveSession session, string id)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync($"cluster/backup/{Uri.EscapeDataString(id)}")
|
||||
.GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveBackupJob>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a scheduled backup job.
|
||||
/// </summary>
|
||||
public void CreateBackupJob(PveSession session, Dictionary<string, string> config)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync("cluster/backup", config).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a scheduled backup job.
|
||||
/// </summary>
|
||||
public void UpdateBackupJob(PveSession session, string id, Dictionary<string, string> config)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id));
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PutAsync($"cluster/backup/{Uri.EscapeDataString(id)}", config)
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a scheduled backup job.
|
||||
/// </summary>
|
||||
public void RemoveBackupJob(PveSession session, string id)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"cluster/backup/{Uri.EscapeDataString(id)}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private static PveTask ParseTask(string response, string node)
|
||||
{
|
||||
var data = JObject.Parse(response)["data"];
|
||||
if (data?.Type == JTokenType.String)
|
||||
return new PveTask { Upid = data.ToString(), Node = node };
|
||||
|
||||
var task = data?.ToObject<PveTask>() ?? new PveTask();
|
||||
task.Node = node;
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,7 +173,7 @@ namespace PSProxmoxVE.Core.Services
|
||||
if (string.IsNullOrWhiteSpace(snapname)) throw new ArgumentNullException(nameof(snapname));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.DeleteAsync($"nodes/{node}/lxc/{vmid}/snapshot/{snapname}")
|
||||
var response = client.DeleteAsync($"nodes/{node}/lxc/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}")
|
||||
.GetAwaiter().GetResult();
|
||||
return ParseTask(response, node);
|
||||
}
|
||||
@@ -192,7 +192,7 @@ namespace PSProxmoxVE.Core.Services
|
||||
if (string.IsNullOrWhiteSpace(snapname)) throw new ArgumentNullException(nameof(snapname));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.PostAsync($"nodes/{node}/lxc/{vmid}/snapshot/{snapname}/rollback")
|
||||
var response = client.PostAsync($"nodes/{node}/lxc/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}/rollback")
|
||||
.GetAwaiter().GetResult();
|
||||
return ParseTask(response, node);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PSProxmoxVE.Core.Authentication;
|
||||
using PSProxmoxVE.Core.Client;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
|
||||
namespace PSProxmoxVE.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for Proxmox VE Firewall API operations.
|
||||
/// Supports firewall management at cluster, node, VM, and container levels.
|
||||
/// </summary>
|
||||
public class FirewallService
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// Rules
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns firewall rules at the specified level.
|
||||
/// </summary>
|
||||
public PveFirewallRule[] GetRules(PveSession session, string level, string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync($"{basePath}/rules").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveFirewallRule[]>() ?? Array.Empty<PveFirewallRule>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a firewall rule at the specified level.
|
||||
/// </summary>
|
||||
public void CreateRule(PveSession session, string level, Dictionary<string, string> config,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync($"{basePath}/rules", config).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a firewall rule at the specified position.
|
||||
/// </summary>
|
||||
public void UpdateRule(PveSession session, string level, int pos, Dictionary<string, string> config,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PutAsync($"{basePath}/rules/{pos}", config).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a firewall rule at the specified position.
|
||||
/// </summary>
|
||||
public void RemoveRule(PveSession session, string level, int pos,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"{basePath}/rules/{pos}").GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Security Groups (cluster level only)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns all firewall security groups.
|
||||
/// </summary>
|
||||
public PveFirewallGroup[] GetGroups(PveSession session)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync("cluster/firewall/groups").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveFirewallGroup[]>() ?? Array.Empty<PveFirewallGroup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a firewall security group.
|
||||
/// </summary>
|
||||
public void CreateGroup(PveSession session, string name, string? comment = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
var formData = new Dictionary<string, string> { ["group"] = name };
|
||||
if (!string.IsNullOrEmpty(comment))
|
||||
formData["comment"] = comment!;
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync("cluster/firewall/groups", formData).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a firewall security group.
|
||||
/// </summary>
|
||||
public void RemoveGroup(PveSession session, string name)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"cluster/firewall/groups/{Uri.EscapeDataString(name)}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns firewall rules within a security group.
|
||||
/// </summary>
|
||||
public PveFirewallRule[] GetGroupRules(PveSession session, string group)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}")
|
||||
.GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveFirewallRule[]>() ?? Array.Empty<PveFirewallRule>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a rule within a security group.
|
||||
/// </summary>
|
||||
public void CreateGroupRule(PveSession session, string group, Dictionary<string, string> config)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group));
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}", config)
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a rule within a security group.
|
||||
/// </summary>
|
||||
public void UpdateGroupRule(PveSession session, string group, int pos, Dictionary<string, string> config)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group));
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PutAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}/{pos}", config)
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a rule from a security group.
|
||||
/// </summary>
|
||||
public void RemoveGroupRule(PveSession session, string group, int pos)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"cluster/firewall/groups/{Uri.EscapeDataString(group)}/{pos}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Aliases
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns firewall aliases at the specified level.
|
||||
/// </summary>
|
||||
public PveFirewallAlias[] GetAliases(PveSession session, string level, string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync($"{basePath}/aliases").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveFirewallAlias[]>() ?? Array.Empty<PveFirewallAlias>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a firewall alias.
|
||||
/// </summary>
|
||||
public void CreateAlias(PveSession session, string level, string name, string cidr,
|
||||
string? comment = null, string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
if (string.IsNullOrWhiteSpace(cidr)) throw new ArgumentNullException(nameof(cidr));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
var formData = new Dictionary<string, string>
|
||||
{
|
||||
["name"] = name,
|
||||
["cidr"] = cidr
|
||||
};
|
||||
if (!string.IsNullOrEmpty(comment))
|
||||
formData["comment"] = comment!;
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync($"{basePath}/aliases", formData).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a firewall alias.
|
||||
/// </summary>
|
||||
public void UpdateAlias(PveSession session, string level, string name,
|
||||
string? cidr = null, string? comment = null, string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
var formData = new Dictionary<string, string>();
|
||||
if (!string.IsNullOrEmpty(cidr))
|
||||
formData["cidr"] = cidr!;
|
||||
if (!string.IsNullOrEmpty(comment))
|
||||
formData["comment"] = comment!;
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PutAsync($"{basePath}/aliases/{Uri.EscapeDataString(name)}", formData)
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a firewall alias.
|
||||
/// </summary>
|
||||
public void RemoveAlias(PveSession session, string level, string name,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"{basePath}/aliases/{Uri.EscapeDataString(name)}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IP Sets
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns firewall IP sets at the specified level.
|
||||
/// </summary>
|
||||
public PveFirewallIpSet[] GetIpSets(PveSession session, string level, string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync($"{basePath}/ipset").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveFirewallIpSet[]>() ?? Array.Empty<PveFirewallIpSet>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a firewall IP set.
|
||||
/// </summary>
|
||||
public void CreateIpSet(PveSession session, string level, string name,
|
||||
string? comment = null, string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
var formData = new Dictionary<string, string> { ["name"] = name };
|
||||
if (!string.IsNullOrEmpty(comment))
|
||||
formData["comment"] = comment!;
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync($"{basePath}/ipset", formData).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a firewall IP set.
|
||||
/// </summary>
|
||||
public void RemoveIpSet(PveSession session, string level, string name,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IP Set Entries
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns entries within an IP set.
|
||||
/// </summary>
|
||||
public PveFirewallIpSetEntry[] GetIpSetEntries(PveSession session, string level, string name,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}")
|
||||
.GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveFirewallIpSetEntry[]>() ?? Array.Empty<PveFirewallIpSetEntry>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an entry to an IP set.
|
||||
/// </summary>
|
||||
public void AddIpSetEntry(PveSession session, string level, string name, string cidr,
|
||||
bool nomatch = false, string? comment = null, string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
if (string.IsNullOrWhiteSpace(cidr)) throw new ArgumentNullException(nameof(cidr));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
var formData = new Dictionary<string, string> { ["cidr"] = cidr };
|
||||
if (nomatch)
|
||||
formData["nomatch"] = "1";
|
||||
if (!string.IsNullOrEmpty(comment))
|
||||
formData["comment"] = comment!;
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync($"{basePath}/ipset/{Uri.EscapeDataString(name)}", formData)
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an entry in an IP set.
|
||||
/// </summary>
|
||||
public void UpdateIpSetEntry(PveSession session, string level, string name, string cidr,
|
||||
bool? nomatch = null, string? comment = null, string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
if (string.IsNullOrWhiteSpace(cidr)) throw new ArgumentNullException(nameof(cidr));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
var formData = new Dictionary<string, string>();
|
||||
if (nomatch.HasValue)
|
||||
formData["nomatch"] = nomatch.Value ? "1" : "0";
|
||||
if (!string.IsNullOrEmpty(comment))
|
||||
formData["comment"] = comment!;
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PutAsync(
|
||||
$"{basePath}/ipset/{Uri.EscapeDataString(name)}/{Uri.EscapeDataString(cidr)}",
|
||||
formData).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an entry from an IP set.
|
||||
/// </summary>
|
||||
public void RemoveIpSetEntry(PveSession session, string level, string name, string cidr,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name));
|
||||
if (string.IsNullOrWhiteSpace(cidr)) throw new ArgumentNullException(nameof(cidr));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync(
|
||||
$"{basePath}/ipset/{Uri.EscapeDataString(name)}/{Uri.EscapeDataString(cidr)}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Options
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns firewall options at the specified level.
|
||||
/// </summary>
|
||||
public PveFirewallOptions GetOptions(PveSession session, string level,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync($"{basePath}/options").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveFirewallOptions>() ?? new PveFirewallOptions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets firewall options at the specified level.
|
||||
/// </summary>
|
||||
public void SetOptions(PveSession session, string level, Dictionary<string, string> config,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PutAsync($"{basePath}/options", config).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Refs
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns firewall references (aliases, IP sets) at the specified level.
|
||||
/// </summary>
|
||||
public PveFirewallRef[] GetRefs(PveSession session, string level, string? type = null,
|
||||
string? node = null, int? vmid = null)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
var basePath = BuildBasePath(level, node, vmid);
|
||||
|
||||
var resource = $"{basePath}/refs";
|
||||
if (!string.IsNullOrEmpty(type))
|
||||
resource += $"?type={Uri.EscapeDataString(type!)}";
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync(resource).GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveFirewallRef[]>() ?? Array.Empty<PveFirewallRef>();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Builds the API base path for the specified firewall level.
|
||||
/// </summary>
|
||||
private static string BuildBasePath(string level, string? node, int? vmid)
|
||||
{
|
||||
switch (level.ToLowerInvariant())
|
||||
{
|
||||
case "cluster":
|
||||
return "cluster/firewall";
|
||||
case "node":
|
||||
if (string.IsNullOrWhiteSpace(node))
|
||||
throw new ArgumentException("Node is required for node-level firewall operations.", nameof(node));
|
||||
return $"nodes/{Uri.EscapeDataString(node)}/firewall";
|
||||
case "vm":
|
||||
if (string.IsNullOrWhiteSpace(node))
|
||||
throw new ArgumentException("Node is required for VM-level firewall operations.", nameof(node));
|
||||
if (!vmid.HasValue)
|
||||
throw new ArgumentException("VmId is required for VM-level firewall operations.", nameof(vmid));
|
||||
return $"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid.Value}/firewall";
|
||||
case "container":
|
||||
if (string.IsNullOrWhiteSpace(node))
|
||||
throw new ArgumentException("Node is required for container-level firewall operations.", nameof(node));
|
||||
if (!vmid.HasValue)
|
||||
throw new ArgumentException("VmId is required for container-level firewall operations.", nameof(vmid));
|
||||
return $"nodes/{Uri.EscapeDataString(node)}/lxc/{vmid.Value}/firewall";
|
||||
default:
|
||||
throw new ArgumentException($"Invalid firewall level: {level}. Must be Cluster, Node, Vm, or Container.", nameof(level));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,6 +304,141 @@ namespace PSProxmoxVE.Core.Services
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// SDN IPAM
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns all SDN IPAM plugins. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public PveSdnIpam[] GetSdnIpams(PveSession session)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync("cluster/sdn/ipams").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveSdnIpam[]>() ?? Array.Empty<PveSdnIpam>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an SDN IPAM plugin. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public void CreateSdnIpam(PveSession session, Dictionary<string, string> config)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync("cluster/sdn/ipams", config).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an SDN IPAM plugin. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public void RemoveSdnIpam(PveSession session, string ipam)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
if (string.IsNullOrWhiteSpace(ipam)) throw new ArgumentNullException(nameof(ipam));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"cluster/sdn/ipams/{Uri.EscapeDataString(ipam)}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// SDN DNS
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns all SDN DNS plugins. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public PveSdnDns[] GetSdnDnsPlugins(PveSession session)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync("cluster/sdn/dns").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveSdnDns[]>() ?? Array.Empty<PveSdnDns>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an SDN DNS plugin. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public void CreateSdnDnsPlugin(PveSession session, Dictionary<string, string> config)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync("cluster/sdn/dns", config).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an SDN DNS plugin. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public void RemoveSdnDnsPlugin(PveSession session, string dns)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
if (string.IsNullOrWhiteSpace(dns)) throw new ArgumentNullException(nameof(dns));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"cluster/sdn/dns/{Uri.EscapeDataString(dns)}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// SDN Controllers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns all SDN controllers. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public PveSdnController[] GetSdnControllers(PveSession session)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.GetAsync("cluster/sdn/controllers").GetAwaiter().GetResult();
|
||||
var data = JObject.Parse(response)["data"];
|
||||
return data?.ToObject<PveSdnController[]>() ?? Array.Empty<PveSdnController>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an SDN controller. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public void CreateSdnController(PveSession session, Dictionary<string, string> config)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
if (config == null) throw new ArgumentNullException(nameof(config));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.PostAsync("cluster/sdn/controllers", config).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an SDN controller. Requires PVE 8.0+.
|
||||
/// </summary>
|
||||
public void RemoveSdnController(PveSession session, string controller)
|
||||
{
|
||||
if (session == null) throw new ArgumentNullException(nameof(session));
|
||||
RequireSdn(session);
|
||||
if (string.IsNullOrWhiteSpace(controller)) throw new ArgumentNullException(nameof(controller));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
client.DeleteAsync($"cluster/sdn/controllers/{Uri.EscapeDataString(controller)}")
|
||||
.GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace PSProxmoxVE.Core.Services
|
||||
if (string.IsNullOrWhiteSpace(snapname)) throw new ArgumentNullException(nameof(snapname));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.DeleteAsync($"nodes/{node}/qemu/{vmid}/snapshot/{snapname}")
|
||||
var response = client.DeleteAsync($"nodes/{node}/qemu/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}")
|
||||
.GetAwaiter().GetResult();
|
||||
return ParseTask(response, node);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ namespace PSProxmoxVE.Core.Services
|
||||
if (string.IsNullOrWhiteSpace(snapname)) throw new ArgumentNullException(nameof(snapname));
|
||||
|
||||
using var client = new PveHttpClient(session);
|
||||
var response = client.PostAsync($"nodes/{node}/qemu/{vmid}/snapshot/{snapname}/rollback")
|
||||
var response = client.PostAsync($"nodes/{node}/qemu/{vmid}/snapshot/{Uri.EscapeDataString(snapname)}/rollback")
|
||||
.GetAwaiter().GetResult();
|
||||
return ParseTask(response, node);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Backup;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Backup
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Lists Proxmox VE backup jobs.</para>
|
||||
/// <para type="description">
|
||||
/// Returns scheduled backup job configurations from the Proxmox VE cluster.
|
||||
/// Optionally filter by job ID.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Get, "PveBackupJob")]
|
||||
[OutputType(typeof(PveBackupJob))]
|
||||
public sealed class GetPveBackupJobCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="description">The backup job ID to retrieve. When omitted, all jobs are returned.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The backup job ID.")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var session = GetSession();
|
||||
var service = new BackupService();
|
||||
|
||||
if (!string.IsNullOrEmpty(Id))
|
||||
{
|
||||
WriteVerbose($"Getting backup job '{Id}'...");
|
||||
var job = service.GetBackupJob(session, Id!);
|
||||
WriteObject(job);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteVerbose("Getting all backup jobs...");
|
||||
var jobs = service.GetBackupJobs(session);
|
||||
foreach (var job in jobs)
|
||||
{
|
||||
WriteObject(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Vms;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Backup
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Creates an ad-hoc backup of a virtual machine via vzdump.</para>
|
||||
/// <para type="description">
|
||||
/// Triggers an immediate backup (vzdump) of the specified VM on the given node.
|
||||
/// Returns a PveTask representing the backup operation. Use -Wait to block until
|
||||
/// the backup completes.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.New, "PveBackup", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(PveTask))]
|
||||
public sealed class NewPveBackupCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="description">The node on which the VM resides.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The PVE node name.")]
|
||||
public string Node { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">The ID of the VM to back up.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = true, Position = 1, ValueFromPipelineByPropertyName = true, HelpMessage = "The VM identifier.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">The target storage for the backup.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The target storage for the backup.")]
|
||||
public string? Storage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">The backup mode (snapshot, suspend, or stop).</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The backup mode.")]
|
||||
[ValidateSet("snapshot", "suspend", "stop")]
|
||||
public string? Mode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">The compression algorithm to use.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The compression algorithm.")]
|
||||
[ValidateSet("zstd", "lzo", "gzip", "none")]
|
||||
public string? Compress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">When specified, waits for the backup task to complete before returning.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Wait for the task to complete before returning.")]
|
||||
public SwitchParameter Wait { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Maximum seconds to wait when -Wait is specified. Default 3600.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Timeout in seconds for -Wait (default 3600).")]
|
||||
public int Timeout { get; set; } = 3600;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"VM {VmId} on node '{Node}'", "New-PveBackup"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new BackupService();
|
||||
|
||||
var formData = new Dictionary<string, string>
|
||||
{
|
||||
["vmid"] = VmId.ToString()
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(Storage)) formData["storage"] = Storage!;
|
||||
if (!string.IsNullOrEmpty(Mode)) formData["mode"] = Mode!;
|
||||
if (!string.IsNullOrEmpty(Compress)) formData["compress"] = Compress!;
|
||||
|
||||
WriteVerbose($"Creating backup of VM {VmId} on node '{Node}'...");
|
||||
var task = service.CreateBackup(session, Node, formData);
|
||||
|
||||
if (Wait.IsPresent)
|
||||
{
|
||||
var taskService = new TaskService();
|
||||
task = taskService.WaitForTask(session, task.Node ?? Node, task.Upid, TimeSpan.FromSeconds(Timeout));
|
||||
}
|
||||
|
||||
WriteObject(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Backup
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Creates a scheduled backup job on Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Creates a new scheduled backup job in the Proxmox VE cluster configuration.
|
||||
/// Specify a cron-style schedule and target storage. Use VmId for specific VMs
|
||||
/// or -All to back up all VMs.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.New, "PveBackupJob", SupportsShouldProcess = true)]
|
||||
public sealed class NewPveBackupJobCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="description">Cron-style schedule expression (e.g., "0 2 * * *").</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = true, HelpMessage = "Cron-style schedule expression (e.g., '0 2 * * *').")]
|
||||
public string Schedule { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">The target storage for backups.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = true, HelpMessage = "The target storage for backups.")]
|
||||
public string Storage { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">The backup mode (snapshot, suspend, or stop).</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The backup mode.")]
|
||||
[ValidateSet("snapshot", "suspend", "stop")]
|
||||
public string? Mode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Comma-separated list of VM IDs to include in the backup job.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Comma-separated list of VM IDs to back up.")]
|
||||
public string? VmId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">When specified, backs up all VMs on the node.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Back up all VMs.")]
|
||||
public SwitchParameter All { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">The compression algorithm to use.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The compression algorithm.")]
|
||||
[ValidateSet("zstd", "lzo", "gzip", "none")]
|
||||
public string? Compress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">The node to restrict the backup job to.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node to restrict the backup job to.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Email address(es) to send notifications to.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Email address(es) for notifications.")]
|
||||
public string? MailTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">When to send email notifications (always or failure).</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "When to send email notifications.")]
|
||||
[ValidateSet("always", "failure")]
|
||||
public string? MailNotification { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Whether the backup job is enabled. Default is true.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Whether the backup job is enabled.")]
|
||||
public SwitchParameter Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">A comment or description for the backup job.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "A comment or description for the backup job.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Prune-backups configuration string (e.g., "keep-daily=7,keep-weekly=4").</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Prune-backups configuration string.")]
|
||||
public string? PruneBackups { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess("cluster backup configuration", "New-PveBackupJob"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new BackupService();
|
||||
|
||||
var config = new Dictionary<string, string>
|
||||
{
|
||||
["schedule"] = Schedule,
|
||||
["storage"] = Storage,
|
||||
["enabled"] = Enabled.IsPresent ? "1" : "0"
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(Mode)) config["mode"] = Mode!;
|
||||
if (!string.IsNullOrEmpty(VmId)) config["vmid"] = VmId!;
|
||||
if (All.IsPresent) config["all"] = "1";
|
||||
if (!string.IsNullOrEmpty(Compress)) config["compress"] = Compress!;
|
||||
if (!string.IsNullOrEmpty(Node)) config["node"] = Node!;
|
||||
if (!string.IsNullOrEmpty(MailTo)) config["mailto"] = MailTo!;
|
||||
if (!string.IsNullOrEmpty(MailNotification)) config["mailnotification"] = MailNotification!;
|
||||
if (!string.IsNullOrEmpty(Comment)) config["comment"] = Comment!;
|
||||
if (!string.IsNullOrEmpty(PruneBackups)) config["prune-backups"] = PruneBackups!;
|
||||
|
||||
WriteVerbose("Creating backup job...");
|
||||
service.CreateBackupJob(session, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Backup
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Removes a scheduled backup job from Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Deletes a backup job from the Proxmox VE cluster configuration.
|
||||
/// This operation is destructive and requires confirmation unless -Force is specified.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Remove, "PveBackupJob",
|
||||
SupportsShouldProcess = true,
|
||||
ConfirmImpact = ConfirmImpact.High)]
|
||||
public sealed class RemovePveBackupJobCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="description">The backup job ID to remove.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The backup job ID.")]
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"backup job '{Id}'", "Remove-PveBackupJob"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new BackupService();
|
||||
|
||||
WriteVerbose($"Removing backup job '{Id}'...");
|
||||
service.RemoveBackupJob(session, Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Backup
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Updates a scheduled backup job on Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Modifies properties of an existing backup job. Only specified parameters are
|
||||
/// updated; omitted parameters retain their current values.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Set, "PveBackupJob", SupportsShouldProcess = true)]
|
||||
public sealed class SetPveBackupJobCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="description">The backup job ID to update.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The backup job ID.")]
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated cron-style schedule expression.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Cron-style schedule expression (e.g., '0 2 * * *').")]
|
||||
public string? Schedule { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated target storage for backups.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The target storage for backups.")]
|
||||
public string? Storage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated backup mode (snapshot, suspend, or stop).</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The backup mode.")]
|
||||
[ValidateSet("snapshot", "suspend", "stop")]
|
||||
public string? Mode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated comma-separated list of VM IDs.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Comma-separated list of VM IDs to back up.")]
|
||||
public string? VmId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">When specified, backs up all VMs.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Back up all VMs.")]
|
||||
public SwitchParameter All { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated compression algorithm.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The compression algorithm.")]
|
||||
[ValidateSet("zstd", "lzo", "gzip", "none")]
|
||||
public string? Compress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated node restriction.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node to restrict the backup job to.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated email address(es) for notifications.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Email address(es) for notifications.")]
|
||||
public string? MailTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated notification policy.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "When to send email notifications.")]
|
||||
[ValidateSet("always", "failure")]
|
||||
public string? MailNotification { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Enable or disable the backup job.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Enable or disable the backup job.")]
|
||||
public SwitchParameter Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated comment or description.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "A comment or description for the backup job.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para type="description">Updated prune-backups configuration string.</para>
|
||||
/// </summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Prune-backups configuration string.")]
|
||||
public string? PruneBackups { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"backup job '{Id}'", "Set-PveBackupJob"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new BackupService();
|
||||
|
||||
var config = new Dictionary<string, string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(Schedule)) config["schedule"] = Schedule!;
|
||||
if (!string.IsNullOrEmpty(Storage)) config["storage"] = Storage!;
|
||||
if (!string.IsNullOrEmpty(Mode)) config["mode"] = Mode!;
|
||||
if (!string.IsNullOrEmpty(VmId)) config["vmid"] = VmId!;
|
||||
if (All.IsPresent) config["all"] = "1";
|
||||
if (!string.IsNullOrEmpty(Compress)) config["compress"] = Compress!;
|
||||
if (!string.IsNullOrEmpty(Node)) config["node"] = Node!;
|
||||
if (!string.IsNullOrEmpty(MailTo)) config["mailto"] = MailTo!;
|
||||
if (!string.IsNullOrEmpty(MailNotification)) config["mailnotification"] = MailNotification!;
|
||||
if (MyInvocation.BoundParameters.ContainsKey("Enabled")) config["enabled"] = Enabled.IsPresent ? "1" : "0";
|
||||
if (!string.IsNullOrEmpty(Comment)) config["comment"] = Comment!;
|
||||
if (!string.IsNullOrEmpty(PruneBackups)) config["prune-backups"] = PruneBackups!;
|
||||
|
||||
WriteVerbose($"Updating backup job '{Id}'...");
|
||||
service.UpdateBackupJob(session, Id, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "PveFirewallAlias")]
|
||||
[OutputType(typeof(PveFirewallAlias))]
|
||||
public class GetPveFirewallAliasCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Optional alias name to filter by.")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Getting firewall aliases at level '{level}'...");
|
||||
var aliases = service.GetAliases(session, level, Node, vmid);
|
||||
|
||||
if (!string.IsNullOrEmpty(Name))
|
||||
{
|
||||
var filtered = aliases.Where(a => string.Equals(a.Name, Name, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
WriteObject(filtered, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteObject(aliases, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "PveFirewallGroup")]
|
||||
[OutputType(typeof(PveFirewallGroup), typeof(PveFirewallRule))]
|
||||
public class GetPveFirewallGroupCmdlet : PveCmdletBase
|
||||
{
|
||||
[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()
|
||||
{
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
|
||||
if (!string.IsNullOrEmpty(Group))
|
||||
{
|
||||
WriteVerbose($"Getting firewall rules for security group '{Group}'...");
|
||||
var rules = service.GetGroupRules(session, Group!);
|
||||
WriteObject(rules, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteVerbose("Getting firewall security groups...");
|
||||
var groups = service.GetGroups(session);
|
||||
WriteObject(groups, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "PveFirewallIpSet")]
|
||||
[OutputType(typeof(PveFirewallIpSet))]
|
||||
public class GetPveFirewallIpSetCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Optional IP set name to filter by.")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Getting firewall IP sets at level '{level}'...");
|
||||
var ipSets = service.GetIpSets(session, level, Node, vmid);
|
||||
|
||||
if (!string.IsNullOrEmpty(Name))
|
||||
{
|
||||
var filtered = ipSets.Where(s => string.Equals(s.Name, Name, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
WriteObject(filtered, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteObject(ipSets, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "PveFirewallIpSetEntry")]
|
||||
[OutputType(typeof(PveFirewallIpSetEntry))]
|
||||
public class GetPveFirewallIpSetEntryCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The IP set name.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Getting entries for IP set '{Name}' at level '{level}'...");
|
||||
var entries = service.GetIpSetEntries(session, level, Name, Node, vmid);
|
||||
WriteObject(entries, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "PveFirewallOptions")]
|
||||
[OutputType(typeof(PveFirewallOptions))]
|
||||
public class GetPveFirewallOptionsCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Getting firewall options at level '{level}'...");
|
||||
var options = service.GetOptions(session, level, Node, vmid);
|
||||
WriteObject(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "PveFirewallRef")]
|
||||
[OutputType(typeof(PveFirewallRef))]
|
||||
public class GetPveFirewallRefCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Optional type filter for references.")]
|
||||
public string? Type { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Getting firewall references at level '{level}'...");
|
||||
var refs = service.GetRefs(session, level, Type, Node, vmid);
|
||||
WriteObject(refs, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Get, "PveFirewallRule")]
|
||||
[OutputType(typeof(PveFirewallRule))]
|
||||
public class GetPveFirewallRuleCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Optional rule position to filter by.")]
|
||||
public int? Position { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Getting firewall rules at level '{level}'...");
|
||||
var rules = service.GetRules(session, level, Node, vmid);
|
||||
|
||||
if (Position.HasValue)
|
||||
{
|
||||
var filtered = rules.Where(r => r.Pos == Position.Value).ToArray();
|
||||
WriteObject(filtered, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteObject(rules, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.New, "PveFirewallAlias", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(void))]
|
||||
public class NewPveFirewallAliasCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The alias name.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The CIDR network address.")]
|
||||
public string Cidr { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Optional comment for the alias.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall alias '{Name}' ({Level})", "Create"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Creating firewall alias '{Name}' at level '{level}'...");
|
||||
service.CreateAlias(session, level, Name, Cidr, Comment, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.New, "PveFirewallGroup", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(void))]
|
||||
public class NewPveFirewallGroupCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The security group name.")]
|
||||
public string Group { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Optional comment for the security group.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"firewall security group '{Group}'", "Create"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
|
||||
WriteVerbose($"Creating firewall security group '{Group}'...");
|
||||
service.CreateGroup(session, Group, Comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.New, "PveFirewallIpSet", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(void))]
|
||||
public class NewPveFirewallIpSetCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The IP set name.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Optional comment for the IP set.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall IP set '{Name}' ({Level})", "Create"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Creating firewall IP set '{Name}' at level '{level}'...");
|
||||
service.CreateIpSet(session, level, Name, Comment, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.New, "PveFirewallIpSetEntry", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(void))]
|
||||
public class NewPveFirewallIpSetEntryCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The IP set name.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The CIDR network address to add.")]
|
||||
public string Cidr { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Exclude this entry from the IP set match.")]
|
||||
public SwitchParameter NoMatch { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Optional comment for the entry.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"IP set entry '{Cidr}' in '{Name}' ({Level})", "Create"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Adding entry '{Cidr}' to IP set '{Name}' at level '{level}'...");
|
||||
service.AddIpSetEntry(session, level, Name, Cidr, NoMatch.IsPresent, Comment, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.New, "PveFirewallRule", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(PveFirewallRule))]
|
||||
public class NewPveFirewallRuleCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The rule type: in, out, or group.")]
|
||||
[ValidateSet("in", "out", "group")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The rule action: ACCEPT, DROP, or REJECT.")]
|
||||
[ValidateSet("ACCEPT", "DROP", "REJECT")]
|
||||
public string Action { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Enable the rule.")]
|
||||
public SwitchParameter Enable { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Source address or alias.")]
|
||||
public string? Source { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Destination address or alias.")]
|
||||
public string? Dest { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Protocol (e.g., tcp, udp, icmp).")]
|
||||
public string? Proto { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Destination port.")]
|
||||
public string? Dport { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Source port.")]
|
||||
public string? Sport { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Rule comment.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Macro name.")]
|
||||
public string? Macro { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Log level.")]
|
||||
public string? Log { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Network interface.")]
|
||||
public string? Iface { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall rule ({Level})", "Create"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
var config = new Dictionary<string, string>
|
||||
{
|
||||
["type"] = Type,
|
||||
["action"] = Action
|
||||
};
|
||||
|
||||
if (Enable.IsPresent)
|
||||
config["enable"] = "1";
|
||||
if (!string.IsNullOrEmpty(Source))
|
||||
config["source"] = Source!;
|
||||
if (!string.IsNullOrEmpty(Dest))
|
||||
config["dest"] = Dest!;
|
||||
if (!string.IsNullOrEmpty(Proto))
|
||||
config["proto"] = Proto!;
|
||||
if (!string.IsNullOrEmpty(Dport))
|
||||
config["dport"] = Dport!;
|
||||
if (!string.IsNullOrEmpty(Sport))
|
||||
config["sport"] = Sport!;
|
||||
if (!string.IsNullOrEmpty(Comment))
|
||||
config["comment"] = Comment!;
|
||||
if (!string.IsNullOrEmpty(Macro))
|
||||
config["macro"] = Macro!;
|
||||
if (!string.IsNullOrEmpty(Log))
|
||||
config["log"] = Log!;
|
||||
if (!string.IsNullOrEmpty(Iface))
|
||||
config["iface"] = Iface!;
|
||||
|
||||
WriteVerbose($"Creating firewall rule at level '{level}'...");
|
||||
service.CreateRule(session, level, config, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Remove, "PveFirewallAlias", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
[OutputType(typeof(void))]
|
||||
public class RemovePveFirewallAliasCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The alias name to remove.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall alias '{Name}' ({Level})", "Remove"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Removing firewall alias '{Name}' at level '{level}'...");
|
||||
service.RemoveAlias(session, level, Name, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Remove, "PveFirewallGroup", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
[OutputType(typeof(void))]
|
||||
public class RemovePveFirewallGroupCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The security group name to remove.")]
|
||||
public string Group { get; set; } = string.Empty;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"firewall security group '{Group}'", "Remove"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
|
||||
WriteVerbose($"Removing firewall security group '{Group}'...");
|
||||
service.RemoveGroup(session, Group);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Remove, "PveFirewallIpSet", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
[OutputType(typeof(void))]
|
||||
public class RemovePveFirewallIpSetCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The IP set name to remove.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall IP set '{Name}' ({Level})", "Remove"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Removing firewall IP set '{Name}' at level '{level}'...");
|
||||
service.RemoveIpSet(session, level, Name, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Remove, "PveFirewallIpSetEntry", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
[OutputType(typeof(void))]
|
||||
public class RemovePveFirewallIpSetEntryCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The IP set name.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The CIDR network address to remove.")]
|
||||
public string Cidr { get; set; } = string.Empty;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"IP set entry '{Cidr}' from '{Name}' ({Level})", "Remove"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Removing entry '{Cidr}' from IP set '{Name}' at level '{level}'...");
|
||||
service.RemoveIpSetEntry(session, level, Name, Cidr, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Remove, "PveFirewallRule", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
[OutputType(typeof(void))]
|
||||
public class RemovePveFirewallRuleCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The rule position to remove.")]
|
||||
public int Position { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall rule at position {Position} ({Level})", "Remove"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Removing firewall rule at position {Position} ({level})...");
|
||||
service.RemoveRule(session, level, Position, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Set, "PveFirewallAlias", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(void))]
|
||||
public class SetPveFirewallAliasCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The alias name to update.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The updated CIDR network address.")]
|
||||
public string? Cidr { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Updated comment for the alias.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall alias '{Name}' ({Level})", "Update"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
WriteVerbose($"Updating firewall alias '{Name}' at level '{level}'...");
|
||||
service.UpdateAlias(session, level, Name, Cidr, Comment, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Set, "PveFirewallIpSetEntry", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(void))]
|
||||
public class SetPveFirewallIpSetEntryCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The IP set name.")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The CIDR network address to update.")]
|
||||
public string Cidr { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Exclude this entry from the IP set match.")]
|
||||
public SwitchParameter NoMatch { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Updated comment for the entry.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"IP set entry '{Cidr}' in '{Name}' ({Level})", "Update"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
bool? nomatch = NoMatch.IsPresent ? true : (bool?)null;
|
||||
|
||||
WriteVerbose($"Updating entry '{Cidr}' in IP set '{Name}' at level '{level}'...");
|
||||
service.UpdateIpSetEntry(session, level, Name, Cidr, nomatch, Comment, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Set, "PveFirewallOptions", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(void))]
|
||||
public class SetPveFirewallOptionsCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Enable or disable the firewall.")]
|
||||
public SwitchParameter Enable { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Default policy for incoming traffic: ACCEPT, DROP, or REJECT.")]
|
||||
[ValidateSet("ACCEPT", "DROP", "REJECT")]
|
||||
public string? PolicyIn { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Default policy for outgoing traffic: ACCEPT, DROP, or REJECT.")]
|
||||
[ValidateSet("ACCEPT", "DROP", "REJECT")]
|
||||
public string? PolicyOut { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Log level for incoming traffic.")]
|
||||
public string? LogLevelIn { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Log level for outgoing traffic.")]
|
||||
public string? LogLevelOut { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Enable DHCP.")]
|
||||
public SwitchParameter Dhcp { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Enable NDP.")]
|
||||
public SwitchParameter Ndp { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Enable MAC address filter.")]
|
||||
public SwitchParameter MacFilter { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Enable IP filter.")]
|
||||
public SwitchParameter IpFilter { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall options ({Level})", "Update"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
var config = new Dictionary<string, string>();
|
||||
|
||||
if (Enable.IsPresent)
|
||||
config["enable"] = "1";
|
||||
if (!string.IsNullOrEmpty(PolicyIn))
|
||||
config["policy_in"] = PolicyIn!;
|
||||
if (!string.IsNullOrEmpty(PolicyOut))
|
||||
config["policy_out"] = PolicyOut!;
|
||||
if (!string.IsNullOrEmpty(LogLevelIn))
|
||||
config["log_level_in"] = LogLevelIn!;
|
||||
if (!string.IsNullOrEmpty(LogLevelOut))
|
||||
config["log_level_out"] = LogLevelOut!;
|
||||
if (Dhcp.IsPresent)
|
||||
config["dhcp"] = "1";
|
||||
if (Ndp.IsPresent)
|
||||
config["ndp"] = "1";
|
||||
if (MacFilter.IsPresent)
|
||||
config["macfilter"] = "1";
|
||||
if (IpFilter.IsPresent)
|
||||
config["ipfilter"] = "1";
|
||||
|
||||
WriteVerbose($"Setting firewall options at level '{level}'...");
|
||||
service.SetOptions(session, level, config, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Firewall;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Firewall
|
||||
{
|
||||
[Cmdlet(VerbsCommon.Set, "PveFirewallRule", SupportsShouldProcess = true)]
|
||||
[OutputType(typeof(void))]
|
||||
public class SetPveFirewallRuleCmdlet : PveCmdletBase
|
||||
{
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The firewall level: Cluster, Node, Vm, or Container.")]
|
||||
[ValidateSet("Cluster", "Node", "Vm", "Container")]
|
||||
public string Level { get; set; } = string.Empty;
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node name. Required when Level is Node, Vm, or Container.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The VM/Container ID. Required when Level is Vm or Container.")]
|
||||
[ValidateRange(100, 999999999)]
|
||||
public int VmId { get; set; }
|
||||
|
||||
[Parameter(Mandatory = true, HelpMessage = "The rule position to update.")]
|
||||
public int Position { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The rule type: in, out, or group.")]
|
||||
[ValidateSet("in", "out", "group")]
|
||||
public string? Type { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "The rule action: ACCEPT, DROP, or REJECT.")]
|
||||
[ValidateSet("ACCEPT", "DROP", "REJECT")]
|
||||
public string? Action { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Enable the rule.")]
|
||||
public SwitchParameter Enable { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Source address or alias.")]
|
||||
public string? Source { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Destination address or alias.")]
|
||||
public string? Dest { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Protocol (e.g., tcp, udp, icmp).")]
|
||||
public string? Proto { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Destination port.")]
|
||||
public string? Dport { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Source port.")]
|
||||
public string? Sport { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Rule comment.")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Macro name.")]
|
||||
public string? Macro { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Log level.")]
|
||||
public string? Log { get; set; }
|
||||
|
||||
[Parameter(Mandatory = false, HelpMessage = "Network interface.")]
|
||||
public string? Iface { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var level = Level;
|
||||
if (!string.Equals(level, "Cluster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Node))
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("Node is required when Level is not Cluster."),
|
||||
"NodeRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (string.Equals(level, "Vm", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(level, "Container", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (VmId == 0)
|
||||
{
|
||||
ThrowTerminatingError(new ErrorRecord(
|
||||
new PSArgumentException("VmId is required when Level is Vm or Container."),
|
||||
"VmIdRequired", ErrorCategory.InvalidArgument, null));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ShouldProcess($"firewall rule at position {Position} ({Level})", "Update"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new FirewallService();
|
||||
int? vmid = VmId > 0 ? VmId : (int?)null;
|
||||
|
||||
var config = new Dictionary<string, string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(Type))
|
||||
config["type"] = Type!;
|
||||
if (!string.IsNullOrEmpty(Action))
|
||||
config["action"] = Action!;
|
||||
if (Enable.IsPresent)
|
||||
config["enable"] = "1";
|
||||
if (!string.IsNullOrEmpty(Source))
|
||||
config["source"] = Source!;
|
||||
if (!string.IsNullOrEmpty(Dest))
|
||||
config["dest"] = Dest!;
|
||||
if (!string.IsNullOrEmpty(Proto))
|
||||
config["proto"] = Proto!;
|
||||
if (!string.IsNullOrEmpty(Dport))
|
||||
config["dport"] = Dport!;
|
||||
if (!string.IsNullOrEmpty(Sport))
|
||||
config["sport"] = Sport!;
|
||||
if (!string.IsNullOrEmpty(Comment))
|
||||
config["comment"] = Comment!;
|
||||
if (!string.IsNullOrEmpty(Macro))
|
||||
config["macro"] = Macro!;
|
||||
if (!string.IsNullOrEmpty(Log))
|
||||
config["log"] = Log!;
|
||||
if (!string.IsNullOrEmpty(Iface))
|
||||
config["iface"] = Iface!;
|
||||
|
||||
WriteVerbose($"Updating firewall rule at position {Position} ({level})...");
|
||||
service.UpdateRule(session, level, Position, config, Node, vmid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Network;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Lists SDN controllers in Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Returns Software-Defined Networking controller definitions.
|
||||
/// Optionally filters by controller identifier.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Get, "PveSdnController")]
|
||||
[OutputType(typeof(PveSdnController))]
|
||||
public class GetPveSdnControllerCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>Optional controller identifier filter.</summary>
|
||||
[Parameter(Mandatory = false, Position = 0, HelpMessage = "Filter by controller identifier.")]
|
||||
public string? Controller { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose("Getting SDN controllers...");
|
||||
var controllers = service.GetSdnControllers(session);
|
||||
|
||||
foreach (var item in controllers)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Controller) &&
|
||||
!string.Equals(item.Controller, Controller, System.StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
WriteObject(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Network;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Lists SDN DNS plugins in Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Returns Software-Defined Networking DNS plugin definitions.
|
||||
/// Optionally filters by DNS identifier.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Get, "PveSdnDns")]
|
||||
[OutputType(typeof(PveSdnDns))]
|
||||
public class GetPveSdnDnsCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>Optional DNS plugin identifier filter.</summary>
|
||||
[Parameter(Mandatory = false, Position = 0, HelpMessage = "Filter by DNS plugin identifier.")]
|
||||
public string? Dns { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose("Getting SDN DNS plugins...");
|
||||
var plugins = service.GetSdnDnsPlugins(session);
|
||||
|
||||
foreach (var item in plugins)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Dns) &&
|
||||
!string.Equals(item.Dns, Dns, System.StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
WriteObject(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Models.Network;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Lists SDN IPAM plugins in Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Returns Software-Defined Networking IPAM plugin definitions.
|
||||
/// Optionally filters by IPAM identifier.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Get, "PveSdnIpam")]
|
||||
[OutputType(typeof(PveSdnIpam))]
|
||||
public class GetPveSdnIpamCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>Optional IPAM identifier filter.</summary>
|
||||
[Parameter(Mandatory = false, Position = 0, HelpMessage = "Filter by IPAM plugin identifier.")]
|
||||
public string? Ipam { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose("Getting SDN IPAM plugins...");
|
||||
var ipams = service.GetSdnIpams(session);
|
||||
|
||||
foreach (var item in ipams)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Ipam) &&
|
||||
!string.Equals(item.Ipam, Ipam, System.StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
WriteObject(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Creates a new SDN controller in Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Adds a new Software-Defined Networking controller.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.New, "PveSdnController", SupportsShouldProcess = true)]
|
||||
public class NewPveSdnControllerCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>The controller identifier.</summary>
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The controller identifier.")]
|
||||
public string Controller { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The controller type.</summary>
|
||||
[Parameter(Mandatory = true, Position = 1, HelpMessage = "The controller type.")]
|
||||
[ValidateSet("evpn", "bgp")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The Autonomous System Number for BGP/EVPN.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The Autonomous System Number for BGP/EVPN.")]
|
||||
public int? Asn { get; set; }
|
||||
|
||||
/// <summary>Comma-separated list of BGP peer addresses.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "Comma-separated list of BGP peer addresses.")]
|
||||
public string? Peers { get; set; }
|
||||
|
||||
/// <summary>The node this controller is configured on.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The node this controller is configured on.")]
|
||||
public string? Node { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"SDN Controller '{Controller}'", "Create PVE SDN Controller"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose($"Creating SDN controller '{Controller}' of type '{Type}'...");
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
["controller"] = Controller,
|
||||
["type"] = Type
|
||||
};
|
||||
|
||||
if (Asn.HasValue) data["asn"] = Asn.Value.ToString();
|
||||
if (!string.IsNullOrEmpty(Peers)) data["peers"] = Peers!;
|
||||
if (!string.IsNullOrEmpty(Node)) data["node"] = Node!;
|
||||
|
||||
service.CreateSdnController(session, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Creates a new SDN DNS plugin in Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Adds a new Software-Defined Networking DNS plugin.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.New, "PveSdnDns", SupportsShouldProcess = true)]
|
||||
public class NewPveSdnDnsCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>The DNS plugin identifier.</summary>
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The DNS plugin identifier.")]
|
||||
public string Dns { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The DNS plugin type.</summary>
|
||||
[Parameter(Mandatory = true, Position = 1, HelpMessage = "The DNS plugin type.")]
|
||||
[ValidateSet("powerdns")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The URL of the DNS service API.</summary>
|
||||
[Parameter(Mandatory = true, HelpMessage = "The URL of the DNS service API.")]
|
||||
public string Url { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The API key for the DNS service.</summary>
|
||||
[Parameter(Mandatory = true, HelpMessage = "The API key for the DNS service.")]
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The IPv6 reverse zone mask length.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The IPv6 reverse zone mask length.")]
|
||||
public int? ReverseMaskV6 { get; set; }
|
||||
|
||||
/// <summary>The TTL (time-to-live) for DNS records.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The TTL (time-to-live) for DNS records.")]
|
||||
public int? Ttl { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"SDN DNS '{Dns}'", "Create PVE SDN DNS"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose($"Creating SDN DNS plugin '{Dns}' of type '{Type}'...");
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
["dns"] = Dns,
|
||||
["type"] = Type,
|
||||
["url"] = Url,
|
||||
["key"] = Key
|
||||
};
|
||||
|
||||
if (ReverseMaskV6.HasValue) data["reversemaskv6"] = ReverseMaskV6.Value.ToString();
|
||||
if (Ttl.HasValue) data["ttl"] = Ttl.Value.ToString();
|
||||
|
||||
service.CreateSdnDnsPlugin(session, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Creates a new SDN IPAM plugin in Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Adds a new Software-Defined Networking IPAM plugin.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.New, "PveSdnIpam", SupportsShouldProcess = true)]
|
||||
public class NewPveSdnIpamCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>The IPAM plugin identifier.</summary>
|
||||
[Parameter(Mandatory = true, Position = 0, HelpMessage = "The IPAM plugin identifier.")]
|
||||
public string Ipam { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The IPAM plugin type.</summary>
|
||||
[Parameter(Mandatory = true, Position = 1, HelpMessage = "The IPAM plugin type.")]
|
||||
[ValidateSet("pve", "netbox", "phpipam")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The URL of the external IPAM service.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The URL of the external IPAM service.")]
|
||||
public string? Url { get; set; }
|
||||
|
||||
/// <summary>The API token for the external IPAM service.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The API token for the external IPAM service.")]
|
||||
public string? Token { get; set; }
|
||||
|
||||
/// <summary>The configuration section identifier.</summary>
|
||||
[Parameter(Mandatory = false, HelpMessage = "The configuration section identifier.")]
|
||||
public int? Section { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"SDN IPAM '{Ipam}'", "Create PVE SDN IPAM"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose($"Creating SDN IPAM plugin '{Ipam}' of type '{Type}'...");
|
||||
var data = new Dictionary<string, string>
|
||||
{
|
||||
["ipam"] = Ipam,
|
||||
["type"] = Type
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(Url)) data["url"] = Url!;
|
||||
if (!string.IsNullOrEmpty(Token)) data["token"] = Token!;
|
||||
if (Section.HasValue) data["section"] = Section.Value.ToString();
|
||||
|
||||
service.CreateSdnIpam(session, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Removes an SDN controller from Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Deletes the specified Software-Defined Networking controller.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Remove, "PveSdnController", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
public class RemovePveSdnControllerCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>The controller identifier to remove.</summary>
|
||||
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The controller identifier to remove.")]
|
||||
public string Controller { get; set; } = string.Empty;
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
if (!ShouldProcess($"SDN Controller '{Controller}'", "Remove PVE SDN Controller"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose($"Removing SDN controller '{Controller}'...");
|
||||
service.RemoveSdnController(session, Controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Removes an SDN DNS plugin from Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Deletes the specified Software-Defined Networking DNS plugin.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Remove, "PveSdnDns", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
public class RemovePveSdnDnsCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>The DNS plugin identifier to remove.</summary>
|
||||
[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()
|
||||
{
|
||||
if (!ShouldProcess($"SDN DNS '{Dns}'", "Remove PVE SDN DNS"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose($"Removing SDN DNS plugin '{Dns}'...");
|
||||
service.RemoveSdnDnsPlugin(session, Dns);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.Management.Automation;
|
||||
using PSProxmoxVE.Core.Services;
|
||||
|
||||
namespace PSProxmoxVE.Cmdlets.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// <para type="synopsis">Removes an SDN IPAM plugin from Proxmox VE.</para>
|
||||
/// <para type="description">
|
||||
/// Deletes the specified Software-Defined Networking IPAM plugin.
|
||||
/// Requires Proxmox VE 8.0 or later.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Remove, "PveSdnIpam", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
public class RemovePveSdnIpamCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>The IPAM plugin identifier to remove.</summary>
|
||||
[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()
|
||||
{
|
||||
if (!ShouldProcess($"SDN IPAM '{Ipam}'", "Remove PVE SDN IPAM"))
|
||||
return;
|
||||
|
||||
var session = GetSession();
|
||||
var service = new NetworkService();
|
||||
|
||||
WriteVerbose($"Removing SDN IPAM plugin '{Ipam}'...");
|
||||
service.RemoveSdnIpam(session, Ipam);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace PSProxmoxVE.Cmdlets.Users
|
||||
/// Built-in roles cannot be removed.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Remove, "PveRole", SupportsShouldProcess = true)]
|
||||
[Cmdlet(VerbsCommon.Remove, "PveRole", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.High)]
|
||||
public class RemovePveRoleCmdlet : PveCmdletBase
|
||||
{
|
||||
/// <summary>The role identifier to remove.</summary>
|
||||
|
||||
@@ -442,5 +442,507 @@
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Firewall.PveFirewallRule -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Firewall.PveFirewallRule</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Firewall.PveFirewallRule</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Pos</Label>
|
||||
<Width>5</Width>
|
||||
<Alignment>Right</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Type</Label>
|
||||
<Width>6</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Action</Label>
|
||||
<Width>8</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Proto</Label>
|
||||
<Width>6</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Dport</Label>
|
||||
<Width>12</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Source</Label>
|
||||
<Width>20</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Dest</Label>
|
||||
<Width>20</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Enable</Label>
|
||||
<Width>7</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Comment</Label>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Pos</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Type</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Action</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Proto</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Dport</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Source</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Dest</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<ScriptBlock>if ($_.Enable -eq 1) { 'Yes' } else { 'No' }</ScriptBlock>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Comment</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Firewall.PveFirewallGroup -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Firewall.PveFirewallGroup</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Firewall.PveFirewallGroup</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Group</Label>
|
||||
<Width>25</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Comment</Label>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Group</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Comment</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Firewall.PveFirewallAlias -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Firewall.PveFirewallAlias</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Firewall.PveFirewallAlias</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Name</Label>
|
||||
<Width>25</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Cidr</Label>
|
||||
<Width>25</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Comment</Label>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Name</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Cidr</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Comment</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Firewall.PveFirewallIpSet -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Firewall.PveFirewallIpSet</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Firewall.PveFirewallIpSet</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Name</Label>
|
||||
<Width>25</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Comment</Label>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Name</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Comment</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Firewall.PveFirewallIpSetEntry -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Firewall.PveFirewallIpSetEntry</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Firewall.PveFirewallIpSetEntry</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Cidr</Label>
|
||||
<Width>25</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>NoMatch</Label>
|
||||
<Width>8</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Comment</Label>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Cidr</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<ScriptBlock>if ($_.NoMatch -eq 1) { 'Yes' } else { 'No' }</ScriptBlock>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Comment</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Firewall.PveFirewallOptions -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Firewall.PveFirewallOptions</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Firewall.PveFirewallOptions</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Enable</Label>
|
||||
<Width>8</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>PolicyIn</Label>
|
||||
<Width>10</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>PolicyOut</Label>
|
||||
<Width>10</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>LogLevelIn</Label>
|
||||
<Width>12</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>LogLevelOut</Label>
|
||||
<Width>12</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<ScriptBlock>if ($_.Enable -eq 1) { 'Yes' } else { 'No' }</ScriptBlock>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>PolicyIn</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>PolicyOut</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>LogLevelIn</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>LogLevelOut</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Backup.PveBackupJob -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Backup.PveBackupJob</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Backup.PveBackupJob</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Id</Label>
|
||||
<Width>20</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Enabled</Label>
|
||||
<Width>8</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Schedule</Label>
|
||||
<Width>16</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Storage</Label>
|
||||
<Width>15</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Mode</Label>
|
||||
<Width>10</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>VmId</Label>
|
||||
<Width>15</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Comment</Label>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Id</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<ScriptBlock>if ($_.Enabled -eq 1) { 'Yes' } else { 'No' }</ScriptBlock>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Schedule</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Storage</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Mode</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>VmId</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Comment</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Network.PveSdnIpam -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Network.PveSdnIpam</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Network.PveSdnIpam</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Ipam</Label>
|
||||
<Width>20</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Type</Label>
|
||||
<Width>15</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Ipam</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Type</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Network.PveSdnDns -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Network.PveSdnDns</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Network.PveSdnDns</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Dns</Label>
|
||||
<Width>20</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Type</Label>
|
||||
<Width>15</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Url</Label>
|
||||
<Width>40</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Dns</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Type</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Url</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
<!-- PSProxmoxVE.Core.Models.Network.PveSdnController -->
|
||||
<View>
|
||||
<Name>PSProxmoxVE.Core.Models.Network.PveSdnController</Name>
|
||||
<ViewSelectedBy>
|
||||
<TypeName>PSProxmoxVE.Core.Models.Network.PveSdnController</TypeName>
|
||||
</ViewSelectedBy>
|
||||
<TableControl>
|
||||
<TableHeaders>
|
||||
<TableColumnHeader>
|
||||
<Label>Controller</Label>
|
||||
<Width>20</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Type</Label>
|
||||
<Width>10</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Asn</Label>
|
||||
<Width>10</Width>
|
||||
<Alignment>Right</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Peers</Label>
|
||||
<Width>30</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
<TableColumnHeader>
|
||||
<Label>Node</Label>
|
||||
<Width>15</Width>
|
||||
<Alignment>Left</Alignment>
|
||||
</TableColumnHeader>
|
||||
</TableHeaders>
|
||||
<TableRowEntries>
|
||||
<TableRowEntry>
|
||||
<TableColumnItems>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Controller</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Type</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Asn</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Peers</PropertyName>
|
||||
</TableColumnItem>
|
||||
<TableColumnItem>
|
||||
<PropertyName>Node</PropertyName>
|
||||
</TableColumnItem>
|
||||
</TableColumnItems>
|
||||
</TableRowEntry>
|
||||
</TableRowEntries>
|
||||
</TableControl>
|
||||
</View>
|
||||
|
||||
</ViewDefinitions>
|
||||
</Configuration>
|
||||
|
||||
@@ -170,7 +170,52 @@
|
||||
|
||||
# Tasks
|
||||
'Get-PveTask',
|
||||
'Wait-PveTask'
|
||||
'Wait-PveTask',
|
||||
|
||||
# Firewall
|
||||
'Get-PveFirewallRule',
|
||||
'New-PveFirewallRule',
|
||||
'Set-PveFirewallRule',
|
||||
'Remove-PveFirewallRule',
|
||||
'Get-PveFirewallGroup',
|
||||
'New-PveFirewallGroup',
|
||||
'Remove-PveFirewallGroup',
|
||||
'Get-PveFirewallAlias',
|
||||
'New-PveFirewallAlias',
|
||||
'Set-PveFirewallAlias',
|
||||
'Remove-PveFirewallAlias',
|
||||
'Get-PveFirewallIpSet',
|
||||
'New-PveFirewallIpSet',
|
||||
'Remove-PveFirewallIpSet',
|
||||
'Get-PveFirewallIpSetEntry',
|
||||
'New-PveFirewallIpSetEntry',
|
||||
'Set-PveFirewallIpSetEntry',
|
||||
'Remove-PveFirewallIpSetEntry',
|
||||
'Get-PveFirewallOptions',
|
||||
'Set-PveFirewallOptions',
|
||||
'Get-PveFirewallRef',
|
||||
|
||||
# Backup
|
||||
'New-PveBackup',
|
||||
'Get-PveBackupJob',
|
||||
'New-PveBackupJob',
|
||||
'Set-PveBackupJob',
|
||||
'Remove-PveBackupJob',
|
||||
|
||||
# SDN — IPAM
|
||||
'Get-PveSdnIpam',
|
||||
'New-PveSdnIpam',
|
||||
'Remove-PveSdnIpam',
|
||||
|
||||
# SDN — DNS
|
||||
'Get-PveSdnDns',
|
||||
'New-PveSdnDns',
|
||||
'Remove-PveSdnDns',
|
||||
|
||||
# SDN — Controller
|
||||
'Get-PveSdnController',
|
||||
'New-PveSdnController',
|
||||
'Remove-PveSdnController'
|
||||
)
|
||||
|
||||
# Variables to export from this module
|
||||
|
||||
Reference in New Issue
Block a user