[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