All checks were successful
		
		
	
	Run Check Script / check (push) Successful in 1m52s
				
			Reviewed-on: https://git.nationtech.io/NationTech/harmony/pulls/59 Co-authored-by: Jean-Gabriel Gill-Couture <jg@nationtech.io> Co-committed-by: Jean-Gabriel Gill-Couture <jg@nationtech.io>
		
			
				
	
	
		
			147 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
## 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`:
 | 
						|
 | 
						|
```rust
 | 
						|
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`.
 | 
						|
 | 
						|
```rust
 | 
						|
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`
 | 
						|
 | 
						|
```rust
 | 
						|
#[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`:
 | 
						|
 | 
						|
```rust
 | 
						|
#[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`
 | 
						|
```rust
 | 
						|
#[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`
 | 
						|
 | 
						|
```rust
 | 
						|
pub struct DhcpScore {
 | 
						|
    ...
 | 
						|
    pub filename: Option<String>,
 | 
						|
```
 | 
						|
 | 
						|
Define it in its implementation in `modules/okd/dhcp.rs`
 | 
						|
 | 
						|
```rust
 | 
						|
impl OKDDhcpScore {
 | 
						|
        ...
 | 
						|
        Self {
 | 
						|
            dhcp_score: DhcpScore {
 | 
						|
                ...
 | 
						|
                filename: Some("undionly.kpxe".to_string()),
 | 
						|
```
 | 
						|
 | 
						|
Define it in its implementation in `modules/okd/bootstrap_dhcp.rs`
 | 
						|
 | 
						|
```rust
 | 
						|
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`
 | 
						|
 | 
						|
```rust
 | 
						|
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
 | 
						|
            )
 | 
						|
            ...
 | 
						|
```
 |