feat: Harmony now sets dhcp next boot server for PXE on okd setup
This commit is contained in:
parent
b15df3c93f
commit
18c67adfad
@ -7,7 +7,6 @@ use super::{
|
|||||||
data::{Id, Version},
|
data::{Id, Version},
|
||||||
executors::ExecutorError,
|
executors::ExecutorError,
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
score::Score,
|
|
||||||
topology::HAClusterTopology,
|
topology::HAClusterTopology,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,9 +40,13 @@ pub struct Outcome {
|
|||||||
status: InterpretStatus,
|
status: InterpretStatus,
|
||||||
message: String,
|
message: String,
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for Outcome {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
impl Outcome {
|
||||||
f.write_fmt(format_args!("Outcome {}: {}", self.status, self.message))
|
pub fn noop() -> Self {
|
||||||
|
Self {
|
||||||
|
status: InterpretStatus::NOOP,
|
||||||
|
message: String::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +57,7 @@ pub enum InterpretStatus {
|
|||||||
RUNNING,
|
RUNNING,
|
||||||
QUEUED,
|
QUEUED,
|
||||||
BLOCKED,
|
BLOCKED,
|
||||||
|
NOOP,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for InterpretStatus {
|
impl std::fmt::Display for InterpretStatus {
|
||||||
@ -64,6 +68,7 @@ impl std::fmt::Display for InterpretStatus {
|
|||||||
InterpretStatus::RUNNING => "RUNNING",
|
InterpretStatus::RUNNING => "RUNNING",
|
||||||
InterpretStatus::QUEUED => "QUEUED",
|
InterpretStatus::QUEUED => "QUEUED",
|
||||||
InterpretStatus::BLOCKED => "BLOCKED",
|
InterpretStatus::BLOCKED => "BLOCKED",
|
||||||
|
InterpretStatus::NOOP => "NO_OP",
|
||||||
};
|
};
|
||||||
f.write_str(msg)
|
f.write_str(msg)
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ pub trait DhcpServer: Send + Sync {
|
|||||||
async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>;
|
async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>;
|
||||||
async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>;
|
async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>;
|
||||||
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>;
|
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>;
|
||||||
|
async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError>;
|
||||||
fn get_ip(&self) -> IpAddress;
|
fn get_ip(&self) -> IpAddress;
|
||||||
fn get_host(&self) -> LogicalHost;
|
fn get_host(&self) -> LogicalHost;
|
||||||
async fn commit_config(&self) -> Result<(), ExecutorError>;
|
async fn commit_config(&self) -> Result<(), ExecutorError>;
|
||||||
|
@ -136,9 +136,24 @@ impl DhcpServer for OPNSenseFirewall {
|
|||||||
fn get_ip(&self) -> IpAddress {
|
fn get_ip(&self) -> IpAddress {
|
||||||
OPNSenseFirewall::get_ip(self)
|
OPNSenseFirewall::get_ip(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_host(&self) -> LogicalHost {
|
fn get_host(&self) -> LogicalHost {
|
||||||
self.host.clone()
|
self.host.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError> {
|
||||||
|
let ipv4 = match ip {
|
||||||
|
std::net::IpAddr::V4(ipv4_addr) => ipv4_addr,
|
||||||
|
std::net::IpAddr::V6(_) => todo!("ipv6 not supported yet"),
|
||||||
|
};
|
||||||
|
{
|
||||||
|
let mut writable_opnsense = self.opnsense_config.write().await;
|
||||||
|
writable_opnsense.dhcp().set_next_server(ipv4);
|
||||||
|
debug!("OPNsense dhcp server set next server {ipv4}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DnsServer for OPNSenseFirewall {
|
impl DnsServer for OPNSenseFirewall {
|
||||||
|
@ -11,7 +11,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
interpret::{Interpret, InterpretError, InterpretName, Outcome},
|
interpret::{Interpret, InterpretError, InterpretName, Outcome},
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
topology::{DHCPStaticEntry, HAClusterTopology, HostBinding},
|
topology::{DHCPStaticEntry, HAClusterTopology, HostBinding, IpAddress},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::domain::score::Score;
|
use crate::domain::score::Score;
|
||||||
@ -19,6 +19,7 @@ use crate::domain::score::Score;
|
|||||||
#[derive(Debug, new, Clone)]
|
#[derive(Debug, new, Clone)]
|
||||||
pub struct DhcpScore {
|
pub struct DhcpScore {
|
||||||
host_binding: Vec<HostBinding>,
|
host_binding: Vec<HostBinding>,
|
||||||
|
next_server: Option<IpAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Score for DhcpScore {
|
impl Score for DhcpScore {
|
||||||
@ -53,6 +54,67 @@ impl DhcpInterpret {
|
|||||||
status: InterpretStatus::QUEUED,
|
status: InterpretStatus::QUEUED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async fn add_static_entries(
|
||||||
|
&self,
|
||||||
|
_inventory: &Inventory,
|
||||||
|
topology: &HAClusterTopology,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let dhcp_entries: Vec<DHCPStaticEntry> = self
|
||||||
|
.score
|
||||||
|
.host_binding
|
||||||
|
.iter()
|
||||||
|
.map(|binding| {
|
||||||
|
let ip = match binding.logical_host.ip {
|
||||||
|
std::net::IpAddr::V4(ipv4) => ipv4,
|
||||||
|
std::net::IpAddr::V6(_) => {
|
||||||
|
unimplemented!("DHCPStaticEntry only supports ipv4 at the moment")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DHCPStaticEntry {
|
||||||
|
name: binding.logical_host.name.clone(),
|
||||||
|
mac: binding.physical_host.cluster_mac(),
|
||||||
|
ip,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
info!("DHCPStaticEntry : {:?}", dhcp_entries);
|
||||||
|
|
||||||
|
let dhcp_server = Arc::new(topology.dhcp_server.clone());
|
||||||
|
info!("DHCP server : {:?}", dhcp_server);
|
||||||
|
|
||||||
|
let number_new_entries = dhcp_entries.len();
|
||||||
|
|
||||||
|
for entry in dhcp_entries.into_iter() {
|
||||||
|
match dhcp_server.add_static_mapping(&entry).await {
|
||||||
|
Ok(_) => info!("Successfully registered DHCPStaticEntry {}", entry),
|
||||||
|
Err(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Outcome::new(
|
||||||
|
InterpretStatus::SUCCESS,
|
||||||
|
format!("Dhcp Interpret registered {} entries", number_new_entries),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_next_server(
|
||||||
|
&self,
|
||||||
|
_inventory: &Inventory,
|
||||||
|
topology: &HAClusterTopology,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
match self.score.next_server {
|
||||||
|
Some(next_server) => {
|
||||||
|
let dhcp_server = Arc::new(topology.dhcp_server.clone());
|
||||||
|
dhcp_server.set_next_server(next_server).await?;
|
||||||
|
Ok(Outcome::new(
|
||||||
|
InterpretStatus::SUCCESS,
|
||||||
|
format!("Dhcp Interpret Set next boot to {next_server}"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
None => Ok(Outcome::noop()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -80,68 +142,15 @@ impl Interpret for DhcpInterpret {
|
|||||||
) -> Result<Outcome, InterpretError> {
|
) -> Result<Outcome, InterpretError> {
|
||||||
info!("Executing {} on inventory {inventory:?}", self.get_name());
|
info!("Executing {} on inventory {inventory:?}", self.get_name());
|
||||||
|
|
||||||
let dhcp_entries: Vec<DHCPStaticEntry> = self
|
self.set_next_server(inventory, topology).await?;
|
||||||
.score
|
|
||||||
.host_binding
|
|
||||||
.iter()
|
|
||||||
.map(|binding| {
|
|
||||||
let ip = match binding.logical_host.ip {
|
|
||||||
std::net::IpAddr::V4(ipv4) => ipv4,
|
|
||||||
std::net::IpAddr::V6(_) => {
|
|
||||||
unimplemented!("DHCPStaticEntry only supports ipv4 at the moment")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DHCPStaticEntry {
|
// self.add_static_entries(inventory, topology).await?;
|
||||||
name: binding.logical_host.name.clone(),
|
|
||||||
mac: binding.physical_host.cluster_mac(),
|
|
||||||
ip,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
info!("DHCPStaticEntry : {:?}", dhcp_entries);
|
|
||||||
|
|
||||||
let dhcp_server = Arc::new(Box::new(topology.dhcp_server.clone()));
|
topology.dhcp_server.commit_config().await?;
|
||||||
info!("DHCP server : {:?}", dhcp_server);
|
|
||||||
|
|
||||||
let number_new_entries = dhcp_entries.len();
|
|
||||||
|
|
||||||
for entry in dhcp_entries.into_iter() {
|
|
||||||
match dhcp_server.add_static_mapping(&entry).await {
|
|
||||||
Ok(_) => info!("Successfully registered DHCPStaticEntry {}", entry),
|
|
||||||
Err(_) => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dhcp_server.commit_config().await;
|
|
||||||
|
|
||||||
Ok(Outcome::new(
|
Ok(Outcome::new(
|
||||||
InterpretStatus::SUCCESS,
|
InterpretStatus::SUCCESS,
|
||||||
format!("Dhcp Interpret registered {} entries", number_new_entries),
|
format!("Dhcp Interpret execution successful"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn opnsense_dns_score_should_do_nothing_on_empty_inventory() {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn opnsense_dns_score_should_set_entry_for_bootstrap_node() {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn opnsense_dns_score_should_set_entry_for_control_plane_members() {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn opnsense_dns_score_should_set_entry_for_workers() {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -12,25 +12,25 @@ pub struct OKDBootstrapDhcpScore {
|
|||||||
|
|
||||||
impl OKDBootstrapDhcpScore {
|
impl OKDBootstrapDhcpScore {
|
||||||
pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self {
|
pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self {
|
||||||
|
let host_binding = 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();
|
||||||
Self {
|
Self {
|
||||||
dhcp_score: DhcpScore::new(
|
dhcp_score: DhcpScore::new(
|
||||||
topology
|
host_binding,
|
||||||
.control_plane
|
// TODO : we should add a tftp server to the topology instead of relying on the
|
||||||
.iter()
|
// router address, this is leaking implementation details
|
||||||
.enumerate()
|
Some(topology.router.get_gateway()),
|
||||||
.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(),
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ pub struct DhcpInterface {
|
|||||||
pub enable: Option<MaybeString>,
|
pub enable: Option<MaybeString>,
|
||||||
pub gateway: Option<MaybeString>,
|
pub gateway: Option<MaybeString>,
|
||||||
pub domain: Option<MaybeString>,
|
pub domain: Option<MaybeString>,
|
||||||
|
pub netboot: Option<u32>,
|
||||||
|
pub nextserver: Option<String>,
|
||||||
#[yaserde(rename = "ddnsdomainalgorithm")]
|
#[yaserde(rename = "ddnsdomainalgorithm")]
|
||||||
pub ddns_domain_algorithm: Option<MaybeString>,
|
pub ddns_domain_algorithm: Option<MaybeString>,
|
||||||
#[yaserde(rename = "numberoptions")]
|
#[yaserde(rename = "numberoptions")]
|
||||||
|
@ -54,7 +54,9 @@ impl<'a> DhcpConfig<'a> {
|
|||||||
|
|
||||||
pub fn remove_static_mapping(&mut self, mac: &str) {
|
pub fn remove_static_mapping(&mut self, mac: &str) {
|
||||||
let lan_dhcpd = self.get_lan_dhcpd();
|
let lan_dhcpd = self.get_lan_dhcpd();
|
||||||
lan_dhcpd.staticmaps.retain(|static_entry| static_entry.mac != mac);
|
lan_dhcpd
|
||||||
|
.staticmaps
|
||||||
|
.retain(|static_entry| static_entry.mac != mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_lan_dhcpd(&mut self) -> &mut opnsense_config_xml::DhcpInterface {
|
fn get_lan_dhcpd(&mut self) -> &mut opnsense_config_xml::DhcpInterface {
|
||||||
@ -77,7 +79,6 @@ impl<'a> DhcpConfig<'a> {
|
|||||||
let mac = mac.to_string();
|
let mac = mac.to_string();
|
||||||
let hostname = hostname.to_string();
|
let hostname = hostname.to_string();
|
||||||
let lan_dhcpd = self.get_lan_dhcpd();
|
let lan_dhcpd = self.get_lan_dhcpd();
|
||||||
let range = &lan_dhcpd.range;
|
|
||||||
let existing_mappings: &mut Vec<StaticMap> = &mut lan_dhcpd.staticmaps;
|
let existing_mappings: &mut Vec<StaticMap> = &mut lan_dhcpd.staticmaps;
|
||||||
|
|
||||||
if !Self::is_valid_mac(&mac) {
|
if !Self::is_valid_mac(&mac) {
|
||||||
@ -87,7 +88,7 @@ impl<'a> DhcpConfig<'a> {
|
|||||||
// TODO verify if address is in subnet range
|
// TODO verify if address is in subnet range
|
||||||
// This check here does not do what we want to do, as we want to assign static leases
|
// This check here does not do what we want to do, as we want to assign static leases
|
||||||
// outside of the dynamic DHCP pool
|
// outside of the dynamic DHCP pool
|
||||||
//
|
// let range = &lan_dhcpd.range;
|
||||||
// if !Self::is_ip_in_range(&ipaddr, range) {
|
// if !Self::is_ip_in_range(&ipaddr, range) {
|
||||||
// return Err(DhcpError::IpAddressOutOfRange(ipaddr.to_string()));
|
// return Err(DhcpError::IpAddressOutOfRange(ipaddr.to_string()));
|
||||||
// }
|
// }
|
||||||
@ -177,6 +178,14 @@ impl<'a> DhcpConfig<'a> {
|
|||||||
|
|
||||||
Ok(static_maps)
|
Ok(static_maps)
|
||||||
}
|
}
|
||||||
|
pub fn enable_netboot(&mut self) {
|
||||||
|
self.get_lan_dhcpd().netboot = Some(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_next_server(&mut self, ip: Ipv4Addr) {
|
||||||
|
self.enable_netboot();
|
||||||
|
self.get_lan_dhcpd().nextserver = Some(ip.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
Reference in New Issue
Block a user