forked from NationTech/harmony
		
	fix(ha_cluster): inject switch client for better testability
This commit is contained in:
		
							parent
							
								
									05205f4ac1
								
							
						
					
					
						commit
						7368184917
					
				
							
								
								
									
										27
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										27
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1780,6 +1780,7 @@ dependencies = [ | |||||||
| name = "example-nanodc" | name = "example-nanodc" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "brocade", | ||||||
|  "cidr", |  "cidr", | ||||||
|  "env_logger", |  "env_logger", | ||||||
|  "harmony", |  "harmony", | ||||||
| @ -1788,6 +1789,7 @@ dependencies = [ | |||||||
|  "harmony_tui", |  "harmony_tui", | ||||||
|  "harmony_types", |  "harmony_types", | ||||||
|  "log", |  "log", | ||||||
|  |  "serde", | ||||||
|  "tokio", |  "tokio", | ||||||
|  "url", |  "url", | ||||||
| ] | ] | ||||||
| @ -1806,6 +1808,7 @@ dependencies = [ | |||||||
| name = "example-okd-install" | name = "example-okd-install" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "brocade", | ||||||
|  "cidr", |  "cidr", | ||||||
|  "env_logger", |  "env_logger", | ||||||
|  "harmony", |  "harmony", | ||||||
| @ -1836,13 +1839,16 @@ dependencies = [ | |||||||
| name = "example-opnsense" | name = "example-opnsense" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "brocade", | ||||||
|  "cidr", |  "cidr", | ||||||
|  "env_logger", |  "env_logger", | ||||||
|  "harmony", |  "harmony", | ||||||
|  "harmony_macros", |  "harmony_macros", | ||||||
|  |  "harmony_secret", | ||||||
|  "harmony_tui", |  "harmony_tui", | ||||||
|  "harmony_types", |  "harmony_types", | ||||||
|  "log", |  "log", | ||||||
|  |  "serde", | ||||||
|  "tokio", |  "tokio", | ||||||
|  "url", |  "url", | ||||||
| ] | ] | ||||||
| @ -1851,6 +1857,7 @@ dependencies = [ | |||||||
| name = "example-pxe" | name = "example-pxe" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "brocade", | ||||||
|  "cidr", |  "cidr", | ||||||
|  "env_logger", |  "env_logger", | ||||||
|  "harmony", |  "harmony", | ||||||
| @ -1865,6 +1872,15 @@ dependencies = [ | |||||||
|  "url", |  "url", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "example-remove-rook-osd" | ||||||
|  | version = "0.1.0" | ||||||
|  | dependencies = [ | ||||||
|  |  "harmony", | ||||||
|  |  "harmony_cli", | ||||||
|  |  "tokio", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "example-rust" | name = "example-rust" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| @ -1918,8 +1934,6 @@ dependencies = [ | |||||||
|  "env_logger", |  "env_logger", | ||||||
|  "harmony", |  "harmony", | ||||||
|  "harmony_macros", |  "harmony_macros", | ||||||
|  "harmony_secret", |  | ||||||
|  "harmony_secret_derive", |  | ||||||
|  "harmony_tui", |  "harmony_tui", | ||||||
|  "harmony_types", |  "harmony_types", | ||||||
|  "log", |  "log", | ||||||
| @ -4613,15 +4627,6 @@ version = "0.8.6" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" | checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "remove_rook_osd" |  | ||||||
| version = "0.1.0" |  | ||||||
| dependencies = [ |  | ||||||
|  "harmony", |  | ||||||
|  "harmony_cli", |  | ||||||
|  "tokio", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "reqwest" | name = "reqwest" | ||||||
| version = "0.11.27" | version = "0.11.27" | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ use log::{debug, info}; | |||||||
| use regex::Regex; | use regex::Regex; | ||||||
| use std::{collections::HashSet, str::FromStr}; | use std::{collections::HashSet, str::FromStr}; | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug)] | ||||||
| pub struct FastIronClient { | pub struct FastIronClient { | ||||||
|     shell: BrocadeShell, |     shell: BrocadeShell, | ||||||
|     version: BrocadeInfo, |     version: BrocadeInfo, | ||||||
|  | |||||||
| @ -162,7 +162,7 @@ pub async fn init( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait BrocadeClient { | pub trait BrocadeClient: std::fmt::Debug { | ||||||
|     /// Retrieves the operating system and version details from the connected Brocade switch.
 |     /// Retrieves the operating system and version details from the connected Brocade switch.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// This is typically the first call made after establishing a connection to determine
 |     /// This is typically the first call made after establishing a connection to determine
 | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ use crate::{ | |||||||
|     parse_brocade_mac_address, shell::BrocadeShell, |     parse_brocade_mac_address, shell::BrocadeShell, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug)] | ||||||
| pub struct NetworkOperatingSystemClient { | pub struct NetworkOperatingSystemClient { | ||||||
|     shell: BrocadeShell, |     shell: BrocadeShell, | ||||||
|     version: BrocadeInfo, |     version: BrocadeInfo, | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ use log::info; | |||||||
| use russh::ChannelMsg; | use russh::ChannelMsg; | ||||||
| use tokio::time::timeout; | use tokio::time::timeout; | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug)] | ||||||
| pub struct BrocadeShell { | pub struct BrocadeShell { | ||||||
|     ip: IpAddr, |     ip: IpAddr, | ||||||
|     port: u16, |     port: u16, | ||||||
|  | |||||||
| @ -17,3 +17,5 @@ harmony_secret = { path = "../../harmony_secret" } | |||||||
| log = { workspace = true } | log = { workspace = true } | ||||||
| env_logger = { workspace = true } | env_logger = { workspace = true } | ||||||
| url = { workspace = true } | url = { workspace = true } | ||||||
|  | serde = { workspace = true } | ||||||
|  | brocade = { path = "../../brocade" } | ||||||
|  | |||||||
| @ -3,12 +3,13 @@ use std::{ | |||||||
|     sync::Arc, |     sync::Arc, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | use brocade::BrocadeOptions; | ||||||
| use cidr::Ipv4Cidr; | use cidr::Ipv4Cidr; | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     config::secret::SshKeyPair, |     config::secret::SshKeyPair, | ||||||
|     data::{FileContent, FilePath}, |     data::{FileContent, FilePath}, | ||||||
|     hardware::{HostCategory, Location, PhysicalHost, SwitchGroup}, |     hardware::{HostCategory, Location, PhysicalHost, SwitchGroup}, | ||||||
|     infra::opnsense::OPNSenseManagementInterface, |     infra::{brocade::BrocadeSwitchClient, opnsense::OPNSenseManagementInterface}, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     modules::{ |     modules::{ | ||||||
|         http::StaticFilesHttpScore, |         http::StaticFilesHttpScore, | ||||||
| @ -22,8 +23,9 @@ use harmony::{ | |||||||
|     topology::{LogicalHost, UnmanagedRouter}, |     topology::{LogicalHost, UnmanagedRouter}, | ||||||
| }; | }; | ||||||
| use harmony_macros::{ip, mac_address}; | use harmony_macros::{ip, mac_address}; | ||||||
| use harmony_secret::SecretManager; | use harmony_secret::{Secret, SecretManager}; | ||||||
| use harmony_types::net::Url; | use harmony_types::net::Url; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
| #[tokio::main] | #[tokio::main] | ||||||
| async fn main() { | async fn main() { | ||||||
| @ -32,6 +34,26 @@ async fn main() { | |||||||
|         name: String::from("fw0"), |         name: String::from("fw0"), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     let switch_auth = SecretManager::get_or_prompt::<BrocadeSwitchAuth>() | ||||||
|  |         .await | ||||||
|  |         .expect("Failed to get credentials"); | ||||||
|  | 
 | ||||||
|  |     let switches: Vec<IpAddr> = vec![ip!("192.168.33.101")]; | ||||||
|  |     let brocade_options = Some(BrocadeOptions { | ||||||
|  |         dry_run: *harmony::config::DRY_RUN, | ||||||
|  |         ..Default::default() | ||||||
|  |     }); | ||||||
|  |     let switch_client = BrocadeSwitchClient::init( | ||||||
|  |         &switches, | ||||||
|  |         &switch_auth.username, | ||||||
|  |         &switch_auth.password, | ||||||
|  |         brocade_options, | ||||||
|  |     ) | ||||||
|  |     .await | ||||||
|  |     .expect("Failed to connect to switch"); | ||||||
|  | 
 | ||||||
|  |     let switch_client = Arc::new(switch_client); | ||||||
|  | 
 | ||||||
|     let opnsense = Arc::new( |     let opnsense = Arc::new( | ||||||
|         harmony::infra::opnsense::OPNSenseFirewall::new(firewall, None, "root", "opnsense").await, |         harmony::infra::opnsense::OPNSenseFirewall::new(firewall, None, "root", "opnsense").await, | ||||||
|     ); |     ); | ||||||
| @ -83,7 +105,7 @@ async fn main() { | |||||||
|                 name: "wk2".to_string(), |                 name: "wk2".to_string(), | ||||||
|             }, |             }, | ||||||
|         ], |         ], | ||||||
|         switch: vec![], |         switch_client: switch_client.clone(), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let inventory = Inventory { |     let inventory = Inventory { | ||||||
| @ -166,3 +188,9 @@ async fn main() { | |||||||
|     .await |     .await | ||||||
|     .unwrap(); |     .unwrap(); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Secret, Serialize, Deserialize, Debug)] | ||||||
|  | pub struct BrocadeSwitchAuth { | ||||||
|  |     pub username: String, | ||||||
|  |     pub password: String, | ||||||
|  | } | ||||||
|  | |||||||
| @ -19,3 +19,4 @@ log = { workspace = true } | |||||||
| env_logger = { workspace = true } | env_logger = { workspace = true } | ||||||
| url = { workspace = true } | url = { workspace = true } | ||||||
| serde.workspace = true | serde.workspace = true | ||||||
|  | brocade = { path = "../../brocade" } | ||||||
|  | |||||||
| @ -1,7 +1,8 @@ | |||||||
|  | use brocade::BrocadeOptions; | ||||||
| use cidr::Ipv4Cidr; | use cidr::Ipv4Cidr; | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     hardware::{Location, SwitchGroup}, |     hardware::{Location, SwitchGroup}, | ||||||
|     infra::opnsense::OPNSenseManagementInterface, |     infra::{brocade::BrocadeSwitchClient, opnsense::OPNSenseManagementInterface}, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     topology::{HAClusterTopology, LogicalHost, UnmanagedRouter}, |     topology::{HAClusterTopology, LogicalHost, UnmanagedRouter}, | ||||||
| }; | }; | ||||||
| @ -22,6 +23,26 @@ pub async fn get_topology() -> HAClusterTopology { | |||||||
|         name: String::from("opnsense-1"), |         name: String::from("opnsense-1"), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     let switch_auth = SecretManager::get_or_prompt::<BrocadeSwitchAuth>() | ||||||
|  |         .await | ||||||
|  |         .expect("Failed to get credentials"); | ||||||
|  | 
 | ||||||
|  |     let switches: Vec<IpAddr> = vec![ip!("192.168.1.101")]; // TODO: Adjust me
 | ||||||
|  |     let brocade_options = Some(BrocadeOptions { | ||||||
|  |         dry_run: *harmony::config::DRY_RUN, | ||||||
|  |         ..Default::default() | ||||||
|  |     }); | ||||||
|  |     let switch_client = BrocadeSwitchClient::init( | ||||||
|  |         &switches, | ||||||
|  |         &switch_auth.username, | ||||||
|  |         &switch_auth.password, | ||||||
|  |         brocade_options, | ||||||
|  |     ) | ||||||
|  |     .await | ||||||
|  |     .expect("Failed to connect to switch"); | ||||||
|  | 
 | ||||||
|  |     let switch_client = Arc::new(switch_client); | ||||||
|  | 
 | ||||||
|     let config = SecretManager::get_or_prompt::<OPNSenseFirewallConfig>().await; |     let config = SecretManager::get_or_prompt::<OPNSenseFirewallConfig>().await; | ||||||
|     let config = config.unwrap(); |     let config = config.unwrap(); | ||||||
| 
 | 
 | ||||||
| @ -58,7 +79,7 @@ pub async fn get_topology() -> HAClusterTopology { | |||||||
|             name: "bootstrap".to_string(), |             name: "bootstrap".to_string(), | ||||||
|         }, |         }, | ||||||
|         workers: vec![], |         workers: vec![], | ||||||
|         switch: vec![], |         switch_client: switch_client.clone(), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -75,3 +96,9 @@ pub fn get_inventory() -> Inventory { | |||||||
|         control_plane_host: vec![], |         control_plane_host: vec![], | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Secret, Serialize, Deserialize, Debug)] | ||||||
|  | pub struct BrocadeSwitchAuth { | ||||||
|  |     pub username: String, | ||||||
|  |     pub password: String, | ||||||
|  | } | ||||||
|  | |||||||
| @ -19,3 +19,4 @@ log = { workspace = true } | |||||||
| env_logger = { workspace = true } | env_logger = { workspace = true } | ||||||
| url = { workspace = true } | url = { workspace = true } | ||||||
| serde.workspace = true | serde.workspace = true | ||||||
|  | brocade = { path = "../../brocade" } | ||||||
|  | |||||||
| @ -1,13 +1,15 @@ | |||||||
|  | use brocade::BrocadeOptions; | ||||||
| use cidr::Ipv4Cidr; | use cidr::Ipv4Cidr; | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     config::secret::OPNSenseFirewallCredentials, |     config::secret::OPNSenseFirewallCredentials, | ||||||
|     hardware::{Location, SwitchGroup}, |     hardware::{Location, SwitchGroup}, | ||||||
|     infra::opnsense::OPNSenseManagementInterface, |     infra::{brocade::BrocadeSwitchClient, opnsense::OPNSenseManagementInterface}, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     topology::{HAClusterTopology, LogicalHost, UnmanagedRouter}, |     topology::{HAClusterTopology, LogicalHost, UnmanagedRouter}, | ||||||
| }; | }; | ||||||
| use harmony_macros::{ip, ipv4}; | use harmony_macros::{ip, ipv4}; | ||||||
| use harmony_secret::SecretManager; | use harmony_secret::{Secret, SecretManager}; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
| use std::{net::IpAddr, sync::Arc}; | use std::{net::IpAddr, sync::Arc}; | ||||||
| 
 | 
 | ||||||
| pub async fn get_topology() -> HAClusterTopology { | pub async fn get_topology() -> HAClusterTopology { | ||||||
| @ -16,6 +18,26 @@ pub async fn get_topology() -> HAClusterTopology { | |||||||
|         name: String::from("opnsense-1"), |         name: String::from("opnsense-1"), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     let switch_auth = SecretManager::get_or_prompt::<BrocadeSwitchAuth>() | ||||||
|  |         .await | ||||||
|  |         .expect("Failed to get credentials"); | ||||||
|  | 
 | ||||||
|  |     let switches: Vec<IpAddr> = vec![ip!("192.168.1.101")]; // TODO: Adjust me
 | ||||||
|  |     let brocade_options = Some(BrocadeOptions { | ||||||
|  |         dry_run: *harmony::config::DRY_RUN, | ||||||
|  |         ..Default::default() | ||||||
|  |     }); | ||||||
|  |     let switch_client = BrocadeSwitchClient::init( | ||||||
|  |         &switches, | ||||||
|  |         &switch_auth.username, | ||||||
|  |         &switch_auth.password, | ||||||
|  |         brocade_options, | ||||||
|  |     ) | ||||||
|  |     .await | ||||||
|  |     .expect("Failed to connect to switch"); | ||||||
|  | 
 | ||||||
|  |     let switch_client = Arc::new(switch_client); | ||||||
|  | 
 | ||||||
|     let config = SecretManager::get_or_prompt::<OPNSenseFirewallCredentials>().await; |     let config = SecretManager::get_or_prompt::<OPNSenseFirewallCredentials>().await; | ||||||
|     let config = config.unwrap(); |     let config = config.unwrap(); | ||||||
| 
 | 
 | ||||||
| @ -52,7 +74,7 @@ pub async fn get_topology() -> HAClusterTopology { | |||||||
|             name: "cp0".to_string(), |             name: "cp0".to_string(), | ||||||
|         }, |         }, | ||||||
|         workers: vec![], |         workers: vec![], | ||||||
|         switch: vec![], |         switch_client: switch_client.clone(), | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -69,3 +91,9 @@ pub fn get_inventory() -> Inventory { | |||||||
|         control_plane_host: vec![], |         control_plane_host: vec![], | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Secret, Serialize, Deserialize, Debug)] | ||||||
|  | pub struct BrocadeSwitchAuth { | ||||||
|  |     pub username: String, | ||||||
|  |     pub password: String, | ||||||
|  | } | ||||||
|  | |||||||
| @ -16,3 +16,6 @@ harmony_macros = { path = "../../harmony_macros" } | |||||||
| log = { workspace = true } | log = { workspace = true } | ||||||
| env_logger = { workspace = true } | env_logger = { workspace = true } | ||||||
| url = { workspace = true } | url = { workspace = true } | ||||||
|  | harmony_secret = { path = "../../harmony_secret" } | ||||||
|  | brocade = { path = "../../brocade" } | ||||||
|  | serde = { workspace = true } | ||||||
|  | |||||||
| @ -3,10 +3,11 @@ use std::{ | |||||||
|     sync::Arc, |     sync::Arc, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | use brocade::BrocadeOptions; | ||||||
| use cidr::Ipv4Cidr; | use cidr::Ipv4Cidr; | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     hardware::{HostCategory, Location, PhysicalHost, SwitchGroup}, |     hardware::{HostCategory, Location, PhysicalHost, SwitchGroup}, | ||||||
|     infra::opnsense::OPNSenseManagementInterface, |     infra::{brocade::BrocadeSwitchClient, opnsense::OPNSenseManagementInterface}, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     modules::{ |     modules::{ | ||||||
|         dummy::{ErrorScore, PanicScore, SuccessScore}, |         dummy::{ErrorScore, PanicScore, SuccessScore}, | ||||||
| @ -18,7 +19,9 @@ use harmony::{ | |||||||
|     topology::{LogicalHost, UnmanagedRouter}, |     topology::{LogicalHost, UnmanagedRouter}, | ||||||
| }; | }; | ||||||
| use harmony_macros::{ip, mac_address}; | use harmony_macros::{ip, mac_address}; | ||||||
|  | use harmony_secret::{Secret, SecretManager}; | ||||||
| use harmony_types::net::Url; | use harmony_types::net::Url; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
| #[tokio::main] | #[tokio::main] | ||||||
| async fn main() { | async fn main() { | ||||||
| @ -27,6 +30,26 @@ async fn main() { | |||||||
|         name: String::from("opnsense-1"), |         name: String::from("opnsense-1"), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     let switch_auth = SecretManager::get_or_prompt::<BrocadeSwitchAuth>() | ||||||
|  |         .await | ||||||
|  |         .expect("Failed to get credentials"); | ||||||
|  | 
 | ||||||
|  |     let switches: Vec<IpAddr> = vec![ip!("192.168.5.101")]; // TODO: Adjust me
 | ||||||
|  |     let brocade_options = Some(BrocadeOptions { | ||||||
|  |         dry_run: *harmony::config::DRY_RUN, | ||||||
|  |         ..Default::default() | ||||||
|  |     }); | ||||||
|  |     let switch_client = BrocadeSwitchClient::init( | ||||||
|  |         &switches, | ||||||
|  |         &switch_auth.username, | ||||||
|  |         &switch_auth.password, | ||||||
|  |         brocade_options, | ||||||
|  |     ) | ||||||
|  |     .await | ||||||
|  |     .expect("Failed to connect to switch"); | ||||||
|  | 
 | ||||||
|  |     let switch_client = Arc::new(switch_client); | ||||||
|  | 
 | ||||||
|     let opnsense = Arc::new( |     let opnsense = Arc::new( | ||||||
|         harmony::infra::opnsense::OPNSenseFirewall::new(firewall, None, "root", "opnsense").await, |         harmony::infra::opnsense::OPNSenseFirewall::new(firewall, None, "root", "opnsense").await, | ||||||
|     ); |     ); | ||||||
| @ -54,7 +77,7 @@ async fn main() { | |||||||
|             name: "cp0".to_string(), |             name: "cp0".to_string(), | ||||||
|         }, |         }, | ||||||
|         workers: vec![], |         workers: vec![], | ||||||
|         switch: vec![], |         switch_client: switch_client.clone(), | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let inventory = Inventory { |     let inventory = Inventory { | ||||||
| @ -109,3 +132,9 @@ async fn main() { | |||||||
|     .await |     .await | ||||||
|     .unwrap(); |     .unwrap(); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[derive(Secret, Serialize, Deserialize, Debug)] | ||||||
|  | pub struct BrocadeSwitchAuth { | ||||||
|  |     pub username: String, | ||||||
|  |     pub password: String, | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,7 +1,5 @@ | |||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use brocade::BrocadeOptions; |  | ||||||
| use harmony_macros::ip; | use harmony_macros::ip; | ||||||
| use harmony_secret::SecretManager; |  | ||||||
| use harmony_types::{ | use harmony_types::{ | ||||||
|     net::{MacAddress, Url}, |     net::{MacAddress, Url}, | ||||||
|     switch::PortLocation, |     switch::PortLocation, | ||||||
| @ -14,8 +12,6 @@ use log::info; | |||||||
| use crate::data::FileContent; | use crate::data::FileContent; | ||||||
| use crate::executors::ExecutorError; | use crate::executors::ExecutorError; | ||||||
| use crate::hardware::PhysicalHost; | use crate::hardware::PhysicalHost; | ||||||
| use crate::infra::brocade::BrocadeSwitchAuth; |  | ||||||
| use crate::infra::brocade::BrocadeSwitchClient; |  | ||||||
| use crate::modules::okd::crd::{ | use crate::modules::okd::crd::{ | ||||||
|     InstallPlanApproval, OperatorGroup, OperatorGroupSpec, Subscription, SubscriptionSpec, |     InstallPlanApproval, OperatorGroup, OperatorGroupSpec, Subscription, SubscriptionSpec, | ||||||
|     nmstate::{self, NMState, NodeNetworkConfigurationPolicy, NodeNetworkConfigurationPolicySpec}, |     nmstate::{self, NMState, NodeNetworkConfigurationPolicy, NodeNetworkConfigurationPolicySpec}, | ||||||
| @ -30,7 +26,6 @@ use super::{ | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use std::collections::BTreeMap; | use std::collections::BTreeMap; | ||||||
| use std::net::IpAddr; |  | ||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug, Clone)] | ||||||
| @ -43,10 +38,10 @@ pub struct HAClusterTopology { | |||||||
|     pub tftp_server: Arc<dyn TftpServer>, |     pub tftp_server: Arc<dyn TftpServer>, | ||||||
|     pub http_server: Arc<dyn HttpServer>, |     pub http_server: Arc<dyn HttpServer>, | ||||||
|     pub dns_server: Arc<dyn DnsServer>, |     pub dns_server: Arc<dyn DnsServer>, | ||||||
|  |     pub switch_client: Arc<dyn SwitchClient>, | ||||||
|     pub bootstrap_host: LogicalHost, |     pub bootstrap_host: LogicalHost, | ||||||
|     pub control_plane: Vec<LogicalHost>, |     pub control_plane: Vec<LogicalHost>, | ||||||
|     pub workers: Vec<LogicalHost>, |     pub workers: Vec<LogicalHost>, | ||||||
|     pub switch: Vec<LogicalHost>, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| @ -280,36 +275,15 @@ impl HAClusterTopology { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async fn get_switch_client(&self) -> Result<Box<dyn SwitchClient>, SwitchError> { |  | ||||||
|         let auth = SecretManager::get_or_prompt::<BrocadeSwitchAuth>() |  | ||||||
|             .await |  | ||||||
|             .map_err(|e| SwitchError::new(format!("Failed to get credentials: {e}")))?; |  | ||||||
| 
 |  | ||||||
|         // FIXME: We assume Brocade switches
 |  | ||||||
|         let switches: Vec<IpAddr> = self.switch.iter().map(|s| s.ip).collect(); |  | ||||||
|         let brocade_options = Some(BrocadeOptions { |  | ||||||
|             dry_run: *crate::config::DRY_RUN, |  | ||||||
|             ..Default::default() |  | ||||||
|         }); |  | ||||||
|         let client = |  | ||||||
|             BrocadeSwitchClient::init(&switches, &auth.username, &auth.password, brocade_options) |  | ||||||
|                 .await |  | ||||||
|                 .map_err(|e| SwitchError::new(format!("Failed to connect to switch: {e}")))?; |  | ||||||
| 
 |  | ||||||
|         Ok(Box::new(client)) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     async fn configure_port_channel( |     async fn configure_port_channel( | ||||||
|         &self, |         &self, | ||||||
|         host: &PhysicalHost, |         host: &PhysicalHost, | ||||||
|         config: &HostNetworkConfig, |         config: &HostNetworkConfig, | ||||||
|     ) -> Result<(), SwitchError> { |     ) -> Result<(), SwitchError> { | ||||||
|         debug!("Configuring port channel: {config:#?}"); |         debug!("Configuring port channel: {config:#?}"); | ||||||
|         let client = self.get_switch_client().await?; |  | ||||||
| 
 |  | ||||||
|         let switch_ports = config.switch_ports.iter().map(|s| s.port.clone()).collect(); |         let switch_ports = config.switch_ports.iter().map(|s| s.port.clone()).collect(); | ||||||
| 
 | 
 | ||||||
|         client |         self.switch_client | ||||||
|             .configure_port_channel(&format!("Harmony_{}", host.id), switch_ports) |             .configure_port_channel(&format!("Harmony_{}", host.id), switch_ports) | ||||||
|             .await |             .await | ||||||
|             .map_err(|e| SwitchError::new(format!("Failed to configure switch: {e}")))?; |             .map_err(|e| SwitchError::new(format!("Failed to configure switch: {e}")))?; | ||||||
| @ -333,10 +307,10 @@ impl HAClusterTopology { | |||||||
|             tftp_server: dummy_infra.clone(), |             tftp_server: dummy_infra.clone(), | ||||||
|             http_server: dummy_infra.clone(), |             http_server: dummy_infra.clone(), | ||||||
|             dns_server: dummy_infra.clone(), |             dns_server: dummy_infra.clone(), | ||||||
|  |             switch_client: dummy_infra.clone(), | ||||||
|             bootstrap_host: dummy_host, |             bootstrap_host: dummy_host, | ||||||
|             control_plane: vec![], |             control_plane: vec![], | ||||||
|             workers: vec![], |             workers: vec![], | ||||||
|             switch: vec![], |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -494,8 +468,7 @@ impl HttpServer for HAClusterTopology { | |||||||
| #[async_trait] | #[async_trait] | ||||||
| impl Switch for HAClusterTopology { | impl Switch for HAClusterTopology { | ||||||
|     async fn setup_switch(&self) -> Result<(), SwitchError> { |     async fn setup_switch(&self) -> Result<(), SwitchError> { | ||||||
|         let client = self.get_switch_client().await?; |         self.switch_client.setup().await?; | ||||||
|         client.setup().await?; |  | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -503,8 +476,7 @@ impl Switch for HAClusterTopology { | |||||||
|         &self, |         &self, | ||||||
|         mac_address: &MacAddress, |         mac_address: &MacAddress, | ||||||
|     ) -> Result<Option<PortLocation>, SwitchError> { |     ) -> Result<Option<PortLocation>, SwitchError> { | ||||||
|         let client = self.get_switch_client().await?; |         let port = self.switch_client.find_port(mac_address).await?; | ||||||
|         let port = client.find_port(mac_address).await?; |  | ||||||
|         Ok(port) |         Ok(port) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -704,3 +676,25 @@ impl DnsServer for DummyInfra { | |||||||
|         unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA) |         unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[async_trait] | ||||||
|  | impl SwitchClient for DummyInfra { | ||||||
|  |     async fn setup(&self) -> Result<(), SwitchError> { | ||||||
|  |         unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn find_port( | ||||||
|  |         &self, | ||||||
|  |         _mac_address: &MacAddress, | ||||||
|  |     ) -> Result<Option<PortLocation>, SwitchError> { | ||||||
|  |         unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async fn configure_port_channel( | ||||||
|  |         &self, | ||||||
|  |         _channel_name: &str, | ||||||
|  |         _switch_ports: Vec<PortLocation>, | ||||||
|  |     ) -> Result<u8, SwitchError> { | ||||||
|  |         unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,4 +1,10 @@ | |||||||
| use std::{error::Error, net::Ipv4Addr, str::FromStr, sync::Arc}; | use std::{ | ||||||
|  |     error::Error, | ||||||
|  |     fmt::{self, Debug}, | ||||||
|  |     net::Ipv4Addr, | ||||||
|  |     str::FromStr, | ||||||
|  |     sync::Arc, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use derive_new::new; | use derive_new::new; | ||||||
| @ -19,8 +25,8 @@ pub struct DHCPStaticEntry { | |||||||
|     pub ip: Ipv4Addr, |     pub ip: Ipv4Addr, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl std::fmt::Display for DHCPStaticEntry { | impl fmt::Display for DHCPStaticEntry { | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         let mac = self |         let mac = self | ||||||
|             .mac |             .mac | ||||||
|             .iter() |             .iter() | ||||||
| @ -42,8 +48,8 @@ pub trait Firewall: Send + Sync { | |||||||
|     fn get_host(&self) -> LogicalHost; |     fn get_host(&self) -> LogicalHost; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl std::fmt::Debug for dyn Firewall { | impl Debug for dyn Firewall { | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         f.write_fmt(format_args!("Firewall {}", self.get_ip())) |         f.write_fmt(format_args!("Firewall {}", self.get_ip())) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -65,7 +71,7 @@ pub struct PxeOptions { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait DhcpServer: Send + Sync + std::fmt::Debug { | pub trait DhcpServer: Send + Sync + Debug { | ||||||
|     async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>; |     async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>; | ||||||
|     async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>; |     async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>; | ||||||
|     async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>; |     async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>; | ||||||
| @ -104,8 +110,8 @@ pub trait DnsServer: Send + Sync { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl std::fmt::Debug for dyn DnsServer { | impl Debug for dyn DnsServer { | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         f.write_fmt(format_args!("DnsServer {}", self.get_ip())) |         f.write_fmt(format_args!("DnsServer {}", self.get_ip())) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -141,8 +147,8 @@ pub enum DnsRecordType { | |||||||
|     TXT, |     TXT, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl std::fmt::Display for DnsRecordType { | impl fmt::Display for DnsRecordType { | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         match self { |         match self { | ||||||
|             DnsRecordType::A => write!(f, "A"), |             DnsRecordType::A => write!(f, "A"), | ||||||
|             DnsRecordType::AAAA => write!(f, "AAAA"), |             DnsRecordType::AAAA => write!(f, "AAAA"), | ||||||
| @ -216,8 +222,8 @@ pub struct SwitchError { | |||||||
|     msg: String, |     msg: String, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl std::fmt::Display for SwitchError { | impl fmt::Display for SwitchError { | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|         f.write_str(&self.msg) |         f.write_str(&self.msg) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -225,7 +231,7 @@ impl std::fmt::Display for SwitchError { | |||||||
| impl Error for SwitchError {} | impl Error for SwitchError {} | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait SwitchClient: Send + Sync { | pub trait SwitchClient: Debug + Send + Sync { | ||||||
|     /// Executes essential, idempotent, one-time initial configuration steps.
 |     /// Executes essential, idempotent, one-time initial configuration steps.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// This is an opiniated procedure that setups a switch to provide high availability
 |     /// This is an opiniated procedure that setups a switch to provide high availability
 | ||||||
|  | |||||||
| @ -1,15 +1,14 @@ | |||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use brocade::{BrocadeClient, BrocadeOptions, InterSwitchLink, InterfaceStatus, PortOperatingMode}; | use brocade::{BrocadeClient, BrocadeOptions, InterSwitchLink, InterfaceStatus, PortOperatingMode}; | ||||||
| use harmony_secret::Secret; |  | ||||||
| use harmony_types::{ | use harmony_types::{ | ||||||
|     net::{IpAddress, MacAddress}, |     net::{IpAddress, MacAddress}, | ||||||
|     switch::{PortDeclaration, PortLocation}, |     switch::{PortDeclaration, PortLocation}, | ||||||
| }; | }; | ||||||
| use option_ext::OptionExt; | use option_ext::OptionExt; | ||||||
| use serde::{Deserialize, Serialize}; |  | ||||||
| 
 | 
 | ||||||
| use crate::topology::{SwitchClient, SwitchError}; | use crate::topology::{SwitchClient, SwitchError}; | ||||||
| 
 | 
 | ||||||
|  | #[derive(Debug)] | ||||||
| pub struct BrocadeSwitchClient { | pub struct BrocadeSwitchClient { | ||||||
|     brocade: Box<dyn BrocadeClient + Send + Sync>, |     brocade: Box<dyn BrocadeClient + Send + Sync>, | ||||||
| } | } | ||||||
| @ -114,12 +113,6 @@ impl SwitchClient for BrocadeSwitchClient { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Secret, Serialize, Deserialize, Debug)] |  | ||||||
| pub struct BrocadeSwitchAuth { |  | ||||||
|     pub username: String, |  | ||||||
|     pub password: String, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use std::sync::{Arc, Mutex}; |     use std::sync::{Arc, Mutex}; | ||||||
| @ -235,7 +228,7 @@ mod tests { | |||||||
|         assert_that!(*configured_interfaces).is_empty(); |         assert_that!(*configured_interfaces).is_empty(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[derive(Clone)] |     #[derive(Debug, Clone)] | ||||||
|     struct FakeBrocadeClient { |     struct FakeBrocadeClient { | ||||||
|         stack_topology: Vec<InterSwitchLink>, |         stack_topology: Vec<InterSwitchLink>, | ||||||
|         interfaces: Vec<InterfaceInfo>, |         interfaces: Vec<InterfaceInfo>, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user