minor doc nits On Wed, Jul 02, 2025 at 04:50:06PM +0200, Gabriel Goller wrote: > From: Stefan Hanreich <s.hanre...@proxmox.com> > > 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 <g.gol...@proxmox.com> > Signed-off-by: Stefan Hanreich <s.hanre...@proxmox.com> > --- > .../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<T> { > #[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<NodeSection<OspfNodeProperties>> 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<T>]: API-friendly version of [NodeSection<T>] that flattens > the node identifier > +/// into separate `fabric_id` and `node_id` fields > +/// - [Node]: API-version of [super::Node] > +/// - [NodeDataUpdater] > +/// - [NodeDeletableProperties]
^ The types in those links should also be in backticks. > +/// > +/// 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<T>]. ^ backticks > + /// > + /// 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<T> { > + fabric_id: FabricId, > + node_id: NodeId, > + > + /// IPv4 for this node in the Ospf fabric > + #[serde(skip_serializing_if = "Option::is_none")] > + ip: Option<Ipv4Addr>, > + > + /// IPv6 for this node in the Ospf fabric > + #[serde(skip_serializing_if = "Option::is_none")] > + ip6: Option<Ipv6Addr>, > + > + #[serde(flatten)] > + properties: T, > + } > + > + impl<T> From<NodeSection<T>> for NodeData<T> { > + fn from(value: NodeSection<T>) -> Self { > + Self { > + fabric_id: value.id.fabric_id, > + node_id: value.id.node_id, > + ip: value.ip, > + ip6: value.ip6, > + properties: value.properties, > + } > + } > + } > + > + impl<T> From<NodeData<T>> for NodeSection<T> { > + fn from(value: NodeData<T>) -> 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]. ^ backticks > + #[derive(Debug, Clone, Serialize, Deserialize)] > + #[serde(rename_all = "snake_case", tag = "protocol")] > + pub enum Node { > + Openfabric(NodeData<OpenfabricNodeProperties>), > + Ospf(NodeData<OspfNodeProperties>), > + } > + > + impl From<super::Node> 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<Node> 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<OpenfabricNodeProperties> { > + type Updater = > + NodeDataUpdater<OpenfabricNodePropertiesUpdater, > OpenfabricNodeDeletableProperties>; > + } > + > + impl UpdaterType for NodeData<OspfNodeProperties> { > + type Updater = NodeDataUpdater<OspfNodePropertiesUpdater, > OspfNodeDeletableProperties>; > + } > + > + #[derive(Debug, Clone, Serialize, Deserialize)] > + pub struct NodeDataUpdater<T, D> { > + #[serde(skip_serializing_if = "Option::is_none")] > + pub(crate) ip: Option<Ipv4Addr>, > + > + #[serde(skip_serializing_if = "Option::is_none")] > + pub(crate) ip6: Option<Ipv6Addr>, > + > + #[serde(flatten)] > + pub(crate) properties: T, > + > + #[serde(skip_serializing_if = "Vec::is_empty", default = "Vec::new")] (^ the `= "Vec::new"` should not be necessary, but doesn't matter either - new is `const` so it's probably more efficient this way anyawy...) > + pub(crate) delete: Vec<NodeDeletableProperties<D>>, > + } > + > + impl<T: UpdaterType + Updater, D> UpdaterType for NodeDataUpdater<T, D> { > + type Updater = NodeDataUpdater<T::Updater, D>; > + } > + > + impl<T: Updater, D> Updater for NodeDataUpdater<T, D> { > + 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<OpenfabricNodePropertiesUpdater, > OpenfabricNodeDeletableProperties>, > + ), > + Ospf(NodeDataUpdater<OspfNodePropertiesUpdater, > OspfNodeDeletableProperties>), > + } > + > + #[derive(Debug, Clone, Serialize, Deserialize)] > + #[serde(rename_all = "snake_case")] > + pub enum NodeDeletableProperties<T> { > + 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