Compare commits
	
		
			No commits in common. "c069207f124d9fcba9c24a5772b030dc508e1f25" and "05205f4ac1e24d240ad0c32242d3dd5f00628e96" have entirely different histories.
		
	
	
		
			c069207f12
			...
			05205f4ac1
		
	
		
							
								
								
									
										27
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										27
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1780,7 +1780,6 @@ 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", | ||||||
| @ -1789,7 +1788,6 @@ dependencies = [ | |||||||
|  "harmony_tui", |  "harmony_tui", | ||||||
|  "harmony_types", |  "harmony_types", | ||||||
|  "log", |  "log", | ||||||
|  "serde", |  | ||||||
|  "tokio", |  "tokio", | ||||||
|  "url", |  "url", | ||||||
| ] | ] | ||||||
| @ -1808,7 +1806,6 @@ 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", | ||||||
| @ -1839,16 +1836,13 @@ 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", | ||||||
| ] | ] | ||||||
| @ -1857,7 +1851,6 @@ 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", | ||||||
| @ -1872,15 +1865,6 @@ 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" | ||||||
| @ -1934,6 +1918,8 @@ 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", | ||||||
| @ -4627,6 +4613,15 @@ 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,7 +10,6 @@ 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: std::fmt::Debug { | pub trait BrocadeClient { | ||||||
|     /// 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,7 +10,6 @@ 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,7 +13,6 @@ 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,5 +17,3 @@ 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,13 +3,12 @@ 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::{brocade::BrocadeSwitchClient, opnsense::OPNSenseManagementInterface}, |     infra::opnsense::OPNSenseManagementInterface, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     modules::{ |     modules::{ | ||||||
|         http::StaticFilesHttpScore, |         http::StaticFilesHttpScore, | ||||||
| @ -23,9 +22,8 @@ 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_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() { | ||||||
| @ -34,26 +32,6 @@ 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, | ||||||
|     ); |     ); | ||||||
| @ -105,7 +83,7 @@ async fn main() { | |||||||
|                 name: "wk2".to_string(), |                 name: "wk2".to_string(), | ||||||
|             }, |             }, | ||||||
|         ], |         ], | ||||||
|         switch_client: switch_client.clone(), |         switch: vec![], | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let inventory = Inventory { |     let inventory = Inventory { | ||||||
| @ -188,9 +166,3 @@ async fn main() { | |||||||
|     .await |     .await | ||||||
|     .unwrap(); |     .unwrap(); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[derive(Secret, Serialize, Deserialize, Debug)] |  | ||||||
| pub struct BrocadeSwitchAuth { |  | ||||||
|     pub username: String, |  | ||||||
|     pub password: String, |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -19,4 +19,3 @@ 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,8 +1,7 @@ | |||||||
| use brocade::BrocadeOptions; |  | ||||||
| use cidr::Ipv4Cidr; | use cidr::Ipv4Cidr; | ||||||
| use harmony::{ | use harmony::{ | ||||||
|     hardware::{Location, SwitchGroup}, |     hardware::{Location, SwitchGroup}, | ||||||
|     infra::{brocade::BrocadeSwitchClient, opnsense::OPNSenseManagementInterface}, |     infra::opnsense::OPNSenseManagementInterface, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     topology::{HAClusterTopology, LogicalHost, UnmanagedRouter}, |     topology::{HAClusterTopology, LogicalHost, UnmanagedRouter}, | ||||||
| }; | }; | ||||||
| @ -23,26 +22,6 @@ 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(); | ||||||
| 
 | 
 | ||||||
| @ -79,7 +58,7 @@ pub async fn get_topology() -> HAClusterTopology { | |||||||
|             name: "bootstrap".to_string(), |             name: "bootstrap".to_string(), | ||||||
|         }, |         }, | ||||||
|         workers: vec![], |         workers: vec![], | ||||||
|         switch_client: switch_client.clone(), |         switch: vec![], | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -96,9 +75,3 @@ 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,4 +19,3 @@ 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,15 +1,13 @@ | |||||||
| 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::{brocade::BrocadeSwitchClient, opnsense::OPNSenseManagementInterface}, |     infra::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::{Secret, SecretManager}; | use harmony_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 { | ||||||
| @ -18,26 +16,6 @@ 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(); | ||||||
| 
 | 
 | ||||||
| @ -74,7 +52,7 @@ pub async fn get_topology() -> HAClusterTopology { | |||||||
|             name: "cp0".to_string(), |             name: "cp0".to_string(), | ||||||
|         }, |         }, | ||||||
|         workers: vec![], |         workers: vec![], | ||||||
|         switch_client: switch_client.clone(), |         switch: vec![], | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -91,9 +69,3 @@ 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,6 +16,3 @@ 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,11 +3,10 @@ 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::{brocade::BrocadeSwitchClient, opnsense::OPNSenseManagementInterface}, |     infra::opnsense::OPNSenseManagementInterface, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     modules::{ |     modules::{ | ||||||
|         dummy::{ErrorScore, PanicScore, SuccessScore}, |         dummy::{ErrorScore, PanicScore, SuccessScore}, | ||||||
| @ -19,9 +18,7 @@ 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() { | ||||||
| @ -30,26 +27,6 @@ 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, | ||||||
|     ); |     ); | ||||||
| @ -77,7 +54,7 @@ async fn main() { | |||||||
|             name: "cp0".to_string(), |             name: "cp0".to_string(), | ||||||
|         }, |         }, | ||||||
|         workers: vec![], |         workers: vec![], | ||||||
|         switch_client: switch_client.clone(), |         switch: vec![], | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     let inventory = Inventory { |     let inventory = Inventory { | ||||||
| @ -132,9 +109,3 @@ async fn main() { | |||||||
|     .await |     .await | ||||||
|     .unwrap(); |     .unwrap(); | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[derive(Secret, Serialize, Deserialize, Debug)] |  | ||||||
| pub struct BrocadeSwitchAuth { |  | ||||||
|     pub username: String, |  | ||||||
|     pub password: String, |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| 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, | ||||||
| @ -12,6 +14,8 @@ 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}, | ||||||
| @ -26,6 +30,7 @@ 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)] | ||||||
| @ -38,10 +43,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] | ||||||
| @ -275,15 +280,36 @@ 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(); | ||||||
| 
 | 
 | ||||||
|         self.switch_client |         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}")))?; | ||||||
| @ -307,10 +333,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![], | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -468,7 +494,8 @@ 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> { | ||||||
|         self.switch_client.setup().await?; |         let client = self.get_switch_client().await?; | ||||||
|  |         client.setup().await?; | ||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -476,7 +503,8 @@ impl Switch for HAClusterTopology { | |||||||
|         &self, |         &self, | ||||||
|         mac_address: &MacAddress, |         mac_address: &MacAddress, | ||||||
|     ) -> Result<Option<PortLocation>, SwitchError> { |     ) -> Result<Option<PortLocation>, SwitchError> { | ||||||
|         let port = self.switch_client.find_port(mac_address).await?; |         let client = self.get_switch_client().await?; | ||||||
|  |         let port = client.find_port(mac_address).await?; | ||||||
|         Ok(port) |         Ok(port) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -676,25 +704,3 @@ 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,10 +1,4 @@ | |||||||
| use std::{ | use std::{error::Error, net::Ipv4Addr, str::FromStr, sync::Arc}; | ||||||
|     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; | ||||||
| @ -25,8 +19,8 @@ pub struct DHCPStaticEntry { | |||||||
|     pub ip: Ipv4Addr, |     pub ip: Ipv4Addr, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Display for DHCPStaticEntry { | impl std::fmt::Display for DHCPStaticEntry { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|         let mac = self |         let mac = self | ||||||
|             .mac |             .mac | ||||||
|             .iter() |             .iter() | ||||||
| @ -48,8 +42,8 @@ pub trait Firewall: Send + Sync { | |||||||
|     fn get_host(&self) -> LogicalHost; |     fn get_host(&self) -> LogicalHost; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Debug for dyn Firewall { | impl std::fmt::Debug for dyn Firewall { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|         f.write_fmt(format_args!("Firewall {}", self.get_ip())) |         f.write_fmt(format_args!("Firewall {}", self.get_ip())) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -71,7 +65,7 @@ pub struct PxeOptions { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait DhcpServer: Send + Sync + Debug { | pub trait DhcpServer: Send + Sync + std::fmt::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)>; | ||||||
| @ -110,8 +104,8 @@ pub trait DnsServer: Send + Sync { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Debug for dyn DnsServer { | impl std::fmt::Debug for dyn DnsServer { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|         f.write_fmt(format_args!("DnsServer {}", self.get_ip())) |         f.write_fmt(format_args!("DnsServer {}", self.get_ip())) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -147,8 +141,8 @@ pub enum DnsRecordType { | |||||||
|     TXT, |     TXT, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Display for DnsRecordType { | impl std::fmt::Display for DnsRecordType { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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"), | ||||||
| @ -222,8 +216,8 @@ pub struct SwitchError { | |||||||
|     msg: String, |     msg: String, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl fmt::Display for SwitchError { | impl std::fmt::Display for SwitchError { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||||
|         f.write_str(&self.msg) |         f.write_str(&self.msg) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -231,7 +225,7 @@ impl fmt::Display for SwitchError { | |||||||
| impl Error for SwitchError {} | impl Error for SwitchError {} | ||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait SwitchClient: Debug + Send + Sync { | pub trait SwitchClient: 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,14 +1,15 @@ | |||||||
| 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>, | ||||||
| } | } | ||||||
| @ -113,6 +114,12 @@ 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}; | ||||||
| @ -228,7 +235,7 @@ mod tests { | |||||||
|         assert_that!(*configured_interfaces).is_empty(); |         assert_that!(*configured_interfaces).is_empty(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[derive(Debug, Clone)] |     #[derive(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