Fix vlan and bond config generation

This change fixes how glean generates the vlan and bond configurations for Red Hat and related distros.
- Problem: glean set TYPE=ETHERNET for all interface types in the ifcfg files.
  Fix: glean sets proper "TYPE=Bond" and "TYPE=Vlan" for bond and vlan interfaces respectively
- Problem: glean set type=802-3-ethernet for all interface types in the NetworkManager keyfiles
  Fix: glean sets proper "type=bond" and "type=vlan" for bond and vlan interfaces respectively
- Problem: glean generates netdev file for physical interfaces
  Fix: glean generates netdev files for virtual interfaces only.

Story: 2011658
Change-Id: Iee68d154cd8e31d1a1781d517ad61f000dd5c4cc
Signed-off-by: Laszlo Janosi <laszlo.janosi@nokia.com>
This commit is contained in:
Laszlo Janosi
2026-01-22 15:38:36 +00:00
parent 10df0bd91b
commit 642fffe020
5 changed files with 183 additions and 54 deletions
+49 -22
View File
@@ -116,9 +116,14 @@ def _network_files(distro):
return network_files
def _network_config(args):
distro = args.distro
def _network_config(args, interface):
"""Generate network configuration preamble.
Args:
args: Command line arguments
interface: Interface dictionary to determine type from
"""
distro = args.distro
if _is_suse(distro):
preamble = textwrap.dedent("""\
# Automatically generated, do not edit
@@ -127,28 +132,50 @@ def _network_config(args):
STARTMODE=auto
""")
elif is_keyfile_format():
# Map interface type to connection type
if 'bond_mode' in interface:
connection_type = "bond"
elif 'vlan_id' in interface:
connection_type = "vlan"
else:
connection_type = "802-3-ethernet"
preamble = textwrap.dedent("""\
# Automatically generated, do not edit
[connection]
id={id}
type=802-3-ethernet
id={{id}}
type={type}
[ipv4]
method={method}
method={{method}}
""").format(type=connection_type)
[802-3-ethernet]
mac_address={mac_address}
""")
# Only regular ethernet needs the [802-3-ethernet] section
if connection_type == "802-3-ethernet":
preamble += textwrap.dedent("""\
[802-3-ethernet]
mac_address={mac_address}
""")
else:
# For Red Hat ifcfg format
nm_controlled = "yes" if args.use_nm else "no"
if 'bond_mode' in interface:
type_line = "Bond"
elif 'vlan_id' in interface:
type_line = "Vlan"
else:
type_line = "Ethernet"
preamble = textwrap.dedent("""\
# Automatically generated, do not edit
DEVICE={name}
BOOTPROTO={bootproto}
HWADDR={hwaddr}
DEVICE={{name}}
BOOTPROTO={{bootproto}}
HWADDR={{hwaddr}}
ONBOOT=yes
NM_CONTROLLED=%s
TYPE=Ethernet
""" % ("yes" if args.use_nm else "no"))
NM_CONTROLLED={nm}
TYPE={type}
""").format(nm=nm_controlled, type=type_line)
return preamble
@@ -178,7 +205,7 @@ def _set_rh_bonding(name, interface, distro, results):
elif is_keyfile_format():
if 'bond_slaves' in interface:
results += "type=bond\n"
pass
else:
results += "master={0}\n".format(interface['bond_master'])
@@ -312,7 +339,7 @@ def _write_rh_v6_keyfile_interface(name, interface, args, files):
def _write_rh_interface(name, interface, args):
distro = args.distro
files_to_write = dict()
results = _network_config(args).format(
results = _network_config(args, interface).format(
bootproto="static",
name=name,
hwaddr=interface['mac_address'],
@@ -363,7 +390,7 @@ def _write_rh_interface(name, interface, args):
def _write_rh_dhcp(name, interface, args):
distro = args.distro
filename = _network_files(distro)["ifcfg"] + '-{name}'.format(name=name)
results = _network_config(args).format(
results = _network_config(args, interface).format(
bootproto="dhcp", name=name, hwaddr=interface['mac_address'])
results += _set_rh_vlan(name, interface, distro)
# set_rh_bonding takes results as argument so we need to assign
@@ -378,7 +405,7 @@ def _write_rh_keyfile_dhcp(name, interface, args):
filename = \
'/etc/NetworkManager/system-connections/{name}.nmconnection'.format(
name=name)
results = _network_config(args).format(
results = _network_config(args, interface).format(
method="auto", id=name, mac_address=interface['mac_address'])
results += _set_rh_vlan(name, interface, distro)
# set_rh_bonding takes results as argument so we need to assign
@@ -394,12 +421,12 @@ def _write_rh_manual(name, interface, args):
filename = \
'/etc/NetworkManager/system-connections/{name}.nmconnection' \
.format(name=name)
results = _network_config(args).format(
results = _network_config(args, interface).format(
method="manual", id=name, mac_address=interface['mac_address'])
else:
filename = _network_files(distro)["ifcfg"] + '-{name}'.format(
name=name)
results = _network_config(args).format(
results = _network_config(args, interface).format(
bootproto="none", name=name, hwaddr=interface['mac_address'])
results += _set_rh_vlan(name, interface, distro)
# set_rh_bonding takes results as argument so we need to assign
@@ -418,7 +445,7 @@ def _write_rh_keyfile_interface(_name, interface, args):
filename = \
'/etc/NetworkManager/system-connections/{name}.nmconnection'.format(
name=_name)
results = _network_config(args).format(
results = _network_config(args, interface).format(
method="manual", id=_name, mac_address=interface['mac_address'])
# insert value after specific option using slicing
@@ -705,7 +732,7 @@ def _write_networkd_interface(name, interfaces, args, files_struct=dict()):
})
# create netdev files
if 'bond_mode' or 'vlan_id' in interface:
if any(key in interface for key in ['bond_mode', 'vlan_id']):
netdev_file = \
'/etc/systemd/network/{name}.netdev'.format(name=iname)
if netdev_file not in files_struct:
+5 -5
View File
@@ -38,7 +38,7 @@ BOOTPROTO=dhcp
HWADDR=bc:76:4e:12:a4:bc
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Vlan
VLAN=yes
### Write /etc/sysconfig/network-scripts/ifcfg-eth4.26
# Automatically generated, do not edit
@@ -47,7 +47,7 @@ BOOTPROTO=dhcp
HWADDR=bc:76:4e:12:a4:bd
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Vlan
VLAN=yes
### Write /etc/sysconfig/network-scripts/ifcfg-eth5
# Automatically generated, do not edit
@@ -76,7 +76,7 @@ BOOTPROTO=dhcp
HWADDR=bc:76:4e:05:7b:13
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Bond
### Write /etc/sysconfig/network-scripts/ifcfg-eth7
# Automatically generated, do not edit
DEVICE=eth7
@@ -104,7 +104,7 @@ BOOTPROTO=none
HWADDR=bc:76:4e:05:7b:15
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Bond
### Write /etc/sysconfig/network-scripts/ifcfg-bond1.27
# Automatically generated, do not edit
DEVICE=bond1.27
@@ -112,7 +112,7 @@ BOOTPROTO=dhcp
HWADDR=bc:76:4e:12:a4:be
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Vlan
VLAN=yes
### Write /etc/sysconfig/network-scripts/route-eth1
ADDRESS0=10.176.0.0
+5 -22
View File
@@ -43,14 +43,11 @@ mac_address=bc:76:4e:12:a4:bb
# Automatically generated, do not edit
[connection]
id=eth4.25
type=802-3-ethernet
type=vlan
[ipv4]
method=auto
[802-3-ethernet]
mac_address=bc:76:4e:12:a4:bc
[vlan]
id=25
parent=ethwithvlan
@@ -58,14 +55,11 @@ parent=ethwithvlan
# Automatically generated, do not edit
[connection]
id=eth4.26
type=802-3-ethernet
type=vlan
[ipv4]
method=auto
[802-3-ethernet]
mac_address=bc:76:4e:12:a4:bd
[vlan]
id=26
parent=ethwithvlan
@@ -99,14 +93,10 @@ slave-type=bond
# Automatically generated, do not edit
[connection]
id=bond0
type=802-3-ethernet
type=bond
[ipv4]
method=auto
[802-3-ethernet]
mac_address=bc:76:4e:05:7b:13
type=bond
### Write /etc/NetworkManager/system-connections/eth7.nmconnection
# Automatically generated, do not edit
[connection]
@@ -137,26 +127,19 @@ slave-type=bond
# Automatically generated, do not edit
[connection]
id=bond1
type=802-3-ethernet
type=bond
[ipv4]
method=manual
[802-3-ethernet]
mac_address=bc:76:4e:05:7b:15
type=bond
### Write /etc/NetworkManager/system-connections/bond1.27.nmconnection
# Automatically generated, do not edit
[connection]
id=bond1.27
type=802-3-ethernet
type=vlan
[ipv4]
method=auto
[802-3-ethernet]
mac_address=bc:76:4e:12:a4:be
[vlan]
id=27
parent=bond1
+5 -5
View File
@@ -38,7 +38,7 @@ BOOTPROTO=dhcp
HWADDR=bc:76:4e:12:a4:bc
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Vlan
VLAN=yes
### Write /etc/sysconfig/network-scripts/ifcfg-eth4.26
# Automatically generated, do not edit
@@ -47,7 +47,7 @@ BOOTPROTO=dhcp
HWADDR=bc:76:4e:12:a4:bd
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Vlan
VLAN=yes
### Write /etc/sysconfig/network-scripts/route-eth1
ADDRESS0=10.176.0.0
@@ -83,7 +83,7 @@ BOOTPROTO=dhcp
HWADDR=bc:76:4e:05:7b:13
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Bond
### Write /etc/sysconfig/network-scripts/ifcfg-eth7
# Automatically generated, do not edit
DEVICE=eth7
@@ -111,7 +111,7 @@ BOOTPROTO=none
HWADDR=bc:76:4e:05:7b:15
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Bond
### Write /etc/sysconfig/network-scripts/ifcfg-bond1.27
# Automatically generated, do not edit
DEVICE=bond1.27
@@ -119,5 +119,5 @@ BOOTPROTO=dhcp
HWADDR=bc:76:4e:12:a4:be
ONBOOT=yes
%NM_CONTROLLED%
TYPE=Ethernet
TYPE=Vlan
VLAN=yes
+119
View File
@@ -347,3 +347,122 @@ class TestGlean(base.BaseTestCase):
with mock.patch('glean.systemlock.Lock'):
self._assert_distro_provider(
self.distro, self.style, interfaces[self.style], use_nm=True)
def test_networkd_physical_ethernet_no_netdev(self):
"""Test that physical ethernet interfaces do not generate .netdev files
Only VLAN and bond interfaces should generate .netdev files.
Physical ethernet interfaces should only have .network files.
Uses naming patterns to identify interface types:
- VLANs: "{parent}-vlan{vlan_id}"
- Bonds: "bondX" where X is digit (from link['id'])
- Physical: "ethX" where X is digit (from sys_interfaces[mac])
"""
# Only test for networkd distro
if self.distro != 'networkd':
self.skipTest("networkd-specific test")
# Run the actual code
with mock.patch('glean.systemlock.Lock'):
self._assert_distro_provider(self.distro, self.style,
interfaces[self.style])
found = False
# Check what was generated by examining file_handle_mocks
for file_path in self.file_handle_mocks.keys():
if (file_path.startswith('/etc/systemd/network/') and
file_path.endswith('.network')):
iface_name = file_path.replace(
'/etc/systemd/network/', '').replace('.network', '')
if 'bond' not in iface_name and '-vlan' not in iface_name:
netdev_path = f'/etc/systemd/network/{iface_name}.netdev'
self.assertNotIn(netdev_path, self.file_handle_mocks,
f"Physical ethernet "
f"interface {iface_name} "
f"should NOT generate .netdev")
found = True
if not found:
self.skipTest("No physical interfaces to check for netdev files")
def test_networkd_vlan_generates_netdev(self):
"""Test that VLAN interfaces generate .netdev files
VLANs should generate .netdev files.
Uses naming pattern: "{parent}-vlan{vlan_id}"
"""
# Only test for networkd distro
if self.distro != 'networkd':
self.skipTest("networkd-specific test")
# Run the actual code
with mock.patch('glean.systemlock.Lock'):
self._assert_distro_provider(self.distro, self.style,
interfaces[self.style])
# Check what was generated by examining file_handle_mocks
found_vlan_with_netdev = False
for file_path in self.file_handle_mocks.keys():
if (file_path.startswith('/etc/systemd/network/') and
file_path.endswith('.network')):
iface_name = file_path.replace(
'/etc/systemd/network/', '').replace('.network', '')
# Identify VLAN by naming pattern
is_vlan = ('-vlan' in iface_name and
iface_name.split('-vlan')[1].isdigit())
if is_vlan:
netdev_file = (
f'/etc/systemd/network/{iface_name}.netdev')
self.assertIn(
netdev_file, self.file_handle_mocks,
f"VLAN interface {iface_name} should "
f"generate .netdev")
found_vlan_with_netdev = True
# Ensure we actually tested VLANs
if not found_vlan_with_netdev:
self.skipTest("No VLAN interfaces found to validate")
def test_networkd_bond_generates_netdev(self):
"""Test that bond interfaces generate .netdev files
Bonds should generate .netdev files.
Uses naming pattern: "bondX" where X is digit (from link['id'])
"""
# Only test for networkd distro
if self.distro != 'networkd':
self.skipTest("networkd-specific test")
# Run the actual code
with mock.patch('glean.systemlock.Lock'):
self._assert_distro_provider(self.distro, self.style,
interfaces[self.style])
# Check what was generated by examining file_handle_mocks
found_bond_with_netdev = False
for file_path in self.file_handle_mocks.keys():
if (file_path.startswith('/etc/systemd/network/') and
file_path.endswith('.network')):
iface_name = file_path.replace(
'/etc/systemd/network/', '').replace('.network', '')
# Identify bond by naming pattern
is_bond = (iface_name.startswith('bond') and
iface_name[4:].isdigit())
if is_bond:
netdev_file = (
f'/etc/systemd/network/{iface_name}.netdev')
self.assertIn(
netdev_file, self.file_handle_mocks,
f"Bond interface {iface_name} should "
f"generate .netdev")
found_bond_with_netdev = True
# Ensure we actually tested bonds
if not found_bond_with_netdev:
self.skipTest("No bond interfaces found to validate")