diff --git a/Cargo.toml b/Cargo.toml index afc149e..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" @@ -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 new file mode 100644 index 0000000..51385e1 --- /dev/null +++ b/src/client/capabilities.rs @@ -0,0 +1,115 @@ +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 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() + } + + /// Check if target device supports a given model. + /// + /// # Arguments + /// - 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(), + organization: organization.to_string(), + version: version.to_string() + }) + } + + /// Check if a target device supports a given [`Encoding`]. + /// + /// # 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, + 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..c9c91ff 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; @@ -25,23 +26,23 @@ 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) -> CapabilityResponse { + pub async fn capabilities(&mut self) -> Result { let req = CapabilityRequest::default(); - match self.inner.capabilities(req).await { - Ok(val) => 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/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/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), } 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 {