[pve-devel] [PATCH proxmox-ve-rs v4 15/22] config: sdn: fabrics: add api types

Wolfgang Bumiller w.bumiller at proxmox.com
Tue Jul 8 10:15:33 CEST 2025


minor doc nits

On Wed, Jul 02, 2025 at 04:50:06PM +0200, Gabriel Goller wrote:
> From: Stefan Hanreich <s.hanreich at 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.goller at proxmox.com>
> Signed-off-by: Stefan Hanreich <s.hanreich at 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




More information about the pve-devel mailing list