* [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
@ 2024-04-04 9:51 ` Stefan Lendl
2024-04-04 16:29 ` Thomas Lamprecht
2024-04-18 9:26 ` Folke Gleumes
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 2/9] tests: move network tests to parser.rs Stefan Lendl
` (7 subsequent siblings)
8 siblings, 2 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
Copied from PVE to use in PBS network configuration.
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
src/Makefile | 1 +
src/form/VlanField.js | 40 ++++++++++++++++++++++++++++++++++++++++
src/node/NetworkEdit.js | 6 +++---
3 files changed, 44 insertions(+), 3 deletions(-)
create mode 100644 src/form/VlanField.js
diff --git a/src/Makefile b/src/Makefile
index 89f9962..0b1e88c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -31,6 +31,7 @@ JSSRC= \
form/ExpireDate.js \
form/IntegerField.js \
form/TextField.js \
+ form/VlanField.js \
form/DateTimeField.js \
form/Checkbox.js \
form/KVComboBox.js \
diff --git a/src/form/VlanField.js b/src/form/VlanField.js
new file mode 100644
index 0000000..71b580d
--- /dev/null
+++ b/src/form/VlanField.js
@@ -0,0 +1,40 @@
+Ext.define('Proxmox.form.field.VlanField', {
+ extend: 'Ext.form.field.Number',
+ alias: ['widget.proxmoxvlanfield'],
+
+ deleteEmpty: false,
+
+ emptyText: gettext('no VLAN'),
+
+ fieldLabel: gettext('VLAN Tag'),
+
+ allowBlank: true,
+
+ getSubmitData: function() {
+ var me = this,
+ data = null,
+ val;
+ if (!me.disabled && me.submitValue) {
+ val = me.getSubmitValue();
+ if (val) {
+ data = {};
+ data[me.getName()] = val;
+ } else if (me.deleteEmpty) {
+ data = {};
+ data.delete = me.getName();
+ }
+ }
+ return data;
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ Ext.apply(me, {
+ minValue: 1,
+ maxValue: 4094,
+ });
+
+ me.callParent();
+ },
+});
diff --git a/src/node/NetworkEdit.js b/src/node/NetworkEdit.js
index bb9add3..b81a21d 100644
--- a/src/node/NetworkEdit.js
+++ b/src/node/NetworkEdit.js
@@ -97,7 +97,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
name: 'ovs_bridge',
});
column2.push({
- xtype: 'pveVlanField',
+ xtype: 'proxmoxvlanfield',
deleteEmpty: !me.isCreate,
name: 'ovs_tag',
value: '',
@@ -140,7 +140,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
});
column2.push({
- xtype: 'pveVlanField',
+ xtype: 'proxmoxvlanfield',
name: 'vlan-id',
value: me.vlanidvalue,
disabled: me.disablevlanid,
@@ -211,7 +211,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
name: 'ovs_bridge',
});
column2.push({
- xtype: 'pveVlanField',
+ xtype: 'proxmoxvlanfield',
deleteEmpty: !me.isCreate,
name: 'ovs_tag',
value: '',
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 2/9] tests: move network tests to parser.rs
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
2024-04-04 9:51 ` [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE Stefan Lendl
@ 2024-04-04 9:51 ` Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 3/9] tests: simple tests for writing the network config Stefan Lendl
` (6 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
All current tests in network/mod.rs only test parser functionality and
should therefore live in the parser module.
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
pbs-config/src/network/mod.rs | 147 -------------------------------
pbs-config/src/network/parser.rs | 147 +++++++++++++++++++++++++++++++
2 files changed, 147 insertions(+), 147 deletions(-)
diff --git a/pbs-config/src/network/mod.rs b/pbs-config/src/network/mod.rs
index e2008b7c..21187ec2 100644
--- a/pbs-config/src/network/mod.rs
+++ b/pbs-config/src/network/mod.rs
@@ -503,150 +503,3 @@ pub fn complete_port_list(arg: &str, _param: &HashMap<String, String>) -> Vec<St
.map(|port| format!("{}{}", prefix, port))
.collect()
}
-
-#[cfg(test)]
-mod test {
-
- use anyhow::Error;
-
- use super::*;
-
- #[test]
- fn test_network_config_create_lo_1() -> Result<(), Error> {
- let input = "";
-
- let mut parser = NetworkParser::new(input.as_bytes());
-
- let config = parser.parse_interfaces(None)?;
-
- let output = String::try_from(config)?;
-
- let expected = "auto lo\niface lo inet loopback\n\n";
- assert_eq!(output, expected);
-
- // run again using output as input
- let mut parser = NetworkParser::new(output.as_bytes());
-
- let config = parser.parse_interfaces(None)?;
-
- let output = String::try_from(config)?;
-
- assert_eq!(output, expected);
-
- Ok(())
- }
-
- #[test]
- fn test_network_config_create_lo_2() -> Result<(), Error> {
- let input = "#c1\n\n#c2\n\niface test inet manual\n";
-
- let mut parser = NetworkParser::new(input.as_bytes());
-
- let config = parser.parse_interfaces(None)?;
-
- let output = String::try_from(config)?;
-
- // Note: loopback should be added in front of other interfaces
- let expected = "#c1\n#c2\n\nauto lo\niface lo inet loopback\n\niface test inet manual\n\n";
- assert_eq!(output, expected);
-
- Ok(())
- }
-
- #[test]
- fn test_network_config_parser_no_blank_1() -> Result<(), Error> {
- let input = "auto lo\n\
- iface lo inet loopback\n\
- iface lo inet6 loopback\n\
- auto ens18\n\
- iface ens18 inet static\n\
- \taddress 192.168.20.144/20\n\
- \tgateway 192.168.16.1\n\
- # comment\n\
- iface ens20 inet static\n\
- \taddress 192.168.20.145/20\n\
- iface ens21 inet manual\n\
- iface ens22 inet manual\n";
-
- let mut parser = NetworkParser::new(input.as_bytes());
-
- let config = parser.parse_interfaces(None)?;
-
- let output = String::try_from(config)?;
-
- let expected = "auto lo\n\
- iface lo inet loopback\n\
- \n\
- iface lo inet6 loopback\n\
- \n\
- auto ens18\n\
- iface ens18 inet static\n\
- \taddress 192.168.20.144/20\n\
- \tgateway 192.168.16.1\n\
- #comment\n\
- \n\
- iface ens20 inet static\n\
- \taddress 192.168.20.145/20\n\
- \n\
- iface ens21 inet manual\n\
- \n\
- iface ens22 inet manual\n\
- \n";
- assert_eq!(output, expected);
-
- Ok(())
- }
-
- #[test]
- fn test_network_config_parser_no_blank_2() -> Result<(), Error> {
- // Adapted from bug 2926
- let input = "### Hetzner Online GmbH installimage\n\
- \n\
- source /etc/network/interfaces.d/*\n\
- \n\
- auto lo\n\
- iface lo inet loopback\n\
- iface lo inet6 loopback\n\
- \n\
- auto enp4s0\n\
- iface enp4s0 inet static\n\
- \taddress 10.10.10.10/24\n\
- \tgateway 10.10.10.1\n\
- \t# route 10.10.20.10/24 via 10.10.20.1\n\
- \tup route add -net 10.10.20.10 netmask 255.255.255.0 gw 10.10.20.1 dev enp4s0\n\
- \n\
- iface enp4s0 inet6 static\n\
- \taddress fe80::5496:35ff:fe99:5a6a/64\n\
- \tgateway fe80::1\n";
-
- let mut parser = NetworkParser::new(input.as_bytes());
-
- let config = parser.parse_interfaces(None)?;
-
- let output = String::try_from(config)?;
-
- let expected = "### Hetzner Online GmbH installimage\n\
- \n\
- source /etc/network/interfaces.d/*\n\
- \n\
- auto lo\n\
- iface lo inet loopback\n\
- \n\
- iface lo inet6 loopback\n\
- \n\
- auto enp4s0\n\
- iface enp4s0 inet static\n\
- \taddress 10.10.10.10/24\n\
- \tgateway 10.10.10.1\n\
- \t# route 10.10.20.10/24 via 10.10.20.1\n\
- \tup route add -net 10.10.20.10 netmask 255.255.255.0 gw 10.10.20.1 dev enp4s0\n\
- \n\
- iface enp4s0 inet6 static\n\
- \taddress fe80::5496:35ff:fe99:5a6a/64\n\
- \tgateway fe80::1\n\
- \n";
- assert_eq!(output, expected);
-
- Ok(())
- }
-}
diff --git a/pbs-config/src/network/parser.rs b/pbs-config/src/network/parser.rs
index ec2c64eb..1b55569a 100644
--- a/pbs-config/src/network/parser.rs
+++ b/pbs-config/src/network/parser.rs
@@ -602,3 +602,150 @@ impl<R: BufRead> NetworkParser<R> {
Ok(config)
}
}
+
+#[cfg(test)]
+mod test {
+
+ use anyhow::Error;
+
+ use super::*;
+
+ #[test]
+ fn test_network_config_create_lo_1() -> Result<(), Error> {
+ let input = "";
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+
+ let config = parser.parse_interfaces(None)?;
+
+ let output = String::try_from(config)?;
+
+ let expected = "auto lo\niface lo inet loopback\n\n";
+ assert_eq!(output, expected);
+
+ // run again using output as input
+ let mut parser = NetworkParser::new(output.as_bytes());
+
+ let config = parser.parse_interfaces(None)?;
+
+ let output = String::try_from(config)?;
+
+ assert_eq!(output, expected);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_network_config_create_lo_2() -> Result<(), Error> {
+ let input = "#c1\n\n#c2\n\niface test inet manual\n";
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+
+ let config = parser.parse_interfaces(None)?;
+
+ let output = String::try_from(config)?;
+
+ // Note: loopback should be added in front of other interfaces
+ let expected = "#c1\n#c2\n\nauto lo\niface lo inet loopback\n\niface test inet manual\n\n";
+ assert_eq!(output, expected);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_network_config_parser_no_blank_1() -> Result<(), Error> {
+ let input = "auto lo\n\
+ iface lo inet loopback\n\
+ iface lo inet6 loopback\n\
+ auto ens18\n\
+ iface ens18 inet static\n\
+ \taddress 192.168.20.144/20\n\
+ \tgateway 192.168.16.1\n\
+ # comment\n\
+ iface ens20 inet static\n\
+ \taddress 192.168.20.145/20\n\
+ iface ens21 inet manual\n\
+ iface ens22 inet manual\n";
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+
+ let config = parser.parse_interfaces(None)?;
+
+ let output = String::try_from(config)?;
+
+ let expected = "auto lo\n\
+ iface lo inet loopback\n\
+ \n\
+ iface lo inet6 loopback\n\
+ \n\
+ auto ens18\n\
+ iface ens18 inet static\n\
+ \taddress 192.168.20.144/20\n\
+ \tgateway 192.168.16.1\n\
+ #comment\n\
+ \n\
+ iface ens20 inet static\n\
+ \taddress 192.168.20.145/20\n\
+ \n\
+ iface ens21 inet manual\n\
+ \n\
+ iface ens22 inet manual\n\
+ \n";
+ assert_eq!(output, expected);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_network_config_parser_no_blank_2() -> Result<(), Error> {
+ // Adapted from bug 2926
+ let input = "### Hetzner Online GmbH installimage\n\
+ \n\
+ source /etc/network/interfaces.d/*\n\
+ \n\
+ auto lo\n\
+ iface lo inet loopback\n\
+ iface lo inet6 loopback\n\
+ \n\
+ auto enp4s0\n\
+ iface enp4s0 inet static\n\
+ \taddress 10.10.10.10/24\n\
+ \tgateway 10.10.10.1\n\
+ \t# route 10.10.20.10/24 via 10.10.20.1\n\
+ \tup route add -net 10.10.20.10 netmask 255.255.255.0 gw 10.10.20.1 dev enp4s0\n\
+ \n\
+ iface enp4s0 inet6 static\n\
+ \taddress fe80::5496:35ff:fe99:5a6a/64\n\
+ \tgateway fe80::1\n";
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+
+ let config = parser.parse_interfaces(None)?;
+
+ let output = String::try_from(config)?;
+
+ let expected = "### Hetzner Online GmbH installimage\n\
+ \n\
+ source /etc/network/interfaces.d/*\n\
+ \n\
+ auto lo\n\
+ iface lo inet loopback\n\
+ \n\
+ iface lo inet6 loopback\n\
+ \n\
+ auto enp4s0\n\
+ iface enp4s0 inet static\n\
+ \taddress 10.10.10.10/24\n\
+ \tgateway 10.10.10.1\n\
+ \t# route 10.10.20.10/24 via 10.10.20.1\n\
+ \tup route add -net 10.10.20.10 netmask 255.255.255.0 gw 10.10.20.1 dev enp4s0\n\
+ \n\
+ iface enp4s0 inet6 static\n\
+ \taddress fe80::5496:35ff:fe99:5a6a/64\n\
+ \tgateway fe80::1\n\
+ \n";
+ assert_eq!(output, expected);
+
+ Ok(())
+ }
+}
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 3/9] tests: simple tests for writing the network config
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
2024-04-04 9:51 ` [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 2/9] tests: move network tests to parser.rs Stefan Lendl
@ 2024-04-04 9:51 ` Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 4/9] config: write vlan network interface Stefan Lendl
` (5 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
Simple tests for manual and static configurations.
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
pbs-config/src/network/mod.rs | 79 +++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/pbs-config/src/network/mod.rs b/pbs-config/src/network/mod.rs
index 21187ec2..7fec7e29 100644
--- a/pbs-config/src/network/mod.rs
+++ b/pbs-config/src/network/mod.rs
@@ -503,3 +503,82 @@ pub fn complete_port_list(arg: &str, _param: &HashMap<String, String>) -> Vec<St
.map(|port| format!("{}{}", prefix, port))
.collect()
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use NetworkConfigMethod::*;
+ use NetworkInterfaceType::*;
+ use NetworkOrderEntry::*;
+
+ #[test]
+ fn test_write_network_config_manual() {
+ let iface_name = String::from("enp3s0");
+ let mut iface = Interface::new(iface_name.clone());
+ iface.interface_type = Eth;
+ iface.method = Some(Manual);
+ iface.active = true;
+
+ let nw_config = NetworkConfig {
+ interfaces: BTreeMap::from([(iface_name.clone(), iface)]),
+ order: vec![Iface(iface_name.clone())],
+ };
+
+ assert_eq!(
+ String::try_from(nw_config).unwrap().trim(),
+ r#"iface enp3s0 inet manual"#
+ );
+ }
+
+ #[test]
+ fn test_write_network_config_static() {
+ let iface_name = String::from("enp3s0");
+ let mut iface = Interface::new(iface_name.clone());
+ iface.interface_type = Eth;
+ iface.method = Some(Static);
+ iface.cidr = Some(String::from("10.0.0.100/16"));
+ iface.active = true;
+
+ let nw_config = NetworkConfig {
+ interfaces: BTreeMap::from([(iface_name.clone(), iface)]),
+ order: vec![Iface(iface_name.clone())],
+ };
+ assert_eq!(
+ String::try_from(nw_config).unwrap().trim(),
+ format!(
+ r#"
+iface enp3s0 inet static
+ address 10.0.0.100/16"#
+ )
+ .trim()
+ );
+ }
+
+ #[test]
+ fn test_write_network_config_static_with_gateway() {
+ let iface_name = String::from("enp3s0");
+ let mut iface = Interface::new(iface_name.clone());
+ iface.interface_type = Eth;
+ iface.method = Some(Static);
+ iface.cidr = Some(String::from("10.0.0.100/16"));
+ iface.gateway = Some(String::from("10.0.0.1"));
+ iface.active = true;
+
+ let nw_config = NetworkConfig {
+ interfaces: BTreeMap::from([(iface_name.clone(), iface)]),
+ order: vec![Iface(iface_name.clone())],
+ };
+ assert_eq!(
+ String::try_from(nw_config).unwrap().trim(),
+ format!(
+ r#"
+iface enp3s0 inet static
+ address 10.0.0.100/16
+ gateway 10.0.0.1"#
+ )
+ .trim()
+ );
+ }
+}
+
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 4/9] config: write vlan network interface
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
` (2 preceding siblings ...)
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 3/9] tests: simple tests for writing the network config Stefan Lendl
@ 2024-04-04 9:51 ` Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 5/9] config: parse vlan interface from config Stefan Lendl
` (4 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
* Add vlan_id and vlan_raw_device fields to the Interface api type
* Write to the network config the vlan specific properties for vlan
interface type
* Add several tests to verify the functionally
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
pbs-api-types/src/network.rs | 17 ++++++++
pbs-config/src/network/mod.rs | 73 ++++++++++++++++++++++++++++++++++-
2 files changed, 89 insertions(+), 1 deletion(-)
diff --git a/pbs-api-types/src/network.rs b/pbs-api-types/src/network.rs
index e3a5e481..fe083dc6 100644
--- a/pbs-api-types/src/network.rs
+++ b/pbs-api-types/src/network.rs
@@ -224,6 +224,15 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema =
schema: NETWORK_INTERFACE_ARRAY_SCHEMA,
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,
@@ -287,6 +296,12 @@ pub struct Interface {
/// Enable bridge vlan support.
#[serde(skip_serializing_if = "Option::is_none")]
pub bridge_vlan_aware: Option<bool>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(rename = "vlan-id")]
+ pub vlan_id: Option<u16>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(rename = "vlan-raw-device")]
+ pub vlan_raw_device: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slaves: Option<Vec<String>>,
@@ -319,6 +334,8 @@ impl Interface {
mtu: None,
bridge_ports: None,
bridge_vlan_aware: None,
+ vlan_id: None,
+ vlan_raw_device: None,
slaves: None,
bond_mode: None,
bond_primary: None,
diff --git a/pbs-config/src/network/mod.rs b/pbs-config/src/network/mod.rs
index 7fec7e29..02117535 100644
--- a/pbs-config/src/network/mod.rs
+++ b/pbs-config/src/network/mod.rs
@@ -79,6 +79,14 @@ fn write_iface_attributes(iface: &Interface, w: &mut dyn Write) -> Result<(), Er
writeln!(w, "\tbond-slaves {}", slaves.join(" "))?;
}
}
+ NetworkInterfaceType::Vlan => {
+ if let Some(vlan_id) = iface.vlan_id {
+ writeln!(w, "\tvlan-id {vlan_id}")?;
+ }
+ if let Some(vlan_raw_device) = &iface.vlan_raw_device {
+ writeln!(w, "\tvlan-raw-device {vlan_raw_device}")?;
+ }
+ }
_ => {}
}
@@ -580,5 +588,68 @@ iface enp3s0 inet static
.trim()
);
}
-}
+ #[test]
+ fn test_write_network_config_vlan_id_in_name() {
+ let iface_name = String::from("vmbr0.100");
+ let mut iface = Interface::new(iface_name.clone());
+ iface.interface_type = Vlan;
+ iface.method = Some(Manual);
+ iface.active = true;
+
+ let nw_config = NetworkConfig {
+ interfaces: BTreeMap::from([(iface_name.clone(), iface)]),
+ order: vec![Iface(iface_name.clone())],
+ };
+ assert_eq!(
+ String::try_from(nw_config).unwrap().trim(),
+ "iface vmbr0.100 inet manual"
+ );
+ }
+
+ #[test]
+ fn test_write_network_config_vlan_with_raw_device() {
+ let iface_name = String::from("vlan100");
+ let mut iface = Interface::new(iface_name.clone());
+ iface.interface_type = Vlan;
+ iface.vlan_raw_device = Some(String::from("vmbr0"));
+ iface.method = Some(Manual);
+ iface.active = true;
+
+ let nw_config = NetworkConfig {
+ interfaces: BTreeMap::from([(iface_name.clone(), iface)]),
+ order: vec![Iface(iface_name.clone())],
+ };
+ assert_eq!(
+ String::try_from(nw_config).unwrap().trim(),
+ r#"
+iface vlan100 inet manual
+ vlan-raw-device vmbr0"#
+ .trim()
+ );
+ }
+
+ #[test]
+ fn test_write_network_config_vlan_with_individual_name() {
+ let iface_name = String::from("individual_name");
+ let mut iface = Interface::new(iface_name.clone());
+ iface.interface_type = Vlan;
+ iface.vlan_raw_device = Some(String::from("vmbr0"));
+ iface.vlan_id = Some(100);
+ iface.method = Some(Manual);
+ iface.active = true;
+
+ let nw_config = NetworkConfig {
+ interfaces: BTreeMap::from([(iface_name.clone(), iface)]),
+ order: vec![Iface(iface_name.clone())],
+ };
+ assert_eq!(
+ String::try_from(nw_config).unwrap().trim(),
+ r#"
+iface individual_name inet manual
+ vlan-id 100
+ vlan-raw-device vmbr0"#
+ .trim()
+ );
+ }
+}
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 5/9] config: parse vlan interface from config
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
` (3 preceding siblings ...)
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 4/9] config: write vlan network interface Stefan Lendl
@ 2024-04-04 9:51 ` Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 6/9] config: remove unnecessary pub in various methods in NetworkConfig Stefan Lendl
` (3 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
Support three types of vlan configurations defined in interfaces,
conforming to the PVE configurations:
iface nic.<vlan-id> inet
iface vlan<vlan-id> inet
vlan-raw-device <nic>
iface <arbitraty-name> inet
vlan-id <vlan-id>
vlan-raw-device <nic>
* Add lexer Token enum variants for vlan-id and vlan-raw-device and parse
them in parse_iface_attributes.
* Add tests to verify this works in the above scenarios
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
pbs-config/src/network/lexer.rs | 6 ++
pbs-config/src/network/parser.rs | 97 +++++++++++++++++++++++++++++++-
2 files changed, 102 insertions(+), 1 deletion(-)
diff --git a/pbs-config/src/network/lexer.rs b/pbs-config/src/network/lexer.rs
index fd23e3d8..d0b7d8cd 100644
--- a/pbs-config/src/network/lexer.rs
+++ b/pbs-config/src/network/lexer.rs
@@ -24,6 +24,8 @@ pub enum Token {
MTU,
BridgePorts,
BridgeVlanAware,
+ VlanId,
+ VlanRawDevice,
BondSlaves,
BondMode,
BondPrimary,
@@ -50,6 +52,10 @@ lazy_static! {
map.insert("bridge_ports", Token::BridgePorts);
map.insert("bridge-vlan-aware", Token::BridgeVlanAware);
map.insert("bridge_vlan_aware", Token::BridgeVlanAware);
+ map.insert("vlan-id", Token::VlanId);
+ map.insert("vlan_id", Token::VlanId);
+ map.insert("vlan-raw-device", Token::VlanRawDevice);
+ map.insert("vlan_raw_device", Token::VlanRawDevice);
map.insert("bond-slaves", Token::BondSlaves);
map.insert("bond_slaves", Token::BondSlaves);
map.insert("bond-mode", Token::BondMode);
diff --git a/pbs-config/src/network/parser.rs b/pbs-config/src/network/parser.rs
index 1b55569a..796e9308 100644
--- a/pbs-config/src/network/parser.rs
+++ b/pbs-config/src/network/parser.rs
@@ -361,6 +361,20 @@ impl<R: BufRead> NetworkParser<R> {
interface.bond_xmit_hash_policy = Some(policy);
self.eat(Token::Newline)?;
}
+ Token::VlanId => {
+ self.eat(Token::VlanId)?;
+ let vlan_id = self.next_text()?.parse()?;
+ interface.vlan_id = Some(vlan_id);
+ set_interface_type(interface, NetworkInterfaceType::Vlan)?;
+ self.eat(Token::Newline)?;
+ }
+ Token::VlanRawDevice => {
+ self.eat(Token::VlanRawDevice)?;
+ let vlan_raw_device = self.next_text()?;
+ interface.vlan_raw_device = Some(vlan_raw_device);
+ set_interface_type(interface, NetworkInterfaceType::Vlan)?;
+ self.eat(Token::Newline)?;
+ }
_ => {
// parse addon attributes
let option = self.parse_to_eol()?;
@@ -522,7 +536,7 @@ impl<R: BufRead> NetworkParser<R> {
lazy_static! {
static ref INTERFACE_ALIAS_REGEX: Regex = Regex::new(r"^\S+:\d+$").unwrap();
- static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^\S+\.\d+$").unwrap();
+ static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^\S+\.\d+|vlan\d+$").unwrap();
}
if let Some(existing_interfaces) = existing_interfaces {
@@ -748,4 +762,85 @@ mod test {
Ok(())
}
+
+ #[test]
+ fn test_network_config_parser_vlan_id_in_name() {
+ let input = "iface vmbr0.100 inet static manual";
+ let mut parser = NetworkParser::new(input.as_bytes());
+ let config = parser.parse_interfaces(None).unwrap();
+
+ let iface = config.interfaces.get("vmbr0.100").unwrap();
+ assert_eq!(iface.interface_type, NetworkInterfaceType::Vlan);
+ assert_eq!(iface.vlan_raw_device, None);
+ assert_eq!(iface.vlan_id, None);
+ }
+
+ #[test]
+ fn test_network_config_parser_vlan_with_raw_device() {
+ let input = r#"
+iface vlan100 inet manual
+ vlan-raw-device vmbr0"#;
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+ let config = parser.parse_interfaces(None).unwrap();
+
+ let iface = config.interfaces.get("vlan100").unwrap();
+ assert_eq!(iface.interface_type, NetworkInterfaceType::Vlan);
+ assert_eq!(iface.vlan_raw_device, Some(String::from("vmbr0")));
+ assert_eq!(iface.vlan_id, None);
+ }
+
+ #[test]
+ fn test_network_config_parser_vlan_with_raw_device_static() {
+ let input = r#"
+iface vlan100 inet static
+ vlan-raw-device vmbr0
+ address 10.0.0.100/16"#;
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+ let config = parser.parse_interfaces(None).unwrap();
+
+ let iface = config.interfaces.get("vlan100").unwrap();
+ assert_eq!(iface.interface_type, NetworkInterfaceType::Vlan);
+ assert_eq!(iface.vlan_raw_device, Some(String::from("vmbr0")));
+ assert_eq!(iface.vlan_id, None);
+ 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_vlan_individual_name() {
+ let input = r#"
+iface individual_name inet manual
+ vlan-id 100
+ vlan-raw-device vmbr0"#;
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+ let config = parser.parse_interfaces(None).unwrap();
+
+ let iface = config.interfaces.get("individual_name").unwrap();
+ assert_eq!(iface.interface_type, NetworkInterfaceType::Vlan);
+ assert_eq!(iface.vlan_raw_device, Some(String::from("vmbr0")));
+ assert_eq!(iface.vlan_id, Some(100));
+ }
+
+ #[test]
+ fn test_network_config_parser_vlan_individual_name_static() {
+ let input = r#"
+iface individual_name inet static
+ vlan-id 100
+ vlan-raw-device vmbr0
+ address 10.0.0.100/16
+"#;
+
+ let mut parser = NetworkParser::new(input.as_bytes());
+ let config = parser.parse_interfaces(None).unwrap();
+
+ let iface = config.interfaces.get("individual_name").unwrap();
+ assert_eq!(iface.interface_type, NetworkInterfaceType::Vlan);
+ assert_eq!(iface.vlan_raw_device, Some(String::from("vmbr0")));
+ assert_eq!(iface.vlan_id, Some(100));
+ assert_eq!(iface.method, Some(NetworkConfigMethod::Static));
+ assert_eq!(iface.cidr, Some(String::from("10.0.0.100/16")));
+ }
}
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 6/9] config: remove unnecessary pub in various methods in NetworkConfig
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
` (4 preceding siblings ...)
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 5/9] config: parse vlan interface from config Stefan Lendl
@ 2024-04-04 9:51 ` Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 7/9] fmt: fix intendation in api macro Stefan Lendl
` (2 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
pbs-config/src/network/mod.rs | 10 +++++-----
pbs-config/src/network/parser.rs | 4 ++--
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/pbs-config/src/network/mod.rs b/pbs-config/src/network/mod.rs
index 02117535..51b09937 100644
--- a/pbs-config/src/network/mod.rs
+++ b/pbs-config/src/network/mod.rs
@@ -251,7 +251,7 @@ impl NetworkConfig {
}
/// Check if ports are used only once
- pub fn check_port_usage(&self) -> Result<(), Error> {
+ fn check_port_usage(&self) -> Result<(), Error> {
let mut used_ports = HashMap::new();
let mut check_port_usage = |iface, ports: &Vec<String>| {
for port in ports.iter() {
@@ -280,7 +280,7 @@ impl NetworkConfig {
}
/// Check if child mtu is less or equal than parent mtu
- pub fn check_mtu(&self, parent_name: &str, child_name: &str) -> Result<(), Error> {
+ fn check_mtu(&self, parent_name: &str, child_name: &str) -> Result<(), Error> {
let parent = self
.interfaces
.get(parent_name)
@@ -320,7 +320,7 @@ impl NetworkConfig {
}
/// Check if bond slaves exists
- pub fn check_bond_slaves(&self) -> Result<(), Error> {
+ fn check_bond_slaves(&self) -> Result<(), Error> {
for (iface, interface) in self.interfaces.iter() {
if let Some(slaves) = &interface.slaves {
for slave in slaves.iter() {
@@ -348,7 +348,7 @@ impl NetworkConfig {
}
/// Check if bridge ports exists
- pub fn check_bridge_ports(&self) -> Result<(), Error> {
+ fn check_bridge_ports(&self) -> Result<(), Error> {
lazy_static! {
static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^(\S+)\.(\d+)$").unwrap();
}
@@ -372,7 +372,7 @@ impl NetworkConfig {
Ok(())
}
- pub fn write_config(&self, w: &mut dyn Write) -> Result<(), Error> {
+ fn write_config(&self, w: &mut dyn Write) -> Result<(), Error> {
self.check_port_usage()?;
self.check_bond_slaves()?;
self.check_bridge_ports()?;
diff --git a/pbs-config/src/network/parser.rs b/pbs-config/src/network/parser.rs
index 796e9308..e409c94c 100644
--- a/pbs-config/src/network/parser.rs
+++ b/pbs-config/src/network/parser.rs
@@ -487,11 +487,11 @@ impl<R: BufRead> NetworkParser<R> {
&mut self,
existing_interfaces: Option<&HashMap<String, bool>>,
) -> Result<NetworkConfig, Error> {
- self._parse_interfaces(existing_interfaces)
+ self.do_parse_interfaces(existing_interfaces)
.map_err(|err| format_err!("line {}: {}", self.line_nr, err))
}
- pub fn _parse_interfaces(
+ fn do_parse_interfaces(
&mut self,
existing_interfaces: Option<&HashMap<String, bool>>,
) -> Result<NetworkConfig, Error> {
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 7/9] fmt: fix intendation in api macro
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
` (5 preceding siblings ...)
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 6/9] config: remove unnecessary pub in various methods in NetworkConfig Stefan Lendl
@ 2024-04-04 9:51 ` Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 8/9] api: create and update vlan interfaces Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 9/9] ui: enable vlan widget Stefan Lendl
8 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
src/api2/node/network.rs | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/api2/node/network.rs b/src/api2/node/network.rs
index ade6fe40..187b27a0 100644
--- a/src/api2/node/network.rs
+++ b/src/api2/node/network.rs
@@ -227,9 +227,9 @@ pub fn read_interface(iface: String) -> Result<Value, Error> {
optional: true,
},
bridge_vlan_aware: {
- description: "Enable bridge vlan support.",
- type: bool,
- optional: true,
+ description: "Enable bridge vlan support.",
+ type: bool,
+ optional: true,
},
bond_mode: {
type: LinuxBondMode,
@@ -503,9 +503,9 @@ pub enum DeletableProperty {
optional: true,
},
bridge_vlan_aware: {
- description: "Enable bridge vlan support.",
- type: bool,
- optional: true,
+ description: "Enable bridge vlan support.",
+ type: bool,
+ optional: true,
},
bond_mode: {
type: LinuxBondMode,
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 8/9] api: create and update vlan interfaces
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
` (6 preceding siblings ...)
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 7/9] fmt: fix intendation in api macro Stefan Lendl
@ 2024-04-04 9:51 ` Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 9/9] ui: enable vlan widget Stefan Lendl
8 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
* Implement setting vlan-id and vlan-raw-device in the create and update api.
* Checking if the provided vlan-raw-device exists
* Moved VLAN_INTERFACE_REGEX to top level network module to use it in
the checking functions there. Changed to match with named capture groups.
* Unit tests to verify parsing vlan_id and vlan_raw_device from name.
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
pbs-config/src/network/mod.rs | 35 +++++++++++++++++++
pbs-config/src/network/parser.rs | 3 +-
src/api2/node/network.rs | 60 +++++++++++++++++++++++++++++++-
3 files changed, 96 insertions(+), 2 deletions(-)
diff --git a/pbs-config/src/network/mod.rs b/pbs-config/src/network/mod.rs
index 51b09937..19a2df8c 100644
--- a/pbs-config/src/network/mod.rs
+++ b/pbs-config/src/network/mod.rs
@@ -25,6 +25,8 @@ use crate::{open_backup_lockfile, BackupLockGuard};
lazy_static! {
static ref PHYSICAL_NIC_REGEX: Regex = Regex::new(r"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap();
+ static ref VLAN_INTERFACE_REGEX: Regex =
+ Regex::new(r"^(?P<vlan_raw_device>\S+)\.(?P<vlan_id>\d+)|vlan(?P<vlan_id2>\d+)$").unwrap();
}
pub fn is_physical_nic(iface: &str) -> bool {
@@ -41,6 +43,21 @@ pub fn bond_xmit_hash_policy_from_str(s: &str) -> Result<BondXmitHashPolicy, Err
.map_err(|_: value::Error| format_err!("invalid bond_xmit_hash_policy '{}'", s))
}
+pub fn parse_vlan_id_from_name(iface_name: &str) -> Option<u16> {
+ VLAN_INTERFACE_REGEX.captures(iface_name).and_then(|cap| {
+ cap.name("vlan_id")
+ .or(cap.name("vlan_id2"))
+ .and_then(|id| id.as_str().parse::<u16>().ok())
+ })
+}
+
+pub fn parse_vlan_raw_device_from_name(iface_name: &str) -> Option<&str> {
+ VLAN_INTERFACE_REGEX
+ .captures(iface_name)
+ .and_then(|cap| cap.name("vlan_raw_device"))
+ .map(Into::into)
+}
+
// Write attributes not depending on address family
fn write_iface_attributes(iface: &Interface, w: &mut dyn Write) -> Result<(), Error> {
static EMPTY_LIST: Vec<String> = Vec::new();
@@ -652,4 +669,22 @@ iface individual_name inet manual
.trim()
);
}
+
+ #[test]
+ fn test_vlan_parse_vlan_id_from_name() {
+ assert_eq!(parse_vlan_id_from_name("vlan100"), Some(100));
+ assert_eq!(parse_vlan_id_from_name("vlan"), None);
+ assert_eq!(parse_vlan_id_from_name("arbitrary"), None);
+ assert_eq!(parse_vlan_id_from_name("vmbr0.100"), Some(100));
+ assert_eq!(parse_vlan_id_from_name("vmbr0"), None);
+ // assert_eq!(parse_vlan_id_from_name("vmbr0.1.400"), Some(400)); // NOTE ifupdown2 does actually support this
+ }
+
+ #[test]
+ fn test_vlan_parse_vlan_raw_device_from_name() {
+ assert_eq!(parse_vlan_raw_device_from_name("vlan100"), None);
+ assert_eq!(parse_vlan_raw_device_from_name("arbitrary"), None);
+ assert_eq!(parse_vlan_raw_device_from_name("vmbr0"), None);
+ assert_eq!(parse_vlan_raw_device_from_name("vmbr0.200"), Some("vmbr0"));
+ }
}
diff --git a/pbs-config/src/network/parser.rs b/pbs-config/src/network/parser.rs
index e409c94c..2158a04f 100644
--- a/pbs-config/src/network/parser.rs
+++ b/pbs-config/src/network/parser.rs
@@ -1,3 +1,5 @@
+use crate::network::VLAN_INTERFACE_REGEX;
+
use std::collections::{HashMap, HashSet};
use std::io::BufRead;
use std::iter::{Iterator, Peekable};
@@ -536,7 +538,6 @@ impl<R: BufRead> NetworkParser<R> {
lazy_static! {
static ref INTERFACE_ALIAS_REGEX: Regex = Regex::new(r"^\S+:\d+$").unwrap();
- static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^\S+\.\d+|vlan\d+$").unwrap();
}
if let Some(existing_interfaces) = existing_interfaces {
diff --git a/src/api2/node/network.rs b/src/api2/node/network.rs
index 187b27a0..92297421 100644
--- a/src/api2/node/network.rs
+++ b/src/api2/node/network.rs
@@ -12,7 +12,9 @@ use pbs_api_types::{
NETWORK_INTERFACE_ARRAY_SCHEMA, NETWORK_INTERFACE_LIST_SCHEMA, NETWORK_INTERFACE_NAME_SCHEMA,
NODE_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA,
};
-use pbs_config::network::{self, NetworkConfig};
+use pbs_config::network::{
+ self, parse_vlan_id_from_name, parse_vlan_raw_device_from_name, NetworkConfig,
+};
use proxmox_rest_server::WorkerTask;
@@ -231,6 +233,15 @@ pub fn read_interface(iface: String) -> Result<Value, Error> {
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,
@@ -269,6 +280,8 @@ pub fn create_interface(
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>,
@@ -373,6 +386,24 @@ pub fn create_interface(
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
@@ -507,6 +538,15 @@ pub enum DeletableProperty {
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,
@@ -557,6 +597,8 @@ pub fn update_interface(
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>,
@@ -581,6 +623,15 @@ pub fn update_interface(
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") {
@@ -734,6 +785,13 @@ pub fn update_interface(
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(())
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 9/9] ui: enable vlan widget
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
` (7 preceding siblings ...)
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 8/9] api: create and update vlan interfaces Stefan Lendl
@ 2024-04-04 9:51 ` Stefan Lendl
8 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 9:51 UTC (permalink / raw)
To: s.lendl, pbs-devel
* Enabled the "Linux VLAN" option when creating a new interface.
* This requires the updated widget-toolkit to contain vlan field widget.
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
www/SystemConfiguration.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/www/SystemConfiguration.js b/www/SystemConfiguration.js
index e94fe7ca..23330b6a 100644
--- a/www/SystemConfiguration.js
+++ b/www/SystemConfiguration.js
@@ -41,7 +41,7 @@ Ext.define('PBS.SystemConfiguration', {
flex: 1,
minHeight: 200,
showApplyBtn: true,
- types: ['bond', 'bridge'],
+ types: ['bond', 'bridge', 'vlan'],
nodename: 'localhost',
},
],
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE
2024-04-04 9:51 ` [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE Stefan Lendl
@ 2024-04-04 16:29 ` Thomas Lamprecht
2024-04-05 8:06 ` Stefan Lendl
2024-04-18 9:26 ` Folke Gleumes
1 sibling, 1 reply; 14+ messages in thread
From: Thomas Lamprecht @ 2024-04-04 16:29 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Stefan Lendl
I guess this can be ignored over the re-send that includes widget-toolkit
and cover-letter:
https://lists.proxmox.com/pipermail/pbs-devel/2024-April/008339.html
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE
2024-04-04 16:29 ` Thomas Lamprecht
@ 2024-04-05 8:06 ` Stefan Lendl
0 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-05 8:06 UTC (permalink / raw)
To: Thomas Lamprecht, Proxmox Backup Server development discussion
Thomas Lamprecht <t.lamprecht@proxmox.com> writes:
> I guess this can be ignored over the re-send that includes widget-toolkit
> and cover-letter:
>
> https://lists.proxmox.com/pipermail/pbs-devel/2024-April/008339.html
Yes please.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE
2024-04-04 9:51 ` [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE Stefan Lendl
2024-04-04 16:29 ` Thomas Lamprecht
@ 2024-04-18 9:26 ` Folke Gleumes
2024-04-18 9:40 ` Folke Gleumes
1 sibling, 1 reply; 14+ messages in thread
From: Folke Gleumes @ 2024-04-18 9:26 UTC (permalink / raw)
To: pbs-devel
Retested the series against the current master on the request of Lukas.
Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
On Thu, 2024-04-04 at 11:51 +0200, Stefan Lendl wrote:
> Copied from PVE to use in PBS network configuration.
>
> Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
> Tested-by: Lukas Wagner <l.wagner@proxmox.com>
> Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
> ---
> src/Makefile | 1 +
> src/form/VlanField.js | 40
> ++++++++++++++++++++++++++++++++++++++++
> src/node/NetworkEdit.js | 6 +++---
> 3 files changed, 44 insertions(+), 3 deletions(-)
> create mode 100644 src/form/VlanField.js
>
> diff --git a/src/Makefile b/src/Makefile
> index 89f9962..0b1e88c 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -31,6 +31,7 @@ JSSRC= \
> form/ExpireDate.js \
> form/IntegerField.js \
> form/TextField.js \
> + form/VlanField.js \
> form/DateTimeField.js \
> form/Checkbox.js \
> form/KVComboBox.js \
> diff --git a/src/form/VlanField.js b/src/form/VlanField.js
> new file mode 100644
> index 0000000..71b580d
> --- /dev/null
> +++ b/src/form/VlanField.js
> @@ -0,0 +1,40 @@
> +Ext.define('Proxmox.form.field.VlanField', {
> + extend: 'Ext.form.field.Number',
> + alias: ['widget.proxmoxvlanfield'],
> +
> + deleteEmpty: false,
> +
> + emptyText: gettext('no VLAN'),
> +
> + fieldLabel: gettext('VLAN Tag'),
> +
> + allowBlank: true,
> +
> + getSubmitData: function() {
> + var me = this,
> + data = null,
> + val;
> + if (!me.disabled && me.submitValue) {
> + val = me.getSubmitValue();
> + if (val) {
> + data = {};
> + data[me.getName()] = val;
> + } else if (me.deleteEmpty) {
> + data = {};
> + data.delete = me.getName();
> + }
> + }
> + return data;
> + },
> +
> + initComponent: function() {
> + var me = this;
> +
> + Ext.apply(me, {
> + minValue: 1,
> + maxValue: 4094,
> + });
> +
> + me.callParent();
> + },
> +});
> diff --git a/src/node/NetworkEdit.js b/src/node/NetworkEdit.js
> index bb9add3..b81a21d 100644
> --- a/src/node/NetworkEdit.js
> +++ b/src/node/NetworkEdit.js
> @@ -97,7 +97,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
> name: 'ovs_bridge',
> });
> column2.push({
> - xtype: 'pveVlanField',
> + xtype: 'proxmoxvlanfield',
> deleteEmpty: !me.isCreate,
> name: 'ovs_tag',
> value: '',
> @@ -140,7 +140,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
> });
>
> column2.push({
> - xtype: 'pveVlanField',
> + xtype: 'proxmoxvlanfield',
> name: 'vlan-id',
> value: me.vlanidvalue,
> disabled: me.disablevlanid,
> @@ -211,7 +211,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
> name: 'ovs_bridge',
> });
> column2.push({
> - xtype: 'pveVlanField',
> + xtype: 'proxmoxvlanfield',
> deleteEmpty: !me.isCreate,
> name: 'ovs_tag',
> value: '',
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE
2024-04-18 9:26 ` Folke Gleumes
@ 2024-04-18 9:40 ` Folke Gleumes
0 siblings, 0 replies; 14+ messages in thread
From: Folke Gleumes @ 2024-04-18 9:40 UTC (permalink / raw)
To: pbs-devel
Just found that the patch was posted again with the same version, found
a small bug, will elaborate there.
On Thu, 2024-04-18 at 11:26 +0200, Folke Gleumes wrote:
> Retested the series against the current master on the request of
> Lukas.
>
>
>
> Tested-by: Folke Gleumes <f.gleumes@proxmox.com>
>
> On Thu, 2024-04-04 at 11:51 +0200, Stefan Lendl wrote:
> > Copied from PVE to use in PBS network configuration.
> >
> > Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
> > Tested-by: Lukas Wagner <l.wagner@proxmox.com>
> > Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
> > ---
> > src/Makefile | 1 +
> > src/form/VlanField.js | 40
> > ++++++++++++++++++++++++++++++++++++++++
> > src/node/NetworkEdit.js | 6 +++---
> > 3 files changed, 44 insertions(+), 3 deletions(-)
> > create mode 100644 src/form/VlanField.js
> >
> > diff --git a/src/Makefile b/src/Makefile
> > index 89f9962..0b1e88c 100644
> > --- a/src/Makefile
> > +++ b/src/Makefile
> > @@ -31,6 +31,7 @@ JSSRC= \
> > form/ExpireDate.js \
> > form/IntegerField.js \
> > form/TextField.js \
> > + form/VlanField.js \
> > form/DateTimeField.js \
> > form/Checkbox.js \
> > form/KVComboBox.js \
> > diff --git a/src/form/VlanField.js b/src/form/VlanField.js
> > new file mode 100644
> > index 0000000..71b580d
> > --- /dev/null
> > +++ b/src/form/VlanField.js
> > @@ -0,0 +1,40 @@
> > +Ext.define('Proxmox.form.field.VlanField', {
> > + extend: 'Ext.form.field.Number',
> > + alias: ['widget.proxmoxvlanfield'],
> > +
> > + deleteEmpty: false,
> > +
> > + emptyText: gettext('no VLAN'),
> > +
> > + fieldLabel: gettext('VLAN Tag'),
> > +
> > + allowBlank: true,
> > +
> > + getSubmitData: function() {
> > + var me = this,
> > + data = null,
> > + val;
> > + if (!me.disabled && me.submitValue) {
> > + val = me.getSubmitValue();
> > + if (val) {
> > + data = {};
> > + data[me.getName()] = val;
> > + } else if (me.deleteEmpty) {
> > + data = {};
> > + data.delete = me.getName();
> > + }
> > + }
> > + return data;
> > + },
> > +
> > + initComponent: function() {
> > + var me = this;
> > +
> > + Ext.apply(me, {
> > + minValue: 1,
> > + maxValue: 4094,
> > + });
> > +
> > + me.callParent();
> > + },
> > +});
> > diff --git a/src/node/NetworkEdit.js b/src/node/NetworkEdit.js
> > index bb9add3..b81a21d 100644
> > --- a/src/node/NetworkEdit.js
> > +++ b/src/node/NetworkEdit.js
> > @@ -97,7 +97,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
> > name: 'ovs_bridge',
> > });
> > column2.push({
> > - xtype: 'pveVlanField',
> > + xtype: 'proxmoxvlanfield',
> > deleteEmpty: !me.isCreate,
> > name: 'ovs_tag',
> > value: '',
> > @@ -140,7 +140,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
> > });
> >
> > column2.push({
> > - xtype: 'pveVlanField',
> > + xtype: 'proxmoxvlanfield',
> > name: 'vlan-id',
> > value: me.vlanidvalue,
> > disabled: me.disablevlanid,
> > @@ -211,7 +211,7 @@ Ext.define('Proxmox.node.NetworkEdit', {
> > name: 'ovs_bridge',
> > });
> > column2.push({
> > - xtype: 'pveVlanField',
> > + xtype: 'proxmoxvlanfield',
> > deleteEmpty: !me.isCreate,
> > name: 'ovs_tag',
> > value: '',
>
> _______________________________________________
> pbs-devel mailing list
> pbs-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
^ permalink raw reply [flat|nested] 14+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v3 8/9] api: create and update vlan interfaces
2024-04-04 10:00 [pbs-devel] [PATCH widget-toolkit/proxmox-backup v3 0/9] Fix #3115: VLAN Network Interface Configuration Stefan Lendl
@ 2024-04-04 10:00 ` Stefan Lendl
0 siblings, 0 replies; 14+ messages in thread
From: Stefan Lendl @ 2024-04-04 10:00 UTC (permalink / raw)
To: pbs-devel
* Implement setting vlan-id and vlan-raw-device in the create and update api.
* Checking if the provided vlan-raw-device exists
* Moved VLAN_INTERFACE_REGEX to top level network module to use it in
the checking functions there. Changed to match with named capture groups.
* Unit tests to verify parsing vlan_id and vlan_raw_device from name.
Signed-off-by: Stefan Lendl <s.lendl@proxmox.com>
Tested-by: Lukas Wagner <l.wagner@proxmox.com>
Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
---
pbs-config/src/network/mod.rs | 35 +++++++++++++++++++
pbs-config/src/network/parser.rs | 3 +-
src/api2/node/network.rs | 60 +++++++++++++++++++++++++++++++-
3 files changed, 96 insertions(+), 2 deletions(-)
diff --git a/pbs-config/src/network/mod.rs b/pbs-config/src/network/mod.rs
index 51b09937..19a2df8c 100644
--- a/pbs-config/src/network/mod.rs
+++ b/pbs-config/src/network/mod.rs
@@ -25,6 +25,8 @@ use crate::{open_backup_lockfile, BackupLockGuard};
lazy_static! {
static ref PHYSICAL_NIC_REGEX: Regex = Regex::new(r"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap();
+ static ref VLAN_INTERFACE_REGEX: Regex =
+ Regex::new(r"^(?P<vlan_raw_device>\S+)\.(?P<vlan_id>\d+)|vlan(?P<vlan_id2>\d+)$").unwrap();
}
pub fn is_physical_nic(iface: &str) -> bool {
@@ -41,6 +43,21 @@ pub fn bond_xmit_hash_policy_from_str(s: &str) -> Result<BondXmitHashPolicy, Err
.map_err(|_: value::Error| format_err!("invalid bond_xmit_hash_policy '{}'", s))
}
+pub fn parse_vlan_id_from_name(iface_name: &str) -> Option<u16> {
+ VLAN_INTERFACE_REGEX.captures(iface_name).and_then(|cap| {
+ cap.name("vlan_id")
+ .or(cap.name("vlan_id2"))
+ .and_then(|id| id.as_str().parse::<u16>().ok())
+ })
+}
+
+pub fn parse_vlan_raw_device_from_name(iface_name: &str) -> Option<&str> {
+ VLAN_INTERFACE_REGEX
+ .captures(iface_name)
+ .and_then(|cap| cap.name("vlan_raw_device"))
+ .map(Into::into)
+}
+
// Write attributes not depending on address family
fn write_iface_attributes(iface: &Interface, w: &mut dyn Write) -> Result<(), Error> {
static EMPTY_LIST: Vec<String> = Vec::new();
@@ -652,4 +669,22 @@ iface individual_name inet manual
.trim()
);
}
+
+ #[test]
+ fn test_vlan_parse_vlan_id_from_name() {
+ assert_eq!(parse_vlan_id_from_name("vlan100"), Some(100));
+ assert_eq!(parse_vlan_id_from_name("vlan"), None);
+ assert_eq!(parse_vlan_id_from_name("arbitrary"), None);
+ assert_eq!(parse_vlan_id_from_name("vmbr0.100"), Some(100));
+ assert_eq!(parse_vlan_id_from_name("vmbr0"), None);
+ // assert_eq!(parse_vlan_id_from_name("vmbr0.1.400"), Some(400)); // NOTE ifupdown2 does actually support this
+ }
+
+ #[test]
+ fn test_vlan_parse_vlan_raw_device_from_name() {
+ assert_eq!(parse_vlan_raw_device_from_name("vlan100"), None);
+ assert_eq!(parse_vlan_raw_device_from_name("arbitrary"), None);
+ assert_eq!(parse_vlan_raw_device_from_name("vmbr0"), None);
+ assert_eq!(parse_vlan_raw_device_from_name("vmbr0.200"), Some("vmbr0"));
+ }
}
diff --git a/pbs-config/src/network/parser.rs b/pbs-config/src/network/parser.rs
index e409c94c..2158a04f 100644
--- a/pbs-config/src/network/parser.rs
+++ b/pbs-config/src/network/parser.rs
@@ -1,3 +1,5 @@
+use crate::network::VLAN_INTERFACE_REGEX;
+
use std::collections::{HashMap, HashSet};
use std::io::BufRead;
use std::iter::{Iterator, Peekable};
@@ -536,7 +538,6 @@ impl<R: BufRead> NetworkParser<R> {
lazy_static! {
static ref INTERFACE_ALIAS_REGEX: Regex = Regex::new(r"^\S+:\d+$").unwrap();
- static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^\S+\.\d+|vlan\d+$").unwrap();
}
if let Some(existing_interfaces) = existing_interfaces {
diff --git a/src/api2/node/network.rs b/src/api2/node/network.rs
index 187b27a0..92297421 100644
--- a/src/api2/node/network.rs
+++ b/src/api2/node/network.rs
@@ -12,7 +12,9 @@ use pbs_api_types::{
NETWORK_INTERFACE_ARRAY_SCHEMA, NETWORK_INTERFACE_LIST_SCHEMA, NETWORK_INTERFACE_NAME_SCHEMA,
NODE_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA,
};
-use pbs_config::network::{self, NetworkConfig};
+use pbs_config::network::{
+ self, parse_vlan_id_from_name, parse_vlan_raw_device_from_name, NetworkConfig,
+};
use proxmox_rest_server::WorkerTask;
@@ -231,6 +233,15 @@ pub fn read_interface(iface: String) -> Result<Value, Error> {
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,
@@ -269,6 +280,8 @@ pub fn create_interface(
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>,
@@ -373,6 +386,24 @@ pub fn create_interface(
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
@@ -507,6 +538,15 @@ pub enum DeletableProperty {
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,
@@ -557,6 +597,8 @@ pub fn update_interface(
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>,
@@ -581,6 +623,15 @@ pub fn update_interface(
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") {
@@ -734,6 +785,13 @@ pub fn update_interface(
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(())
--
2.44.0
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2024-04-18 9:40 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20240404095151.184141-1-s.lendl@proxmox.com>
2024-04-04 9:51 ` [pbs-devel] [PATCH widget-toolkit v3 1/9] form: include vlan field widget from PVE Stefan Lendl
2024-04-04 16:29 ` Thomas Lamprecht
2024-04-05 8:06 ` Stefan Lendl
2024-04-18 9:26 ` Folke Gleumes
2024-04-18 9:40 ` Folke Gleumes
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 2/9] tests: move network tests to parser.rs Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 3/9] tests: simple tests for writing the network config Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 4/9] config: write vlan network interface Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 5/9] config: parse vlan interface from config Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 6/9] config: remove unnecessary pub in various methods in NetworkConfig Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 7/9] fmt: fix intendation in api macro Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 8/9] api: create and update vlan interfaces Stefan Lendl
2024-04-04 9:51 ` [pbs-devel] [PATCH proxmox-backup v3 9/9] ui: enable vlan widget Stefan Lendl
2024-04-04 10:00 [pbs-devel] [PATCH widget-toolkit/proxmox-backup v3 0/9] Fix #3115: VLAN Network Interface Configuration Stefan Lendl
2024-04-04 10:00 ` [pbs-devel] [PATCH proxmox-backup v3 8/9] api: create and update vlan interfaces Stefan Lendl
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox