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},
|
||||
executors::ExecutorError,
|
||||
inventory::Inventory,
|
||||
score::Score,
|
||||
topology::HAClusterTopology,
|
||||
};
|
||||
|
||||
@ -41,9 +40,13 @@ pub struct Outcome {
|
||||
status: InterpretStatus,
|
||||
message: String,
|
||||
}
|
||||
impl std::fmt::Display for Outcome {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("Outcome {}: {}", self.status, self.message))
|
||||
|
||||
impl Outcome {
|
||||
pub fn noop() -> Self {
|
||||
Self {
|
||||
status: InterpretStatus::NOOP,
|
||||
message: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +57,7 @@ pub enum InterpretStatus {
|
||||
RUNNING,
|
||||
QUEUED,
|
||||
BLOCKED,
|
||||
NOOP,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InterpretStatus {
|
||||
@ -64,6 +68,7 @@ impl std::fmt::Display for InterpretStatus {
|
||||
InterpretStatus::RUNNING => "RUNNING",
|
||||
InterpretStatus::QUEUED => "QUEUED",
|
||||
InterpretStatus::BLOCKED => "BLOCKED",
|
||||
InterpretStatus::NOOP => "NO_OP",
|
||||
};
|
||||
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 remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>;
|
||||
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_host(&self) -> LogicalHost;
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError>;
|
||||
|
@ -136,9 +136,24 @@ impl DhcpServer for OPNSenseFirewall {
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
OPNSenseFirewall::get_ip(self)
|
||||
}
|
||||
|
||||
fn get_host(&self) -> LogicalHost {
|
||||
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 {
|
||||
|
@ -11,7 +11,7 @@ use crate::{
|
||||
},
|
||||
interpret::{Interpret, InterpretError, InterpretName, Outcome},
|
||||
inventory::Inventory,
|
||||
topology::{DHCPStaticEntry, HAClusterTopology, HostBinding},
|
||||
topology::{DHCPStaticEntry, HAClusterTopology, HostBinding, IpAddress},
|
||||
};
|
||||
|
||||
use crate::domain::score::Score;
|
||||
@ -19,6 +19,7 @@ use crate::domain::score::Score;
|
||||
#[derive(Debug, new, Clone)]
|
||||
pub struct DhcpScore {
|
||||
host_binding: Vec<HostBinding>,
|
||||
next_server: Option<IpAddress>,
|
||||
}
|
||||
|
||||
impl Score for DhcpScore {
|
||||
@ -53,6 +54,67 @@ impl DhcpInterpret {
|
||||
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]
|
||||
@ -80,68 +142,15 @@ impl Interpret for DhcpInterpret {
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
info!("Executing {} on inventory {inventory:?}", self.get_name());
|
||||
|
||||
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")
|
||||
}
|
||||
};
|
||||
self.set_next_server(inventory, topology).await?;
|
||||
|
||||
DHCPStaticEntry {
|
||||
name: binding.logical_host.name.clone(),
|
||||
mac: binding.physical_host.cluster_mac(),
|
||||
ip,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
info!("DHCPStaticEntry : {:?}", dhcp_entries);
|
||||
// self.add_static_entries(inventory, topology).await?;
|
||||
|
||||
let dhcp_server = Arc::new(Box::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!(),
|
||||
}
|
||||
}
|
||||
|
||||
dhcp_server.commit_config().await;
|
||||
topology.dhcp_server.commit_config().await?;
|
||||
|
||||
Ok(Outcome::new(
|
||||
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 {
|
||||
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 {
|
||||
dhcp_score: DhcpScore::new(
|
||||
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,
|
||||
// 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()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ pub struct DhcpInterface {
|
||||
pub enable: Option<MaybeString>,
|
||||
pub gateway: Option<MaybeString>,
|
||||
pub domain: Option<MaybeString>,
|
||||
pub netboot: Option<u32>,
|
||||
pub nextserver: Option<String>,
|
||||
#[yaserde(rename = "ddnsdomainalgorithm")]
|
||||
pub ddns_domain_algorithm: Option<MaybeString>,
|
||||
#[yaserde(rename = "numberoptions")]
|
||||
|
@ -54,7 +54,9 @@ impl<'a> DhcpConfig<'a> {
|
||||
|
||||
pub fn remove_static_mapping(&mut self, mac: &str) {
|
||||
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 {
|
||||
@ -77,7 +79,6 @@ impl<'a> DhcpConfig<'a> {
|
||||
let mac = mac.to_string();
|
||||
let hostname = hostname.to_string();
|
||||
let lan_dhcpd = self.get_lan_dhcpd();
|
||||
let range = &lan_dhcpd.range;
|
||||
let existing_mappings: &mut Vec<StaticMap> = &mut lan_dhcpd.staticmaps;
|
||||
|
||||
if !Self::is_valid_mac(&mac) {
|
||||
@ -87,7 +88,7 @@ impl<'a> DhcpConfig<'a> {
|
||||
// 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
|
||||
// outside of the dynamic DHCP pool
|
||||
//
|
||||
// let range = &lan_dhcpd.range;
|
||||
// if !Self::is_ip_in_range(&ipaddr, range) {
|
||||
// return Err(DhcpError::IpAddressOutOfRange(ipaddr.to_string()));
|
||||
// }
|
||||
@ -177,6 +178,14 @@ impl<'a> DhcpConfig<'a> {
|
||||
|
||||
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)]
|
||||
|
Loading…
Reference in New Issue
Block a user