extract related logic into an OkdIpxeScore
Some checks failed
Run Check Script / check (pull_request) Failing after 33s

This commit is contained in:
Ian Letourneau 2025-08-29 09:52:11 -04:00
parent 7bb3602ab8
commit b857412151
10 changed files with 162 additions and 87 deletions

2
Cargo.lock generated
View File

@ -1732,7 +1732,6 @@ dependencies = [
name = "example-pxe"
version = "0.1.0"
dependencies = [
"askama",
"cidr",
"env_logger",
"harmony",
@ -2169,6 +2168,7 @@ dependencies = [
name = "harmony"
version = "0.1.0"
dependencies = [
"askama",
"async-trait",
"base64 0.22.1",
"bollard",

View File

@ -18,5 +18,4 @@ harmony_macros = { path = "../../harmony_macros" }
log = { workspace = true }
env_logger = { workspace = true }
url = { workspace = true }
askama = "0.14.0"
serde.workspace = true

View File

@ -1,97 +1,24 @@
mod topology;
use std::net::IpAddr;
use askama::Template;
use harmony::{
data::{FileContent, FilePath},
modules::{dhcp::DhcpScore, http::StaticFilesHttpScore, tftp::TftpScore},
score::Score,
topology::{HAClusterTopology, Url},
};
use crate::topology::{get_inventory, get_topology};
use harmony::modules::okd::ipxe::OkdIpxeScore;
#[tokio::main]
async fn main() {
let inventory = get_inventory();
let topology = get_topology().await;
let gateway_ip = &topology.router.get_gateway();
let kickstart_filename = "inventory.kickstart";
let cluster_pubkey_filename = "cluster_ssh_key.pub";
let harmony_inventory_agent = "harmony_inventory_agent";
let kickstart_filename = "inventory.kickstart".to_string();
let cluster_pubkey_filename = "cluster_ssh_key.pub".to_string();
let harmony_inventory_agent = "harmony_inventory_agent".to_string();
// TODO: this should be a single IPXEScore instead of having the user do this step by step
let scores: Vec<Box<dyn Score<HAClusterTopology>>> = vec![
Box::new(DhcpScore {
host_binding: vec![],
next_server: Some(topology.router.get_gateway()),
boot_filename: None,
filename: Some("undionly.kpxe".to_string()),
filename64: Some("ipxe.efi".to_string()),
filenameipxe: Some(format!("http://{gateway_ip}:8080/boot.ipxe").to_string()),
}),
Box::new(TftpScore {
files_to_serve: Url::LocalFolder("./data/pxe/okd/tftpboot/".to_string()),
}),
Box::new(StaticFilesHttpScore {
// TODO The current russh based copy is way too slow, check for a lib update or use scp
// when available
//
// For now just run :
// scp -r data/pxe/okd/http_files/* root@192.168.1.1:/usr/local/http/
//
folder_to_serve: None,
// folder_to_serve: Some(Url::LocalFolder("./data/pxe/okd/http_files/".to_string())),
files: vec![
FileContent {
path: FilePath::Relative("boot.ipxe".to_string()),
content: BootIpxeTpl { gateway_ip }.to_string(),
},
FileContent {
path: FilePath::Relative(kickstart_filename.to_string()),
content: InventoryKickstartTpl {
gateway_ip,
harmony_inventory_agent,
cluster_pubkey_filename,
}
.to_string(),
},
FileContent {
path: FilePath::Relative("fallback.ipxe".to_string()),
content: FallbackIpxeTpl {
gateway_ip,
kickstart_filename,
}
.to_string(),
},
],
}),
];
let ipxe_score = OkdIpxeScore {
kickstart_filename,
harmony_inventory_agent,
cluster_pubkey_filename,
};
harmony_cli::run(inventory, topology, scores, None)
harmony_cli::run(inventory, topology, vec![Box::new(ipxe_score)], None)
.await
.unwrap();
}
#[derive(Template)]
#[template(path = "boot.ipxe.j2")]
struct BootIpxeTpl<'a> {
gateway_ip: &'a IpAddr,
}
#[derive(Template)]
#[template(path = "fallback.ipxe.j2")]
struct FallbackIpxeTpl<'a> {
gateway_ip: &'a IpAddr,
kickstart_filename: &'a str,
}
#[derive(Template)]
#[template(path = "inventory.kickstart.j2")]
struct InventoryKickstartTpl<'a> {
gateway_ip: &'a IpAddr,
cluster_pubkey_filename: &'a str,
harmony_inventory_agent: &'a str,
}

View File

@ -1,5 +1,3 @@
use std::{net::IpAddr, sync::Arc};
use cidr::Ipv4Cidr;
use harmony::{
hardware::{FirewallGroup, HostCategory, Location, PhysicalHost, SwitchGroup},
@ -10,6 +8,7 @@ use harmony::{
use harmony_macros::{ip, ipv4};
use harmony_secret::{Secret, SecretManager};
use serde::{Deserialize, Serialize};
use std::{net::IpAddr, sync::Arc};
#[derive(Secret, Serialize, Deserialize, Debug, PartialEq)]
struct OPNSenseFirewallConfig {

View File

@ -69,6 +69,7 @@ base64.workspace = true
once_cell = "1.21.3"
harmony_inventory_agent = { path = "../harmony_inventory_agent" }
harmony_secret_derive = { version = "0.1.0", path = "../harmony_secret_derive" }
askama = "0.14.0"
[dev-dependencies]
pretty_assertions.workspace = true

View File

@ -0,0 +1,148 @@
use askama::Template;
use async_trait::async_trait;
use derive_new::new;
use serde::Serialize;
use std::net::IpAddr;
use crate::{
data::{FileContent, FilePath, Id, Version},
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
modules::{dhcp::DhcpScore, http::StaticFilesHttpScore, tftp::TftpScore},
score::Score,
topology::{DhcpServer, HttpServer, Router, TftpServer, Topology, Url},
};
#[derive(Debug, new, Clone, Serialize)]
pub struct OkdIpxeScore {
pub kickstart_filename: String,
pub harmony_inventory_agent: String,
pub cluster_pubkey_filename: String,
}
impl<T: Topology + DhcpServer + TftpServer + HttpServer + Router> Score<T> for OkdIpxeScore {
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(IpxeInterpret::new(self.clone()))
}
fn name(&self) -> String {
"OkdIpxeScore".to_string()
}
}
#[derive(Debug, new, Clone)]
pub struct IpxeInterpret {
score: OkdIpxeScore,
}
#[async_trait]
impl<T: Topology + DhcpServer + TftpServer + HttpServer + Router> Interpret<T> for IpxeInterpret {
async fn execute(
&self,
inventory: &Inventory,
topology: &T,
) -> Result<Outcome, InterpretError> {
let gateway_ip = topology.get_gateway();
let scores: Vec<Box<dyn Score<T>>> = vec![
Box::new(DhcpScore {
host_binding: vec![],
next_server: Some(topology.get_gateway()),
boot_filename: None,
filename: Some("undionly.kpxe".to_string()),
filename64: Some("ipxe.efi".to_string()),
filenameipxe: Some(format!("http://{gateway_ip}:8080/boot.ipxe").to_string()),
}),
Box::new(TftpScore {
files_to_serve: Url::LocalFolder("./data/pxe/okd/tftpboot/".to_string()),
}),
Box::new(StaticFilesHttpScore {
// TODO The current russh based copy is way too slow, check for a lib update or use scp
// when available
//
// For now just run :
// scp -r data/pxe/okd/http_files/* root@192.168.1.1:/usr/local/http/
//
folder_to_serve: None,
// folder_to_serve: Some(Url::LocalFolder("./data/pxe/okd/http_files/".to_string())),
files: vec![
FileContent {
path: FilePath::Relative("boot.ipxe".to_string()),
content: BootIpxeTpl {
gateway_ip: &gateway_ip,
}
.to_string(),
},
FileContent {
path: FilePath::Relative(self.score.kickstart_filename.clone()),
content: InventoryKickstartTpl {
gateway_ip: &gateway_ip,
harmony_inventory_agent: &self.score.harmony_inventory_agent,
cluster_pubkey_filename: &self.score.cluster_pubkey_filename,
}
.to_string(),
},
FileContent {
path: FilePath::Relative("fallback.ipxe".to_string()),
content: FallbackIpxeTpl {
gateway_ip: &gateway_ip,
kickstart_filename: &self.score.kickstart_filename,
}
.to_string(),
},
],
}),
];
for score in scores {
let result = score.interpret(inventory, topology).await;
match result {
Ok(outcome) => match outcome.status {
InterpretStatus::SUCCESS => continue,
InterpretStatus::NOOP => continue,
_ => return Err(InterpretError::new(outcome.message)),
},
Err(e) => return Err(e),
};
}
Ok(Outcome::success("Ipxe installed".to_string()))
}
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!()
}
}
#[derive(Template)]
#[template(path = "boot.ipxe.j2")]
struct BootIpxeTpl<'a> {
gateway_ip: &'a IpAddr,
}
#[derive(Template)]
#[template(path = "fallback.ipxe.j2")]
struct FallbackIpxeTpl<'a> {
gateway_ip: &'a IpAddr,
kickstart_filename: &'a str,
}
#[derive(Template)]
#[template(path = "inventory.kickstart.j2")]
struct InventoryKickstartTpl<'a> {
gateway_ip: &'a IpAddr,
cluster_pubkey_filename: &'a str,
harmony_inventory_agent: &'a str,
}

View File

@ -2,5 +2,6 @@ pub mod bootstrap_dhcp;
pub mod bootstrap_load_balancer;
pub mod dhcp;
pub mod dns;
pub mod ipxe;
pub mod load_balancer;
pub mod upgrade;