From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id CAF051FF164 for ; Fri, 4 Jul 2025 14:57:02 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 529143832C; Fri, 4 Jul 2025 14:57:42 +0200 (CEST) Date: Fri, 4 Jul 2025 14:57:08 +0200 From: Wolfgang Bumiller To: Gabriel Goller Message-ID: References: <20250702145101.894299-1-g.goller@proxmox.com> <20250702145101.894299-31-g.goller@proxmox.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20250702145101.894299-31-g.goller@proxmox.com> X-SPAM-LEVEL: Spam detection results: 0 AWL -0.030 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.218 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [fabrics.rs] Subject: Re: [pve-devel] [PATCH proxmox-perl-rs v4 2/5] pve-rs: sdn: fabrics: add api methods X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Cc: pve-devel@lists.proxmox.com Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" On Wed, Jul 02, 2025 at 04:50:15PM +0200, Gabriel Goller wrote: > From: Stefan Hanreich > > 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 > Signed-off-by: Stefan Hanreich > --- > 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, BTreeMap) { > + 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 { > + 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 { > + 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 { > + 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 { > + 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, 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, 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 { > + 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 { > + 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