WIP: configure-switch #159
@ -28,7 +28,6 @@ use super::PreparationOutcome;
|
|||||||
use super::Router;
|
use super::Router;
|
||||||
use super::Switch;
|
use super::Switch;
|
||||||
use super::SwitchError;
|
use super::SwitchError;
|
||||||
use super::SwitchNetworkConfig;
|
|
||||||
use super::TftpServer;
|
use super::TftpServer;
|
||||||
|
|
||||||
use super::Topology;
|
use super::Topology;
|
||||||
@ -270,7 +269,7 @@ impl HttpServer for HAClusterTopology {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Switch for HAClusterTopology {
|
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!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,14 +280,6 @@ impl Switch for HAClusterTopology {
|
|||||||
) -> Result<(), SwitchError> {
|
) -> Result<(), SwitchError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn configure_switch_network(
|
|
||||||
&self,
|
|
||||||
_host: &PhysicalHost,
|
|
||||||
_config: SwitchNetworkConfig,
|
|
||||||
) -> Result<(), SwitchError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -182,40 +182,20 @@ pub trait Switch: Send + Sync {
|
|||||||
host: &PhysicalHost,
|
host: &PhysicalHost,
|
||||||
config: HostNetworkConfig,
|
config: HostNetworkConfig,
|
||||||
) -> Result<(), SwitchError>;
|
) -> Result<(), SwitchError>;
|
||||||
|
|
||||||
async fn configure_switch_network(
|
|
||||||
&self,
|
|
||||||
host: &PhysicalHost,
|
|
||||||
config: SwitchNetworkConfig,
|
|
||||||
) -> Result<(), SwitchError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct HostNetworkConfig {
|
pub struct HostNetworkConfig {
|
||||||
pub bond: Bond,
|
pub switch_ports: Vec<SwitchPort>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Bond {
|
pub struct SwitchPort {
|
||||||
pub interfaces: Vec<SlaveInterface>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct SlaveInterface {
|
|
||||||
pub mac_address: MacAddress,
|
pub mac_address: MacAddress,
|
||||||
|
pub port_name: String,
|
||||||
// FIXME: Should we add speed as well? And other params
|
// 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)]
|
#[derive(Debug, Clone, new)]
|
||||||
pub struct SwitchError {
|
pub struct SwitchError {
|
||||||
msg: String,
|
msg: String,
|
||||||
|
@ -8,10 +8,7 @@ use crate::{
|
|||||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
score::Score,
|
score::Score,
|
||||||
topology::{
|
topology::{HostNetworkConfig, Switch, SwitchPort, Topology},
|
||||||
self, Bond, HostNetworkConfig, PortChannel, SlaveInterface, Switch, SwitchNetworkConfig,
|
|
||||||
Topology,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
@ -60,31 +57,19 @@ impl<T: Topology + Switch> Interpret<T> for HostNetworkConfigurationInterpret {
|
|||||||
topology: &T,
|
topology: &T,
|
||||||
) -> Result<Outcome, InterpretError> {
|
) -> Result<Outcome, InterpretError> {
|
||||||
for host in &self.score.hosts {
|
for host in &self.score.hosts {
|
||||||
let mut interfaces = vec![];
|
let mut switch_ports = vec![];
|
||||||
let mut ports = vec![];
|
|
||||||
|
|
||||||
for mac_address in host.get_mac_address() {
|
for mac_address in host.get_mac_address() {
|
||||||
if let Some(port) = topology.get_port_for_mac_address(&mac_address).await {
|
if let Some(port_name) = topology.get_port_for_mac_address(&mac_address).await {
|
||||||
interfaces.push(SlaveInterface { mac_address });
|
switch_ports.push(SwitchPort {
|
||||||
ports.push(port);
|
mac_address,
|
||||||
|
port_name,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = topology
|
let _ = topology
|
||||||
.configure_host_network(
|
.configure_host_network(host, HostNetworkConfig { switch_ports })
|
||||||
host,
|
|
||||||
HostNetworkConfig {
|
|
||||||
bond: Bond { interfaces },
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let _ = topology
|
|
||||||
.configure_switch_network(
|
|
||||||
host,
|
|
||||||
SwitchNetworkConfig {
|
|
||||||
port_channel: PortChannel { ports },
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await;
|
.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
|
// let port = topology.get_port_for_mac_address(); // si pas de port -> mac address pas connectee
|
||||||
// create port channel for all ports found
|
// create port channel for all ports found
|
||||||
// create bond for all valid addresses (port found)
|
// create bond for all valid addresses (port found)
|
||||||
// apply network to host first, then switch (to avoid losing hosts that are already connected)
|
// apply network config
|
||||||
// topology.configure_host_network(host, config) <--- will create bonds
|
// topology.configure_host_network(host, config) <--- will create both bonds & port channels
|
||||||
// topology.configure_switch_network(host, config) <--- will create port channels
|
|
||||||
|
|
||||||
Ok(Outcome::success("".into()))
|
Ok(Outcome::success("".into()))
|
||||||
}
|
}
|
||||||
@ -110,8 +94,7 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
hardware::HostCategory,
|
hardware::HostCategory,
|
||||||
topology::{
|
topology::{
|
||||||
Bond, HostNetworkConfig, PortChannel, PreparationError, PreparationOutcome,
|
HostNetworkConfig, PreparationError, PreparationOutcome, SwitchError, SwitchPort,
|
||||||
SlaveInterface, SwitchError, SwitchNetworkConfig,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
@ -125,11 +108,11 @@ mod tests {
|
|||||||
pub static ref HOST_ID: Id = Id::from_str("host-1").unwrap();
|
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 ANOTHER_HOST_ID: Id = Id::from_str("host-2").unwrap();
|
||||||
pub static ref EXISTING_INTERFACE: MacAddress =
|
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 =
|
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 =
|
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 PORT: String = "1/0/42".into();
|
||||||
pub static ref ANOTHER_PORT: String = "2/0/42".into();
|
pub static ref ANOTHER_PORT: String = "2/0/42".into();
|
||||||
}
|
}
|
||||||
@ -146,30 +129,10 @@ mod tests {
|
|||||||
assert_that!(*configured_host_networks).contains_exactly(vec![(
|
assert_that!(*configured_host_networks).contains_exactly(vec![(
|
||||||
HOST_ID.clone(),
|
HOST_ID.clone(),
|
||||||
HostNetworkConfig {
|
HostNetworkConfig {
|
||||||
bond: Bond {
|
switch_ports: vec![SwitchPort {
|
||||||
interfaces: vec![SlaveInterface {
|
mac_address: *EXISTING_INTERFACE,
|
||||||
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,16 +151,16 @@ mod tests {
|
|||||||
assert_that!(*configured_host_networks).contains_exactly(vec![(
|
assert_that!(*configured_host_networks).contains_exactly(vec![(
|
||||||
HOST_ID.clone(),
|
HOST_ID.clone(),
|
||||||
HostNetworkConfig {
|
HostNetworkConfig {
|
||||||
bond: Bond {
|
switch_ports: vec![
|
||||||
interfaces: vec![
|
SwitchPort {
|
||||||
SlaveInterface {
|
mac_address: *EXISTING_INTERFACE,
|
||||||
mac_address: *EXISTING_INTERFACE,
|
port_name: PORT.clone(),
|
||||||
},
|
},
|
||||||
SlaveInterface {
|
SwitchPort {
|
||||||
mac_address: *ANOTHER_EXISTING_INTERFACE,
|
mac_address: *ANOTHER_EXISTING_INTERFACE,
|
||||||
},
|
port_name: ANOTHER_PORT.clone(),
|
||||||
],
|
},
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
)]);
|
)]);
|
||||||
}
|
}
|
||||||
@ -217,52 +180,19 @@ mod tests {
|
|||||||
(
|
(
|
||||||
HOST_ID.clone(),
|
HOST_ID.clone(),
|
||||||
HostNetworkConfig {
|
HostNetworkConfig {
|
||||||
bond: Bond {
|
switch_ports: vec![SwitchPort {
|
||||||
interfaces: vec![SlaveInterface {
|
mac_address: *EXISTING_INTERFACE,
|
||||||
mac_address: *EXISTING_INTERFACE,
|
port_name: PORT.clone(),
|
||||||
}],
|
}],
|
||||||
},
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ANOTHER_HOST_ID.clone(),
|
ANOTHER_HOST_ID.clone(),
|
||||||
HostNetworkConfig {
|
HostNetworkConfig {
|
||||||
bond: Bond {
|
switch_ports: vec![SwitchPort {
|
||||||
interfaces: vec![SlaveInterface {
|
mac_address: *ANOTHER_EXISTING_INTERFACE,
|
||||||
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![(
|
assert_that!(*configured_host_networks).contains_exactly(vec![(
|
||||||
HOST_ID.clone(),
|
HOST_ID.clone(),
|
||||||
HostNetworkConfig {
|
HostNetworkConfig {
|
||||||
bond: Bond { interfaces: vec![] },
|
switch_ports: 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![] },
|
|
||||||
},
|
},
|
||||||
)]);
|
)]);
|
||||||
}
|
}
|
||||||
@ -325,25 +248,22 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct TopologyWithSwitch {
|
struct TopologyWithSwitch {
|
||||||
available_ports: Vec<String>,
|
available_ports: Arc<Mutex<Vec<String>>>,
|
||||||
configured_host_networks: Arc<Mutex<Vec<(Id, HostNetworkConfig)>>>,
|
configured_host_networks: Arc<Mutex<Vec<(Id, HostNetworkConfig)>>>,
|
||||||
configured_switch_networks: Arc<Mutex<Vec<(Id, SwitchNetworkConfig)>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TopologyWithSwitch {
|
impl TopologyWithSwitch {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
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_host_networks: Arc::new(Mutex::new(vec![])),
|
||||||
configured_switch_networks: Arc::new(Mutex::new(vec![])),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_port_not_found() -> Self {
|
fn new_port_not_found() -> Self {
|
||||||
Self {
|
Self {
|
||||||
available_ports: vec![],
|
available_ports: Arc::new(Mutex::new(vec![])),
|
||||||
configured_host_networks: 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]
|
#[async_trait]
|
||||||
impl Switch for TopologyWithSwitch {
|
impl Switch for TopologyWithSwitch {
|
||||||
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> {
|
||||||
if self.available_ports.is_empty() {
|
let mut ports = self.available_ports.lock().unwrap();
|
||||||
|
if ports.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
Some(ports.remove(0))
|
||||||
Some(
|
|
||||||
self.available_ports
|
|
||||||
.get(self.configured_host_networks.lock().unwrap().len() % 2)
|
|
||||||
.unwrap()
|
|
||||||
.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn configure_host_network(
|
async fn configure_host_network(
|
||||||
@ -384,16 +299,5 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
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.