diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9f97022 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 4d451ca..eb01230 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,6 +212,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "configparser" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57e3272f0190c3f1584272d613719ba5fc7df7f4942fe542e63d949cf3a649b" + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -754,6 +760,7 @@ version = "0.3.1" dependencies = [ "anyhow", "clap", + "configparser", "env_logger", "log", "network-interface", diff --git a/Cargo.toml b/Cargo.toml index 6b88611..41271f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,4 @@ network-interface = "2.0.0" nmstate = { version = "2.2.36", features = ["gen_conf"] } serde = { version = "1.0.210", features = ["derive"] } serde_yaml = "0.9.34" +configparser = "3.1.0" diff --git a/src/apply_conf.rs b/src/apply_conf.rs index 3aab891..e64e342 100644 --- a/src/apply_conf.rs +++ b/src/apply_conf.rs @@ -181,31 +181,42 @@ fn copy_connection_files( .to_str() .ok_or_else(|| anyhow!("Determining host config path"))?; - for interface in &host.interfaces { + for interface in host.interfaces { info!("Processing interface '{}'...", &interface.logical_name); + let connections = &interface.connection_ids; - let mut filename = &interface.logical_name; + if connections.is_empty() { + return Err(anyhow!( + "Missing connection ids for {}", + &interface.logical_name + )); + } - let filepath = keyfile_path(host_config_dir, filename) - .ok_or_else(|| anyhow!("Determining source keyfile path"))?; + for connection in connections { + info!("Processing connection '{}'...", connection); + let mut filename = connection.clone(); - let mut contents = fs::read_to_string(filepath).context("Reading file")?; + let filepath = keyfile_path(host_config_dir, &filename) + .ok_or_else(|| anyhow!("Determining source keyfile path"))?; - // Update the name and all references of the host NIC in the settings file if there is a difference from the static config. - match local_interfaces.get(&interface.logical_name) { - None => {} - Some(local_name) => { - info!( - "Using interface name '{}' instead of the preconfigured '{}'", - local_name, interface.logical_name - ); - - contents = contents.replace(&interface.logical_name, local_name); - filename = local_name; + let mut contents = fs::read_to_string(filepath).context("Reading file")?; + + // Update the name and all references of the host NIC in the settings file if there is a difference from the static config. + match local_interfaces.get(&interface.logical_name) { + None => {} + Some(local_name) => { + info!( + "Using interface name '{}' instead of the preconfigured '{}'", + local_name, interface.logical_name + ); + + contents = contents.replace(&interface.logical_name, local_name); + filename = filename.replace(&interface.logical_name, local_name); + } } - } - store_connection_file(filename, contents, destination_dir).context("Storing file")?; + store_connection_file(&filename, contents, destination_dir).context("Storing file")?; + } } Ok(()) @@ -302,6 +313,7 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("00:11:22:33:44:55".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }], }, Host { @@ -310,6 +322,7 @@ mod tests { logical_name: "".to_string(), mac_address: Option::from("10:10:10:10:10:10".to_string()), interface_type: "".to_string(), + connection_ids: Vec::new(), }], }, ]; @@ -336,6 +349,7 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("00:11:22:33:44:55".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }] ); } @@ -349,6 +363,7 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("10:20:30:40:50:60".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }], }, Host { @@ -357,6 +372,7 @@ mod tests { logical_name: "".to_string(), mac_address: Option::from("00:10:20:30:40:50".to_string()), interface_type: "".to_string(), + connection_ids: Vec::new(), }], }, ]; @@ -389,21 +405,25 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("00:11:22:33:44:55".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }, Interface { logical_name: "eth1".to_string(), mac_address: Option::from("00:11:22:33:44:58".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth1".to_string()], }, Interface { logical_name: "eth2".to_string(), mac_address: Option::from("36:5e:6b:a2:ed:80".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth2".to_string()], }, Interface { logical_name: "bond0".to_string(), mac_address: Option::from("00:11:22:aa:44:58".to_string()), interface_type: "bond".to_string(), + connection_ids: vec!["bond0".to_string()], }, ], }, @@ -414,11 +434,36 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("36:5e:6b:a2:ed:81".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }, Interface { logical_name: "eth0.1365".to_string(), mac_address: None, interface_type: "vlan".to_string(), + connection_ids: vec!["eth0.1365".to_string()], + }, + ], + }, + Host { + hostname: "node3".to_string(), + interfaces: vec![ + Interface { + logical_name: "br1".to_string(), + mac_address: None, + interface_type: "ovs-bridge".to_string(), + connection_ids: vec!["br1-br".to_string()], + }, + Interface { + logical_name: "ovs0".to_string(), + mac_address: None, + interface_type: "ovs-interface".to_string(), + connection_ids: vec!["ovs0-port".to_string(), "ovs0-if".to_string()], + }, + Interface { + logical_name: "eth0".to_string(), + mac_address: Option::from("95:b2:92:88:1d:3f".to_string()), + interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string(), "eth0-port".to_string()], }, ], }, @@ -435,26 +480,31 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("00:11:22:33:44:55".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }, Interface { logical_name: "eth0.1365".to_string(), mac_address: None, interface_type: "vlan".to_string(), + connection_ids: vec!["eth0.1365".to_string()], }, Interface { logical_name: "eth2".to_string(), mac_address: Option::from("00:11:22:33:44:56".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth2".to_string()], }, Interface { logical_name: "eth2.bridge".to_string(), mac_address: None, interface_type: "linux-bridge".to_string(), + connection_ids: vec!["eth2.bridge".to_string()], }, Interface { logical_name: "bond0".to_string(), mac_address: Option::from("00:11:22:33:44:58".to_string()), interface_type: "bond".to_string(), + connection_ids: vec!["bond0".to_string()], }, ], }; @@ -522,34 +572,49 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("00:11:22:33:44:55".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }, Interface { logical_name: "eth0.1365".to_string(), mac_address: None, interface_type: "vlan".to_string(), + connection_ids: vec!["eth0.1365".to_string()], + }, + Interface { + logical_name: "br1".to_string(), + mac_address: None, + interface_type: "ovs-bridge".to_string(), + connection_ids: vec!["br1-br".to_string()], }, Interface { logical_name: "eth2".to_string(), mac_address: Option::from("00:11:22:33:44:56".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth2".to_string(), "eth2-port".to_string()], }, Interface { logical_name: "eth1".to_string(), mac_address: Option::from("00:11:22:33:44:57".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth1".to_string()], }, Interface { logical_name: "bond0".to_string(), mac_address: Option::from("00:11:22:33:44:58".to_string()), interface_type: "bond".to_string(), + connection_ids: vec!["bond0".to_string()], }, ], }; let detected_interfaces = HashMap::from([("eth2".to_string(), "eth4".to_string())]); - assert!( - copy_connection_files(host, detected_interfaces, source_dir, destination_dir).is_ok() - ); + assert!(copy_connection_files( + host, + detected_interfaces.clone(), + source_dir, + destination_dir + ) + .is_ok()); let source_path = Path::new(source_dir).join("node1"); let destination_path = Path::new(destination_dir); @@ -560,9 +625,15 @@ mod tests { let mut input = fs::read_to_string(entry.path())?; // Adjust the name and content for the "eth2"->"eth4" edge case. - if entry.path().file_stem().is_some_and(|stem| stem == "eth2") { - filename = filename.replace("eth2", "eth4"); - input = input.replace("eth2", "eth4"); + for (src_stem, dst_stem) in detected_interfaces.iter() { + if entry + .path() + .file_stem() + .is_some_and(|stem| stem.to_str().unwrap().contains(src_stem)) + { + filename = filename.replace(src_stem, dst_stem); + input = input.replace(src_stem, dst_stem); + } } let output = fs::read_to_string(destination_path.join(&filename))?; @@ -574,6 +645,30 @@ mod tests { fs::remove_dir_all(destination_dir) } + #[test] + fn copy_connection_files_missing_connection_ids() -> io::Result<()> { + let source_dir = "testdata/apply"; + let destination_dir = "_out2"; + + let host = Host { + hostname: "node1".to_string(), + interfaces: vec![Interface { + logical_name: "eth0".to_string(), + mac_address: Option::from("00:11:22:33:44:55".to_string()), + interface_type: "ethernet".to_string(), + connection_ids: Vec::new(), + }], + }; + + assert!( + copy_connection_files(host, HashMap::new(), source_dir, destination_dir) + .is_err_and(|e| e.to_string().contains("Missing connection ids")) + ); + + // cleanup + fs::remove_dir_all(destination_dir) + } + #[test] fn generate_keyfile_path() { assert_eq!( diff --git a/src/generate_conf.rs b/src/generate_conf.rs index 3deb335..72f1828 100644 --- a/src/generate_conf.rs +++ b/src/generate_conf.rs @@ -2,20 +2,20 @@ use std::ffi::OsStr; use std::fs; use std::path::Path; +use crate::types::{Host, Interface}; +use crate::{ALL_HOSTS_DIR, ALL_HOSTS_FILE, HOST_MAPPING_FILE}; use anyhow::{anyhow, Context}; +use configparser::ini::Ini; use log::{info, warn}; use nmstate::{InterfaceType, NetworkState}; -use crate::types::{Host, Interface}; -use crate::{ALL_HOSTS_DIR, ALL_HOSTS_FILE, HOST_MAPPING_FILE}; - /// `NetworkConfig` contains the generated configurations in the /// following format: `Vec<(config_file_name, config_content>)` type NetworkConfig = Vec<(String, String)>; /// Generate network configurations from all YAML files in the `config_dir` /// and store the result *.nmconnection files and host mapping (if applicable) under `output_dir`. -pub(crate) fn generate(config_dir: &str, output_dir: &str) -> Result<(), anyhow::Error> { +pub(crate) fn generate(config_dir: &str, output_dir: &str) -> anyhow::Result<()> { let files_count = fs::read_dir(config_dir)?.count(); if files_count == 0 { @@ -73,10 +73,10 @@ fn extract_hostname(path: &Path) -> Option<&OsStr> { fn generate_config( data: String, require_mac_addresses: bool, -) -> Result<(Vec, NetworkConfig), anyhow::Error> { +) -> anyhow::Result<(Vec, NetworkConfig)> { let network_state = NetworkState::new_from_yaml(&data)?; - let interfaces = extract_interfaces(&network_state); + let mut interfaces = extract_interfaces(&network_state); validate_interfaces(&interfaces, require_mac_addresses)?; let config = network_state @@ -85,9 +85,63 @@ fn generate_config( .ok_or_else(|| anyhow!("Invalid NM configuration"))? .to_owned(); + populate_connection_ids(&mut interfaces, &config)?; + validate_connection_ids(&interfaces)?; + Ok((interfaces, config)) } +fn validate_connection_ids(interfaces: &[Interface]) -> anyhow::Result<()> { + let empty_connection_ids: Vec = interfaces + .iter() + .filter(|i| i.connection_ids.is_empty()) + .map(|i| i.logical_name.to_owned()) + .collect(); + + if !empty_connection_ids.is_empty() { + return Err(anyhow!( + "Detected interfaces without connection files: {}", + empty_connection_ids.join(", ") + )); + }; + + Ok(()) +} + +fn populate_connection_ids( + interfaces: &mut [Interface], + config: &NetworkConfig, +) -> anyhow::Result<()> { + for (filename, content) in config { + let mut c = Ini::new(); + c.read(content.to_string()).map_err(|e| anyhow!(e))?; + + if c.get("connection", "type").is_some_and(|t| t == "loopback") { + continue; + } + + let interface_name = c + .get("connection", "interface-name") + .ok_or_else(|| anyhow!("No interface-name found in connection file: {}", filename))?; + let connection_id = c + .get("connection", "id") + .ok_or_else(|| anyhow!("No connection id found in connection file: {}", filename))?; + interfaces + .iter_mut() + .find(|x| x.logical_name == interface_name) + .ok_or_else(|| { + anyhow!( + "No matching interface found for connection file: {}", + filename + ) + })? + .connection_ids + .push(connection_id); + } + + Ok(()) +} + fn extract_interfaces(network_state: &NetworkState) -> Vec { network_state .interfaces @@ -97,6 +151,7 @@ fn extract_interfaces(network_state: &NetworkState) -> Vec { logical_name: i.name().to_owned(), mac_address: i.base_iface().mac_address.clone(), interface_type: i.iface_type().to_string(), + connection_ids: Vec::new(), }) .collect() } @@ -138,7 +193,7 @@ fn store_network_config( output_dir: &str, hostname: &str, config: NetworkConfig, -) -> Result<(), anyhow::Error> { +) -> anyhow::Result<()> { let path = Path::new(output_dir).join(hostname); fs::create_dir_all(&path).context("Creating output dir")?; @@ -154,7 +209,7 @@ fn store_network_mapping( output_dir: &str, hostname: String, interfaces: Vec, -) -> Result<(), anyhow::Error> { +) -> anyhow::Result<()> { let path = Path::new(output_dir); let mapping_file = fs::OpenOptions::new() @@ -172,14 +227,14 @@ fn store_network_mapping( #[cfg(test)] mod tests { - use std::fs; - use std::path::Path; - use crate::generate_conf::{ - extract_hostname, extract_interfaces, generate, generate_config, validate_interfaces, + extract_hostname, extract_interfaces, generate, generate_config, populate_connection_ids, + validate_connection_ids, validate_interfaces, }; use crate::types::{Host, Interface}; use crate::HOST_MAPPING_FILE; + use std::fs; + use std::path::Path; #[test] fn generate_successfully() -> Result<(), anyhow::Error> { @@ -188,18 +243,12 @@ mod tests { let out_dir = "_out"; let output_path = Path::new("_out").join("node1"); - assert!(generate(config_dir, out_dir).is_ok()); + generate(config_dir, out_dir)?; - // verify contents of *.nmconnection files - let exp_eth0_conn = fs::read_to_string(exp_output_path.join("eth0.nmconnection"))?; - let exp_bridge_conn = fs::read_to_string(exp_output_path.join("bridge0.nmconnection"))?; + // verify contents of lo.nmconnection files let exp_lo_conn = fs::read_to_string(exp_output_path.join("lo.nmconnection"))?; - let eth0_conn = fs::read_to_string(output_path.join("eth0.nmconnection"))?; - let bridge_conn = fs::read_to_string(output_path.join("bridge0.nmconnection"))?; let lo_conn = fs::read_to_string(output_path.join("lo.nmconnection"))?; - assert_eq!(exp_eth0_conn, eth0_conn); - assert_eq!(exp_bridge_conn, bridge_conn); assert_eq!(exp_lo_conn, lo_conn); // verify contents of the host mapping file @@ -224,6 +273,20 @@ mod tests { assert_eq!(exp_hosts, hosts); + // verify contents of *.nmconnection files based on interface.connection_ids + hosts + .iter_mut() + .flat_map(|h| h.interfaces.iter()) + .flat_map(|interface| &interface.connection_ids) + .for_each(|conn_id| { + let exp_conn = + fs::read_to_string(exp_output_path.join(format!("{conn_id}.nmconnection"))) + .unwrap(); + let conn = fs::read_to_string(output_path.join(format!("{conn_id}.nmconnection"))) + .unwrap(); + assert_eq!(exp_conn, conn); + }); + // cleanup fs::remove_dir_all(out_dir)?; @@ -269,7 +332,13 @@ mod tests { "#, )?; + let config_files = vec![ + generate_config_file("eth1".to_string(), "eth1".to_string()), + generate_config_file("bridge0".to_string(), "bridge0".to_string()), + ]; + let mut interfaces = extract_interfaces(&net_state); + populate_connection_ids(&mut interfaces, &config_files).expect("populate ids"); interfaces.sort_by(|a, b| a.logical_name.cmp(&b.logical_name)); assert_eq!( @@ -279,11 +348,13 @@ mod tests { logical_name: "bridge0".to_string(), mac_address: Option::from("FE:C4:05:42:8B:AB".to_string()), interface_type: "linux-bridge".to_string(), + connection_ids: vec!["bridge0".to_string()], }, Interface { logical_name: "eth1".to_string(), mac_address: Option::from("FE:C4:05:42:8B:AA".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth1".to_string()], }, ] ); @@ -291,6 +362,16 @@ mod tests { Ok(()) } + fn generate_config_file(logical_name: String, connection_id: String) -> (String, String) { + let filename = format!("{connection_id}.nmconnection"); + + let mut config = configparser::ini::Ini::new(); + config.set("connection", "id", Some(connection_id)); + config.set("connection", "interface-name", Some(logical_name)); + + (filename, config.writes()) + } + #[test] fn validate_interfaces_missing_ethernet_interfaces() { let interfaces = vec![ @@ -298,11 +379,13 @@ mod tests { logical_name: "eth3.1365".to_string(), mac_address: None, interface_type: "vlan".to_string(), + connection_ids: vec!["eth3.1365".to_string()], }, Interface { logical_name: "bond0".to_string(), mac_address: None, interface_type: "bond".to_string(), + connection_ids: vec!["bond0".to_string()], }, ]; @@ -317,31 +400,37 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("00:11:22:33:44:55".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }, Interface { logical_name: "eth1".to_string(), mac_address: None, interface_type: "ethernet".to_string(), + connection_ids: vec!["eth1".to_string()], }, Interface { logical_name: "eth2".to_string(), mac_address: Option::from("00:11:22:33:44:56".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth2".to_string()], }, Interface { logical_name: "eth3".to_string(), mac_address: None, interface_type: "ethernet".to_string(), + connection_ids: vec!["eth3".to_string()], }, Interface { logical_name: "eth3.1365".to_string(), mac_address: None, interface_type: "vlan".to_string(), + connection_ids: vec!["eth3.1365".to_string()], }, Interface { logical_name: "bond0".to_string(), mac_address: Option::from("00:11:22:33:44:58".to_string()), interface_type: "bond".to_string(), + connection_ids: vec!["bond0".to_string()], }, ]; @@ -355,6 +444,37 @@ mod tests { assert!(validate_interfaces(&interfaces, false).is_ok()) } + #[test] + fn validate_interfaces_missing_connection_ids() { + let interfaces = vec![ + Interface { + logical_name: "eth0".to_string(), + mac_address: Option::from("00:11:22:33:44:55".to_string()), + interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], + }, + Interface { + logical_name: "eth0.1365".to_string(), + mac_address: None, + interface_type: "vlan".to_string(), + connection_ids: vec!["eth0.1365".to_string()], + }, + Interface { + logical_name: "bond0".to_string(), + mac_address: None, + interface_type: "bond".to_string(), + connection_ids: Vec::new(), + }, + ]; + + assert_eq!( + validate_connection_ids(&interfaces) + .unwrap_err() + .to_string(), + "Detected interfaces without connection files: bond0" + ); + } + #[test] fn validate_interfaces_successfully() { let interfaces = vec![ @@ -362,21 +482,25 @@ mod tests { logical_name: "eth0".to_string(), mac_address: Option::from("00:11:22:33:44:55".to_string()), interface_type: "ethernet".to_string(), + connection_ids: vec!["eth0".to_string()], }, Interface { logical_name: "eth0.1365".to_string(), mac_address: None, interface_type: "vlan".to_string(), + connection_ids: vec!["eth0.1365".to_string()], }, Interface { logical_name: "bond0".to_string(), mac_address: None, interface_type: "bond".to_string(), + connection_ids: vec!["bond0".to_string()], }, ]; assert!(validate_interfaces(&interfaces, true).is_ok()); assert!(validate_interfaces(&interfaces, false).is_ok()); + assert!(validate_connection_ids(&interfaces).is_ok()); } #[test] diff --git a/src/types.rs b/src/types.rs index 7dee144..02d3403 100644 --- a/src/types.rs +++ b/src/types.rs @@ -11,6 +11,8 @@ pub struct Host { #[cfg_attr(test, derive(PartialEq))] pub struct Interface { pub(crate) logical_name: String, + #[serde(default)] + pub(crate) connection_ids: Vec, #[serde(skip_serializing_if = "Option::is_none")] #[serde(default)] pub(crate) mac_address: Option, diff --git a/testdata/apply/config/host_config.yaml b/testdata/apply/config/host_config.yaml index a870694..339d4e3 100644 --- a/testdata/apply/config/host_config.yaml +++ b/testdata/apply/config/host_config.yaml @@ -3,19 +3,48 @@ - logical_name: eth0 mac_address: 00:11:22:33:44:55 interface_type: ethernet + connection_ids: + - eth0 - logical_name: eth1 mac_address: 00:11:22:33:44:58 interface_type: ethernet + connection_ids: + - eth1 - logical_name: eth2 mac_address: 36:5e:6b:a2:ed:80 interface_type: ethernet + connection_ids: + - eth2 - logical_name: bond0 mac_address: 00:11:22:AA:44:58 interface_type: bond + connection_ids: + - bond0 - hostname: node2 interfaces: - logical_name: eth0 mac_address: 36:5E:6B:A2:ED:81 interface_type: ethernet + connection_ids: + - eth0 - logical_name: eth0.1365 interface_type: vlan + connection_ids: + - eth0.1365 +- hostname: node3 + interfaces: + - logical_name: br1 + connection_ids: + - br1-br + interface_type: ovs-bridge + - logical_name: ovs0 + connection_ids: + - ovs0-port + - ovs0-if + interface_type: ovs-interface + - logical_name: eth0 + connection_ids: + - eth0 + - eth0-port + mac_address: 95:B2:92:88:1D:3F + interface_type: ethernet \ No newline at end of file diff --git a/testdata/apply/node1/br1-br.nmconnection b/testdata/apply/node1/br1-br.nmconnection new file mode 100644 index 0000000..d04a3d8 --- /dev/null +++ b/testdata/apply/node1/br1-br.nmconnection @@ -0,0 +1,18 @@ +[connection] +autoconnect=true +autoconnect-slaves=1 +id=br1-br +interface-name=br1 +type=ovs-bridge +uuid=327127e8-ee26-5178-bacd-ebbb191b1bdd + +[ipv4] +dhcp-timeout=2147483647 +method=disabled + +[ipv6] +dhcp-timeout=2147483647 +method=disabled + +[ovs-bridge] +stp-enable=true diff --git a/testdata/apply/node1/eth2-port.nmconnection b/testdata/apply/node1/eth2-port.nmconnection new file mode 100644 index 0000000..bb36b72 --- /dev/null +++ b/testdata/apply/node1/eth2-port.nmconnection @@ -0,0 +1,11 @@ +[connection] +autoconnect=true +autoconnect-slaves=-1 +id=eth2-port +interface-name=eth2 +master=br1 +slave-type=ovs-bridge +type=ovs-port +uuid=5e4ac41f-d8dd-5cbd-98b3-122629af91e0 + +[ovs-port] diff --git a/testdata/apply/node1/eth2.nmconnection b/testdata/apply/node1/eth2.nmconnection index d7d5be7..3994c3e 100644 --- a/testdata/apply/node1/eth2.nmconnection +++ b/testdata/apply/node1/eth2.nmconnection @@ -1,23 +1,11 @@ [connection] -id = eth2 -uuid = ad451df9-e022-4ce4-9ba1-4bc691c9abc1 -type = ethernet -interface-name = eth2 +autoconnect=true +autoconnect-slaves=-1 +id=eth2 +interface-name=eth2 +master=eth2 +slave-type=ovs-port +type=802-3-ethernet +uuid=0523c0a1-5f5e-5603-bcf2-68155d5d322e [ethernet] - -[ipv4] -address1 = 192.168.123.3/24 -dns = 192.168.123.100 -dns-priority = 40 -method = manual -route1 = 0.0.0.0/0,192.168.123.3 -route1_options = table=254 - -[ipv6] -addr-gen-mode = eui64 -dhcp-duid = ll -dhcp-iaid = mac -method = disabled - -[proxy] diff --git a/testdata/generate/expected/br1-br.nmconnection b/testdata/generate/expected/br1-br.nmconnection new file mode 100644 index 0000000..d6e75e3 --- /dev/null +++ b/testdata/generate/expected/br1-br.nmconnection @@ -0,0 +1,21 @@ +[connection] +autoconnect=true +autoconnect-slaves=1 +id=br1-br +interface-name=br1 +type=ovs-bridge +uuid=327127e8-ee26-5178-bacd-ebbb191b1bdd + +[ipv4] +dhcp-timeout=2147483647 +method=disabled + +[ipv6] +dhcp-timeout=2147483647 +method=disabled + +[ovs-bridge] +stp-enable=true + +[user] +nmstate.interface.description=ovs bridge with eth1 as a port and ovs0 as an internal interface diff --git a/testdata/generate/expected/eth1-port.nmconnection b/testdata/generate/expected/eth1-port.nmconnection new file mode 100644 index 0000000..2c81d1c --- /dev/null +++ b/testdata/generate/expected/eth1-port.nmconnection @@ -0,0 +1,11 @@ +[connection] +autoconnect=true +autoconnect-slaves=-1 +id=eth1-port +interface-name=eth1 +master=br1 +slave-type=ovs-bridge +type=ovs-port +uuid=5e4ac41f-d8dd-5cbd-98b3-122629af91e0 + +[ovs-port] diff --git a/testdata/generate/expected/eth1.nmconnection b/testdata/generate/expected/eth1.nmconnection new file mode 100644 index 0000000..2720221 --- /dev/null +++ b/testdata/generate/expected/eth1.nmconnection @@ -0,0 +1,12 @@ +[connection] +autoconnect=true +autoconnect-slaves=-1 +id=eth1 +interface-name=eth1 +master=eth1 +slave-type=ovs-port +type=802-3-ethernet +uuid=0523c0a1-5f5e-5603-bcf2-68155d5d322e + +[ethernet] +cloned-mac-address=5C:C7:C9:5E:FB:EC diff --git a/testdata/generate/expected/host_config.yaml b/testdata/generate/expected/host_config.yaml index e23faae..06aece8 100644 --- a/testdata/generate/expected/host_config.yaml +++ b/testdata/generate/expected/host_config.yaml @@ -1,8 +1,27 @@ - hostname: node1 interfaces: + - logical_name: br1 + connection_ids: + - br1-br + interface_type: ovs-bridge - logical_name: bridge0 + connection_ids: + - bridge0 mac_address: FE:C4:05:42:8B:AA interface_type: linux-bridge + - logical_name: ovs0 + connection_ids: + - ovs0-port + - ovs0-if + interface_type: ovs-interface - logical_name: eth0 + connection_ids: + - eth0 mac_address: 0E:4D:C6:B8:C4:72 interface_type: ethernet + - logical_name: eth1 + connection_ids: + - eth1-port + - eth1 + mac_address: 5c:c7:c9:5e:fb:ec + interface_type: ethernet diff --git a/testdata/generate/expected/ovs0-if.nmconnection b/testdata/generate/expected/ovs0-if.nmconnection new file mode 100644 index 0000000..d17e12a --- /dev/null +++ b/testdata/generate/expected/ovs0-if.nmconnection @@ -0,0 +1,25 @@ +[connection] +autoconnect=true +autoconnect-slaves=-1 +id=ovs0-if +interface-name=ovs0 +master=ovs0 +slave-type=ovs-port +type=ovs-interface +uuid=94e89542-80b4-59a0-b84a-7d82c89c9ed4 + +[ipv4] +dhcp-client-id=mac +dhcp-send-hostname=true +dhcp-timeout=2147483647 +ignore-auto-dns=false +ignore-auto-routes=false +method=auto +never-default=false + +[ipv6] +dhcp-timeout=2147483647 +method=disabled + +[ovs-interface] +type=internal diff --git a/testdata/generate/expected/ovs0-port.nmconnection b/testdata/generate/expected/ovs0-port.nmconnection new file mode 100644 index 0000000..6516819 --- /dev/null +++ b/testdata/generate/expected/ovs0-port.nmconnection @@ -0,0 +1,11 @@ +[connection] +autoconnect=true +autoconnect-slaves=-1 +id=ovs0-port +interface-name=ovs0 +master=br1 +slave-type=ovs-bridge +type=ovs-port +uuid=dde94eac-b114-55b9-8f5f-7d53334bcb78 + +[ovs-port] diff --git a/testdata/generate/node1.yaml b/testdata/generate/node1.yaml index 1dc8c97..af71c54 100644 --- a/testdata/generate/node1.yaml +++ b/testdata/generate/node1.yaml @@ -61,3 +61,23 @@ interfaces: address: - ip: ::1 prefix-length: 128 + - name: eth1 + type: ethernet + state: up + mac-address: 5c:c7:c9:5e:fb:ec + - name: ovs0 + type: ovs-interface + state: up + ipv4: + dhcp: true + enabled: true + - name: br1 + description: ovs bridge with eth1 as a port and ovs0 as an internal interface + type: ovs-bridge + state: up + bridge: + options: + stp: true + port: + - name: eth1 + - name: ovs0 \ No newline at end of file