From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id C7EDE1FF183 for ; Wed, 2 Jul 2025 16:51:21 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 9B70D1E423; Wed, 2 Jul 2025 16:51:25 +0200 (CEST) From: Gabriel Goller To: pve-devel@lists.proxmox.com Date: Wed, 2 Jul 2025 16:50:06 +0200 Message-Id: <20250702145101.894299-22-g.goller@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250702145101.894299-1-g.goller@proxmox.com> References: <20250702145101.894299-1-g.goller@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.019 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 SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [PATCH proxmox-ve-rs v4 15/22] config: sdn: fabrics: add api types X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" From: Stefan Hanreich Add an api submodule to the section config module, that provides the types that are intended to be returned and accepted by the Perl API in Proxmox VE. This allows us to decouple the format returned in the API from the configuration format. This is particularly relevant in the case of the NodeSection type. While the section config stores the composite ID of the node as the ID of the section in the section config (and therefore as a single string / property), we want to be able to return them as independent fields from the API, to avoid having to parse the ID everywhere else we want to use it. Thanks to the generic NodeSection type we only have to define the conversion from / to the API type once, while the protocol-specific types can stay the same. For the fabrics, we simply re-use the section_config types for now, but by re-exporting them as type alias we are more flexible in possibly changing the API types or the underlying section config types later on. Co-authored-by: Gabriel Goller Signed-off-by: Stefan Hanreich --- .../src/sdn/fabric/section_config/fabric.rs | 5 + .../src/sdn/fabric/section_config/node.rs | 159 ++++++++++++++++++ 2 files changed, 164 insertions(+) diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs b/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs index 8ecf725c4641..75a309398ca2 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs @@ -233,3 +233,8 @@ pub enum FabricDeletableProperties { #[serde(untagged)] Protocol(T), } + +pub mod api { + pub type Fabric = super::Fabric; + pub type FabricUpdater = super::FabricUpdater; +} diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/node.rs b/proxmox-ve-config/src/sdn/fabric/section_config/node.rs index bd5ffea854d7..6bccbb7468ed 100644 --- a/proxmox-ve-config/src/sdn/fabric/section_config/node.rs +++ b/proxmox-ve-config/src/sdn/fabric/section_config/node.rs @@ -223,3 +223,162 @@ impl From> for Node { Self::Ospf(value) } } + +/// API types for SDN fabric node configurations. +/// +/// This module provides specialized types that are used for API interactions when retrieving, +/// creating, or updating fabric/node configurations. These types serialize differently than their +/// section-config configuration counterparts to be nicer client-side. +/// +/// The module includes: +/// - [NodeData]: API-friendly version of [NodeSection] that flattens the node identifier +/// into separate `fabric_id` and `node_id` fields +/// - [Node]: API-version of [super::Node] +/// - [NodeDataUpdater] +/// - [NodeDeletableProperties] +/// +/// These types include conversion methods to transform between API representations and internal +/// configuration objects. +pub mod api { + use serde::{Deserialize, Serialize}; + + use proxmox_schema::{Updater, UpdaterType}; + + use crate::sdn::fabric::section_config::protocol::{ + openfabric::{ + OpenfabricNodeDeletableProperties, OpenfabricNodeProperties, + OpenfabricNodePropertiesUpdater, + }, + ospf::{OspfNodeDeletableProperties, OspfNodeProperties, OspfNodePropertiesUpdater}, + }; + + use super::*; + + /// API-equivalent to [NodeSection]. + /// + /// The difference is that instead of serializing fabric_id and node_id into a single string + /// (`{fabric_id}_{node_id}`), are serialized normally as two distinct properties. This + /// prevents us from needing to parse the node_id in the frontend using `split("_")`. + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct NodeData { + fabric_id: FabricId, + node_id: NodeId, + + /// IPv4 for this node in the Ospf fabric + #[serde(skip_serializing_if = "Option::is_none")] + ip: Option, + + /// IPv6 for this node in the Ospf fabric + #[serde(skip_serializing_if = "Option::is_none")] + ip6: Option, + + #[serde(flatten)] + properties: T, + } + + impl From> for NodeData { + fn from(value: NodeSection) -> Self { + Self { + fabric_id: value.id.fabric_id, + node_id: value.id.node_id, + ip: value.ip, + ip6: value.ip6, + properties: value.properties, + } + } + } + + impl From> for NodeSection { + fn from(value: NodeData) -> Self { + let id = NodeSectionId::new(value.fabric_id, value.node_id); + + Self { + id, + ip: value.ip, + ip6: value.ip6, + properties: value.properties, + } + } + } + + /// API-equivalent to [super::Node]. + #[derive(Debug, Clone, Serialize, Deserialize)] + #[serde(rename_all = "snake_case", tag = "protocol")] + pub enum Node { + Openfabric(NodeData), + Ospf(NodeData), + } + + impl From for Node { + fn from(value: super::Node) -> Self { + match value { + super::Node::Openfabric(node_section) => Self::Openfabric(node_section.into()), + super::Node::Ospf(node_section) => Self::Ospf(node_section.into()), + } + } + } + + impl From for super::Node { + fn from(value: Node) -> Self { + match value { + Node::Openfabric(node_section) => Self::Openfabric(node_section.into()), + Node::Ospf(node_section) => Self::Ospf(node_section.into()), + } + } + } + + impl UpdaterType for NodeData { + type Updater = + NodeDataUpdater; + } + + impl UpdaterType for NodeData { + type Updater = NodeDataUpdater; + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct NodeDataUpdater { + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) ip: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) ip6: Option, + + #[serde(flatten)] + pub(crate) properties: T, + + #[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")] + pub(crate) delete: Vec>, + } + + impl UpdaterType for NodeDataUpdater { + type Updater = NodeDataUpdater; + } + + impl Updater for NodeDataUpdater { + fn is_empty(&self) -> bool { + T::is_empty(&self.properties) + && self.ip.is_none() + && self.ip6.is_none() + && self.delete.is_empty() + } + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[serde(rename_all = "snake_case", tag = "protocol")] + pub enum NodeUpdater { + Openfabric( + NodeDataUpdater, + ), + Ospf(NodeDataUpdater), + } + + #[derive(Debug, Clone, Serialize, Deserialize)] + #[serde(rename_all = "snake_case")] + pub enum NodeDeletableProperties { + Ip, + Ip6, + #[serde(untagged)] + Protocol(T), + } +} -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel