WIP: configure-switch #159

Closed
johnride wants to merge 18 commits from configure-switch into master
3 changed files with 47 additions and 172 deletions
Showing only changes of commit 427009bbfe - Show all commits

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
letian marked this conversation as resolved
Review

speed yes, maybe we will need something else but nothing comes to mind right now.

speed yes, maybe we will need something else but nothing comes to mind right now.
}
#[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(())
}
}
}