use std::str::FromStr; use async_trait::async_trait; use brocade::{BrocadeOptions, PortOperatingMode}; use harmony::{ data::Version, infra::brocade::BrocadeSwitchClient, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, score::Score, topology::{ HostNetworkConfig, PortConfig, PreparationError, PreparationOutcome, Switch, SwitchClient, SwitchError, Topology, }, }; use harmony_macros::ip; use harmony_types::{id::Id, net::MacAddress, switch::PortLocation}; use log::{debug, info}; use serde::Serialize; #[tokio::main] async fn main() { let switch_score = BrocadeSwitchScore { port_channels_to_clear: vec![ Id::from_str("17").unwrap(), Id::from_str("19").unwrap(), Id::from_str("18").unwrap(), ], ports_to_configure: vec![ (PortLocation(2, 0, 17), PortOperatingMode::Trunk), (PortLocation(2, 0, 19), PortOperatingMode::Trunk), (PortLocation(1, 0, 18), PortOperatingMode::Trunk), ], }; harmony_cli::run( Inventory::autoload(), SwitchTopology::new().await, vec![Box::new(switch_score)], None, ) .await .unwrap(); } #[derive(Clone, Debug, Serialize)] struct BrocadeSwitchScore { port_channels_to_clear: Vec, ports_to_configure: Vec, } impl Score for BrocadeSwitchScore { fn name(&self) -> String { "BrocadeSwitchScore".to_string() } #[doc(hidden)] fn create_interpret(&self) -> Box> { Box::new(BrocadeSwitchInterpret { score: self.clone(), }) } } #[derive(Debug)] struct BrocadeSwitchInterpret { score: BrocadeSwitchScore, } #[async_trait] impl Interpret for BrocadeSwitchInterpret { async fn execute( &self, _inventory: &Inventory, topology: &T, ) -> Result { info!("Applying switch configuration {:?}", self.score); debug!( "Clearing port channel {:?}", self.score.port_channels_to_clear ); topology .clear_port_channel(&self.score.port_channels_to_clear) .await .map_err(|e| InterpretError::new(e.to_string()))?; debug!("Configuring interfaces {:?}", self.score.ports_to_configure); topology .configure_interface(&self.score.ports_to_configure) .await .map_err(|e| InterpretError::new(e.to_string()))?; Ok(Outcome::success("switch configured".to_string())) } fn get_name(&self) -> InterpretName { InterpretName::Custom("BrocadeSwitchInterpret") } fn get_version(&self) -> Version { todo!() } fn get_status(&self) -> InterpretStatus { todo!() } fn get_children(&self) -> Vec { todo!() } } struct SwitchTopology { client: Box, } #[async_trait] impl Topology for SwitchTopology { fn name(&self) -> &str { "SwitchTopology" } async fn ensure_ready(&self) -> Result { Ok(PreparationOutcome::Noop) } } impl SwitchTopology { async fn new() -> Self { let mut options = BrocadeOptions::default(); options.ssh.port = 2222; let client = BrocadeSwitchClient::init(&vec![ip!("127.0.0.1")], &"admin", &"password", options) .await .expect("Failed to connect to switch"); let client = Box::new(client); Self { client } } } #[async_trait] impl Switch for SwitchTopology { async fn setup_switch(&self) -> Result<(), SwitchError> { todo!() } async fn get_port_for_mac_address( &self, _mac_address: &MacAddress, ) -> Result, SwitchError> { todo!() } async fn configure_port_channel(&self, _config: &HostNetworkConfig) -> Result<(), SwitchError> { todo!() } async fn clear_port_channel(&self, ids: &Vec) -> Result<(), SwitchError> { self.client.clear_port_channel(ids).await } async fn configure_interface(&self, ports: &Vec) -> Result<(), SwitchError> { self.client.configure_interface(ports).await } }