From fdda741ac89ec8ee87d699fa9f7e5f942622f571 Mon Sep 17 00:00:00 2001 From: leonlux Date: Thu, 15 Feb 2024 10:01:47 +0100 Subject: [PATCH 1/5] warp generated capabilities to provide some comfort functions --- src/client/capabilities.rs | 49 ++++++++++++++++++++++++++++++++++++++ src/client/client.rs | 7 +++--- src/client/mod.rs | 8 ++++++- src/lib.rs | 8 +++++-- 4 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 src/client/capabilities.rs diff --git a/src/client/capabilities.rs b/src/client/capabilities.rs new file mode 100644 index 0000000..97feade --- /dev/null +++ b/src/client/capabilities.rs @@ -0,0 +1,49 @@ +use crate::gen::gnmi::CapabilityResponse; +use crate::gen::gnmi::ModelData; + + +pub use crate::gen::gnmi::Encoding; + +/// Capabilities of a given gNMI Target device. +/// +/// Contains information about the capabilities that supported by a gNMI Target device. +#[derive(Debug, Clone)] +pub struct Capabilities(pub(crate) CapabilityResponse); + +impl<'a> Capabilities { + /// Retrieve the gNMI Version that the target device supports. + pub fn gnmi_version(&'a self) -> &'a str { + self.0.g_nmi_version.as_str() + } + + /// Check if target device supports a given model. + /// + /// # Arguments + /// - name: Name of the model + /// - organization: Organization publishing the model + /// - version: Version of the model + pub fn supports_model(&self, name: &str, organization: &str, version: &str) -> bool { + self.0.supported_models.contains(&ModelData { + name: name.to_string(), + organization: organization.to_string(), + version: version.to_string() + }) + } + + /// Check if a target device supports a given [`Encoding`]. + /// + /// # Arguments + /// - encoding: The [`Encoding`] to check for. + pub fn supports_encoding(&self, encoding: Encoding) -> bool { + let enc: i32 = match encoding { + Encoding::Json => 0, + Encoding::Bytes => 1, + Encoding::Proto => 2, + Encoding::Ascii => 3, + Encoding::JsonIetf => 4 + }; + + self.0.supported_encodings.contains(&enc) + } +} + diff --git a/src/client/client.rs b/src/client/client.rs index 4360f81..d2ffb34 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -1,7 +1,8 @@ use crate::auth::AuthService; use crate::error::GinmiError; use crate::gen::gnmi::g_nmi_client::GNmiClient; -use crate::gen::gnmi::{CapabilityRequest, CapabilityResponse}; +use crate::gen::gnmi::CapabilityRequest; +use super::capabilities::Capabilities; use http::HeaderValue; use std::str::FromStr; use std::sync::Arc; @@ -36,10 +37,10 @@ impl<'a> Client { /// let capabilities = client.capabilities().await; /// # } /// ``` - pub async fn capabilities(&mut self) -> CapabilityResponse { + pub async fn capabilities(&mut self) -> Capabilities { let req = CapabilityRequest::default(); match self.inner.capabilities(req).await { - Ok(val) => val.into_inner(), + Ok(val) => Capabilities(val.into_inner()), Err(e) => panic!("Error getting capabilities: {:?}", e), } } diff --git a/src/client/mod.rs b/src/client/mod.rs index 68eaf7c..25090e8 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,3 +1,9 @@ mod client; +mod capabilities; -pub use client::{Client, ClientBuilder}; \ No newline at end of file +pub use client::{Client, ClientBuilder}; + +pub use capabilities::{ + Capabilities, + Encoding +}; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 8e895a4..1df924b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,12 @@ mod auth; mod client; mod error; -pub use client::{Client, ClientBuilder}; - +pub use client::{ + Client, + ClientBuilder, + Encoding, + Capabilities +}; pub use error::GinmiError; pub(crate) mod gen { From 6660a20e4dd74b4c5660a66693fec8304d51f84e Mon Sep 17 00:00:00 2001 From: leonlux Date: Thu, 15 Feb 2024 10:06:58 +0100 Subject: [PATCH 2/5] make client.capabilities return result instead of panicing on error. --- src/client/client.rs | 10 ++++------ src/error.rs | 2 ++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client/client.rs b/src/client/client.rs index d2ffb34..11f7a96 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -34,15 +34,13 @@ impl<'a> Client { /// .await /// .unwrap(); /// - /// let capabilities = client.capabilities().await; + /// let capabilities = client.capabilities().await?; /// # } /// ``` - pub async fn capabilities(&mut self) -> Capabilities { + pub async fn capabilities(&mut self) -> Result { let req = CapabilityRequest::default(); - match self.inner.capabilities(req).await { - Ok(val) => Capabilities(val.into_inner()), - Err(e) => panic!("Error getting capabilities: {:?}", e), - } + let res = self.inner.capabilities(req).await?; + Ok(Capabilities(res.into_inner())) } } diff --git a/src/error.rs b/src/error.rs index 70208be..6a144ca 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,4 +6,6 @@ pub enum GinmiError { InvalidUriError(String), #[error("invalid header in grpc request: {}", .0)] InvalidHeaderValue(#[from] http::header::InvalidHeaderValue), + #[error("error communicating with target device: {}", .0)] + GrpcError(#[from] tonic::Status), } From 2ddb5be5d27e28aeae1e99bac93f49c8a48d6014 Mon Sep 17 00:00:00 2001 From: Leon Lux Date: Thu, 15 Feb 2024 19:12:04 +0100 Subject: [PATCH 3/5] add documentation comments to capabilities.rs --- Cargo.toml | 3 ++ src/client/capabilities.rs | 70 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index afc149e..da9a7bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,5 +27,8 @@ tower-service = "0.3.2" http = "0.2.0" tower = "0.4.13" +[dev-dependencies] +tokio-test = "0.4.3" + [build-dependencies] tonic-build = "0.11.0" \ No newline at end of file diff --git a/src/client/capabilities.rs b/src/client/capabilities.rs index 97feade..51385e1 100644 --- a/src/client/capabilities.rs +++ b/src/client/capabilities.rs @@ -1,17 +1,38 @@ +use crate::Client; use crate::gen::gnmi::CapabilityResponse; use crate::gen::gnmi::ModelData; - pub use crate::gen::gnmi::Encoding; /// Capabilities of a given gNMI Target device. /// /// Contains information about the capabilities that supported by a gNMI Target device. +/// Obtained via [`Client::capabilities`]. #[derive(Debug, Clone)] -pub struct Capabilities(pub(crate) CapabilityResponse); +pub struct Capabilities(pub CapabilityResponse); impl<'a> Capabilities { /// Retrieve the gNMI Version that the target device supports. + /// + /// # Examples + /// ```rust + /// # use ginmi::{Client, Capabilities}; + /// # fn main() -> std::io::Result<()> { + /// # tokio_test::block_on(async { + /// # const CERT: &str = "CA Certificate"; + /// let mut client = Client::builder("https://clab-srl01-srl:57400") + /// .tls(CERT, "clab-srl01-srl") + /// .credentials("admin", "admin") + /// .build() + /// .await + /// .unwrap(); + /// + /// let capabilities = client.capabilities().await.unwrap(); + /// let version = capabilities.gnmi_version(); + /// # }); + /// # Ok(()) + /// # } + /// ``` pub fn gnmi_version(&'a self) -> &'a str { self.0.g_nmi_version.as_str() } @@ -22,6 +43,30 @@ impl<'a> Capabilities { /// - name: Name of the model /// - organization: Organization publishing the model /// - version: Version of the model + /// + /// # Examples + /// ```rust + /// # use ginmi::{Client, Capabilities}; + /// # fn main() -> std::io::Result<()> { + /// # tokio_test::block_on(async { + /// # const CERT: &str = "CA Certificate"; + /// let mut client = Client::builder("https://clab-srl01-srl:57400") + /// .tls(CERT, "clab-srl01-srl") + /// .credentials("admin", "admin") + /// .build() + /// .await + /// .unwrap(); + /// + /// let capabilities = client.capabilities().await.unwrap(); + /// let supports_aaa_nokia = capabilities.supports_model( + /// "urn:srl_nokia/aaa:srl_nokia-aaa", + /// "Nokia", + /// "2023-10-31" + /// ); + /// # }); + /// # Ok(()) + /// # } + /// ``` pub fn supports_model(&self, name: &str, organization: &str, version: &str) -> bool { self.0.supported_models.contains(&ModelData { name: name.to_string(), @@ -34,6 +79,27 @@ impl<'a> Capabilities { /// /// # Arguments /// - encoding: The [`Encoding`] to check for. + /// + /// # Examples + /// ```rust + /// # use ginmi::{Client, Capabilities, Encoding}; + /// # fn main() -> std::io::Result<()> { + /// # tokio_test::block_on(async { + /// # const CERT: &str = "CA Certificate"; + /// let mut client = Client::builder("https://clab-srl01-srl:57400") + /// .tls(CERT, "clab-srl01-srl") + /// .credentials("admin", "admin") + /// .build() + /// .await + /// .unwrap(); + /// + /// let capabilities = client.capabilities().await.unwrap(); + /// let supports_json = capabilities.supports_encoding(Encoding::Json); + /// + /// # }); + /// # Ok(()) + /// # } + /// ``` pub fn supports_encoding(&self, encoding: Encoding) -> bool { let enc: i32 = match encoding { Encoding::Json => 0, From 13fe9469f28f64da2ca8d4601184d540c77bdd2f Mon Sep 17 00:00:00 2001 From: Leon Lux Date: Thu, 15 Feb 2024 19:14:57 +0100 Subject: [PATCH 4/5] fix doc comment on Client::capabilities --- src/client/client.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/client/client.rs b/src/client/client.rs index 11f7a96..c9c91ff 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -26,16 +26,18 @@ impl<'a> Client { /// /// # Examples /// ```rust - /// # use ginmi::Client - /// # tokio_test::block_on({ async + /// # use ginmi::Client; + /// # tokio_test::block_on(async { + /// # const CERT: &str = "CA Certificate"; /// let mut client = Client::builder("https://clab-srl01-srl:57400") /// .tls(CERT, "clab-srl01-srl") + /// .credentials("admin", "admin") /// .build() /// .await /// .unwrap(); /// - /// let capabilities = client.capabilities().await?; - /// # } + /// let capabilities = client.capabilities().await.unwrap(); + /// # }); /// ``` pub async fn capabilities(&mut self) -> Result { let req = CapabilityRequest::default(); From afdad0dcf8934439519001b8abe26e2c7fa3427f Mon Sep 17 00:00:00 2001 From: Leon Lux Date: Thu, 15 Feb 2024 19:20:33 +0100 Subject: [PATCH 5/5] bump version 0.1.1 -> 0.1.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index da9a7bb..a876054 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ description = """ An asynchrounous gNMI client to interact with and manage network devices. """ name = "ginmi" -version = "0.1.1" +version = "0.1.2" edition = "2021" keywords = ["grpc", "async", "gnmi", "network-automation"] license = "MIT OR Apache-2.0"