merge 2 configure steps in 1 and let the topology handle it
All checks were successful
Run Check Script / check (pull_request) Successful in 1m2s

This commit is contained in:
Ian Letourneau 2025-09-15 22:47:56 -04:00
parent fe0501b784
commit 427009bbfe
3 changed files with 47 additions and 172 deletions

View File

@ -28,7 +28,6 @@ use super::PreparationOutcome;
use super::Router;
use super::Switch;
use super::SwitchError;
use super::SwitchNetworkConfig;
use super::TftpServer;
use super::Topology;
@ -270,7 +269,7 @@ impl HttpServer for HAClusterTopology {
#[async_trait]
impl Switch for HAClusterTopology {
async fn get_port_for_mac_address(&self, mac_address: &MacAddress) -> Option<String> {
async fn get_port_for_mac_address(&self, _mac_address: &MacAddress) -> Option<String> {
todo!()
}
@ -281,14 +280,6 @@ impl Switch for HAClusterTopology {
) -> Result<(), SwitchError> {
todo!()
}
async fn configure_switch_network(
&self,
_host: &PhysicalHost,
_config: SwitchNetworkConfig,
) -> Result<(), SwitchError> {
todo!()
}
}
#[derive(Debug)]

View File

@ -182,40 +182,20 @@ pub trait Switch: Send + Sync {
host: &PhysicalHost,
config: HostNetworkConfig,
) -> Result<(), SwitchError>;
async fn configure_switch_network(
&self,
host: &PhysicalHost,
config: SwitchNetworkConfig,
) -> Result<(), SwitchError>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct HostNetworkConfig {
pub bond: Bond,
pub switch_ports: Vec<SwitchPort>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Bond {
pub interfaces: Vec<SlaveInterface>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SlaveInterface {
pub struct SwitchPort {
pub mac_address: MacAddress,
pub port_name: String,
// FIXME: Should we add speed as well? And other params
}
#[derive(Clone, Debug, PartialEq)]
pub struct SwitchNetworkConfig {
pub port_channel: PortChannel,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PortChannel {
pub ports: Vec<String>,
}
#[derive(Debug, Clone, new)]
pub struct SwitchError {
msg: String,

View File

@ -8,10 +8,7 @@ use crate::{
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
topology::{
self, Bond, HostNetworkConfig, PortChannel, SlaveInterface, Switch, SwitchNetworkConfig,
Topology,
},
topology::{HostNetworkConfig, Switch, SwitchPort, Topology},
};
#[derive(Debug, Clone, Serialize)]
@ -60,31 +57,19 @@ impl<T: Topology + Switch> Interpret<T> for HostNetworkConfigurationInterpret {
topology: &T,
) -> Result<Outcome, InterpretError> {
for host in &self.score.hosts {
let mut interfaces = vec![];
let mut ports = vec![];
let mut switch_ports = vec![];
for mac_address in host.get_mac_address() {
if let Some(port) = topology.get_port_for_mac_address(&mac_address).await {
interfaces.push(SlaveInterface { mac_address });
ports.push(port);
if let Some(port_name) = topology.get_port_for_mac_address(&mac_address).await {
switch_ports.push(SwitchPort {
mac_address,
port_name,
});
}
}
let _ = topology
.configure_host_network(
host,
HostNetworkConfig {
bond: Bond { interfaces },
},
)
.await;
let _ = topology
.configure_switch_network(
host,
SwitchNetworkConfig {
port_channel: PortChannel { ports },
},
)
.configure_host_network(host, HostNetworkConfig { switch_ports })
.await;
}
@ -93,9 +78,8 @@ impl<T: Topology + Switch> Interpret<T> for HostNetworkConfigurationInterpret {
// let port = topology.get_port_for_mac_address(); // si pas de port -> mac address pas connectee
// create port channel for all ports found
// create bond for all valid addresses (port found)
// apply network to host first, then switch (to avoid losing hosts that are already connected)
// topology.configure_host_network(host, config) <--- will create bonds
// topology.configure_switch_network(host, config) <--- will create port channels
// apply network config
// topology.configure_host_network(host, config) <--- will create both bonds & port channels
Ok(Outcome::success("".into()))
}
@ -110,8 +94,7 @@ mod tests {
use crate::{
hardware::HostCategory,
topology::{
Bond, HostNetworkConfig, PortChannel, PreparationError, PreparationOutcome,
SlaveInterface, SwitchError, SwitchNetworkConfig,
HostNetworkConfig, PreparationError, PreparationOutcome, SwitchError, SwitchPort,
},
};
use std::{
@ -125,11 +108,11 @@ mod tests {
pub static ref HOST_ID: Id = Id::from_str("host-1").unwrap();
pub static ref ANOTHER_HOST_ID: Id = Id::from_str("host-2").unwrap();
pub static ref EXISTING_INTERFACE: MacAddress =
MacAddress::try_from("00:00:00:00:00:00".to_string()).unwrap();
MacAddress::try_from("AA:BB:CC:DD:EE:F1".to_string()).unwrap();
pub static ref ANOTHER_EXISTING_INTERFACE: MacAddress =
MacAddress::try_from("42:42:42:42:42:42".to_string()).unwrap();
MacAddress::try_from("AA:BB:CC:DD:EE:F2".to_string()).unwrap();
pub static ref UNKNOWN_INTERFACE: MacAddress =
MacAddress::try_from("99:99:99:99:99:99".to_string()).unwrap();
MacAddress::try_from("11:22:33:44:55:61".to_string()).unwrap();
pub static ref PORT: String = "1/0/42".into();
pub static ref ANOTHER_PORT: String = "2/0/42".into();
}
@ -146,31 +129,11 @@ mod tests {
assert_that!(*configured_host_networks).contains_exactly(vec![(
HOST_ID.clone(),
HostNetworkConfig {
bond: Bond {
interfaces: vec![SlaveInterface {
switch_ports: vec![SwitchPort {
mac_address: *EXISTING_INTERFACE,
port_name: PORT.clone(),
}],
},
},
)]);
}
#[tokio::test]
async fn host_with_one_mac_address_should_create_port_channel_with_one_port() {
let host = given_host(&HOST_ID, vec![*EXISTING_INTERFACE]);
let score = given_score(vec![host]);
let topology = TopologyWithSwitch::new();
let _ = score.interpret(&Inventory::empty(), &topology).await;
let configured_switch_networks = topology.configured_switch_networks.lock().unwrap();
assert_that!(*configured_switch_networks).contains_exactly(vec![(
HOST_ID.clone(),
SwitchNetworkConfig {
port_channel: PortChannel {
ports: vec![PORT.clone()],
},
},
)]);
}
@ -188,17 +151,17 @@ mod tests {
assert_that!(*configured_host_networks).contains_exactly(vec![(
HOST_ID.clone(),
HostNetworkConfig {
bond: Bond {
interfaces: vec![
SlaveInterface {
switch_ports: vec![
SwitchPort {
mac_address: *EXISTING_INTERFACE,
port_name: PORT.clone(),
},
SlaveInterface {
SwitchPort {
mac_address: *ANOTHER_EXISTING_INTERFACE,
port_name: ANOTHER_PORT.clone(),
},
],
},
},
)]);
}
@ -217,53 +180,20 @@ mod tests {
(
HOST_ID.clone(),
HostNetworkConfig {
bond: Bond {
interfaces: vec![SlaveInterface {
switch_ports: vec![SwitchPort {
mac_address: *EXISTING_INTERFACE,
port_name: PORT.clone(),
}],
},
},
),
(
ANOTHER_HOST_ID.clone(),
HostNetworkConfig {
bond: Bond {
interfaces: vec![SlaveInterface {
switch_ports: vec![SwitchPort {
mac_address: *ANOTHER_EXISTING_INTERFACE,
port_name: ANOTHER_PORT.clone(),
}],
},
},
),
]);
}
#[tokio::test]
async fn multiple_hosts_should_create_one_port_channel_per_host() {
let score = given_score(vec![
given_host(&HOST_ID, vec![*EXISTING_INTERFACE]),
given_host(&ANOTHER_HOST_ID, vec![*ANOTHER_EXISTING_INTERFACE]),
]);
let topology = TopologyWithSwitch::new();
let _ = score.interpret(&Inventory::empty(), &topology).await;
let configured_switch_networks = topology.configured_switch_networks.lock().unwrap();
assert_that!(*configured_switch_networks).contains_exactly(vec![
(
HOST_ID.clone(),
SwitchNetworkConfig {
port_channel: PortChannel {
ports: vec![PORT.clone()],
},
},
),
(
ANOTHER_HOST_ID.clone(),
SwitchNetworkConfig {
port_channel: PortChannel {
ports: vec![ANOTHER_PORT.clone()],
},
},
),
]);
}
@ -280,14 +210,7 @@ mod tests {
assert_that!(*configured_host_networks).contains_exactly(vec![(
HOST_ID.clone(),
HostNetworkConfig {
bond: Bond { interfaces: vec![] },
},
)]);
let configured_switch_networks = topology.configured_switch_networks.lock().unwrap();
assert_that!(*configured_switch_networks).contains_exactly(vec![(
HOST_ID.clone(),
SwitchNetworkConfig {
port_channel: PortChannel { ports: vec![] },
switch_ports: vec![],
},
)]);
}
@ -325,25 +248,22 @@ mod tests {
}
struct TopologyWithSwitch {
available_ports: Vec<String>,
available_ports: Arc<Mutex<Vec<String>>>,
configured_host_networks: Arc<Mutex<Vec<(Id, HostNetworkConfig)>>>,
configured_switch_networks: Arc<Mutex<Vec<(Id, SwitchNetworkConfig)>>>,
}
impl TopologyWithSwitch {
fn new() -> Self {
Self {
available_ports: vec![PORT.clone(), ANOTHER_PORT.clone()],
available_ports: Arc::new(Mutex::new(vec![PORT.clone(), ANOTHER_PORT.clone()])),
configured_host_networks: Arc::new(Mutex::new(vec![])),
configured_switch_networks: Arc::new(Mutex::new(vec![])),
}
}
fn new_port_not_found() -> Self {
Self {
available_ports: vec![],
available_ports: Arc::new(Mutex::new(vec![])),
configured_host_networks: Arc::new(Mutex::new(vec![])),
configured_switch_networks: Arc::new(Mutex::new(vec![])),
}
}
}
@ -362,16 +282,11 @@ mod tests {
#[async_trait]
impl Switch for TopologyWithSwitch {
async fn get_port_for_mac_address(&self, _mac_address: &MacAddress) -> Option<String> {
if self.available_ports.is_empty() {
let mut ports = self.available_ports.lock().unwrap();
if ports.is_empty() {
return None;
}
Some(
self.available_ports
.get(self.configured_host_networks.lock().unwrap().len() % 2)
.unwrap()
.clone(),
)
Some(ports.remove(0))
}
async fn configure_host_network(
@ -384,16 +299,5 @@ mod tests {
Ok(())
}
async fn configure_switch_network(
&self,
host: &PhysicalHost,
config: SwitchNetworkConfig,
) -> Result<(), SwitchError> {
let mut configured_switch_networks = self.configured_switch_networks.lock().unwrap();
configured_switch_networks.push((host.id.clone(), config.clone()));
Ok(())
}
}
}