This commit adds the protocol-specific properties, that are required
for an Openfabric fabric. They correspond to the respective properties
in the FRR fabricd configuration. For more information, see the FRR
documentation [1].

While the hello interval and csnp interval could be set on a
per-interface basis, it is recommended to keep them the same across
the whole fabric. This is why they can only be configured globally and
the value will be applied to all interfaces that are part of the
fabric. We expose the hello multiplier in the interface properties, so
users can define a longer hello interval on a per-interface basis by
supplying the hello multiplier parameter. The upside of this is, that
everything scales from a single value and users can just edit the
hello interval in the fabric and everything else will adjust based on
that setting.

We also introduce two new general enums: Fabric and Node. They contain
the concrete FabricSection and NodeSection types that are used for
each protocol and add the Openfabric sections to it. We provide
dedicated updater structs for the enum as well, that fall back to the
concrete updater structs of the respective variant.

New protocols can simply be added by adding another variant
to the Fabric and Node enums.

[1] https://docs.frrouting.org/en/latest/fabricd.html

Co-authored-by: Gabriel Goller <g.gol...@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanre...@proxmox.com>
---
 proxmox-ve-config/Cargo.toml                  |   2 +
 proxmox-ve-config/debian/control              |   4 +
 .../src/sdn/fabric/section_config/fabric.rs   |  85 +++++++++++++-
 .../src/sdn/fabric/section_config/mod.rs      |   1 +
 .../src/sdn/fabric/section_config/node.rs     |  46 ++++++++
 .../sdn/fabric/section_config/protocol/mod.rs |   1 +
 .../section_config/protocol/openfabric.rs     | 105 ++++++++++++++++++
 7 files changed, 243 insertions(+), 1 deletion(-)
 create mode 100644 
proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs
 create mode 100644 
proxmox-ve-config/src/sdn/fabric/section_config/protocol/openfabric.rs

diff --git a/proxmox-ve-config/Cargo.toml b/proxmox-ve-config/Cargo.toml
index 7674dc7..c5aeba9 100644
--- a/proxmox-ve-config/Cargo.toml
+++ b/proxmox-ve-config/Cargo.toml
@@ -22,5 +22,7 @@ proxmox-serde = { version = "0.1.2", features = [ "perl" ]}
 
 proxmox-network-types = { workspace = true, features = [ "api-types" ] }
 proxmox-schema = { workspace = true, features = [ "api-types" ] }
+proxmox-sdn-types = { workspace = true }
+proxmox-section-config = { version = "3" }
 proxmox-sys = "0.6.4"
 proxmox-sortable-macro = "0.1.3"
diff --git a/proxmox-ve-config/debian/control b/proxmox-ve-config/debian/control
index f416316..57d7987 100644
--- a/proxmox-ve-config/debian/control
+++ b/proxmox-ve-config/debian/control
@@ -14,6 +14,8 @@ Build-Depends-Arch: cargo:native <!nocheck>,
  librust-proxmox-network-types-0.1+default-dev <!nocheck>,
  librust-proxmox-schema-4+api-types-dev <!nocheck>,
  librust-proxmox-schema-4+default-dev <!nocheck>,
+ librust-proxmox-sdn-types-0.1+default-dev <!nocheck>,
+ librust-proxmox-section-config-3+default-dev <!nocheck>,
  librust-proxmox-serde-0.1+default-dev (>= 0.1.2-~~) <!nocheck>,
  librust-proxmox-serde-0.1+perl-dev (>= 0.1.2-~~) <!nocheck>,
  librust-proxmox-sortable-macro-0.1+default-dev (>= 0.1.3-~~) <!nocheck>,
@@ -45,6 +47,8 @@ Depends:
  librust-proxmox-network-types-0.1+default-dev,
  librust-proxmox-schema-4+api-types-dev,
  librust-proxmox-schema-4+default-dev,
+ librust-proxmox-sdn-types-0.1+default-dev,
+ librust-proxmox-section-config-3+default-dev,
  librust-proxmox-serde-0.1+default-dev (>= 0.1.2-~~),
  librust-proxmox-serde-0.1+perl-dev (>= 0.1.2-~~),
  librust-proxmox-sortable-macro-0.1+default-dev (>= 0.1.3-~~),
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 9787d5d..3e56e2b 100644
--- a/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs
+++ b/proxmox-ve-config/src/sdn/fabric/section_config/fabric.rs
@@ -4,7 +4,11 @@ use serde::{Deserialize, Serialize};
 use proxmox_network_types::ip_address::{Ipv4Cidr, Ipv6Cidr};
 use proxmox_schema::{
     api, api_string_type, const_regex, AllOfSchema, ApiStringFormat, ApiType, 
ObjectSchema, Schema,
-    Updater,
+    Updater, UpdaterType,
+};
+
+use crate::sdn::fabric::section_config::protocol::openfabric::{
+    OpenfabricDeletableProperties, OpenfabricProperties, 
OpenfabricPropertiesUpdater,
 };
 
 pub const FABRIC_ID_REGEX_STR: &str = 
r"(?:[a-zA-Z0-9])(?:[a-zA-Z0-9\-]){0,6}(?:[a-zA-Z0-9])?";
@@ -122,6 +126,85 @@ impl<T: Updater, D> Updater for FabricSectionUpdater<T, D> 
{
     }
 }
 
+impl UpdaterType for FabricSection<OpenfabricProperties> {
+    type Updater = FabricSectionUpdater<OpenfabricPropertiesUpdater, 
OpenfabricDeletableProperties>;
+}
+
+/// Enum containing all types of fabrics.
+///
+/// It utilizes [`FabricSection<T>`] to define all possible types of fabrics. 
For parsing the
+/// configuration, please use the [`Section`] enum, which contains the Node 
sections as well. This
+/// struct is used for sorting the sections into their sub-types after parsing 
the configuration
+/// via [`Section`].
+#[api(
+    "id-property": "id",
+    "id-schema": {
+        type: String,
+        description: "Fabric ID",
+        format: &FABRIC_ID_FORMAT,
+    },
+    "type-key": "protocol",
+)]
+#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
+#[serde(rename_all = "snake_case", tag = "protocol")]
+pub enum Fabric {
+    Openfabric(FabricSection<OpenfabricProperties>),
+}
+
+impl UpdaterType for Fabric {
+    type Updater = FabricUpdater;
+}
+
+impl Fabric {
+    /// Get the id of the [Fabric].
+    ///
+    /// This is a common property for all protocols.
+    pub fn id(&self) -> &FabricId {
+        match self {
+            Self::Openfabric(fabric_section) => fabric_section.id(),
+        }
+    }
+
+    /// Get the ip-prefix (IPv4 CIDR) of the [Fabric].
+    ///
+    /// This is a common property for all protocols.
+    pub fn ip_prefix(&self) -> Option<Ipv4Cidr> {
+        match self {
+            Fabric::Openfabric(fabric_section) => fabric_section.ip_prefix(),
+        }
+    }
+
+    /// Get the ip6-prefix (IPv6 CIDR) of the [Fabric].
+    ///
+    /// This is a common property for all protocols.
+    pub fn ip6_prefix(&self) -> Option<Ipv6Cidr> {
+        match self {
+            Fabric::Openfabric(fabric_section) => fabric_section.ip6_prefix(),
+        }
+    }
+}
+
+impl From<FabricSection<OpenfabricProperties>> for Fabric {
+    fn from(section: FabricSection<OpenfabricProperties>) -> Self {
+        Fabric::Openfabric(section)
+    }
+}
+
+/// Enum containing all updater types for fabrics
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case", tag = "protocol")]
+pub enum FabricUpdater {
+    Openfabric(<FabricSection<OpenfabricProperties> as UpdaterType>::Updater),
+}
+
+impl Updater for FabricUpdater {
+    fn is_empty(&self) -> bool {
+        match self {
+            FabricUpdater::Openfabric(updater) => updater.is_empty(),
+        }
+    }
+}
+
 /// Deletable properties for a [`FabricSection<T>`]
 #[derive(Debug, Clone, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case", untagged)]
diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs 
b/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs
index b61bc43..7db3788 100644
--- a/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs
+++ b/proxmox-ve-config/src/sdn/fabric/section_config/mod.rs
@@ -1,3 +1,4 @@
 pub mod fabric;
 pub mod interface;
 pub mod node;
+pub mod protocol;
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 b1202a2..510bfde 100644
--- a/proxmox-ve-config/src/sdn/fabric/section_config/node.rs
+++ b/proxmox-ve-config/src/sdn/fabric/section_config/node.rs
@@ -12,6 +12,7 @@ use proxmox_schema::{
 
 use crate::sdn::fabric::section_config::{
     fabric::{FabricId, FABRIC_ID_REGEX_STR},
+    protocol::openfabric::OpenfabricNodeProperties,
 };
 
 pub const NODE_ID_REGEX_STR: &str = 
r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]){0,61}(?:[a-zA-Z0-9]){0,1})";
@@ -167,3 +168,48 @@ impl<T: ApiType> ApiType for NodeSection<T> {
     )
     .schema();
 }
+
+/// Enum containing all types of nodes.
+#[api(
+    "id-property": "id",
+    "id-schema": {
+        type: String,
+        description: "Node ID",
+        format: &NODE_ID_FORMAT,
+    },
+    "type-key": "protocol",
+)]
+#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
+#[serde(rename_all = "snake_case", tag = "protocol")]
+pub enum Node {
+    Openfabric(NodeSection<OpenfabricNodeProperties>),
+}
+
+impl Node {
+    /// Get the id of the [Node].
+    pub fn id(&self) -> &NodeSectionId {
+        match self {
+            Node::Openfabric(node_section) => node_section.id(),
+        }
+    }
+
+    /// Get the ip (IPv4) of the [Node].
+    pub fn ip(&self) -> Option<std::net::Ipv4Addr> {
+        match self {
+            Node::Openfabric(node_section) => node_section.ip(),
+        }
+    }
+
+    /// Get the ip (IPv6) of the [Node].
+    pub fn ip6(&self) -> Option<std::net::Ipv6Addr> {
+        match self {
+            Node::Openfabric(node_section) => node_section.ip6(),
+        }
+    }
+}
+
+impl From<NodeSection<OpenfabricNodeProperties>> for Node {
+    fn from(value: NodeSection<OpenfabricNodeProperties>) -> Self {
+        Self::Openfabric(value)
+    }
+}
diff --git a/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs 
b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs
new file mode 100644
index 0000000..e5b800b
--- /dev/null
+++ b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/mod.rs
@@ -0,0 +1 @@
+pub mod openfabric;
diff --git 
a/proxmox-ve-config/src/sdn/fabric/section_config/protocol/openfabric.rs 
b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/openfabric.rs
new file mode 100644
index 0000000..156ff2b
--- /dev/null
+++ b/proxmox-ve-config/src/sdn/fabric/section_config/protocol/openfabric.rs
@@ -0,0 +1,105 @@
+use std::ops::Deref;
+
+use proxmox_network_types::ip_address::{Ipv4Cidr, Ipv6Cidr};
+use serde::{Deserialize, Serialize};
+
+use proxmox_schema::{api, property_string::PropertyString, ApiStringFormat, 
Updater};
+use proxmox_sdn_types::openfabric::{CsnpInterval, HelloInterval, 
HelloMultiplier};
+
+use crate::sdn::fabric::section_config::interface::InterfaceName;
+
+/// Protocol-specific options for an OpenFabric Fabric.
+#[api]
+#[derive(Debug, Clone, Serialize, Deserialize, Updater, Hash)]
+pub struct OpenfabricProperties {
+    /// This will be distributed to all interfaces on every node. The Hello 
Interval for a given
+    /// interface in seconds. The range is 1 to 600. Hello packets are used to 
establish and
+    /// maintain adjacency between OpenFabric neighbors.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub(crate) hello_interval: Option<HelloInterval>,
+
+    /// This will be distributed to all interfaces on every node.The Complete 
Sequence Number
+    /// Packets (CSNP) interval in seconds. The interval range is 1 to 600.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub(crate) csnp_interval: Option<CsnpInterval>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize, Hash)]
+#[serde(rename_all = "snake_case")]
+pub enum OpenfabricDeletableProperties {
+    HelloInterval,
+    CsnpInterval,
+}
+
+/// Properties for an OpenFabric node
+#[api(
+    properties: {
+        interfaces: {
+            type: Array,
+            optional: true,
+            items: {
+                type: String,
+                description: "OpenFabric interface",
+                format: 
&ApiStringFormat::PropertyString(&OpenfabricInterfaceProperties::API_SCHEMA),
+            }
+        },
+    }
+)]
+#[derive(Debug, Clone, Serialize, Deserialize, Updater, Hash)]
+pub struct OpenfabricNodeProperties {
+    /// Interfaces for this node
+    #[serde(default)]
+    pub(crate) interfaces: Vec<PropertyString<OpenfabricInterfaceProperties>>,
+}
+
+impl OpenfabricNodeProperties {
+    /// Returns an interator over all the interfaces.
+    pub fn interfaces(&self) -> impl Iterator<Item = 
&OpenfabricInterfaceProperties> {
+        self.interfaces
+            .iter()
+            .map(|property_string| property_string.deref())
+    }
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+pub enum OpenfabricNodeDeletableProperties {
+    Interfaces,
+}
+
+/// Properties for an OpenFabric interface
+#[api]
+#[derive(Debug, Clone, Serialize, Deserialize, Updater, Hash)]
+pub struct OpenfabricInterfaceProperties {
+    pub(crate) name: InterfaceName,
+
+    /// The multiplier for the hello holding time on a given interface. The 
range is 2 to
+    /// 100.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub(crate) hello_multiplier: Option<HelloMultiplier>,
+
+    /// If ip and ip6 are unset, then this is an point-to-point interface
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub(crate) ip: Option<Ipv4Cidr>,
+
+    /// If ip6 and ip are unset, then this is an point-to-point interface
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub(crate) ip6: Option<Ipv6Cidr>,
+}
+
+impl OpenfabricInterfaceProperties {
+    /// Get the name of the interface.
+    pub fn name(&self) -> &InterfaceName {
+        &self.name
+    }
+
+    /// Get the ip (IPv4) of the interface.
+    pub fn ip(&self) -> Option<Ipv4Cidr> {
+        self.ip
+    }
+
+    /// Get the ip6 (IPv6) of the interface.
+    pub fn ip6(&self) -> Option<Ipv6Cidr> {
+        self.ip6
+    }
+}
-- 
2.39.5


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to