WIP: configure-switch #159
@ -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)]
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
#[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,
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user
speed yes, maybe we will need something else but nothing comes to mind right now.