From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pdm-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 246DD1FF164 for <inbox@lore.proxmox.com>; Fri, 28 Feb 2025 16:19:12 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 377321230F; Fri, 28 Feb 2025 16:19:09 +0100 (CET) From: Stefan Hanreich <s.hanreich@proxmox.com> To: pdm-devel@lists.proxmox.com Date: Fri, 28 Feb 2025 16:17:39 +0100 Message-Id: <20250228151803.158984-3-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250228151803.158984-1-s.hanreich@proxmox.com> References: <20250228151803.158984-1-s.hanreich@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.374 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pdm-devel] [PATCH proxmox-api-types 02/12] sdn: generate zones endpoints X-BeenThere: pdm-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Datacenter Manager development discussion <pdm-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pdm-devel>, <mailto:pdm-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pdm-devel/> List-Post: <mailto:pdm-devel@lists.proxmox.com> List-Help: <mailto:pdm-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel>, <mailto:pdm-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox Datacenter Manager development discussion <pdm-devel@lists.proxmox.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pdm-devel-bounces@lists.proxmox.com Sender: "pdm-devel" <pdm-devel-bounces@lists.proxmox.com> Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com> --- pve-api-types/src/generated/code.rs | 39 +- pve-api-types/src/generated/types.rs | 789 +++++++++++++++++++++++++++ 2 files changed, 827 insertions(+), 1 deletion(-) diff --git a/pve-api-types/src/generated/code.rs b/pve-api-types/src/generated/code.rs index 401d3bc..c1f0e74 100644 --- a/pve-api-types/src/generated/code.rs +++ b/pve-api-types/src/generated/code.rs @@ -116,6 +116,8 @@ /// - /cluster/sdn/ipams /// - /cluster/sdn/ipams/{ipam} /// - /cluster/sdn/ipams/{ipam}/status +/// - /cluster/sdn/lock +/// - /cluster/sdn/rollback /// - /cluster/sdn/vnets /// - /cluster/sdn/vnets/{vnet} /// - /cluster/sdn/vnets/{vnet}/firewall @@ -125,7 +127,6 @@ /// - /cluster/sdn/vnets/{vnet}/ips /// - /cluster/sdn/vnets/{vnet}/subnets /// - /cluster/sdn/vnets/{vnet}/subnets/{subnet} -/// - /cluster/sdn/zones /// - /cluster/sdn/zones/{zone} /// - /cluster/tasks /// - /nodes/{node} @@ -410,6 +411,11 @@ pub trait PveClient { Err(Error::Other("create_token not implemented")) } + /// Create a new sdn zone object. + async fn create_zone(&self, params: CreateZone) -> Result<(), Error> { + Err(Error::Other("create_zone not implemented")) + } + /// Read subscription info. async fn get_subscription(&self, node: &str) -> Result<NodeSubscriptionInfo, Error> { Err(Error::Other("get_subscription not implemented")) @@ -483,6 +489,16 @@ pub trait PveClient { Err(Error::Other("list_storages not implemented")) } + /// SDN zones index. + async fn list_zones( + &self, + pending: Option<bool>, + running: Option<bool>, + ty: Option<ListZonesType>, + ) -> Result<Vec<SdnZone>, Error> { + Err(Error::Other("list_zones not implemented")) + } + /// Get container configuration. async fn lxc_get_config( &self, @@ -710,6 +726,12 @@ where Ok(self.0.post(url, ¶ms).await?.expect_json()?.data) } + /// Create a new sdn zone object. + async fn create_zone(&self, params: CreateZone) -> Result<(), Error> { + let url = "/api2/extjs/cluster/sdn/zones"; + self.0.post(url, ¶ms).await?.nodata() + } + /// Read subscription info. async fn get_subscription(&self, node: &str) -> Result<NodeSubscriptionInfo, Error> { let url = &format!("/api2/extjs/nodes/{node}/subscription"); @@ -830,6 +852,21 @@ where Ok(self.0.get(url).await?.expect_json()?.data) } + /// SDN zones index. + async fn list_zones( + &self, + pending: Option<bool>, + running: Option<bool>, + ty: Option<ListZonesType>, + ) -> Result<Vec<SdnZone>, Error> { + let (mut query, mut sep) = (String::new(), '?'); + add_query_bool(&mut query, &mut sep, "pending", pending); + add_query_bool(&mut query, &mut sep, "running", running); + add_query_arg(&mut query, &mut sep, "type", &ty); + let url = &format!("/api2/extjs/cluster/sdn/zones{query}"); + Ok(self.0.get(url).await?.expect_json()?.data) + } + /// Get container configuration. async fn lxc_get_config( &self, diff --git a/pve-api-types/src/generated/types.rs b/pve-api-types/src/generated/types.rs index 5a656ba..408d8ca 100644 --- a/pve-api-types/src/generated/types.rs +++ b/pve-api-types/src/generated/types.rs @@ -930,6 +930,262 @@ pub struct CreateTokenResponseInfo { pub privsep: Option<bool>, } +const_regex! { + +CREATE_ZONE_EXITNODES_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##; +CREATE_ZONE_EXITNODES_PRIMARY_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##; +CREATE_ZONE_MAC_RE = r##"^(?i)[a-f0-9][02468ace](?::[a-f0-9]{2}){5}$"##; +CREATE_ZONE_NODES_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##; +CREATE_ZONE_RT_IMPORT_RE = r##"^(\d+):(\d+)$"##; +CREATE_ZONE_ZONE_RE = r##"^[a-z][a-z0-9]*[a-z0-9]$"##; + +} + +#[api( + properties: { + "advertise-subnets": { + default: false, + optional: true, + }, + bridge: { + optional: true, + type: String, + }, + "bridge-disable-mac-learning": { + default: false, + optional: true, + }, + controller: { + optional: true, + type: String, + }, + dhcp: { + optional: true, + type: SdnZoneDhcp, + }, + "disable-arp-nd-suppression": { + default: false, + optional: true, + }, + dns: { + optional: true, + type: String, + }, + dnszone: { + format: &ApiStringFormat::VerifyFn(verifiers::verify_dns_name), + optional: true, + type: String, + }, + "dp-id": { + optional: true, + type: Integer, + }, + exitnodes: { + format: &ApiStringFormat::Pattern(&CREATE_ZONE_EXITNODES_RE), + optional: true, + type: String, + }, + "exitnodes-local-routing": { + default: false, + optional: true, + }, + "exitnodes-primary": { + format: &ApiStringFormat::Pattern(&CREATE_ZONE_EXITNODES_PRIMARY_RE), + optional: true, + type: String, + }, + ipam: { + optional: true, + type: String, + }, + "lock-secret": { + optional: true, + type: String, + }, + mac: { + format: &ApiStringFormat::Pattern(&CREATE_ZONE_MAC_RE), + optional: true, + type: String, + }, + mtu: { + optional: true, + type: Integer, + }, + nodes: { + format: &ApiStringFormat::Pattern(&CREATE_ZONE_NODES_RE), + optional: true, + type: String, + }, + peers: { + format: &ApiStringFormat::VerifyFn(verifiers::verify_ip), + optional: true, + type: String, + }, + reversedns: { + optional: true, + type: String, + }, + "rt-import": { + format: &ApiStringFormat::Pattern(&CREATE_ZONE_RT_IMPORT_RE), + optional: true, + type: String, + }, + tag: { + minimum: 0, + optional: true, + type: Integer, + }, + type: { + type: ListZonesType, + }, + "vlan-protocol": { + optional: true, + type: NetworkInterfaceVlanProtocol, + }, + "vrf-vxlan": { + maximum: 16777215, + minimum: 1, + optional: true, + type: Integer, + }, + "vxlan-port": { + maximum: 65536, + minimum: 1, + optional: true, + type: Integer, + }, + zone: { + format: &ApiStringFormat::Pattern(&CREATE_ZONE_ZONE_RE), + type: String, + }, + }, +)] +/// Object. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CreateZone { + /// Advertise IP prefixes (Type-5 routes) instead of MAC/IP pairs (Type-2 + /// routes). + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "advertise-subnets")] + pub advertise_subnets: Option<bool>, + + /// the bridge for which VLANs should be managed + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bridge: Option<String>, + + /// Disable auto mac learning. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "bridge-disable-mac-learning")] + pub bridge_disable_mac_learning: Option<bool>, + + /// Controller for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub controller: Option<String>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dhcp: Option<SdnZoneDhcp>, + + /// Suppress IPv4 ARP && IPv6 Neighbour Discovery messages. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "disable-arp-nd-suppression")] + pub disable_arp_nd_suppression: Option<bool>, + + /// dns api server + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dns: Option<String>, + + /// dns domain zone ex: mydomain.com + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dnszone: Option<String>, + + /// Faucet dataplane id + #[serde(deserialize_with = "proxmox_login::parse::deserialize_i64")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "dp-id")] + pub dp_id: Option<i64>, + + /// List of cluster node names. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub exitnodes: Option<String>, + + /// Allow exitnodes to connect to EVPN guests. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "exitnodes-local-routing")] + pub exitnodes_local_routing: Option<bool>, + + /// Force traffic through this exitnode first. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "exitnodes-primary")] + pub exitnodes_primary: Option<String>, + + /// use a specific ipam + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ipam: Option<String>, + + /// the secret for unlocking the global SDN configuration + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "lock-secret")] + pub lock_secret: Option<String>, + + /// Anycast logical router mac address. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mac: Option<String>, + + /// MTU of the zone, will be used for the created VNet bridges. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_i64")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mtu: Option<i64>, + + /// List of cluster node names. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nodes: Option<String>, + + /// Comma-separated list of peers, that are part of the VXLAN zone. Usually + /// the IPs of the nodes. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub peers: Option<String>, + + /// reverse dns api server + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reversedns: Option<String>, + + /// List of Route Targets that should be imported into the VRF of the zone + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "rt-import")] + pub rt_import: Option<String>, + + /// Service-VLAN Tag (outer VLAN) + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u64")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option<u64>, + + #[serde(rename = "type")] + pub ty: ListZonesType, + + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vlan-protocol")] + pub vlan_protocol: Option<NetworkInterfaceVlanProtocol>, + + /// VNI for the zone VRF. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u32")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vrf-vxlan")] + pub vrf_vxlan: Option<u32>, + + /// UDP port that should be used for the VXLAN tunnel (default 4789). + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u32")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vxlan-port")] + pub vxlan_port: Option<u32>, + + /// The SDN zone object identifier. + pub zone: String, +} + #[api] /// A guest's run state. #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] @@ -1334,6 +1590,32 @@ pub enum ListTasksSource { serde_plain::derive_display_from_serialize!(ListTasksSource); serde_plain::derive_fromstr_from_deserialize!(ListTasksSource); +#[api] +/// Only list SDN zones of specific type +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum ListZonesType { + #[serde(rename = "evpn")] + /// evpn. + Evpn, + #[serde(rename = "faucet")] + /// faucet. + Faucet, + #[serde(rename = "qinq")] + /// qinq. + Qinq, + #[serde(rename = "simple")] + /// simple. + Simple, + #[serde(rename = "vlan")] + /// vlan. + Vlan, + #[serde(rename = "vxlan")] + /// vxlan. + Vxlan, +} +serde_plain::derive_display_from_serialize!(ListZonesType); +serde_plain::derive_fromstr_from_deserialize!(ListZonesType); + const_regex! { LXC_CONFIG_TAGS_RE = r##"^(?i)[a-z0-9_][a-z0-9_\-+.]*$"##; @@ -8828,6 +9110,513 @@ mod storage_info_content { } } +#[api] +/// The state of an SDN object. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum SdnObjectState { + #[serde(rename = "new")] + /// new. + New, + #[serde(rename = "deleted")] + /// deleted. + Deleted, + #[serde(rename = "changed")] + /// changed. + Changed, +} +serde_plain::derive_display_from_serialize!(SdnObjectState); +serde_plain::derive_fromstr_from_deserialize!(SdnObjectState); + +const_regex! { + +SDN_ZONE_EXITNODES_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##; +SDN_ZONE_EXITNODES_PRIMARY_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##; +SDN_ZONE_RT_IMPORT_RE = r##"^(\d+):(\d+)$"##; + +} + +#[api( + properties: { + "advertise-subnets": { + default: false, + optional: true, + }, + bridge: { + optional: true, + type: String, + }, + "bridge-disable-mac-learning": { + default: false, + optional: true, + }, + controller: { + optional: true, + type: String, + }, + dhcp: { + optional: true, + type: SdnZoneDhcp, + }, + digest: { + optional: true, + type: String, + }, + "disable-arp-nd-suppression": { + default: false, + optional: true, + }, + dns: { + optional: true, + type: String, + }, + dnszone: { + optional: true, + type: String, + }, + exitnodes: { + format: &ApiStringFormat::Pattern(&SDN_ZONE_EXITNODES_RE), + optional: true, + type: String, + }, + "exitnodes-local-routing": { + default: false, + optional: true, + }, + "exitnodes-primary": { + format: &ApiStringFormat::Pattern(&SDN_ZONE_EXITNODES_PRIMARY_RE), + optional: true, + type: String, + }, + ipam: { + optional: true, + type: String, + }, + mac: { + optional: true, + type: String, + }, + mtu: { + optional: true, + type: Integer, + }, + nodes: { + optional: true, + type: String, + }, + peers: { + format: &ApiStringFormat::VerifyFn(verifiers::verify_ip), + optional: true, + type: String, + }, + pending: { + optional: true, + type: SdnZonePending, + }, + reversedns: { + optional: true, + type: String, + }, + "rt-import": { + format: &ApiStringFormat::Pattern(&SDN_ZONE_RT_IMPORT_RE), + optional: true, + type: String, + }, + state: { + optional: true, + type: SdnObjectState, + }, + tag: { + minimum: 0, + optional: true, + type: Integer, + }, + type: { + type: ListZonesType, + }, + "vlan-protocol": { + optional: true, + type: NetworkInterfaceVlanProtocol, + }, + "vrf-vxlan": { + maximum: 16777215, + minimum: 1, + optional: true, + type: Integer, + }, + "vxlan-port": { + maximum: 65536, + minimum: 1, + optional: true, + type: Integer, + }, + zone: { + type: String, + }, + }, +)] +/// Object. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SdnZone { + /// Advertise IP prefixes (Type-5 routes) instead of MAC/IP pairs (Type-2 + /// routes). EVPN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "advertise-subnets")] + pub advertise_subnets: Option<bool>, + + /// the bridge for which VLANs should be managed. VLAN & QinQ zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bridge: Option<String>, + + /// Disable auto mac learning. VLAN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "bridge-disable-mac-learning")] + pub bridge_disable_mac_learning: Option<bool>, + + /// ID of the controller for this zone. EVPN zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub controller: Option<String>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dhcp: Option<SdnZoneDhcp>, + + /// Digest of the controller section. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub digest: Option<String>, + + /// Suppress IPv4 ARP && IPv6 Neighbour Discovery messages. EVPN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "disable-arp-nd-suppression")] + pub disable_arp_nd_suppression: Option<bool>, + + /// ID of the DNS server for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dns: Option<String>, + + /// Domain name for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dnszone: Option<String>, + + /// List of PVE Nodes that should act as exit node for this zone. EVPN zone + /// only. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub exitnodes: Option<String>, + + /// Create routes on the exit nodes, so they can connect to EVPN guests. + /// EVPN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "exitnodes-local-routing")] + pub exitnodes_local_routing: Option<bool>, + + /// Force traffic through this exitnode first. EVPN zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "exitnodes-primary")] + pub exitnodes_primary: Option<String>, + + /// ID of the IPAM for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ipam: Option<String>, + + /// MAC address of the anycast router for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mac: Option<String>, + + /// MTU of the zone, will be used for the created VNet bridges. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_i64")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mtu: Option<i64>, + + /// Nodes where this zone should be created. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nodes: Option<String>, + + /// Comma-separated list of peers, that are part of the VXLAN zone. Usually + /// the IPs of the nodes. VXLAN zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub peers: Option<String>, + + /// Changes that have not yet been applied to the running configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pending: Option<SdnZonePending>, + + /// ID of the reverse DNS server for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reversedns: Option<String>, + + /// Route-Targets that should be imported into the VRF of this zone via BGP. + /// EVPN zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "rt-import")] + pub rt_import: Option<String>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub state: Option<SdnObjectState>, + + /// Service-VLAN Tag (outer VLAN). QinQ zone only + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u64")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option<u64>, + + #[serde(rename = "type")] + pub ty: ListZonesType, + + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vlan-protocol")] + pub vlan_protocol: Option<NetworkInterfaceVlanProtocol>, + + /// VNI for the zone VRF. EVPN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u32")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vrf-vxlan")] + pub vrf_vxlan: Option<u32>, + + /// UDP port that should be used for the VXLAN tunnel (default 4789). VXLAN + /// zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u32")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vxlan-port")] + pub vxlan_port: Option<u32>, + + /// Name of the zone. + pub zone: String, +} + +#[api] +/// Name of DHCP server backend for this zone. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum SdnZoneDhcp { + #[serde(rename = "dnsmasq")] + /// dnsmasq. + Dnsmasq, +} +serde_plain::derive_display_from_serialize!(SdnZoneDhcp); +serde_plain::derive_fromstr_from_deserialize!(SdnZoneDhcp); + +const_regex! { + +SDN_ZONE_PENDING_EXITNODES_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##; +SDN_ZONE_PENDING_EXITNODES_PRIMARY_RE = r##"^(?i:[a-z0-9](?i:[a-z0-9\-]*[a-z0-9])?)$"##; +SDN_ZONE_PENDING_RT_IMPORT_RE = r##"^(\d+):(\d+)$"##; + +} + +#[api( + properties: { + "advertise-subnets": { + default: false, + optional: true, + }, + bridge: { + optional: true, + type: String, + }, + "bridge-disable-mac-learning": { + default: false, + optional: true, + }, + controller: { + optional: true, + type: String, + }, + dhcp: { + optional: true, + type: SdnZoneDhcp, + }, + "disable-arp-nd-suppression": { + default: false, + optional: true, + }, + dns: { + optional: true, + type: String, + }, + dnszone: { + optional: true, + type: String, + }, + exitnodes: { + format: &ApiStringFormat::Pattern(&SDN_ZONE_PENDING_EXITNODES_RE), + optional: true, + type: String, + }, + "exitnodes-local-routing": { + default: false, + optional: true, + }, + "exitnodes-primary": { + format: &ApiStringFormat::Pattern(&SDN_ZONE_PENDING_EXITNODES_PRIMARY_RE), + optional: true, + type: String, + }, + ipam: { + optional: true, + type: String, + }, + mac: { + optional: true, + type: String, + }, + mtu: { + optional: true, + type: Integer, + }, + nodes: { + optional: true, + type: String, + }, + peers: { + format: &ApiStringFormat::VerifyFn(verifiers::verify_ip), + optional: true, + type: String, + }, + reversedns: { + optional: true, + type: String, + }, + "rt-import": { + format: &ApiStringFormat::Pattern(&SDN_ZONE_PENDING_RT_IMPORT_RE), + optional: true, + type: String, + }, + tag: { + minimum: 0, + optional: true, + type: Integer, + }, + "vlan-protocol": { + optional: true, + type: NetworkInterfaceVlanProtocol, + }, + "vrf-vxlan": { + maximum: 16777215, + minimum: 1, + optional: true, + type: Integer, + }, + "vxlan-port": { + maximum: 65536, + minimum: 1, + optional: true, + type: Integer, + }, + }, +)] +/// Changes that have not yet been applied to the running configuration. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SdnZonePending { + /// Advertise IP prefixes (Type-5 routes) instead of MAC/IP pairs (Type-2 + /// routes). EVPN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "advertise-subnets")] + pub advertise_subnets: Option<bool>, + + /// the bridge for which VLANs should be managed. VLAN & QinQ zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub bridge: Option<String>, + + /// Disable auto mac learning. VLAN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "bridge-disable-mac-learning")] + pub bridge_disable_mac_learning: Option<bool>, + + /// ID of the controller for this zone. EVPN zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub controller: Option<String>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dhcp: Option<SdnZoneDhcp>, + + /// Suppress IPv4 ARP && IPv6 Neighbour Discovery messages. EVPN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "disable-arp-nd-suppression")] + pub disable_arp_nd_suppression: Option<bool>, + + /// ID of the DNS server for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dns: Option<String>, + + /// Domain name for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dnszone: Option<String>, + + /// List of PVE Nodes that should act as exit node for this zone. EVPN zone + /// only. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub exitnodes: Option<String>, + + /// Create routes on the exit nodes, so they can connect to EVPN guests. + /// EVPN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_bool")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "exitnodes-local-routing")] + pub exitnodes_local_routing: Option<bool>, + + /// Force traffic through this exitnode first. EVPN zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "exitnodes-primary")] + pub exitnodes_primary: Option<String>, + + /// ID of the IPAM for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ipam: Option<String>, + + /// MAC address of the anycast router for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mac: Option<String>, + + /// MTU of the zone, will be used for the created VNet bridges. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_i64")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mtu: Option<i64>, + + /// Nodes where this zone should be created. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub nodes: Option<String>, + + /// Comma-separated list of peers, that are part of the VXLAN zone. Usually + /// the IPs of the nodes. VXLAN zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub peers: Option<String>, + + /// ID of the reverse DNS server for this zone. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reversedns: Option<String>, + + /// Route-Targets that should be imported into the VRF of this zone via BGP. + /// EVPN zone only. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "rt-import")] + pub rt_import: Option<String>, + + /// Service-VLAN Tag (outer VLAN). QinQ zone only + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u64")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tag: Option<u64>, + + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vlan-protocol")] + pub vlan_protocol: Option<NetworkInterfaceVlanProtocol>, + + /// VNI for the zone VRF. EVPN zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u32")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vrf-vxlan")] + pub vrf_vxlan: Option<u32>, + + /// UDP port that should be used for the VXLAN tunnel (default 4789). VXLAN + /// zone only. + #[serde(deserialize_with = "proxmox_login::parse::deserialize_u32")] + #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(rename = "vxlan-port")] + pub vxlan_port: Option<u32>, +} + #[api( properties: { forceStop: { -- 2.39.5 _______________________________________________ pdm-devel mailing list pdm-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel