On Wed, Jul 02, 2025 at 04:50:15PM +0200, Gabriel Goller wrote: > From: Stefan Hanreich <s.hanre...@proxmox.com> > > The FabricConfig from proxmox-ve-config implements CRUD functionality > for Fabrics and Nodes stored in the section config. We expose them via > perlmod, so they can be used in the API endpoints defined in perl. > they map 1:1 to the respective API endpoints. > > They are simply calling the respective implementation of FabricConfig, > and convert from / to the API representations of the Fabrics / Nodes > returned by FabricConfig. > > Co-authored-by: Gabriel Goller <g.gol...@proxmox.com> > Signed-off-by: Stefan Hanreich <s.hanre...@proxmox.com> > --- > pve-rs/src/bindings/sdn/fabrics.rs | 212 ++++++++++++++++++++++++++++- > 1 file changed, 211 insertions(+), 1 deletion(-) > > diff --git a/pve-rs/src/bindings/sdn/fabrics.rs > b/pve-rs/src/bindings/sdn/fabrics.rs > index fac5602c0241..2efa1c6306ae 100644 > --- a/pve-rs/src/bindings/sdn/fabrics.rs > +++ b/pve-rs/src/bindings/sdn/fabrics.rs > @@ -17,7 +17,20 @@ pub mod pve_rs_sdn_fabrics { > use proxmox_section_config::typed::SectionConfigData; > use proxmox_ve_config::common::valid::Validatable; > > - use proxmox_ve_config::sdn::fabric::{section_config::Section, > FabricConfig}; > + use proxmox_ve_config::sdn::fabric::{ > + section_config::{ > + fabric::{ > + api::{Fabric, FabricUpdater}, > + FabricId, > + }, > + node::{ > + api::{Node, NodeUpdater}, > + Node as ConfigNode, NodeId, > + }, > + Section, > + }, > + FabricConfig, FabricEntry, > + }; > > /// A SDN Fabric config instance. > #[derive(Serialize, Deserialize)] > @@ -57,6 +70,203 @@ pub mod pve_rs_sdn_fabrics { > ) > } > > + /// Class method: Returns all fabrics and nodes from the configuration.
^ Regular `Method:` - all of them. > + #[export] > + fn list_all( > + #[try_from_ref] this: &PerlFabricConfig, > + ) -> (BTreeMap<String, Fabric>, BTreeMap<String, Node>) { > + let config = this.fabric_config.lock().unwrap(); > + > + let mut fabrics = BTreeMap::new(); > + let mut nodes = BTreeMap::new(); > + > + for entry in config.values() { > + fabrics.insert(entry.fabric().id().to_string(), > entry.fabric().clone()); ^ Just noting this here for potential later improvements: it may be possible to skip all the temporary clones if the method returns `(Value, Value)` and explicitly turns the then-`BTreeMap<&str, &Fabric>`s into `Values` before dropping the lock. > + > + nodes.extend( > + entry > + .nodes() > + .map(|(_node_id, node)| (node.id().to_string(), > node.clone().into())), > + ); > + } > + > + (fabrics, nodes) > + } > + > + /// Class method: Returns all fabrics from the configuration. > + #[export] > + fn list_fabrics(#[try_from_ref] this: &PerlFabricConfig) -> > BTreeMap<String, Fabric> { > + this.fabric_config > + .lock() > + .unwrap() > + .iter() > + .map(|(id, entry)| (id.to_string(), entry.fabric().clone())) > + .collect() > + } > + > + /// Class method: Returns all fabrics configured on a specific node in > the cluster. > + #[export] > + fn list_fabrics_by_node( > + #[try_from_ref] this: &PerlFabricConfig, > + node_id: NodeId, > + ) -> BTreeMap<String, Fabric> { > + this.fabric_config > + .lock() > + .unwrap() > + .iter() > + .filter(|(_id, entry)| entry.get_node(&node_id).is_ok()) > + .map(|(id, entry)| (id.to_string(), entry.fabric().clone())) > + .collect() > + } > + > + /// Class method: Adds a new Fabric to the configuration. > + #[export] > + fn add_fabric(#[try_from_ref] this: &PerlFabricConfig, fabric: Fabric) > -> Result<(), Error> { > + this.fabric_config > + .lock() > + .unwrap() > + .add_fabric(fabric) ^ If all we do is forward to an existing method, it would be nice to include a `See [...]` link in the method docs. (The `make doc/doc-open` make targets currently add `--external-html-root-url` parameters to `cargo doc` to make these links work (just updated to the new rustdoc layout). > + .map_err(anyhow::Error::msg) > + } > + > + /// Class method: Read a Fabric from the configuration. > + #[export] > + fn get_fabric(#[try_from_ref] this: &PerlFabricConfig, id: FabricId) -> > Result<Fabric, Error> { > + this.fabric_config > + .lock() > + .unwrap() > + .get_fabric(&id) > + .map(|entry| entry.fabric().clone()) > + .map_err(anyhow::Error::msg) > + } > + > + /// Class method: Update a fabric in the configuration. > + #[export] > + fn update_fabric( > + #[try_from_ref] this: &PerlFabricConfig, > + id: FabricId, > + updater: FabricUpdater, > + ) -> Result<(), Error> { > + this.fabric_config > + .lock() > + .unwrap() > + .update_fabric(&id, updater) > + .map_err(anyhow::Error::msg) > + } > + > + /// Class method: Delete a fabric from the configuration. > + #[export] > + fn delete_fabric( > + #[try_from_ref] this: &PerlFabricConfig, > + id: FabricId, > + ) -> Result<FabricEntry, Error> { > + this.fabric_config > + .lock() > + .unwrap() > + .delete_fabric(&id) > + .map_err(anyhow::Error::msg) > + } > + > + /// Class method: List all nodes in the configuraiton. > + #[export] > + fn list_nodes( > + #[try_from_ref] this: &PerlFabricConfig, > + ) -> Result<BTreeMap<String, Node>, Error> { > + Ok(this > + .fabric_config > + .lock() > + .unwrap() > + .values() > + .flat_map(|entry| { > + entry > + .nodes() > + .map(|(id, node)| (id.to_string(), node.clone().into())) > + }) > + .collect()) > + } > + > + /// Class method: List all nodes for a specific fabric. > + #[export] > + fn list_nodes_fabric( > + #[try_from_ref] this: &PerlFabricConfig, > + fabric_id: FabricId, > + ) -> Result<BTreeMap<String, Node>, Error> { > + Ok(this > + .fabric_config > + .lock() > + .unwrap() > + .get_fabric(&fabric_id) > + .map_err(anyhow::Error::msg)? > + .nodes() > + .map(|(id, node)| (id.to_string(), node.clone().into())) > + .collect()) > + } > + > + /// Class method: Get a node from a fabric. > + #[export] > + fn get_node( > + #[try_from_ref] this: &PerlFabricConfig, > + fabric_id: FabricId, > + node_id: NodeId, > + ) -> Result<Node, Error> { > + this.fabric_config > + .lock() > + .unwrap() > + .get_fabric(&fabric_id) > + .map_err(anyhow::Error::msg)? > + .get_node(&node_id) > + .map(|node| node.clone().into()) > + .map_err(anyhow::Error::msg) > + } > + > + /// Class method: Add a node to a fabric. > + #[export] > + fn add_node(#[try_from_ref] this: &PerlFabricConfig, node: Node) -> > Result<(), Error> { > + let node = ConfigNode::from(node); > + > + this.fabric_config > + .lock() > + .unwrap() > + .get_fabric_mut(node.id().fabric_id()) > + .map_err(anyhow::Error::msg)? > + .add_node(node) > + .map_err(anyhow::Error::msg) > + } > + > + /// Class method: Update a node in a fabric. > + #[export] > + fn update_node( > + #[try_from_ref] this: &PerlFabricConfig, > + fabric_id: FabricId, > + node_id: NodeId, > + updater: NodeUpdater, > + ) -> Result<(), Error> { > + this.fabric_config > + .lock() > + .unwrap() > + .get_fabric_mut(&fabric_id) > + .map_err(anyhow::Error::msg)? > + .update_node(&node_id, updater) > + .map_err(anyhow::Error::msg) > + } > + > + /// Class method: Delete a node in a fabric. > + #[export] > + fn delete_node( > + #[try_from_ref] this: &PerlFabricConfig, > + fabric_id: FabricId, > + node_id: NodeId, > + ) -> Result<Node, Error> { > + this.fabric_config > + .lock() > + .unwrap() > + .get_fabric_mut(&fabric_id) > + .map_err(anyhow::Error::msg)? > + .delete_node(&node_id) > + .map(Node::from) > + .map_err(anyhow::Error::msg) > + } > + > /// Class method: Convert the configuration into the section config > sections. > /// > /// Used for writing the running configuration. > -- > 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel