wip: PXE setup for ipxe and okd files in progress
Some checks failed
Run Check Script / check (pull_request) Failing after 36s
Some checks failed
Run Check Script / check (pull_request) Failing after 36s
This commit is contained in:
22
harmony/src/domain/data/file.rs
Normal file
22
harmony/src/domain/data/file.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FileContent {
|
||||
pub path: FilePath,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum FilePath {
|
||||
Relative(String),
|
||||
Absolute(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FilePath {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FilePath::Relative(path) => f.write_fmt(format_args!("./{path}")),
|
||||
FilePath::Absolute(path) => f.write_fmt(format_args!("/{path}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
mod id;
|
||||
mod version;
|
||||
mod file;
|
||||
pub use id::*;
|
||||
pub use version::*;
|
||||
pub use file::*;
|
||||
|
||||
@@ -4,7 +4,9 @@ use harmony_types::net::MacAddress;
|
||||
use log::debug;
|
||||
use log::info;
|
||||
|
||||
use crate::data::FileContent;
|
||||
use crate::executors::ExecutorError;
|
||||
use crate::topology::PxeOptions;
|
||||
|
||||
use super::DHCPStaticEntry;
|
||||
use super::DhcpServer;
|
||||
@@ -155,12 +157,10 @@ impl DhcpServer for HAClusterTopology {
|
||||
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
|
||||
self.dhcp_server.list_static_mappings().await
|
||||
}
|
||||
async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError> {
|
||||
self.dhcp_server.set_next_server(ip).await
|
||||
}
|
||||
async fn set_boot_filename(&self, boot_filename: &str) -> Result<(), ExecutorError> {
|
||||
self.dhcp_server.set_boot_filename(boot_filename).await
|
||||
async fn set_pxe_options(&self, options: PxeOptions) -> Result<(), ExecutorError> {
|
||||
self.dhcp_server.set_pxe_options(options).await
|
||||
}
|
||||
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
self.dhcp_server.get_ip()
|
||||
}
|
||||
@@ -170,16 +170,6 @@ impl DhcpServer for HAClusterTopology {
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||
self.dhcp_server.commit_config().await
|
||||
}
|
||||
|
||||
async fn set_filename(&self, filename: &str) -> Result<(), ExecutorError> {
|
||||
self.dhcp_server.set_filename(filename).await
|
||||
}
|
||||
async fn set_filename64(&self, filename64: &str) -> Result<(), ExecutorError> {
|
||||
self.dhcp_server.set_filename64(filename64).await
|
||||
}
|
||||
async fn set_filenameipxe(&self, filenameipxe: &str) -> Result<(), ExecutorError> {
|
||||
self.dhcp_server.set_filenameipxe(filenameipxe).await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -223,17 +213,21 @@ impl HttpServer for HAClusterTopology {
|
||||
self.http_server.serve_files(url).await
|
||||
}
|
||||
|
||||
async fn serve_file_content(&self, file: &FileContent) -> Result<(), ExecutorError> {
|
||||
self.http_server.serve_file_content(file).await
|
||||
}
|
||||
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
self.http_server.get_ip()
|
||||
}
|
||||
async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
self.http_server.ensure_initialized().await
|
||||
}
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
self.http_server.commit_config().await
|
||||
}
|
||||
async fn reload_restart(&self) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
self.http_server.reload_restart().await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,19 +295,7 @@ impl DhcpServer for DummyInfra {
|
||||
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
async fn set_next_server(&self, _ip: IpAddress) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
async fn set_boot_filename(&self, _boot_filename: &str) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
async fn set_filename(&self, _filename: &str) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
async fn set_filename64(&self, _filename: &str) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
async fn set_filenameipxe(&self, _filenameipxe: &str) -> Result<(), ExecutorError> {
|
||||
async fn set_pxe_options(&self, _options: PxeOptions) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
@@ -383,6 +365,9 @@ impl HttpServer for DummyInfra {
|
||||
async fn serve_files(&self, _url: &Url) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
async fn serve_file_content(&self, _file: &FileContent) -> Result<(), ExecutorError> {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::executors::ExecutorError;
|
||||
use crate::{data::FileContent, executors::ExecutorError};
|
||||
use async_trait::async_trait;
|
||||
|
||||
use super::{IpAddress, Url};
|
||||
@@ -6,6 +6,7 @@ use super::{IpAddress, Url};
|
||||
#[async_trait]
|
||||
pub trait HttpServer: Send + Sync {
|
||||
async fn serve_files(&self, url: &Url) -> Result<(), ExecutorError>;
|
||||
async fn serve_file_content(&self, file: &FileContent) -> Result<(), ExecutorError>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
|
||||
// async fn set_ip(&self, ip: IpAddress) -> Result<(), ExecutorError>;
|
||||
|
||||
@@ -46,16 +46,19 @@ pub trait K8sclient: Send + Sync {
|
||||
async fn k8s_client(&self) -> Result<Arc<K8sClient>, String>;
|
||||
}
|
||||
|
||||
pub struct PxeOptions {
|
||||
pub ipxe_filename: String,
|
||||
pub bios_filename: String,
|
||||
pub efi_filename: String,
|
||||
pub tftp_ip: Option<IpAddress>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait DhcpServer: Send + Sync + std::fmt::Debug {
|
||||
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>;
|
||||
async fn set_boot_filename(&self, boot_filename: &str) -> Result<(), ExecutorError>;
|
||||
async fn set_filename(&self, filename: &str) -> Result<(), ExecutorError>;
|
||||
async fn set_filename64(&self, filename64: &str) -> Result<(), ExecutorError>;
|
||||
async fn set_filenameipxe(&self, filenameipxe: &str) -> Result<(), ExecutorError>;
|
||||
async fn set_pxe_options(&self, pxe_options: PxeOptions) -> Result<(), ExecutorError>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
fn get_host(&self) -> LogicalHost;
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError>;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use async_trait::async_trait;
|
||||
use harmony_types::net::MacAddress;
|
||||
use log::debug;
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::{
|
||||
executors::ExecutorError,
|
||||
topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost},
|
||||
topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost, PxeOptions},
|
||||
};
|
||||
|
||||
use super::OPNSenseFirewall;
|
||||
@@ -26,7 +26,7 @@ impl DhcpServer for OPNSenseFirewall {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
debug!("Registered {:?}", entry);
|
||||
info!("Registered {:?}", entry);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -46,57 +46,25 @@ impl DhcpServer for OPNSenseFirewall {
|
||||
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(())
|
||||
}
|
||||
|
||||
async fn set_boot_filename(&self, boot_filename: &str) -> Result<(), ExecutorError> {
|
||||
{
|
||||
let mut writable_opnsense = self.opnsense_config.write().await;
|
||||
writable_opnsense.dhcp().set_boot_filename(boot_filename);
|
||||
debug!("OPNsense dhcp server set boot filename {boot_filename}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_filename(&self, filename: &str) -> Result<(), ExecutorError> {
|
||||
{
|
||||
let mut writable_opnsense = self.opnsense_config.write().await;
|
||||
writable_opnsense.dhcp().set_filename(filename);
|
||||
debug!("OPNsense dhcp server set filename {filename}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_filename64(&self, filename: &str) -> Result<(), ExecutorError> {
|
||||
{
|
||||
let mut writable_opnsense = self.opnsense_config.write().await;
|
||||
writable_opnsense.dhcp().set_filename64(filename);
|
||||
debug!("OPNsense dhcp server set filename {filename}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_filenameipxe(&self, filenameipxe: &str) -> Result<(), ExecutorError> {
|
||||
{
|
||||
let mut writable_opnsense = self.opnsense_config.write().await;
|
||||
writable_opnsense.dhcp().set_filenameipxe(filenameipxe);
|
||||
debug!("OPNsense dhcp server set filenameipxe {filenameipxe}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
async fn set_pxe_options(&self, options: PxeOptions) -> Result<(), ExecutorError> {
|
||||
let mut writable_opnsense = self.opnsense_config.write().await;
|
||||
let PxeOptions {
|
||||
ipxe_filename,
|
||||
bios_filename,
|
||||
efi_filename,
|
||||
tftp_ip,
|
||||
} = options;
|
||||
writable_opnsense
|
||||
.dhcp()
|
||||
.set_pxe_options(
|
||||
tftp_ip.map(|i| i.to_string()),
|
||||
bios_filename,
|
||||
efi_filename,
|
||||
ipxe_filename,
|
||||
)
|
||||
.await
|
||||
.map_err(|dhcp_error| {
|
||||
ExecutorError::UnexpectedError(format!("Failed to set_pxe_options : {dhcp_error}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,23 @@ use async_trait::async_trait;
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
data::FileContent,
|
||||
executors::ExecutorError,
|
||||
topology::{HttpServer, IpAddress, Url},
|
||||
};
|
||||
|
||||
use super::OPNSenseFirewall;
|
||||
const OPNSENSE_HTTP_ROOT_PATH: &str = "/usr/local/http";
|
||||
|
||||
#[async_trait]
|
||||
impl HttpServer for OPNSenseFirewall {
|
||||
async fn serve_files(&self, url: &Url) -> Result<(), ExecutorError> {
|
||||
let http_root_path = "/usr/local/http";
|
||||
|
||||
let config = self.opnsense_config.read().await;
|
||||
info!("Uploading files from url {url} to {http_root_path}");
|
||||
info!("Uploading files from url {url} to {OPNSENSE_HTTP_ROOT_PATH}");
|
||||
match url {
|
||||
Url::LocalFolder(path) => {
|
||||
config
|
||||
.upload_files(path, http_root_path)
|
||||
.upload_files(path, OPNSENSE_HTTP_ROOT_PATH)
|
||||
.await
|
||||
.map_err(|e| ExecutorError::UnexpectedError(e.to_string()))?;
|
||||
}
|
||||
@@ -27,8 +27,29 @@ impl HttpServer for OPNSenseFirewall {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn serve_file_content(&self, file: &FileContent) -> Result<(), ExecutorError> {
|
||||
let path = match &file.path {
|
||||
crate::data::FilePath::Relative(path) => {
|
||||
format!("{OPNSENSE_HTTP_ROOT_PATH}/{}", path.to_string())
|
||||
}
|
||||
crate::data::FilePath::Absolute(path) => {
|
||||
return Err(ExecutorError::ConfigurationError(format!(
|
||||
"Cannot serve file from http server with absolute path : {path}"
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let config = self.opnsense_config.read().await;
|
||||
info!("Uploading file content to {}", path);
|
||||
config
|
||||
.upload_file_content(&path, &file.content)
|
||||
.await
|
||||
.map_err(|e| ExecutorError::UnexpectedError(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
todo!();
|
||||
OPNSenseFirewall::get_ip(self)
|
||||
}
|
||||
|
||||
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||
|
||||
@@ -28,7 +28,7 @@ impl TftpServer for OPNSenseFirewall {
|
||||
}
|
||||
|
||||
fn get_ip(&self) -> IpAddress {
|
||||
todo!()
|
||||
OPNSenseFirewall::get_ip(self)
|
||||
}
|
||||
|
||||
async fn set_ip(&self, ip: IpAddress) -> Result<(), ExecutorError> {
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
domain::{data::Version, interpret::InterpretStatus},
|
||||
interpret::{Interpret, InterpretError, InterpretName, Outcome},
|
||||
inventory::Inventory,
|
||||
topology::{DHCPStaticEntry, DhcpServer, HostBinding, IpAddress, Topology},
|
||||
topology::{DHCPStaticEntry, DhcpServer, HostBinding, IpAddress, PxeOptions, Topology},
|
||||
};
|
||||
|
||||
use crate::domain::score::Score;
|
||||
@@ -98,69 +98,14 @@ impl DhcpInterpret {
|
||||
_inventory: &Inventory,
|
||||
dhcp_server: &D,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
let next_server_outcome = match self.score.next_server {
|
||||
Some(next_server) => {
|
||||
dhcp_server.set_next_server(next_server).await?;
|
||||
Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!("Dhcp Interpret Set next boot to {next_server}"),
|
||||
)
|
||||
}
|
||||
None => Outcome::noop(),
|
||||
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,
|
||||
};
|
||||
|
||||
let boot_filename_outcome = match &self.score.boot_filename {
|
||||
Some(boot_filename) => {
|
||||
dhcp_server.set_boot_filename(boot_filename).await?;
|
||||
Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!("Dhcp Interpret Set boot filename to {boot_filename}"),
|
||||
)
|
||||
}
|
||||
None => Outcome::noop(),
|
||||
};
|
||||
|
||||
let filename_outcome = match &self.score.filename {
|
||||
Some(filename) => {
|
||||
dhcp_server.set_filename(filename).await?;
|
||||
Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!("Dhcp Interpret Set filename to {filename}"),
|
||||
)
|
||||
}
|
||||
None => Outcome::noop(),
|
||||
};
|
||||
|
||||
let filename64_outcome = match &self.score.filename64 {
|
||||
Some(filename64) => {
|
||||
dhcp_server.set_filename64(filename64).await?;
|
||||
Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!("Dhcp Interpret Set filename64 to {filename64}"),
|
||||
)
|
||||
}
|
||||
None => Outcome::noop(),
|
||||
};
|
||||
|
||||
let filenameipxe_outcome = match &self.score.filenameipxe {
|
||||
Some(filenameipxe) => {
|
||||
dhcp_server.set_filenameipxe(filenameipxe).await?;
|
||||
Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
format!("Dhcp Interpret Set filenameipxe to {filenameipxe}"),
|
||||
)
|
||||
}
|
||||
None => Outcome::noop(),
|
||||
};
|
||||
|
||||
if next_server_outcome.status == InterpretStatus::NOOP
|
||||
&& boot_filename_outcome.status == InterpretStatus::NOOP
|
||||
&& filename_outcome.status == InterpretStatus::NOOP
|
||||
&& filename64_outcome.status == InterpretStatus::NOOP
|
||||
&& filenameipxe_outcome.status == InterpretStatus::NOOP
|
||||
{
|
||||
return Ok(Outcome::noop());
|
||||
}
|
||||
dhcp_server.set_pxe_options(pxe_options).await?;
|
||||
|
||||
Ok(Outcome::new(
|
||||
InterpretStatus::SUCCESS,
|
||||
|
||||
@@ -3,7 +3,7 @@ use derive_new::new;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
data::{Id, Version},
|
||||
data::{FileContent, Id, Version},
|
||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||
inventory::Inventory,
|
||||
score::Score,
|
||||
@@ -23,7 +23,8 @@ use crate::{
|
||||
/// ```
|
||||
#[derive(Debug, new, Clone, Serialize)]
|
||||
pub struct StaticFilesHttpScore {
|
||||
files_to_serve: Url,
|
||||
pub folder_to_serve: Option<Url>,
|
||||
pub files: Vec<FileContent>,
|
||||
}
|
||||
|
||||
impl<T: Topology + HttpServer> Score<T> for StaticFilesHttpScore {
|
||||
@@ -50,12 +51,20 @@ impl<T: Topology + HttpServer> Interpret<T> for StaticFilesHttpInterpret {
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
http_server.ensure_initialized().await?;
|
||||
// http_server.set_ip(topology.router.get_gateway()).await?;
|
||||
http_server.serve_files(&self.score.files_to_serve).await?;
|
||||
if let Some(folder) = self.score.folder_to_serve.as_ref() {
|
||||
http_server.serve_files(folder).await?;
|
||||
}
|
||||
|
||||
for f in self.score.files.iter() {
|
||||
http_server.serve_file_content(&f).await?
|
||||
}
|
||||
|
||||
http_server.commit_config().await?;
|
||||
http_server.reload_restart().await?;
|
||||
Ok(Outcome::success(format!(
|
||||
"Http Server running and serving files from {}",
|
||||
self.score.files_to_serve
|
||||
"Http Server running and serving files from folder {:?} and content for {}",
|
||||
self.score.folder_to_serve,
|
||||
self.score.files.iter().map(|f| f.path.to_string()).collect::<Vec<String>>().join(",")
|
||||
)))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user