feat(bootstrapping): add bootstrap load balancer and DHCP configurations
- Introduce `bootstrap_load_balancer` module for handling initial load balancing configuration. - Add `bootstrap_dhcp` module for bootstrapping DHCP settings. - Create `harmony_types` crate to house shared types, including `MacAddress`. - Update `harmony_macros` to use `harmony_types` instead of directly referencing `harmony`.
This commit is contained in:
parent
a80ead418e
commit
bec96c2954
8
harmony-rs/Cargo.lock
generated
8
harmony-rs/Cargo.lock
generated
@ -893,6 +893,8 @@ dependencies = [
|
|||||||
"cidr",
|
"cidr",
|
||||||
"derive-new",
|
"derive-new",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"harmony_macros",
|
||||||
|
"harmony_types",
|
||||||
"libredfish",
|
"libredfish",
|
||||||
"log",
|
"log",
|
||||||
"opnsense-config",
|
"opnsense-config",
|
||||||
@ -912,11 +914,15 @@ dependencies = [
|
|||||||
name = "harmony_macros"
|
name = "harmony_macros"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"harmony",
|
"harmony_types",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.90",
|
"syn 2.0.90",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "harmony_types"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
|
|||||||
@ -4,7 +4,10 @@ members = [
|
|||||||
"private_repos/*",
|
"private_repos/*",
|
||||||
"demo/*",
|
"demo/*",
|
||||||
"harmony",
|
"harmony",
|
||||||
"opnsense-config", "opnsense-config-xml", "harmony_macros",
|
"harmony_types",
|
||||||
|
"harmony_macros",
|
||||||
|
"opnsense-config",
|
||||||
|
"opnsense-config-xml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
|||||||
@ -10,7 +10,9 @@ use harmony::{
|
|||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
maestro::Maestro,
|
maestro::Maestro,
|
||||||
modules::{
|
modules::{
|
||||||
http::HttpScore, okd::{dhcp::OKDBootstrapDhcpScore, dns::OKDBootstrapDnsScore}, tftp::TftpScore
|
http::HttpScore,
|
||||||
|
okd::{dhcp::OKDDhcpScore, dns::OKDDnsScore},
|
||||||
|
tftp::TftpScore,
|
||||||
},
|
},
|
||||||
topology::{LogicalHost, UnmanagedRouter, Url},
|
topology::{LogicalHost, UnmanagedRouter, Url},
|
||||||
};
|
};
|
||||||
@ -48,6 +50,10 @@ async fn main() {
|
|||||||
ip: ip!("10.100.8.20"),
|
ip: ip!("10.100.8.20"),
|
||||||
name: "cp0".to_string(),
|
name: "cp0".to_string(),
|
||||||
}],
|
}],
|
||||||
|
bootstrap_host: LogicalHost {
|
||||||
|
ip: ip!("10.100.8.20"),
|
||||||
|
name: "cp0".to_string(),
|
||||||
|
},
|
||||||
workers: vec![],
|
workers: vec![],
|
||||||
switch: vec![],
|
switch: vec![],
|
||||||
};
|
};
|
||||||
@ -73,8 +79,8 @@ async fn main() {
|
|||||||
// TODO regroup smaller scores in a larger one such as this
|
// TODO regroup smaller scores in a larger one such as this
|
||||||
// let okd_boostrap_preparation();
|
// let okd_boostrap_preparation();
|
||||||
|
|
||||||
// let dhcp_score = OKDBootstrapDhcpScore::new(&topology, &inventory);
|
// let dhcp_score = OKDDhcpScore::new(&topology, &inventory);
|
||||||
// let dns_score = OKDBootstrapDnsScore::new(&topology);
|
// let dns_score = OKDDnsScore::new(&topology);
|
||||||
// let load_balancer_score =
|
// let load_balancer_score =
|
||||||
// harmony::modules::okd::load_balancer::OKDLoadBalancerScore::new(&topology);
|
// harmony::modules::okd::load_balancer::OKDLoadBalancerScore::new(&topology);
|
||||||
|
|
||||||
|
|||||||
@ -19,5 +19,7 @@ async-trait = { workspace = true }
|
|||||||
cidr = { workspace = true }
|
cidr = { workspace = true }
|
||||||
opnsense-config = { path = "../opnsense-config" }
|
opnsense-config = { path = "../opnsense-config" }
|
||||||
opnsense-config-xml = { path = "../opnsense-config-xml" }
|
opnsense-config-xml = { path = "../opnsense-config-xml" }
|
||||||
|
harmony_macros = { path = "../harmony_macros" }
|
||||||
|
harmony_types = { path = "../harmony_types" }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
use harmony_types::net::MacAddress;
|
||||||
|
|
||||||
use crate::topology::MacAddress;
|
|
||||||
|
|
||||||
pub type HostGroup = Vec<PhysicalHost>;
|
pub type HostGroup = Vec<PhysicalHost>;
|
||||||
pub type SwitchGroup = Vec<Switch>;
|
pub type SwitchGroup = Vec<Switch>;
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
mod host_binding;
|
mod host_binding;
|
||||||
|
mod http;
|
||||||
mod load_balancer;
|
mod load_balancer;
|
||||||
mod router;
|
mod router;
|
||||||
mod tftp;
|
mod tftp;
|
||||||
mod http;
|
|
||||||
pub use load_balancer::*;
|
pub use load_balancer::*;
|
||||||
pub use router::*;
|
pub use router::*;
|
||||||
mod network;
|
mod network;
|
||||||
pub use host_binding::*;
|
pub use host_binding::*;
|
||||||
|
pub use http::*;
|
||||||
pub use network::*;
|
pub use network::*;
|
||||||
pub use tftp::*;
|
pub use tftp::*;
|
||||||
pub use http::*;
|
|
||||||
|
|
||||||
use std::{net::IpAddr, sync::Arc};
|
use std::{net::IpAddr, sync::Arc};
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ 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 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>,
|
pub switch: Vec<LogicalHost>,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::{error::Error, net::Ipv4Addr, str::FromStr};
|
use std::{error::Error, net::Ipv4Addr, str::FromStr};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use harmony_types::net::MacAddress;
|
||||||
|
|
||||||
use crate::executors::ExecutorError;
|
use crate::executors::ExecutorError;
|
||||||
|
|
||||||
@ -117,35 +118,6 @@ pub enum Action {
|
|||||||
Deny,
|
Deny,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct MacAddress(pub [u8; 6]);
|
|
||||||
|
|
||||||
|
|
||||||
impl MacAddress {
|
|
||||||
#[cfg(test)]
|
|
||||||
pub fn dummy() -> Self {
|
|
||||||
Self([0, 0, 0, 0, 0, 0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&MacAddress> for String {
|
|
||||||
fn from(value: &MacAddress) -> Self {
|
|
||||||
format!(
|
|
||||||
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
|
|
||||||
value.0[0], value.0[1], value.0[2], value.0[3], value.0[4], value.0[5]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for MacAddress {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_fmt(format_args!(
|
|
||||||
"MacAddress {}",
|
|
||||||
String::from(self)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum DnsRecordType {
|
pub enum DnsRecordType {
|
||||||
A,
|
A,
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
use crate::{hardware::ManagementInterface, topology::{IpAddress, MacAddress}};
|
use crate::hardware::ManagementInterface;
|
||||||
|
use crate::topology::IpAddress;
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
use harmony_types::net::MacAddress;
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
pub struct HPIlo {
|
pub struct HPIlo {
|
||||||
ip_address: IpAddress,
|
ip_address: Option<IpAddress>,
|
||||||
|
mac_address: Option<MacAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManagementInterface for HPIlo {
|
impl ManagementInterface for HPIlo {
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
use crate::{
|
use crate::hardware::ManagementInterface;
|
||||||
hardware::ManagementInterface,
|
|
||||||
topology::{IpAddress, MacAddress},
|
|
||||||
};
|
|
||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
use harmony_types::net::MacAddress;
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
pub struct IntelAmtManagement {
|
pub struct IntelAmtManagement {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use harmony_types::net::MacAddress;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::{executors::ExecutorError, topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost}};
|
use crate::{executors::ExecutorError, topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost}};
|
||||||
@ -28,12 +29,12 @@ impl DhcpServer for OPNSenseFirewall {
|
|||||||
|
|
||||||
async fn remove_static_mapping(
|
async fn remove_static_mapping(
|
||||||
&self,
|
&self,
|
||||||
_mac: &crate::topology::MacAddress,
|
_mac: &MacAddress,
|
||||||
) -> Result<(), ExecutorError> {
|
) -> Result<(), ExecutorError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_static_mappings(&self) -> Vec<(crate::topology::MacAddress, IpAddress)> {
|
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
|
use crate::hardware::ManagementInterface;
|
||||||
use crate::{hardware::ManagementInterface, topology::MacAddress};
|
|
||||||
|
|
||||||
#[derive(new)]
|
#[derive(new)]
|
||||||
pub struct OPNSenseManagementInterface {}
|
pub struct OPNSenseManagementInterface {}
|
||||||
|
|||||||
55
harmony-rs/harmony/src/modules/okd/bootstrap_dhcp.rs
Normal file
55
harmony-rs/harmony/src/modules/okd/bootstrap_dhcp.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use crate::{
|
||||||
|
inventory::Inventory,
|
||||||
|
modules::dhcp::DhcpScore,
|
||||||
|
score::Score,
|
||||||
|
topology::{HAClusterTopology, HostBinding, LogicalHost},
|
||||||
|
};
|
||||||
|
|
||||||
|
use harmony_macros::ip;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OKDBootstrapDhcpScore {
|
||||||
|
dhcp_score: DhcpScore,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OKDBootstrapDhcpScore {
|
||||||
|
pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self {
|
||||||
|
let mut host_binding: Vec<_> = topology
|
||||||
|
.control_plane
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, topology_entry)| HostBinding {
|
||||||
|
logical_host: topology_entry.clone(),
|
||||||
|
physical_host: inventory
|
||||||
|
.control_plane_host
|
||||||
|
.get(index)
|
||||||
|
.expect("Iventory should contain at least as many physical hosts as topology")
|
||||||
|
.clone(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
host_binding.push(HostBinding {
|
||||||
|
logical_host: topology.bootstrap_host.clone(),
|
||||||
|
physical_host: inventory
|
||||||
|
.worker_host
|
||||||
|
.get(0)
|
||||||
|
.expect("Should have at least one worker to be used as bootstrap node")
|
||||||
|
.clone(),
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
dhcp_score: DhcpScore::new(
|
||||||
|
host_binding,
|
||||||
|
// TODO : we should add a tftp server to the topology instead of relying on the
|
||||||
|
// router address, this is leaking implementation details
|
||||||
|
Some(topology.router.get_gateway()),
|
||||||
|
Some("bootx64.efi".to_string()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Score for OKDBootstrapDhcpScore {
|
||||||
|
type InterpretType = <DhcpScore as Score>::InterpretType;
|
||||||
|
|
||||||
|
fn create_interpret(self) -> Self::InterpretType {
|
||||||
|
self.dhcp_score.create_interpret()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
modules::load_balancer::LoadBalancerScore,
|
||||||
|
score::Score,
|
||||||
|
topology::{
|
||||||
|
BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode,
|
||||||
|
LoadBalancerService,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OKDBootstrapLoadBalancerScore {
|
||||||
|
load_balancer_score: LoadBalancerScore,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OKDBootstrapLoadBalancerScore {
|
||||||
|
pub fn new(topology: &HAClusterTopology) -> Self {
|
||||||
|
let private_ip = topology.router.get_gateway();
|
||||||
|
|
||||||
|
let bootstrap_host = &topology.bootstrap_host;
|
||||||
|
|
||||||
|
let private_services = vec![
|
||||||
|
LoadBalancerService {
|
||||||
|
backend_servers: vec![BackendServer {
|
||||||
|
address: bootstrap_host.ip.to_string(),
|
||||||
|
port: 80,
|
||||||
|
}],
|
||||||
|
listening_port: SocketAddr::new(private_ip, 80),
|
||||||
|
health_check: Some(HealthCheck::TCP(None)),
|
||||||
|
},
|
||||||
|
LoadBalancerService {
|
||||||
|
backend_servers: vec![BackendServer {
|
||||||
|
address: bootstrap_host.ip.to_string(),
|
||||||
|
port: 443,
|
||||||
|
}],
|
||||||
|
listening_port: SocketAddr::new(private_ip, 443),
|
||||||
|
health_check: Some(HealthCheck::TCP(None)),
|
||||||
|
},
|
||||||
|
LoadBalancerService {
|
||||||
|
backend_servers: vec![BackendServer {
|
||||||
|
address: bootstrap_host.ip.to_string(),
|
||||||
|
port: 22623,
|
||||||
|
}],
|
||||||
|
listening_port: SocketAddr::new(private_ip, 22623),
|
||||||
|
health_check: Some(HealthCheck::TCP(None)),
|
||||||
|
},
|
||||||
|
LoadBalancerService {
|
||||||
|
backend_servers: vec![BackendServer {
|
||||||
|
address: bootstrap_host.ip.to_string(),
|
||||||
|
port: 6443,
|
||||||
|
}],
|
||||||
|
listening_port: SocketAddr::new(private_ip, 6443),
|
||||||
|
health_check: Some(HealthCheck::HTTP(
|
||||||
|
"/readyz".to_string(),
|
||||||
|
HttpMethod::GET,
|
||||||
|
HttpStatusCode::Success2xx,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
Self {
|
||||||
|
load_balancer_score: LoadBalancerScore {
|
||||||
|
public_services: vec![],
|
||||||
|
private_services,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Score for OKDBootstrapLoadBalancerScore {
|
||||||
|
type InterpretType = <LoadBalancerScore as Score>::InterpretType;
|
||||||
|
|
||||||
|
fn create_interpret(self) -> Self::InterpretType {
|
||||||
|
self.load_balancer_score.create_interpret()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,11 +6,11 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct OKDBootstrapDhcpScore {
|
pub struct OKDDhcpScore {
|
||||||
dhcp_score: DhcpScore,
|
dhcp_score: DhcpScore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OKDBootstrapDhcpScore {
|
impl OKDDhcpScore {
|
||||||
pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self {
|
pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self {
|
||||||
let host_binding = topology
|
let host_binding = topology
|
||||||
.control_plane
|
.control_plane
|
||||||
@ -37,7 +37,7 @@ impl OKDBootstrapDhcpScore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Score for OKDBootstrapDhcpScore {
|
impl Score for OKDDhcpScore {
|
||||||
type InterpretType = <DhcpScore as Score>::InterpretType;
|
type InterpretType = <DhcpScore as Score>::InterpretType;
|
||||||
|
|
||||||
fn create_interpret(self) -> Self::InterpretType {
|
fn create_interpret(self) -> Self::InterpretType {
|
||||||
|
|||||||
@ -5,11 +5,11 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct OKDBootstrapDnsScore {
|
pub struct OKDDnsScore {
|
||||||
dns_score: DnsScore,
|
dns_score: DnsScore,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OKDBootstrapDnsScore {
|
impl OKDDnsScore {
|
||||||
pub fn new(topology: &HAClusterTopology) -> Self {
|
pub fn new(topology: &HAClusterTopology) -> Self {
|
||||||
let cluster_domain_name = &topology.domain_name;
|
let cluster_domain_name = &topology.domain_name;
|
||||||
let dns_entries = vec![
|
let dns_entries = vec![
|
||||||
@ -39,7 +39,7 @@ impl OKDBootstrapDnsScore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Score for OKDBootstrapDnsScore {
|
impl Score for OKDDnsScore {
|
||||||
type InterpretType = <DnsScore as Score>::InterpretType;
|
type InterpretType = <DnsScore as Score>::InterpretType;
|
||||||
|
|
||||||
fn create_interpret(self) -> Self::InterpretType {
|
fn create_interpret(self) -> Self::InterpretType {
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
pub mod dhcp;
|
pub mod dhcp;
|
||||||
pub mod dns;
|
pub mod dns;
|
||||||
pub mod load_balancer;
|
pub mod load_balancer;
|
||||||
|
pub mod bootstrap_load_balancer;
|
||||||
|
pub mod bootstrap_dhcp;
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,6 @@ version = "1.0.0"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
harmony = { version = "0.1.0", path = "../harmony" }
|
harmony_types = { path = "../harmony_types" }
|
||||||
quote = "1.0.37"
|
quote = "1.0.37"
|
||||||
syn = "2.0.90"
|
syn = "2.0.90"
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
use harmony::topology::MacAddress;
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{LitStr, parse_macro_input};
|
use syn::{LitStr, parse_macro_input};
|
||||||
|
|||||||
4
harmony-rs/harmony_types/Cargo.toml
Normal file
4
harmony-rs/harmony_types/Cargo.toml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[package]
|
||||||
|
name = "harmony_types"
|
||||||
|
edition = "2024"
|
||||||
|
version = "1.0.0"
|
||||||
26
harmony-rs/harmony_types/src/lib.rs
Normal file
26
harmony-rs/harmony_types/src/lib.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
pub mod net {
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct MacAddress(pub [u8; 6]);
|
||||||
|
|
||||||
|
impl MacAddress {
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn dummy() -> Self {
|
||||||
|
Self([0, 0, 0, 0, 0, 0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&MacAddress> for String {
|
||||||
|
fn from(value: &MacAddress) -> Self {
|
||||||
|
format!(
|
||||||
|
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
|
||||||
|
value.0[0], value.0[1], value.0[2], value.0[3], value.0[4], value.0[5]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for MacAddress {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!("MacAddress {}", String::from(self)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user