Go to file
2025-05-06 11:45:19 -04:00
adr feat(modules/opnsense): refactor and add upgrade functionality 2025-02-21 11:24:04 -05:00
data/watchguard feat: ncd0 example complete. Missing files for authentication, ignition etc are accessible upon deman. This is yet another great step towards full UPI automated provisionning 2025-05-06 11:44:40 -04:00
examples feat: ncd0 example complete. Missing files for authentication, ignition etc are accessible upon deman. This is yet another great step towards full UPI automated provisionning 2025-05-06 11:44:40 -04:00
harmony feat: ncd0 example complete. Missing files for authentication, ignition etc are accessible upon deman. This is yet another great step towards full UPI automated provisionning 2025-05-06 11:44:40 -04:00
harmony_macros chore: Reorganize file tree for easier onboarding. Rust project now at the root for simple git clone && cargo run 2025-02-12 15:32:59 -05:00
harmony_tui feat: add .gitignore and update file paths 2025-02-12 16:27:56 -05:00
harmony_types chore: Reorganize file tree for easier onboarding. Rust project now at the root for simple git clone && cargo run 2025-02-12 15:32:59 -05:00
opnsense-config feat: support new configurable field in dhcp config: filename 2025-03-01 08:56:41 -05:00
opnsense-config-xml feat: Upgrade opnsense-config crates to be compatible with opnsense 25.1_5 2025-05-06 11:45:19 -04:00
private_repos/example chore: Reorganize file tree for easier onboarding. Rust project now at the root for simple git clone && cargo run 2025-02-12 15:32:59 -05:00
.gitattributes Try out bifrost and see if we want to use it as bare metal provisionner 2024-08-28 16:16:36 -04:00
.gitignore feat: add .gitignore and update file paths 2025-02-12 16:27:56 -05:00
Cargo.lock feat(modules/opnsense): refactor and add upgrade functionality 2025-02-21 11:24:04 -05:00
Cargo.toml chore: Reorganize file tree for easier onboarding. Rust project now at the root for simple git clone && cargo run 2025-02-12 15:32:59 -05:00
LICENSE chore: Reorganize file tree for easier onboarding. Rust project now at the root for simple git clone && cargo run 2025-02-12 15:32:59 -05:00
README.md doc: fix 2025-03-01 10:29:22 -05:00

Watch the whole repo on every change

Due to the current setup being a mix of separate repositories with gitignore and rust workspace, a few options are required for cargo-watch to have the desired behavior :

RUST_LOG=info cargo watch --ignore-nothing -w harmony -w private_repos/ -x 'run --bin nationtech' 

This will run the nationtech bin (likely private_repos/nationtech/src/main.rs) on any change in the harmony or private_repos folders.

Supporting a new field in OPNSense config.xml

Two steps:

  • Supporting the field in opnsense-config-xml
  • Enabling Harmony to control the field

We'll use the filename field in the dhcpcd section of the file as an example.

Supporting the field

As type checking if enforced, every field from config.xml must be known by the code. Each subsection of config.xml has its .rs file. For the dhcpcd section, we'll modify opnsense-config-xml/src/data/dhcpd.rs.

When a new field appears in the xml file, an error like this will be thrown and Harmony will panic :

     Running `/home/stremblay/nt/dir/harmony/target/debug/example-nanodc`
Found unauthorized element filename
thread 'main' panicked at opnsense-config-xml/src/data/opnsense.rs:54:14:
OPNSense received invalid string, should be full XML: ()

Define the missing field (filename) in the DhcpInterface struct of opnsense-config-xml/src/data/dhcpd.rs:

pub struct DhcpInterface {
    ...
    pub filename: Option<String>,

Harmony should now be fixed, build and run.

Controlling the field

Define the xml field setter in opnsense-config/src/modules/dhcpd.rs.

impl<'a> DhcpConfig<'a> {
    ...
    pub fn set_filename(&mut self, filename: &str) {
        self.enable_netboot();
        self.get_lan_dhcpd().filename = Some(filename.to_string());
    }
    ...

Define the value setter in the DhcpServer trait in domain/topology/network.rs

#[async_trait]
pub trait DhcpServer: Send + Sync {
    ...
    async fn set_filename(&self, filename: &str) -> Result<(), ExecutorError>;
    ...

Implement the value setter in each DhcpServer implementation. infra/opnsense/dhcp.rs:

#[async_trait]
impl DhcpServer for OPNSenseFirewall {
    ...
    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(())
    }
    ...

domain/topology/ha_cluster.rs

#[async_trait]
impl DhcpServer for DummyInfra {
    ...
    async fn set_filename(&self, _filename: &str) -> Result<(), ExecutorError> {
        unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
    }
    ...

Add the new field to the DhcpScore in modules/dhcp.rs

pub struct DhcpScore {
    ...
    pub filename: Option<String>,

Define it in its implementation in modules/okd/dhcp.rs

impl OKDDhcpScore {
        ...
        Self {
            dhcp_score: DhcpScore {
                ...
                filename: Some("undionly.kpxe".to_string()),

Define it in its implementation in modules/okd/bootstrap_dhcp.rs

impl OKDDhcpScore {
        ...
        Self {
            dhcp_score: DhcpScore::new(
                ...
                Some("undionly.kpxe".to_string()),

Update the interpret (function called by the execute fn of the interpret) so it now updates the filename field value in modules/dhcp.rs

impl DhcpInterpret {
        ...
        let filename_outcome = match &self.score.filename {
            Some(filename) => {
                let dhcp_server = Arc::new(topology.dhcp_server.clone());
                dhcp_server.set_filename(&filename).await?;
                Outcome::new(
                    InterpretStatus::SUCCESS,
                    format!("Dhcp Interpret Set filename to {filename}"),
                )
            }
            None => Outcome::noop(),
        };

        if next_server_outcome.status == InterpretStatus::NOOP
            && boot_filename_outcome.status == InterpretStatus::NOOP
            && filename_outcome.status == InterpretStatus::NOOP

            ...

            Ok(Outcome::new(
            InterpretStatus::SUCCESS,
            format!(
                "Dhcp Interpret Set next boot to [{:?}], boot_filename to [{:?}], filename to [{:?}]",
                self.score.boot_filename, self.score.boot_filename, self.score.filename
            )
            ...