diff --git a/brocade/src/fast_iron.rs b/brocade/src/fast_iron.rs index 5a3474e..6e256a8 100644 --- a/brocade/src/fast_iron.rs +++ b/brocade/src/fast_iron.rs @@ -1,7 +1,8 @@ use super::BrocadeClient; use crate::{ BrocadeInfo, Error, ExecutionMode, InterSwitchLink, InterfaceInfo, MacAddressEntry, - PortChannelId, PortOperatingMode, parse_brocade_mac_address, shell::BrocadeShell, + PortChannelId, PortOperatingMode, SecurityLevel, parse_brocade_mac_address, + shell::BrocadeShell, }; use async_trait::async_trait; @@ -209,4 +210,20 @@ impl BrocadeClient for FastIronClient { info!("[Brocade] Port-channel '{channel_name}' cleared."); Ok(()) } + + async fn enable_snmp(&self, user_name: &str, auth: &str, des: &str) -> Result<(), Error> { + let commands = vec![ + "configure terminal".into(), + "snmp-server view ALL 1 included".into(), + "snmp-server group public v3 priv read ALL".into(), + format!( + "snmp-server user {user_name} groupname public auth md5 auth-password {auth} priv des priv-password {des}" + ), + "exit".into(), + ]; + self.shell + .run_commands(commands, ExecutionMode::Regular) + .await?; + Ok(()) + } } diff --git a/brocade/src/lib.rs b/brocade/src/lib.rs index c0b5b70..32ca31f 100644 --- a/brocade/src/lib.rs +++ b/brocade/src/lib.rs @@ -237,6 +237,15 @@ pub trait BrocadeClient: std::fmt::Debug { ports: &[PortLocation], ) -> Result<(), Error>; + /// Enables Simple Network Management Protocol (SNMP) server for switch + /// + /// # Parameters + /// + /// * `user_name`: The user name for the snmp server + /// * `auth`: The password for authentication process for verifying the identity of a device + /// * `des`: The Data Encryption Standard algorithm key + async fn enable_snmp(&self, user_name: &str, auth: &str, des: &str) -> Result<(), Error>; + /// Removes all configuration associated with the specified Port-Channel name. /// /// This operation should be idempotent; attempting to clear a non-existent @@ -300,6 +309,11 @@ fn parse_brocade_mac_address(value: &str) -> Result { Ok(MacAddress(bytes)) } +#[derive(Debug)] +pub enum SecurityLevel { + AuthPriv(String), +} + #[derive(Debug)] pub enum Error { NetworkError(String), diff --git a/brocade/src/network_operating_system.rs b/brocade/src/network_operating_system.rs index f4db713..1985aba 100644 --- a/brocade/src/network_operating_system.rs +++ b/brocade/src/network_operating_system.rs @@ -8,7 +8,7 @@ use regex::Regex; use crate::{ BrocadeClient, BrocadeInfo, Error, ExecutionMode, InterSwitchLink, InterfaceInfo, InterfaceStatus, InterfaceType, MacAddressEntry, PortChannelId, PortOperatingMode, - parse_brocade_mac_address, shell::BrocadeShell, + SecurityLevel, parse_brocade_mac_address, shell::BrocadeShell, }; #[derive(Debug)] @@ -330,4 +330,20 @@ impl BrocadeClient for NetworkOperatingSystemClient { info!("[Brocade] Port-channel '{channel_name}' cleared."); Ok(()) } + + async fn enable_snmp(&self, user_name: &str, auth: &str, des: &str) -> Result<(), Error> { + let commands = vec![ + "configure terminal".into(), + "snmp-server view ALL 1 included".into(), + "snmp-server group public v3 priv read ALL".into(), + format!( + "snmp-server user {user_name} groupname public auth md5 auth-password {auth} priv des priv-password {des}" + ), + "exit".into(), + ]; + self.shell + .run_commands(commands, ExecutionMode::Regular) + .await?; + Ok(()) + } } diff --git a/examples/brocade_snmp_server/Cargo.toml b/examples/brocade_snmp_server/Cargo.toml new file mode 100644 index 0000000..0f476a8 --- /dev/null +++ b/examples/brocade_snmp_server/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "brocade-snmp-server" +edition = "2024" +version.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +harmony = { path = "../../harmony" } +brocade = { path = "../../brocade" } +harmony_secret = { path = "../../harmony_secret" } +harmony_cli = { path = "../../harmony_cli" } +harmony_types = { path = "../../harmony_types" } +harmony_macros = { path = "../../harmony_macros" } +tokio = { workspace = true } +log = { workspace = true } +env_logger = { workspace = true } +url = { workspace = true } +base64.workspace = true +serde.workspace = true diff --git a/examples/brocade_snmp_server/src/main.rs b/examples/brocade_snmp_server/src/main.rs new file mode 100644 index 0000000..ff9b671 --- /dev/null +++ b/examples/brocade_snmp_server/src/main.rs @@ -0,0 +1,22 @@ +use std::net::{IpAddr, Ipv4Addr}; + +use harmony::{ + inventory::Inventory, modules::brocade::BrocadeEnableSnmpScore, topology::K8sAnywhereTopology, +}; + +#[tokio::main] +async fn main() { + let brocade_snmp_server = BrocadeEnableSnmpScore { + server_ips: vec![IpAddr::V4(Ipv4Addr::new(192, 168, 1, 111))], + dry_run: true, + }; + + harmony_cli::run( + Inventory::autoload(), + K8sAnywhereTopology::from_env(), + vec![Box::new(brocade_snmp_server)], + None, + ) + .await + .unwrap(); +} diff --git a/harmony/src/infra/brocade.rs b/harmony/src/infra/brocade.rs index 774c8f8..0cac5ed 100644 --- a/harmony/src/infra/brocade.rs +++ b/harmony/src/infra/brocade.rs @@ -121,7 +121,7 @@ mod tests { use async_trait::async_trait; use brocade::{ BrocadeClient, BrocadeInfo, Error, InterSwitchLink, InterfaceInfo, InterfaceStatus, - InterfaceType, MacAddressEntry, PortChannelId, PortOperatingMode, + InterfaceType, MacAddressEntry, PortChannelId, PortOperatingMode, SecurityLevel, }; use harmony_types::switch::PortLocation; @@ -279,6 +279,10 @@ mod tests { async fn clear_port_channel(&self, _channel_name: &str) -> Result<(), Error> { todo!() } + + async fn enable_snmp(&self, user_name: &str, auth: &str, des: &str) -> Result<(), Error> { + todo!() + } } impl FakeBrocadeClient { diff --git a/harmony/src/modules/brocade.rs b/harmony/src/modules/brocade.rs new file mode 100644 index 0000000..7264609 --- /dev/null +++ b/harmony/src/modules/brocade.rs @@ -0,0 +1,117 @@ +use std::net::{IpAddr, Ipv4Addr}; + +use async_trait::async_trait; +use brocade::BrocadeOptions; +use harmony_secret::{Secret, SecretManager}; +use harmony_types::id::Id; +use serde::{Deserialize, Serialize}; + +use crate::{ + data::Version, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::Inventory, + score::Score, + topology::Topology, +}; + +#[derive(Debug, Clone, Serialize)] +pub struct BrocadeEnableSnmpScore { + pub server_ips: Vec, + pub dry_run: bool, +} + +impl Score for BrocadeEnableSnmpScore { + fn name(&self) -> String { + "BrocadeEnableSnmpScore".to_string() + } + + fn create_interpret(&self) -> Box> { + Box::new(BrocadeEnableSnmpInterpret { + score: self.clone(), + }) + } +} + +#[derive(Debug, Clone, Serialize)] +pub struct BrocadeEnableSnmpInterpret { + score: BrocadeEnableSnmpScore, +} + +#[derive(Secret, Clone, Debug, Serialize, Deserialize)] +struct BrocadeSwitchAuth { + username: String, + password: String, +} + +#[derive(Secret, Clone, Debug, Serialize, Deserialize)] +struct BrocadeSnmpAuth { + username: String, + auth_password: String, + des_password: String, +} + +#[async_trait] +impl Interpret for BrocadeEnableSnmpInterpret { + async fn execute( + &self, + _inventory: &Inventory, + _topology: &T, + ) -> Result { + let switch_addresses = &self.score.server_ips; + + let snmp_auth = SecretManager::get_or_prompt::() + .await + .unwrap(); + + let config = SecretManager::get_or_prompt::() + .await + .unwrap(); + + let brocade = brocade::init( + &switch_addresses, + 22, + &config.username, + &config.password, + Some(BrocadeOptions { + dry_run: self.score.dry_run, + ..Default::default() + }), + ) + .await + .expect("Brocade client failed to connect"); + + brocade + .enable_snmp( + &snmp_auth.username, + &snmp_auth.auth_password, + &snmp_auth.des_password, + ) + .await + .map_err(|e| InterpretError::new(e.to_string()))?; + + Ok(Outcome::success(format!( + "Activated snmp server for Brocade at {}", + switch_addresses + .iter() + .map(|s| s.to_string()) + .collect::>() + .join(", ") + ))) + } + + fn get_name(&self) -> InterpretName { + InterpretName::Custom("BrocadeEnableSnmpInterpret") + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } +} diff --git a/harmony/src/modules/mod.rs b/harmony/src/modules/mod.rs index 682e16b..a5a2ad1 100644 --- a/harmony/src/modules/mod.rs +++ b/harmony/src/modules/mod.rs @@ -1,4 +1,5 @@ pub mod application; +pub mod brocade; pub mod cert_manager; pub mod dhcp; pub mod dns;