-
Notifications
You must be signed in to change notification settings - Fork 521
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[netdog] Add version 3 of network configuration
This adds version 3 of network configuration along with bonding and vlan support end to end from net.toml to wicked xml. The support now works as intended for both vlan virtual devices and bonding devices on metal. This also moves around some of the device files into a new module so that future expansion of devices is seperated from expansion of network configuration versions. This also changes the validation logic to add in traits that allow a generic IP Addressing validation function to run on any network device that accepts addressing.
- Loading branch information
Showing
12 changed files
with
624 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use super::validate_addressing; | ||
use super::{Dhcp4ConfigV1, Dhcp6ConfigV1, Result, Validate}; | ||
use crate::net_config::devices::generate_addressing_validation; | ||
use crate::net_config::{RouteV1, StaticConfigV1}; | ||
use serde::Deserialize; | ||
|
||
#[derive(Debug, Deserialize)] | ||
#[serde(deny_unknown_fields)] | ||
pub(crate) struct NetInterfaceV2 { | ||
// Use this interface as the primary interface for the system | ||
pub(crate) primary: Option<bool>, | ||
pub(crate) dhcp4: Option<Dhcp4ConfigV1>, | ||
pub(crate) dhcp6: Option<Dhcp6ConfigV1>, | ||
pub(crate) static4: Option<StaticConfigV1>, | ||
pub(crate) static6: Option<StaticConfigV1>, | ||
#[serde(rename = "route")] | ||
pub(crate) routes: Option<Vec<RouteV1>>, | ||
} | ||
|
||
impl Validate for NetInterfaceV2 { | ||
fn validate(&self) -> Result<()> { | ||
validate_addressing(self) | ||
} | ||
} | ||
|
||
// Generate the traits for IP Address validation | ||
generate_addressing_validation!(&NetInterfaceV2); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
//! The devices module contains all the types of network devices that `netdog` supports. These are | ||
//! intended to be the structs used for net.toml deserialization including the validation logic for | ||
//! each device. | ||
pub(crate) mod bonding; | ||
pub(crate) mod interface; | ||
pub(crate) mod vlan; | ||
|
||
use super::{error, Result, Validate}; | ||
use crate::net_config::{Dhcp4ConfigV1, Dhcp6ConfigV1, RouteV1, StaticConfigV1}; | ||
use bonding::NetBondV1; | ||
use interface::NetInterfaceV2; | ||
use serde::Deserialize; | ||
use vlan::NetVlanV1; | ||
|
||
#[derive(Debug, Deserialize)] | ||
#[serde(untagged)] | ||
pub(crate) enum NetworkDeviceV1 { | ||
Interface(NetInterfaceV2), | ||
BondDevice(NetBondV1), | ||
VlanDevice(NetVlanV1), | ||
} | ||
|
||
impl NetworkDeviceV1 { | ||
pub(crate) fn primary(&self) -> Option<bool> { | ||
match self { | ||
Self::Interface(i) => i.primary, | ||
Self::BondDevice(i) => i.primary, | ||
Self::VlanDevice(i) => i.primary, | ||
} | ||
} | ||
} | ||
|
||
impl Validate for NetworkDeviceV1 { | ||
fn validate(&self) -> Result<()> { | ||
match self { | ||
Self::Interface(config) => config.validate()?, | ||
Self::BondDevice(config) => config.validate()?, | ||
Self::VlanDevice(config) => config.validate()?, | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(Debug, Deserialize)] | ||
#[serde(untagged)] | ||
pub(crate) enum DeviceType { | ||
#[serde(rename = "bond")] | ||
Bond, | ||
#[serde(rename = "vlan")] | ||
Vlan, | ||
} | ||
|
||
pub(crate) trait HasIpAddressing { | ||
fn has_static(&self) -> bool; | ||
|
||
fn validate_static4(&self) -> Result<()>; | ||
fn validate_static6(&self) -> Result<()>; | ||
|
||
fn has_dhcp(&self) -> bool; | ||
fn has_routes(&self) -> bool; | ||
} | ||
|
||
pub(crate) fn validate_addressing<D>(device: D) -> Result<()> | ||
where | ||
D: HasIpAddressing, | ||
{ | ||
if !device.has_dhcp() && !device.has_static() { | ||
return error::InvalidNetConfigSnafu { | ||
reason: "each interface must configure dhcp and/or static addresses", | ||
} | ||
.fail(); | ||
} | ||
|
||
// wicked doesn't support static routes with dhcp | ||
if device.has_dhcp() && device.has_routes() { | ||
return error::InvalidNetConfigSnafu { | ||
reason: "static routes are not supported with dhcp", | ||
} | ||
.fail(); | ||
} | ||
|
||
if device.has_routes() && !device.has_static() { | ||
return error::InvalidNetConfigSnafu { | ||
reason: "interfaces must set static addresses in order to use routes", | ||
} | ||
.fail(); | ||
} | ||
|
||
// call into struct for access to fields for validation | ||
device.validate_static4()?; | ||
device.validate_static6()?; | ||
|
||
Ok(()) | ||
} | ||
|
||
// For all devices that have IP Addressing available, generate the trait implementation | ||
macro_rules! generate_addressing_validation { | ||
($name:ty) => { | ||
use crate::net_config::devices::HasIpAddressing; | ||
impl HasIpAddressing for $name { | ||
fn has_static(&self) -> bool { | ||
self.static4.is_some() || self.static6.is_some() | ||
} | ||
fn validate_static4(&self) -> Result<()> { | ||
if let Some(config) = &self.static4 { | ||
config.validate()? | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn validate_static6(&self) -> Result<()> { | ||
if let Some(config) = &self.static6 { | ||
config.validate()? | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn has_dhcp(&self) -> bool { | ||
self.dhcp4.is_some() || self.dhcp6.is_some() | ||
} | ||
fn has_routes(&self) -> bool { | ||
self.routes.is_some() | ||
} | ||
} | ||
}; | ||
} | ||
pub(crate) use generate_addressing_validation; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
macro_rules! bonding_tests { | ||
($version:expr) => { | ||
mod bonding { | ||
use $crate::net_config::deserialize_config; | ||
use $crate::net_config::test_macros::gen_boilerplate; | ||
|
||
gen_boilerplate!($version, "bonding"); | ||
|
||
#[test] | ||
fn ok_config() { | ||
let ok = net_config().join("net_config.toml"); | ||
let rendered = render_config_template(ok); | ||
assert!(deserialize_config(&rendered).is_ok()) | ||
} | ||
|
||
#[test] | ||
fn missing_kind() { | ||
let bad = net_config().join("missing_kind.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn no_monitoring() { | ||
let bad = net_config().join("no_monitoring.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn both_monitoring() { | ||
let bad = net_config().join("both_monitoring.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn no_interfaces() { | ||
let bad = net_config().join("no_interfaces.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn disabled_miimon() { | ||
let bad = net_config().join("disabled_miimon.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn disabled_arpmon() { | ||
let bad = net_config().join("disabled_arpmon.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn too_many_min_links() { | ||
let bad = net_config().join("too_many_min_links.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn arpmon_no_targets() { | ||
let bad = net_config().join("arpmon_no_targets.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn vlan_using_bond_interface() { | ||
let bad = net_config().join("vlan_using_bond.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
} | ||
}; | ||
} | ||
pub(crate) use bonding_tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
macro_rules! vlan_tests { | ||
($version:expr) => { | ||
mod vlan { | ||
use $crate::net_config::deserialize_config; | ||
use $crate::net_config::test_macros::gen_boilerplate; | ||
|
||
gen_boilerplate!($version, "vlan"); | ||
|
||
#[test] | ||
fn ok_config() { | ||
let ok = net_config().join("net_config.toml"); | ||
let rendered = render_config_template(ok); | ||
assert!(deserialize_config(&rendered).is_ok()) | ||
} | ||
|
||
#[test] | ||
fn no_id() { | ||
let bad = net_config().join("no_id.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn out_of_bounds_id() { | ||
let bad = net_config().join("oob_id.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn missing_kind() { | ||
let bad = net_config().join("missing_kind.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
|
||
#[test] | ||
fn no_device() { | ||
let bad = net_config().join("no_device.toml"); | ||
let rendered = render_config_template(bad); | ||
assert!(deserialize_config(&rendered).is_err()) | ||
} | ||
} | ||
}; | ||
} | ||
pub(crate) use vlan_tests; |
Oops, something went wrong.