wip: bootstrap step of okd installation required some refactoring, its getting there
Some checks failed
Run Check Script / check (pull_request) Failing after 30s
Some checks failed
Run Check Script / check (pull_request) Failing after 30s
This commit is contained in:
parent
138e414727
commit
f076d36297
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -7049,7 +7049,7 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
[[package]]
|
||||
name = "yaserde"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/jggc/yaserde.git#c94ca32b6505f9c9a668702a1b1f1f88c6374301"
|
||||
source = "git+https://github.com/jggc/yaserde.git#adfdb1c5f4d054f114e5bd0ea7bda9c07a369def"
|
||||
dependencies = [
|
||||
"log",
|
||||
"xml-rs",
|
||||
@ -7058,7 +7058,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "yaserde_derive"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/jggc/yaserde.git#c94ca32b6505f9c9a668702a1b1f1f88c6374301"
|
||||
source = "git+https://github.com/jggc/yaserde.git#adfdb1c5f4d054f114e5bd0ea7bda9c07a369def"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"log",
|
||||
|
@ -173,6 +173,10 @@ impl PhysicalHost {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_mac_address(&self) -> Vec<MacAddress> {
|
||||
self.network.iter().map(|nic| nic.mac_address).collect()
|
||||
}
|
||||
|
||||
pub fn label(mut self, name: String, value: String) -> Self {
|
||||
self.labels.push(Label { name, value });
|
||||
self
|
||||
|
@ -52,6 +52,104 @@ impl DhcpInterpret {
|
||||
status: InterpretStatus::QUEUED,
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_pxe_options<D: DhcpServer>(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
dhcp_server: &D,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
todo!(
|
||||
"I don't think this set_pxe_options function still works since the major dnsmasq refactoring. It certainly is not far off, but we use the dedicated okd ipxe score now. They should work together, this needs refactoring."
|
||||
);
|
||||
let pxe_options = PxeOptions {
|
||||
ipxe_filename: self.score.filenameipxe.clone().unwrap_or_default(),
|
||||
bios_filename: self.score.filename.clone().unwrap_or_default(),
|
||||
efi_filename: self.score.filename64.clone().unwrap_or_default(),
|
||||
tftp_ip: self.score.next_server,
|
||||
};
|
||||
|
||||
dhcp_server.set_pxe_options(pxe_options).await?;
|
||||
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!(
|
||||
"Dhcp Interpret Set next boot to [{:?}], boot_filename to [{:?}], filename to [{:?}], filename64 to [{:?}], filenameipxe to [:{:?}]",
|
||||
self.score.boot_filename,
|
||||
self.score.boot_filename,
|
||||
self.score.filename,
|
||||
self.score.filename64,
|
||||
self.score.filenameipxe
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology + DhcpServer> Interpret<T> for DhcpInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::OPNSenseDHCP
|
||||
}
|
||||
|
||||
fn get_version(&self) -> crate::domain::data::Version {
|
||||
self.version.clone()
|
||||
}
|
||||
|
||||
fn get_status(&self) -> InterpretStatus {
|
||||
self.status.clone()
|
||||
}
|
||||
|
||||
fn get_children(&self) -> Vec<Id> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn execute(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
info!("Executing DhcpInterpret on inventory {inventory:?}");
|
||||
|
||||
self.set_pxe_options(inventory, topology).await?;
|
||||
|
||||
DhcpHostBindingScore {
|
||||
host_binding: self.score.host_binding.clone(),
|
||||
}
|
||||
.interpret(inventory, topology)
|
||||
.await?;
|
||||
|
||||
topology.commit_config().await?;
|
||||
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
"Dhcp Interpret execution successful".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct DhcpHostBindingScore {
|
||||
pub host_binding: Vec<HostBinding>,
|
||||
}
|
||||
|
||||
impl<T: Topology + DhcpServer> Score<T> for DhcpHostBindingScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
Box::new(DhcpHostBindingInterpret {
|
||||
score: self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
"DhcpHostBindingScore".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.opnsense.org/manual/dhcp.html#advanced-settings
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DhcpHostBindingInterpret {
|
||||
score: DhcpScore,
|
||||
}
|
||||
|
||||
impl DhcpHostBindingInterpret {
|
||||
async fn add_static_entries<D: DhcpServer>(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
@ -94,47 +192,20 @@ impl DhcpInterpret {
|
||||
format!("Dhcp Interpret registered {} entries", number_new_entries),
|
||||
))
|
||||
}
|
||||
|
||||
async fn set_pxe_options<D: DhcpServer>(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
dhcp_server: &D,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
let pxe_options = PxeOptions {
|
||||
ipxe_filename: self.score.filenameipxe.clone().unwrap_or_default(),
|
||||
bios_filename: self.score.filename.clone().unwrap_or_default(),
|
||||
efi_filename: self.score.filename64.clone().unwrap_or_default(),
|
||||
tftp_ip: self.score.next_server,
|
||||
};
|
||||
|
||||
dhcp_server.set_pxe_options(pxe_options).await?;
|
||||
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!(
|
||||
"Dhcp Interpret Set next boot to [{:?}], boot_filename to [{:?}], filename to [{:?}], filename64 to [{:?}], filenameipxe to [:{:?}]",
|
||||
self.score.boot_filename,
|
||||
self.score.boot_filename,
|
||||
self.score.filename,
|
||||
self.score.filename64,
|
||||
self.score.filenameipxe
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: DhcpServer> Interpret<T> for DhcpInterpret {
|
||||
impl<T: DhcpServer> Interpret<T> for DhcpHostBindingInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::OPNSenseDHCP
|
||||
InterpretName::Custom("DhcpHostBindingInterpret")
|
||||
}
|
||||
|
||||
fn get_version(&self) -> crate::domain::data::Version {
|
||||
self.version.clone()
|
||||
Version::from("1.0.0").unwrap()
|
||||
}
|
||||
|
||||
fn get_status(&self) -> InterpretStatus {
|
||||
self.status.clone()
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_children(&self) -> Vec<Id> {
|
||||
@ -146,9 +217,10 @@ impl<T: DhcpServer> Interpret<T> for DhcpInterpret {
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
info!("Executing DhcpInterpret on inventory {inventory:?}");
|
||||
|
||||
self.set_pxe_options(inventory, topology).await?;
|
||||
info!(
|
||||
"Executing DhcpHostBindingInterpret on {} bindings",
|
||||
self.score.host_binding.len()
|
||||
);
|
||||
|
||||
self.add_static_entries(inventory, topology).await?;
|
||||
|
||||
@ -156,7 +228,10 @@ impl<T: DhcpServer> Interpret<T> for DhcpInterpret {
|
||||
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
"Dhcp Interpret execution successful".to_string(),
|
||||
format!(
|
||||
"Dhcp Host Binding Interpret execution successful on {} hosts",
|
||||
self.score.host_binding.len()
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,14 @@ use derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::{FileContent, Version},
|
||||
data::{FileContent, FilePath, Version},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
score::Score,
|
||||
topology::{HttpServer, Topology},
|
||||
};
|
||||
use harmony_types::id::Id;
|
||||
use harmony_types::net::Url;
|
||||
use harmony_types::{id::Id, net::MacAddress};
|
||||
|
||||
/// Configure an HTTP server that is provided by the Topology
|
||||
///
|
||||
@ -91,3 +91,33 @@ impl<T: Topology + HttpServer> Interpret<T> for StaticFilesHttpInterpret {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct IPxeMacBootFileScore {
|
||||
pub content: String,
|
||||
pub mac_address: Vec<MacAddress>,
|
||||
}
|
||||
|
||||
impl<T: Topology + HttpServer> Score<T> for IPxeMacBootFileScore {
|
||||
fn name(&self) -> String {
|
||||
"IPxeMacBootFileScore".to_string()
|
||||
}
|
||||
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
StaticFilesHttpScore {
|
||||
folder_to_serve: None,
|
||||
files: self
|
||||
.mac_address
|
||||
.iter()
|
||||
.map(|mac| FileContent {
|
||||
path: FilePath::Relative(format!(
|
||||
"byMAC/01-{}.ipxe",
|
||||
mac.to_string().replace(":", "-")
|
||||
)),
|
||||
content: self.content.clone(),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
.create_interpret()
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::Version,
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
score::Score,
|
||||
topology::Topology,
|
||||
};
|
||||
use harmony_types::id::Id;
|
||||
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct IpxeScore {
|
||||
//files_to_serve: Url,
|
||||
}
|
||||
|
||||
impl<T: Topology> Score<T> for IpxeScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
Box::new(IpxeInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
"IpxeScore".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
pub struct IpxeInterpret {
|
||||
_score: IpxeScore,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology> Interpret<T> for IpxeInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
_topology: &T,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
/*
|
||||
let http_server = &topology.http_server;
|
||||
http_server.ensure_initialized().await?;
|
||||
Ok(Outcome::success(format!(
|
||||
"Http Server running and serving files from {}",
|
||||
self.score.files_to_serve
|
||||
)))
|
||||
*/
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Ipxe
|
||||
}
|
||||
|
||||
fn get_version(&self) -> Version {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_status(&self) -> InterpretStatus {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_children(&self) -> Vec<Id> {
|
||||
todo!()
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@ pub mod dummy;
|
||||
pub mod helm;
|
||||
pub mod http;
|
||||
pub mod inventory;
|
||||
pub mod ipxe;
|
||||
pub mod k3d;
|
||||
pub mod k8s;
|
||||
pub mod lamp;
|
||||
|
@ -50,18 +50,19 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use harmony_macros::ip;
|
||||
use harmony_types::id::Id;
|
||||
use log::info;
|
||||
use log::{error, info};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
data::Version,
|
||||
hardware::PhysicalHost,
|
||||
instrumentation::{HarmonyEvent, instrument},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
modules::{dhcp::DhcpHostBindingScore, http::IPxeMacBootFileScore},
|
||||
score::Score,
|
||||
topology::{DnsRecord, DnsRecordType, DnsServer, Topology},
|
||||
topology::{HAClusterTopology, HostBinding},
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
@ -78,8 +79,8 @@ pub struct OKDInstallationScore {
|
||||
pub internal_domain: String,
|
||||
}
|
||||
|
||||
impl<T: Topology + DnsServer + 'static> Score<T> for OKDInstallationScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
impl Score<HAClusterTopology> for OKDInstallationScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<HAClusterTopology>> {
|
||||
Box::new(OKDInstallationInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
@ -109,10 +110,10 @@ impl OKDInstallationInterpret {
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_inventory_phase<T: Topology + DnsServer>(
|
||||
async fn run_inventory_phase(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
// 1) Prepare DNS and DHCP lease registration (optional)
|
||||
let dns_score = OKDSetup01InventoryDnsScore::new(
|
||||
@ -129,10 +130,10 @@ impl OKDInstallationInterpret {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_bootstrap_phase<T: Topology + DnsServer>(
|
||||
async fn run_bootstrap_phase(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
// Select and provision bootstrap
|
||||
let bootstrap_score = OKDSetup02BootstrapScore::new(
|
||||
@ -143,40 +144,40 @@ impl OKDInstallationInterpret {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_control_plane_phase<T: Topology + DnsServer>(
|
||||
async fn run_control_plane_phase(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
let control_plane_score = OKDSetup03ControlPlaneScore::new();
|
||||
control_plane_score.interpret(inventory, topology).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_workers_phase<T: Topology + DnsServer>(
|
||||
async fn run_workers_phase(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
let workers_score = OKDSetup04WorkersScore::new();
|
||||
workers_score.interpret(inventory, topology).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_sanity_phase<T: Topology + DnsServer>(
|
||||
async fn run_sanity_phase(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
let sanity_score = OKDSetup05SanityCheckScore::new();
|
||||
sanity_score.interpret(inventory, topology).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_report_phase<T: Topology + DnsServer>(
|
||||
async fn run_report_phase(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
let report_score = OKDSetup06InstallationReportScore::new(
|
||||
self.score.public_domain.clone(),
|
||||
@ -188,7 +189,7 @@ impl OKDInstallationInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology + DnsServer> Interpret<T> for OKDInstallationInterpret {
|
||||
impl Interpret<HAClusterTopology> for OKDInstallationInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Custom("OKDInstallationInterpret")
|
||||
}
|
||||
@ -208,7 +209,7 @@ impl<T: Topology + DnsServer> Interpret<T> for OKDInstallationInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
instrument(HarmonyEvent::HarmonyStarted).ok();
|
||||
|
||||
@ -251,8 +252,8 @@ struct OKDSetup01InventoryDnsScore {
|
||||
register_dhcp_leases: Option<bool>,
|
||||
}
|
||||
|
||||
impl<T: Topology + DnsServer> Score<T> for OKDSetup01InventoryDnsScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
impl Score<HAClusterTopology> for OKDSetup01InventoryDnsScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<HAClusterTopology>> {
|
||||
Box::new(OKDSetup01InventoryDnsInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
@ -277,42 +278,10 @@ impl OKDSetup01InventoryDnsInterpret {
|
||||
status: InterpretStatus::QUEUED,
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_dns<T: DnsServer>(&self, dns: &T) -> Result<(), InterpretError> {
|
||||
// Minimal records placeholders; real IPs are set elsewhere in the flow.
|
||||
// We register the names early to ensure resolvability for clients relying on DNS.
|
||||
let mut records: Vec<DnsRecord> = vec![
|
||||
DnsRecord {
|
||||
value: ip!("0.0.0.0"),
|
||||
host: "api".to_string(),
|
||||
domain: self.score.internal_domain.clone(),
|
||||
record_type: DnsRecordType::A,
|
||||
},
|
||||
DnsRecord {
|
||||
value: ip!("0.0.0.0"),
|
||||
host: "api-int".to_string(),
|
||||
domain: self.score.internal_domain.clone(),
|
||||
record_type: DnsRecordType::A,
|
||||
},
|
||||
DnsRecord {
|
||||
value: ip!("0.0.0.0"),
|
||||
host: "*.apps.".to_string(),
|
||||
domain: self.score.internal_domain.clone(),
|
||||
record_type: DnsRecordType::A,
|
||||
},
|
||||
];
|
||||
dns.ensure_hosts_registered(records.drain(..).collect())
|
||||
.await?;
|
||||
if let Some(register) = self.score.register_dhcp_leases {
|
||||
dns.register_dhcp_leases(register).await?;
|
||||
}
|
||||
dns.commit_config().await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology + DnsServer> Interpret<T> for OKDSetup01InventoryDnsInterpret {
|
||||
impl Interpret<HAClusterTopology> for OKDSetup01InventoryDnsInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Custom("OKDSetup01InventoryDns")
|
||||
}
|
||||
@ -332,10 +301,10 @@ impl<T: Topology + DnsServer> Interpret<T> for OKDSetup01InventoryDnsInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
info!("Ensuring base DNS and DHCP lease registration for discovery phase");
|
||||
self.ensure_dns(topology).await?;
|
||||
error!("TODO setup ipxe score here and launch inventory agent");
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
"Inventory DNS prepared".into(),
|
||||
@ -354,8 +323,8 @@ struct OKDSetup01InventoryScore {
|
||||
lan_cidr: String,
|
||||
}
|
||||
|
||||
impl<T: Topology> Score<T> for OKDSetup01InventoryScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
impl Score<HAClusterTopology> for OKDSetup01InventoryScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<HAClusterTopology>> {
|
||||
Box::new(OKDSetup01InventoryInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
@ -381,9 +350,9 @@ impl OKDSetup01InventoryInterpret {
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_inventory_assets<T: Topology>(
|
||||
async fn ensure_inventory_assets(
|
||||
&self,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
// Placeholder: push or verify iPXE default, Kickstart, and Rust inventory agent are hosted.
|
||||
// Real implementation: publish to the PXE/HTTP server via the topology.
|
||||
@ -408,7 +377,7 @@ impl OKDSetup01InventoryInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology> Interpret<T> for OKDSetup01InventoryInterpret {
|
||||
impl Interpret<HAClusterTopology> for OKDSetup01InventoryInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Custom("OKDSetup01Inventory")
|
||||
}
|
||||
@ -428,7 +397,7 @@ impl<T: Topology> Interpret<T> for OKDSetup01InventoryInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
topology: &T,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
self.ensure_inventory_assets(topology).await?;
|
||||
let count = self.discover_nodes().await?;
|
||||
@ -454,8 +423,8 @@ struct OKDSetup02BootstrapScore {
|
||||
internal_domain: String,
|
||||
}
|
||||
|
||||
impl<T: Topology> Score<T> for OKDSetup02BootstrapScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
impl Score<HAClusterTopology> for OKDSetup02BootstrapScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<HAClusterTopology>> {
|
||||
Box::new(OKDSetup02BootstrapInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
@ -481,9 +450,46 @@ impl OKDSetup02BootstrapInterpret {
|
||||
}
|
||||
}
|
||||
|
||||
async fn render_per_mac_pxe(&self) -> Result<(), InterpretError> {
|
||||
fn get_bootstrap_node<'a>(&self, inventory: &'a Inventory) -> &'a PhysicalHost {
|
||||
inventory
|
||||
.worker_host
|
||||
.first()
|
||||
.expect("At least one worker host is required to be used as bootstrap node")
|
||||
}
|
||||
|
||||
async fn configure_host_binding(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
let binding = HostBinding {
|
||||
logical_host: topology.bootstrap_host.clone(),
|
||||
physical_host: self.get_bootstrap_node(inventory).clone(),
|
||||
};
|
||||
info!("Configuring host binding for bootstrap node {binding:?}");
|
||||
|
||||
DhcpHostBindingScore {
|
||||
host_binding: vec![binding],
|
||||
}
|
||||
.interpret(inventory, topology)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn render_per_mac_pxe(
|
||||
&self,
|
||||
inventory: &Inventory,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<(), InterpretError> {
|
||||
// Placeholder: use Harmony templates to emit {MAC}.ipxe selecting SCOS live + bootstrap ignition.
|
||||
info!("[Bootstrap] Rendering per-MAC PXE for bootstrap node");
|
||||
let bootstrap_node = self.get_bootstrap_node(inventory);
|
||||
IPxeMacBootFileScore {
|
||||
mac_address: bootstrap_node.get_mac_address(),
|
||||
content: todo!("templace for bootstrap node"),
|
||||
}
|
||||
.interpret(inventory, topology)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -501,7 +507,7 @@ impl OKDSetup02BootstrapInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology> Interpret<T> for OKDSetup02BootstrapInterpret {
|
||||
impl Interpret<HAClusterTopology> for OKDSetup02BootstrapInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Custom("OKDSetup02Bootstrap")
|
||||
}
|
||||
@ -520,10 +526,11 @@ impl<T: Topology> Interpret<T> for OKDSetup02BootstrapInterpret {
|
||||
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
_topology: &T,
|
||||
inventory: &Inventory,
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
self.render_per_mac_pxe().await?;
|
||||
self.configure_host_binding(inventory, topology).await?;
|
||||
self.render_per_mac_pxe(inventory, topology).await?;
|
||||
self.reboot_target().await?;
|
||||
self.wait_for_bootstrap_complete().await?;
|
||||
|
||||
@ -543,8 +550,8 @@ impl<T: Topology> Interpret<T> for OKDSetup02BootstrapInterpret {
|
||||
#[derive(Debug, Clone, Serialize, new)]
|
||||
struct OKDSetup03ControlPlaneScore {}
|
||||
|
||||
impl<T: Topology> Score<T> for OKDSetup03ControlPlaneScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
impl Score<HAClusterTopology> for OKDSetup03ControlPlaneScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<HAClusterTopology>> {
|
||||
Box::new(OKDSetup03ControlPlaneInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
@ -583,7 +590,7 @@ impl OKDSetup03ControlPlaneInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology> Interpret<T> for OKDSetup03ControlPlaneInterpret {
|
||||
impl Interpret<HAClusterTopology> for OKDSetup03ControlPlaneInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Custom("OKDSetup03ControlPlane")
|
||||
}
|
||||
@ -603,7 +610,7 @@ impl<T: Topology> Interpret<T> for OKDSetup03ControlPlaneInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
_topology: &T,
|
||||
_topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
self.render_and_reboot().await?;
|
||||
self.persist_network_bond().await?;
|
||||
@ -623,8 +630,8 @@ impl<T: Topology> Interpret<T> for OKDSetup03ControlPlaneInterpret {
|
||||
#[derive(Debug, Clone, Serialize, new)]
|
||||
struct OKDSetup04WorkersScore {}
|
||||
|
||||
impl<T: Topology> Score<T> for OKDSetup04WorkersScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
impl Score<HAClusterTopology> for OKDSetup04WorkersScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<HAClusterTopology>> {
|
||||
Box::new(OKDSetup04WorkersInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
@ -657,7 +664,7 @@ impl OKDSetup04WorkersInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology> Interpret<T> for OKDSetup04WorkersInterpret {
|
||||
impl Interpret<HAClusterTopology> for OKDSetup04WorkersInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Custom("OKDSetup04Workers")
|
||||
}
|
||||
@ -677,7 +684,7 @@ impl<T: Topology> Interpret<T> for OKDSetup04WorkersInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
_topology: &T,
|
||||
_topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
self.render_and_reboot().await?;
|
||||
Ok(Outcome::new(
|
||||
@ -695,8 +702,8 @@ impl<T: Topology> Interpret<T> for OKDSetup04WorkersInterpret {
|
||||
#[derive(Debug, Clone, Serialize, new)]
|
||||
struct OKDSetup05SanityCheckScore {}
|
||||
|
||||
impl<T: Topology> Score<T> for OKDSetup05SanityCheckScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
impl Score<HAClusterTopology> for OKDSetup05SanityCheckScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<HAClusterTopology>> {
|
||||
Box::new(OKDSetup05SanityCheckInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
@ -729,7 +736,7 @@ impl OKDSetup05SanityCheckInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology> Interpret<T> for OKDSetup05SanityCheckInterpret {
|
||||
impl Interpret<HAClusterTopology> for OKDSetup05SanityCheckInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Custom("OKDSetup05SanityCheck")
|
||||
}
|
||||
@ -749,7 +756,7 @@ impl<T: Topology> Interpret<T> for OKDSetup05SanityCheckInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
_topology: &T,
|
||||
_topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
self.run_checks().await?;
|
||||
Ok(Outcome::new(
|
||||
@ -770,8 +777,8 @@ struct OKDSetup06InstallationReportScore {
|
||||
internal_domain: String,
|
||||
}
|
||||
|
||||
impl<T: Topology> Score<T> for OKDSetup06InstallationReportScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||
impl Score<HAClusterTopology> for OKDSetup06InstallationReportScore {
|
||||
fn create_interpret(&self) -> Box<dyn Interpret<HAClusterTopology>> {
|
||||
Box::new(OKDSetup06InstallationReportInterpret::new(self.clone()))
|
||||
}
|
||||
|
||||
@ -807,7 +814,7 @@ impl OKDSetup06InstallationReportInterpret {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology> Interpret<T> for OKDSetup06InstallationReportInterpret {
|
||||
impl Interpret<HAClusterTopology> for OKDSetup06InstallationReportInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::Custom("OKDSetup06InstallationReport")
|
||||
}
|
||||
@ -827,7 +834,7 @@ impl<T: Topology> Interpret<T> for OKDSetup06InstallationReportInterpret {
|
||||
async fn execute(
|
||||
&self,
|
||||
_inventory: &Inventory,
|
||||
_topology: &T,
|
||||
_topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
self.generate().await?;
|
||||
Ok(Outcome::new(
|
||||
|
@ -36,6 +36,27 @@ pub struct DnsMasq {
|
||||
pub dhcp_options: Vec<DhcpOptions>,
|
||||
pub dhcp_boot: Vec<DhcpBoot>,
|
||||
pub dhcp_tags: Vec<RawXml>,
|
||||
pub hosts: Vec<DnsmasqHost>,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize, Clone)]
|
||||
#[yaserde(rename = "hosts")]
|
||||
pub struct DnsmasqHost {
|
||||
#[yaserde(attribute = true)]
|
||||
pub uuid: String,
|
||||
pub host: String,
|
||||
pub domain: MaybeString,
|
||||
pub local: MaybeString,
|
||||
pub ip: MaybeString,
|
||||
pub cnames: MaybeString,
|
||||
pub client_id: MaybeString,
|
||||
pub hwaddr: MaybeString,
|
||||
pub lease_time: MaybeString,
|
||||
pub ignore: Option<u8>,
|
||||
pub set_tag: MaybeString,
|
||||
pub descr: MaybeString,
|
||||
pub comments: MaybeString,
|
||||
pub aliases: MaybeString,
|
||||
}
|
||||
|
||||
// Represents the <dhcp> element and its nested fields.
|
||||
|
@ -226,6 +226,7 @@ mod tests {
|
||||
"src/tests/data/config-full-ncd0.xml",
|
||||
"src/tests/data/config-full-25.7.xml",
|
||||
"src/tests/data/config-full-25.7-dummy-dnsmasq-options.xml",
|
||||
"src/tests/data/config-25.7-dnsmasq-static-host.xml",
|
||||
] {
|
||||
let mut test_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
test_file_path.push(path);
|
||||
|
@ -442,7 +442,11 @@ mod test {
|
||||
|
||||
// This IP belongs to host-a, but the hostname belongs to host-b.
|
||||
dhcp_config
|
||||
.add_static_mapping("CC:CC:CC:CC:CC:CC", Ipv4Addr::new(192, 168, 1, 10), "host-b")
|
||||
.add_static_mapping(
|
||||
"CC:CC:CC:CC:CC:CC",
|
||||
Ipv4Addr::new(192, 168, 1, 10),
|
||||
"host-b",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -457,7 +461,11 @@ mod test {
|
||||
|
||||
// This IP is ambiguous.
|
||||
dhcp_config
|
||||
.add_static_mapping("CC:CC:CC:CC:CC:CC", Ipv4Addr::new(192, 168, 1, 30), "new-host")
|
||||
.add_static_mapping(
|
||||
"CC:CC:CC:CC:CC:CC",
|
||||
Ipv4Addr::new(192, 168, 1, 30),
|
||||
"new-host",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -509,8 +517,18 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_remove_mac_from_correct_host_only() {
|
||||
let host1 = create_host("uuid-1", "host-1", "192.168.1.50", "AA:AA:AA:AA:AA:AA,BB:BB:BB:BB:BB:BB");
|
||||
let host2 = create_host("uuid-2", "host-2", "192.168.1.51", "CC:CC:CC:CC:CC:CC,DD:DD:DD:DD:DD:DD");
|
||||
let host1 = create_host(
|
||||
"uuid-1",
|
||||
"host-1",
|
||||
"192.168.1.50",
|
||||
"AA:AA:AA:AA:AA:AA,BB:BB:BB:BB:BB:BB",
|
||||
);
|
||||
let host2 = create_host(
|
||||
"uuid-2",
|
||||
"host-2",
|
||||
"192.168.1.51",
|
||||
"CC:CC:CC:CC:CC:CC,DD:DD:DD:DD:DD:DD",
|
||||
);
|
||||
let mut dhcp_config = setup_test_env(vec![host1.clone(), host2.clone()]);
|
||||
|
||||
dhcp_config.remove_static_mapping("AA:AA:AA:AA:AA:AA");
|
||||
|
Loading…
Reference in New Issue
Block a user