* [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces
@ 2026-09-25 12:34 Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces Christoph Heiss
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Christoph Heiss @ 2026-09-25 12:34 UTC (permalink / raw)
To: pbs-devel
Fixes #6121 [0], for both PBS and (through proxmox-network-api) PDM.
Adds a new 'bond-miimon' option to the network interface API for
specifying the link monitoring interval.
This is needed so that (at least) in active-backup mode, the bond
correctly switches over to the backup link if the active one goes down.
[0] https://bugzilla.proxmox.com/show_bug.cgi?id=6121
proxmox:
Christoph Heiss (2):
fix #6121: network-api: api: add `bond-miimon` option for interfaces
fix #6121: network-api: config: add support for `bond-miimon` option
proxmox-network-api/src/api_impl.rs | 9 +++
proxmox-network-api/src/api_types.rs | 18 ++++++
proxmox-network-api/src/config/lexer.rs | 3 +
proxmox-network-api/src/config/mod.rs | 70 ++++++++++++++++++++++++
proxmox-network-api/src/config/parser.rs | 23 ++++++++
5 files changed, 123 insertions(+)
proxmox-backup:
Christoph Heiss (1):
api: network: reuse interface create/update methods from network-api
src/api2/node/network.rs | 674 ++-------------------------------------
1 file changed, 18 insertions(+), 656 deletions(-)
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces
2026-09-25 12:34 [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces Christoph Heiss
@ 2026-09-25 12:34 ` Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api Christoph Heiss
2 siblings, 0 replies; 4+ messages in thread
From: Christoph Heiss @ 2026-09-25 12:34 UTC (permalink / raw)
To: pbs-devel
Partially fixes #6121 [0].
Adds a new 'bond-miimon' option to the network interface API for
specifying the link monitoring interval.
[0] https://bugzilla.proxmox.com/show_bug.cgi?id=6121
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
proxmox-network-api/src/api_impl.rs | 9 +++++++++
proxmox-network-api/src/api_types.rs | 18 ++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/proxmox-network-api/src/api_impl.rs b/proxmox-network-api/src/api_impl.rs
index 78f499c5..0a603017 100644
--- a/proxmox-network-api/src/api_impl.rs
+++ b/proxmox-network-api/src/api_impl.rs
@@ -162,6 +162,9 @@ pub fn create_interface(iface: String, config: InterfaceUpdater) -> Result<(), E
interface.bond_xmit_hash_policy = config.bond_xmit_hash_policy;
}
}
+ if config.bond_miimon.is_some() {
+ interface.bond_miimon = config.bond_miimon;
+ }
if let Some(slaves) = &config.slaves {
interface.set_bond_slave_list(slaves)?;
}
@@ -295,6 +298,9 @@ pub fn update_interface(
DeletableInterfaceProperty::BondXmitHashPolicy => {
interface.bond_xmit_hash_policy = None
}
+ DeletableInterfaceProperty::BondMiimon => {
+ interface.bond_miimon = None;
+ }
}
}
}
@@ -335,6 +341,9 @@ pub fn update_interface(
}
interface.bond_xmit_hash_policy = update.bond_xmit_hash_policy;
}
+ if update.bond_miimon.is_some() {
+ interface.bond_miimon = update.bond_miimon;
+ }
}
if let Some(cidr) = update.cidr {
diff --git a/proxmox-network-api/src/api_types.rs b/proxmox-network-api/src/api_types.rs
index f2467f1e..471f20e8 100644
--- a/proxmox-network-api/src/api_types.rs
+++ b/proxmox-network-api/src/api_types.rs
@@ -228,6 +228,12 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema =
type: BondXmitHashPolicy,
optional: true,
},
+ "bond-miimon": {
+ description: "Link monitoring interval in milliseconds for bond interfaces.",
+ type: u8,
+ optional: true,
+ default: 100,
+ },
altnames: {
description: "List of altnames for this interface",
type: Array,
@@ -302,6 +308,8 @@ pub struct Interface {
pub bond_primary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
+ #[serde(rename = "bond-miimon", skip_serializing_if = "Option::is_none")]
+ pub bond_miimon: Option<u8>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub altnames: Vec<String>,
@@ -333,6 +341,7 @@ impl Interface {
bond_mode: None,
bond_primary: None,
bond_xmit_hash_policy: None,
+ bond_miimon: None,
altnames: Vec::new(),
}
}
@@ -425,6 +434,8 @@ pub enum DeletableInterfaceProperty {
/// Delete bond transmit hash policy
#[serde(rename = "bond_xmit_hash_policy")]
BondXmitHashPolicy,
+ /// Delete bond miimon (set to default 100)
+ BondMiimon,
}
#[api(
@@ -509,6 +520,12 @@ pub enum DeletableInterfaceProperty {
type: BondXmitHashPolicy,
optional: true,
},
+ "bond-miimon": {
+ description: "Link monitoring interval in milliseconds for bond interfaces.",
+ type: u8,
+ optional: true,
+ default: 100,
+ },
slaves: {
schema: NETWORK_INTERFACE_LIST_SCHEMA,
optional: true,
@@ -542,5 +559,6 @@ pub struct InterfaceUpdater {
pub bond_primary: Option<String>,
#[serde(rename = "bond_xmit_hash_policy")]
pub bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
+ pub bond_miimon: Option<u8>,
pub slaves: Option<String>,
}
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option
2026-09-25 12:34 [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces Christoph Heiss
@ 2026-09-25 12:34 ` Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api Christoph Heiss
2 siblings, 0 replies; 4+ messages in thread
From: Christoph Heiss @ 2026-09-25 12:34 UTC (permalink / raw)
To: pbs-devel
Fixes #6121 [0].
Adds support for the 'bond-miimon' option to the ifupdown(2) config
writer and parser.
The option is always written out for bonds, with a default value of 100
- much like PVE does it.
This is needed so that (at least) in active-backup mode, the bond
correctly switches over to the backup link if the active one goes down.
[0] https://bugzilla.proxmox.com/show_bug.cgi?id=6121
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
proxmox-network-api/src/config/lexer.rs | 3 +
proxmox-network-api/src/config/mod.rs | 70 ++++++++++++++++++++++++
proxmox-network-api/src/config/parser.rs | 23 ++++++++
3 files changed, 96 insertions(+)
diff --git a/proxmox-network-api/src/config/lexer.rs b/proxmox-network-api/src/config/lexer.rs
index 4729d462..2a646508 100644
--- a/proxmox-network-api/src/config/lexer.rs
+++ b/proxmox-network-api/src/config/lexer.rs
@@ -30,6 +30,7 @@ pub enum Token {
BondMode,
BondPrimary,
BondXmitHashPolicy,
+ BondMiimon,
EOF,
}
@@ -62,6 +63,8 @@ static KEYWORDS: LazyLock<HashMap<&'static str, Token>> = LazyLock::new(|| {
map.insert("bond_primary", Token::BondPrimary);
map.insert("bond_xmit_hash_policy", Token::BondXmitHashPolicy);
map.insert("bond-xmit-hash-policy", Token::BondXmitHashPolicy);
+ map.insert("bond-miimon", Token::BondMiimon);
+ map.insert("bond_miimon", Token::BondMiimon);
map
});
diff --git a/proxmox-network-api/src/config/mod.rs b/proxmox-network-api/src/config/mod.rs
index 09165bfb..3eff9284 100644
--- a/proxmox-network-api/src/config/mod.rs
+++ b/proxmox-network-api/src/config/mod.rs
@@ -89,6 +89,9 @@ fn write_iface_attributes(iface: &Interface, w: &mut dyn Write) -> Result<(), Er
}
}
+ let miimon = iface.bond_miimon.unwrap_or(100);
+ writeln!(w, "\tbond-miimon {miimon}")?;
+
let slaves = iface.slaves.as_ref().unwrap_or(&EMPTY_LIST);
if slaves.is_empty() {
writeln!(w, "\tbond-slaves none")?;
@@ -791,4 +794,71 @@ iface individual_name inet manual
assert_eq!(parse_vlan_raw_device_from_name("vmbr0"), None);
assert_eq!(parse_vlan_raw_device_from_name("vmbr0.200"), Some("vmbr0"));
}
+
+ #[test]
+ fn test_write_network_config_bond_default_miimon() {
+ let mut bond = Interface::new("bond0".to_owned());
+ bond.interface_type = NetworkInterfaceType::Bond;
+ bond.method = Some(NetworkConfigMethod::Manual);
+ bond.slaves = Some(vec![String::from("eth0"), String::from("eth1")]);
+ bond.bond_mode = Some(LinuxBondMode::ActiveBackup);
+
+ let mut eth0 = Interface::new("eth0".to_owned());
+ eth0.interface_type = NetworkInterfaceType::Eth;
+
+ let mut eth1 = Interface::new("eth1".to_owned());
+ eth1.interface_type = NetworkInterfaceType::Eth;
+
+ let nw_config = NetworkConfig {
+ interfaces: BTreeMap::from([
+ ("eth0".into(), eth0),
+ ("eth1".into(), eth1),
+ ("bond0".into(), bond),
+ ]),
+ order: vec![NetworkOrderEntry::Iface("bond0".to_owned())],
+ };
+ assert_eq!(
+ String::try_from(nw_config).unwrap().trim(),
+ r#"
+iface bond0 inet manual
+ bond-mode active-backup
+ bond-miimon 100
+ bond-slaves eth0 eth1"#
+ .trim()
+ );
+ }
+
+ #[test]
+ fn test_write_network_config_bond_explicit_miimon() {
+ let mut bond = Interface::new("bond0".to_owned());
+ bond.interface_type = NetworkInterfaceType::Bond;
+ bond.method = Some(NetworkConfigMethod::Manual);
+ bond.slaves = Some(vec![String::from("eth0"), String::from("eth1")]);
+ bond.bond_mode = Some(LinuxBondMode::ActiveBackup);
+ bond.bond_miimon = Some(250);
+
+ let mut eth0 = Interface::new("eth0".to_owned());
+ eth0.interface_type = NetworkInterfaceType::Eth;
+
+ let mut eth1 = Interface::new("eth1".to_owned());
+ eth1.interface_type = NetworkInterfaceType::Eth;
+
+ let nw_config = NetworkConfig {
+ interfaces: BTreeMap::from([
+ ("eth0".into(), eth0),
+ ("eth1".into(), eth1),
+ ("bond0".into(), bond),
+ ]),
+ order: vec![NetworkOrderEntry::Iface("bond0".to_owned())],
+ };
+ assert_eq!(
+ String::try_from(nw_config).unwrap().trim(),
+ r#"
+iface bond0 inet manual
+ bond-mode active-backup
+ bond-miimon 250
+ bond-slaves eth0 eth1"#
+ .trim()
+ );
+ }
}
diff --git a/proxmox-network-api/src/config/parser.rs b/proxmox-network-api/src/config/parser.rs
index 71c9ec0f..2b789f64 100644
--- a/proxmox-network-api/src/config/parser.rs
+++ b/proxmox-network-api/src/config/parser.rs
@@ -378,6 +378,12 @@ impl<R: BufRead> NetworkParser<R> {
interface.bond_xmit_hash_policy = Some(policy);
self.eat(Token::Newline)?;
}
+ Token::BondMiimon => {
+ self.eat(Token::BondMiimon)?;
+ let miimon = self.next_text()?.parse()?;
+ interface.bond_miimon = Some(miimon);
+ self.eat(Token::Newline)?;
+ }
Token::VlanId => {
self.eat(Token::VlanId)?;
let vlan_id = self.next_text()?.parse()?;
@@ -918,4 +924,21 @@ iface individual_name inet static
assert_eq!(iface.method, Some(NetworkConfigMethod::Static));
assert_eq!(iface.cidr, Some(String::from("10.0.0.100/16")));
}
+
+ #[test]
+ fn test_network_config_parser_bond_miimon() {
+ let input = r#"
+iface bond0 inet manual
+ bond-slaves eth0 eth1
+ bond-mode active-backup
+ bond-miimon 200
+"#;
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+ let config = parser.parse_interfaces(None).unwrap();
+
+ let iface = config.interfaces.get("bond0").unwrap();
+ assert_eq!(iface.interface_type, NetworkInterfaceType::Bond);
+ assert_eq!(iface.bond_miimon, Some(200));
+ }
}
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api
2026-09-25 12:34 [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option Christoph Heiss
@ 2026-09-25 12:34 ` Christoph Heiss
2 siblings, 0 replies; 4+ messages in thread
From: Christoph Heiss @ 2026-09-25 12:34 UTC (permalink / raw)
To: pbs-devel
They are nearly identical in parameters and implementation, only that the
network-api crate uses a struct instead of a long list of single
parameters.
The only real difference are bit more strict checks, namely:
- the 'auto' method is now rejected for IPv4, which is IPv6 only
- enforces that the method is 'static' when explicitly setting CIDR/GW
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
In combination w/ the earlier patches in the series; also fixes #6121
[0].
[0] https://bugzilla.proxmox.com/show_bug.cgi?id=6121
src/api2/node/network.rs | 674 ++-------------------------------------
1 file changed, 18 insertions(+), 656 deletions(-)
diff --git a/src/api2/node/network.rs b/src/api2/node/network.rs
index 07ef4bb64..591b4b6e8 100644
--- a/src/api2/node/network.rs
+++ b/src/api2/node/network.rs
@@ -1,5 +1,4 @@
-use anyhow::{Error, bail};
-use serde::{Deserialize, Serialize};
+use anyhow::Error;
use serde_json::{Value, to_value};
use proxmox_router::{ApiMethod, Permission, Router, RpcEnvironment};
@@ -9,86 +8,14 @@ use pbs_api_types::{
Authid, NODE_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA,
};
+use proxmox_config_digest::ConfigDigest;
use proxmox_network_api::{
- self as network, BondXmitHashPolicy, CIDR_V4_SCHEMA, CIDR_V6_SCHEMA, IP_V4_SCHEMA,
- IP_V6_SCHEMA, Interface, LinuxBondMode, NETWORK_INTERFACE_ARRAY_SCHEMA,
- NETWORK_INTERFACE_LIST_SCHEMA, NETWORK_INTERFACE_NAME_SCHEMA, NetworkConfig,
- NetworkConfigMethod, NetworkInterfaceType, parse_vlan_id_from_name,
- parse_vlan_raw_device_from_name,
+ self as network, DeletableInterfaceProperty, Interface, InterfaceUpdater,
+ NETWORK_INTERFACE_NAME_SCHEMA,
};
use proxmox_rest_server::WorkerTask;
-fn split_interface_list(list: &str) -> Result<Vec<String>, Error> {
- let value = NETWORK_INTERFACE_ARRAY_SCHEMA.parse_property_string(list)?;
- Ok(value
- .as_array()
- .unwrap()
- .iter()
- .map(|v| v.as_str().unwrap().to_string())
- .collect())
-}
-
-fn check_duplicate_gateway_v4(config: &NetworkConfig, iface: &str) -> Result<(), Error> {
- let current_gateway_v4 = config
- .interfaces
- .iter()
- .find(|(_, interface)| interface.gateway.is_some())
- .map(|(name, _)| name.to_string());
-
- if let Some(current_gateway_v4) = current_gateway_v4 {
- if current_gateway_v4 != iface {
- bail!(
- "Default IPv4 gateway already exists on interface '{}'",
- current_gateway_v4
- );
- }
- }
- Ok(())
-}
-
-fn check_duplicate_gateway_v6(config: &NetworkConfig, iface: &str) -> Result<(), Error> {
- let current_gateway_v6 = config
- .interfaces
- .iter()
- .find(|(_, interface)| interface.gateway6.is_some())
- .map(|(name, _)| name.to_string());
-
- if let Some(current_gateway_v6) = current_gateway_v6 {
- if current_gateway_v6 != iface {
- bail!(
- "Default IPv6 gateway already exists on interface '{}'",
- current_gateway_v6
- );
- }
- }
- Ok(())
-}
-
-fn set_bridge_ports(iface: &mut Interface, ports: Vec<String>) -> Result<(), Error> {
- if iface.interface_type != NetworkInterfaceType::Bridge {
- bail!(
- "interface '{}' is no bridge (type is {:?})",
- iface.name,
- iface.interface_type
- );
- }
- iface.bridge_ports = Some(ports);
- Ok(())
-}
-
-fn set_bond_slaves(iface: &mut Interface, slaves: Vec<String>) -> Result<(), Error> {
- if iface.interface_type != NetworkInterfaceType::Bond {
- bail!(
- "interface '{}' is no bond (type is {:?})",
- iface.name,
- iface.interface_type
- );
- }
- iface.slaves = Some(slaves);
- Ok(())
-}
-
#[api(
input: {
properties: {
@@ -175,89 +102,9 @@ pub fn read_interface(iface: String) -> Result<Value, Error> {
iface: {
schema: NETWORK_INTERFACE_NAME_SCHEMA,
},
- "type": {
- type: NetworkInterfaceType,
- optional: true,
- },
- autostart: {
- description: "Autostart interface.",
- type: bool,
- optional: true,
- },
- method: {
- type: NetworkConfigMethod,
- optional: true,
- },
- method6: {
- type: NetworkConfigMethod,
- optional: true,
- },
- comments: {
- description: "Comments (inet, may span multiple lines)",
- type: String,
- optional: true,
- },
- comments6: {
- description: "Comments (inet5, may span multiple lines)",
- type: String,
- optional: true,
- },
- cidr: {
- schema: CIDR_V4_SCHEMA,
- optional: true,
- },
- cidr6: {
- schema: CIDR_V6_SCHEMA,
- optional: true,
- },
- gateway: {
- schema: IP_V4_SCHEMA,
- optional: true,
- },
- gateway6: {
- schema: IP_V6_SCHEMA,
- optional: true,
- },
- mtu: {
- description: "Maximum Transmission Unit.",
- optional: true,
- minimum: 46,
- maximum: 65535,
- default: 1500,
- },
- bridge_ports: {
- schema: NETWORK_INTERFACE_LIST_SCHEMA,
- optional: true,
- },
- bridge_vlan_aware: {
- description: "Enable bridge vlan support.",
- type: bool,
- optional: true,
- },
- "vlan-id": {
- description: "VLAN ID.",
- type: u16,
- optional: true,
- },
- "vlan-raw-device": {
- schema: NETWORK_INTERFACE_NAME_SCHEMA,
- optional: true,
- },
- bond_mode: {
- type: LinuxBondMode,
- optional: true,
- },
- "bond-primary": {
- schema: NETWORK_INTERFACE_NAME_SCHEMA,
- optional: true,
- },
- bond_xmit_hash_policy: {
- type: BondXmitHashPolicy,
- optional: true,
- },
- slaves: {
- schema: NETWORK_INTERFACE_LIST_SCHEMA,
- optional: true,
+ config: {
+ type: InterfaceUpdater,
+ flatten: true,
},
},
},
@@ -266,210 +113,8 @@ pub fn read_interface(iface: String) -> Result<Value, Error> {
},
)]
/// Create network interface configuration.
-#[allow(clippy::too_many_arguments)]
-pub fn create_interface(
- iface: String,
- autostart: Option<bool>,
- method: Option<NetworkConfigMethod>,
- method6: Option<NetworkConfigMethod>,
- comments: Option<String>,
- comments6: Option<String>,
- cidr: Option<String>,
- gateway: Option<String>,
- cidr6: Option<String>,
- gateway6: Option<String>,
- mtu: Option<u64>,
- bridge_ports: Option<String>,
- bridge_vlan_aware: Option<bool>,
- vlan_id: Option<u16>,
- vlan_raw_device: Option<String>,
- bond_mode: Option<LinuxBondMode>,
- bond_primary: Option<String>,
- bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
- slaves: Option<String>,
- param: Value,
-) -> Result<(), Error> {
- let interface_type = pbs_tools::json::required_string_param(¶m, "type")?;
- let interface_type: NetworkInterfaceType = serde_json::from_value(interface_type.into())?;
-
- let _lock = network::lock_config()?;
-
- let (mut config, _digest) = network::config()?;
-
- if config.interfaces.contains_key(&iface) {
- bail!("interface '{}' already exists", iface);
- }
-
- let mut interface = Interface::new(iface.clone());
- interface.interface_type = interface_type;
-
- if let Some(autostart) = autostart {
- interface.autostart = autostart;
- }
- if method.is_some() {
- interface.method = method;
- }
- if method6.is_some() {
- interface.method6 = method6;
- }
- if mtu.is_some() {
- interface.mtu = mtu;
- }
- if comments.is_some() {
- interface.comments = comments;
- }
- if comments6.is_some() {
- interface.comments6 = comments6;
- }
-
- if let Some(cidr) = cidr {
- let (_, _, is_v6) = network::parse_cidr(&cidr)?;
- if is_v6 {
- bail!("invalid address type (expected IPv4, got IPv6)");
- }
- interface.cidr = Some(cidr);
- }
-
- if let Some(cidr6) = cidr6 {
- let (_, _, is_v6) = network::parse_cidr(&cidr6)?;
- if !is_v6 {
- bail!("invalid address type (expected IPv6, got IPv4)");
- }
- interface.cidr6 = Some(cidr6);
- }
-
- if let Some(gateway) = gateway {
- let is_v6 = gateway.contains(':');
- if is_v6 {
- bail!("invalid address type (expected IPv4, got IPv6)");
- }
- check_duplicate_gateway_v4(&config, &iface)?;
- interface.gateway = Some(gateway);
- }
-
- if let Some(gateway6) = gateway6 {
- let is_v6 = gateway6.contains(':');
- if !is_v6 {
- bail!("invalid address type (expected IPv6, got IPv4)");
- }
- check_duplicate_gateway_v6(&config, &iface)?;
- interface.gateway6 = Some(gateway6);
- }
-
- match interface_type {
- NetworkInterfaceType::Bridge => {
- if let Some(ports) = bridge_ports {
- let ports = split_interface_list(&ports)?;
- set_bridge_ports(&mut interface, ports)?;
- }
- if bridge_vlan_aware.is_some() {
- interface.bridge_vlan_aware = bridge_vlan_aware;
- }
- }
- NetworkInterfaceType::Bond => {
- if let Some(mode) = bond_mode {
- interface.bond_mode = bond_mode;
- if bond_primary.is_some() {
- if mode != LinuxBondMode::ActiveBackup {
- bail!("bond-primary is only valid with Active/Backup mode");
- }
- interface.bond_primary = bond_primary;
- }
- if bond_xmit_hash_policy.is_some() {
- if mode != LinuxBondMode::Ieee802_3ad && mode != LinuxBondMode::BalanceXor {
- bail!(
- "bond_xmit_hash_policy is only valid with LACP(802.3ad) or balance-xor mode"
- );
- }
- interface.bond_xmit_hash_policy = bond_xmit_hash_policy;
- }
- }
- if let Some(slaves) = slaves {
- let slaves = split_interface_list(&slaves)?;
- set_bond_slaves(&mut interface, slaves)?;
- }
- }
- NetworkInterfaceType::Vlan => {
- if vlan_id.is_none() && parse_vlan_id_from_name(&iface).is_none() {
- bail!("vlan-id must be set");
- }
- interface.vlan_id = vlan_id;
-
- if let Some(dev) = vlan_raw_device
- .as_deref()
- .or_else(|| parse_vlan_raw_device_from_name(&iface))
- {
- if !config.interfaces.contains_key(dev) {
- bail!("vlan-raw-device {dev} does not exist");
- }
- } else {
- bail!("vlan-raw-device must be set");
- }
- interface.vlan_raw_device = vlan_raw_device;
- }
- _ => bail!(
- "creating network interface type '{:?}' is not supported",
- interface_type
- ),
- }
-
- if interface.cidr.is_some() || interface.gateway.is_some() {
- interface.method = Some(NetworkConfigMethod::Static);
- } else if interface.method.is_none() {
- interface.method = Some(NetworkConfigMethod::Manual);
- }
-
- if interface.cidr6.is_some() || interface.gateway6.is_some() {
- interface.method6 = Some(NetworkConfigMethod::Static);
- } else if interface.method6.is_none() {
- interface.method6 = Some(NetworkConfigMethod::Manual);
- }
-
- config.interfaces.insert(iface, interface);
-
- network::save_config(&config)?;
-
- Ok(())
-}
-
-#[api()]
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "kebab-case")]
-/// Deletable property name
-pub enum DeletableProperty {
- /// Delete the IPv4 address property.
- Cidr,
- /// Delete the IPv6 address property.
- Cidr6,
- /// Delete the IPv4 gateway property.
- Gateway,
- /// Delete the IPv6 gateway property.
- Gateway6,
- /// Delete the whole IPv4 configuration entry.
- Method,
- /// Delete the whole IPv6 configuration entry.
- Method6,
- /// Delete IPv4 comments
- Comments,
- /// Delete IPv6 comments
- Comments6,
- /// Delete mtu.
- Mtu,
- /// Delete autostart flag
- Autostart,
- /// Delete bridge ports (set to 'none')
- #[serde(rename = "bridge_ports")]
- BridgePorts,
- /// Delete bridge-vlan-aware flag
- #[serde(rename = "bridge_vlan_aware")]
- BridgeVlanAware,
- /// Delete bond-slaves (set to 'none')
- Slaves,
- /// Delete bond-primary
- BondPrimary,
- /// Delete bond transmit hash policy
- #[serde(rename = "bond_xmit_hash_policy")]
- BondXmitHashPolicy,
+pub fn create_interface(iface: String, config: InterfaceUpdater) -> Result<(), Error> {
+ proxmox_network_api::create_interface(iface, config)
}
#[api(
@@ -482,101 +127,21 @@ pub enum DeletableProperty {
iface: {
schema: NETWORK_INTERFACE_NAME_SCHEMA,
},
- "type": {
- type: NetworkInterfaceType,
- optional: true,
- },
- autostart: {
- description: "Autostart interface.",
- type: bool,
- optional: true,
- },
- method: {
- type: NetworkConfigMethod,
- optional: true,
- },
- method6: {
- type: NetworkConfigMethod,
- optional: true,
- },
- comments: {
- description: "Comments (inet, may span multiple lines)",
- type: String,
- optional: true,
- },
- comments6: {
- description: "Comments (inet5, may span multiple lines)",
- type: String,
- optional: true,
- },
- cidr: {
- schema: CIDR_V4_SCHEMA,
- optional: true,
- },
- cidr6: {
- schema: CIDR_V6_SCHEMA,
- optional: true,
- },
- gateway: {
- schema: IP_V4_SCHEMA,
- optional: true,
- },
- gateway6: {
- schema: IP_V6_SCHEMA,
- optional: true,
- },
- mtu: {
- description: "Maximum Transmission Unit.",
- optional: true,
- minimum: 46,
- maximum: 65535,
- default: 1500,
- },
- bridge_ports: {
- schema: NETWORK_INTERFACE_LIST_SCHEMA,
- optional: true,
- },
- bridge_vlan_aware: {
- description: "Enable bridge vlan support.",
- type: bool,
- optional: true,
- },
- "vlan-id": {
- description: "VLAN ID.",
- type: u16,
- optional: true,
- },
- "vlan-raw-device": {
- schema: NETWORK_INTERFACE_NAME_SCHEMA,
- optional: true,
- },
- bond_mode: {
- type: LinuxBondMode,
- optional: true,
- },
- "bond-primary": {
- schema: NETWORK_INTERFACE_NAME_SCHEMA,
- optional: true,
- },
- bond_xmit_hash_policy: {
- type: BondXmitHashPolicy,
- optional: true,
- },
- slaves: {
- schema: NETWORK_INTERFACE_LIST_SCHEMA,
- optional: true,
+ update: {
+ type: InterfaceUpdater,
+ flatten: true,
},
delete: {
description: "List of properties to delete.",
type: Array,
optional: true,
items: {
- type: DeletableProperty,
+ type: DeletableInterfaceProperty,
}
},
digest: {
+ type: ConfigDigest,
optional: true,
- schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
},
},
},
@@ -585,216 +150,13 @@ pub enum DeletableProperty {
},
)]
/// Update network interface config.
-#[allow(clippy::too_many_arguments)]
pub fn update_interface(
iface: String,
- autostart: Option<bool>,
- method: Option<NetworkConfigMethod>,
- method6: Option<NetworkConfigMethod>,
- comments: Option<String>,
- comments6: Option<String>,
- cidr: Option<String>,
- gateway: Option<String>,
- cidr6: Option<String>,
- gateway6: Option<String>,
- mtu: Option<u64>,
- bridge_ports: Option<String>,
- bridge_vlan_aware: Option<bool>,
- vlan_id: Option<u16>,
- vlan_raw_device: Option<String>,
- bond_mode: Option<LinuxBondMode>,
- bond_primary: Option<String>,
- bond_xmit_hash_policy: Option<BondXmitHashPolicy>,
- slaves: Option<String>,
- delete: Option<Vec<DeletableProperty>>,
- digest: Option<String>,
- param: Value,
+ update: InterfaceUpdater,
+ delete: Option<Vec<DeletableInterfaceProperty>>,
+ digest: Option<ConfigDigest>,
) -> Result<(), Error> {
- let _lock = network::lock_config()?;
-
- let (mut config, expected_digest) = network::config()?;
-
- pbs_config::detect_modified_configuration_file(digest, &expected_digest)?;
-
- if gateway.is_some() {
- check_duplicate_gateway_v4(&config, &iface)?;
- }
- if gateway6.is_some() {
- check_duplicate_gateway_v6(&config, &iface)?;
- }
-
- if let Some(dev) = vlan_raw_device
- .as_deref()
- .or_else(|| parse_vlan_raw_device_from_name(&iface))
- {
- if !config.interfaces.contains_key(dev) {
- bail!("vlan-raw-device {dev} does not exist");
- }
- }
-
- let interface = config.lookup_mut(&iface)?;
-
- if let Some(interface_type) = param.get("type") {
- let interface_type = NetworkInterfaceType::deserialize(interface_type)?;
- if interface_type != interface.interface_type {
- bail!(
- "got unexpected interface type ({:?} != {:?})",
- interface_type,
- interface.interface_type
- );
- }
- }
-
- if let Some(delete) = delete {
- for delete_prop in delete {
- match delete_prop {
- DeletableProperty::Cidr => {
- interface.cidr = None;
- }
- DeletableProperty::Cidr6 => {
- interface.cidr6 = None;
- }
- DeletableProperty::Gateway => {
- interface.gateway = None;
- }
- DeletableProperty::Gateway6 => {
- interface.gateway6 = None;
- }
- DeletableProperty::Method => {
- interface.method = None;
- }
- DeletableProperty::Method6 => {
- interface.method6 = None;
- }
- DeletableProperty::Comments => {
- interface.comments = None;
- }
- DeletableProperty::Comments6 => {
- interface.comments6 = None;
- }
- DeletableProperty::Mtu => {
- interface.mtu = None;
- }
- DeletableProperty::Autostart => {
- interface.autostart = false;
- }
- DeletableProperty::BridgePorts => {
- set_bridge_ports(interface, Vec::new())?;
- }
- DeletableProperty::BridgeVlanAware => {
- interface.bridge_vlan_aware = None;
- }
- DeletableProperty::Slaves => {
- set_bond_slaves(interface, Vec::new())?;
- }
- DeletableProperty::BondPrimary => {
- interface.bond_primary = None;
- }
- DeletableProperty::BondXmitHashPolicy => interface.bond_xmit_hash_policy = None,
- }
- }
- }
-
- if let Some(autostart) = autostart {
- interface.autostart = autostart;
- }
- if method.is_some() {
- interface.method = method;
- }
- if method6.is_some() {
- interface.method6 = method6;
- }
- if mtu.is_some() {
- interface.mtu = mtu;
- }
- if let Some(ports) = bridge_ports {
- let ports = split_interface_list(&ports)?;
- set_bridge_ports(interface, ports)?;
- }
- if bridge_vlan_aware.is_some() {
- interface.bridge_vlan_aware = bridge_vlan_aware;
- }
- if let Some(slaves) = slaves {
- let slaves = split_interface_list(&slaves)?;
- set_bond_slaves(interface, slaves)?;
- }
- if let Some(mode) = bond_mode {
- interface.bond_mode = bond_mode;
- if bond_primary.is_some() {
- if mode != LinuxBondMode::ActiveBackup {
- bail!("bond-primary is only valid with Active/Backup mode");
- }
- interface.bond_primary = bond_primary;
- }
- if bond_xmit_hash_policy.is_some() {
- if mode != LinuxBondMode::Ieee802_3ad && mode != LinuxBondMode::BalanceXor {
- bail!("bond_xmit_hash_policy is only valid with LACP(802.3ad) or balance-xor mode");
- }
- interface.bond_xmit_hash_policy = bond_xmit_hash_policy;
- }
- }
-
- if let Some(cidr) = cidr {
- let (_, _, is_v6) = network::parse_cidr(&cidr)?;
- if is_v6 {
- bail!("invalid address type (expected IPv4, got IPv6)");
- }
- interface.cidr = Some(cidr);
- }
-
- if let Some(cidr6) = cidr6 {
- let (_, _, is_v6) = network::parse_cidr(&cidr6)?;
- if !is_v6 {
- bail!("invalid address type (expected IPv6, got IPv4)");
- }
- interface.cidr6 = Some(cidr6);
- }
-
- if let Some(gateway) = gateway {
- let is_v6 = gateway.contains(':');
- if is_v6 {
- bail!("invalid address type (expected IPv4, got IPv6)");
- }
- interface.gateway = Some(gateway);
- }
-
- if let Some(gateway6) = gateway6 {
- let is_v6 = gateway6.contains(':');
- if !is_v6 {
- bail!("invalid address type (expected IPv6, got IPv4)");
- }
- interface.gateway6 = Some(gateway6);
- }
-
- if comments.is_some() {
- interface.comments = comments;
- }
- if comments6.is_some() {
- interface.comments6 = comments6;
- }
-
- if interface.cidr.is_some() || interface.gateway.is_some() {
- interface.method = Some(NetworkConfigMethod::Static);
- } else {
- interface.method = Some(NetworkConfigMethod::Manual);
- }
-
- if interface.cidr6.is_some() || interface.gateway6.is_some() {
- interface.method6 = Some(NetworkConfigMethod::Static);
- } else if interface.method6.is_none() {
- interface.method6 = Some(NetworkConfigMethod::Manual);
- }
-
- if vlan_id.is_some() {
- interface.vlan_id = vlan_id;
- }
- if vlan_raw_device.is_some() {
- interface.vlan_raw_device = vlan_raw_device;
- }
-
- network::save_config(&config)?;
-
- Ok(())
+ proxmox_network_api::update_interface(iface, update, delete, digest)
}
#[api(
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-25 12:35 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-25 12:34 [PATCH proxmox{,-backup} 0/3] fix #6121: add 'bond-miimon' option for network interfaces Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 1/3] fix #6121: network-api: api: add `bond-miimon` option for interfaces Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox 2/3] fix #6121: network-api: config: add support for `bond-miimon` option Christoph Heiss
2026-09-25 12:34 ` [PATCH proxmox-backup 3/3] api: network: reuse interface create/update methods from network-api Christoph Heiss
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.