@@ -82,6 +82,9 @@ tpm_folder = '/tmp/vyos_tpm_test'
8282tpm_sock = f'{ tpm_folder } /swtpm-sock'
8383qemu_name = 'VyOS-QEMU'
8484
85+ # RFC7042 section 2.1.2 MAC addresses used for documentation
86+ macbase = '00:00:5E:00:53'
87+
8588test_timeout = 5 * 3600 # 5 hours (in seconds) to complete individual testcases
8689
8790op_mode_prompt = r'vyos@vyos:~\$'
@@ -137,6 +140,8 @@ parser.add_argument('--configtest', help='Execute load/commit config tests',
137140 action = 'store_true' , default = False )
138141parser .add_argument ('--tpmtest' , help = 'Execute TPM encrypted config tests' ,
139142 action = 'store_true' , default = False )
143+ parser .add_argument ('--ifnametest' , help = 'Execute interface naming/hw-id persistence tests' ,
144+ action = 'store_true' , default = False )
140145parser .add_argument ('--sbtest' , help = 'Execute Secure Boot tests' ,
141146 action = 'store_true' , default = False )
142147parser .add_argument ('--cloud-init' , help = 'Execute cloud-init tests' ,
@@ -268,9 +273,6 @@ def get_qemu_cmd(name, enable_uefi, disk_img, raid=None, iso_img=None, tpm=False
268273 else :
269274 nested_cdrom = f'{ nested_cdrom } -device ide-cd,bus=achi0.1,{ drive_settings } '
270275
271- # RFC7042 section 2.1.2 MAC addresses used for documentation
272- macbase = '00:00:5E:00:53'
273-
274276 # Set QEmu disk image format - this differs if VyOS was installed via smoketest
275277 # or we use an already ewxisting image
276278 disk_format = 'qcow2' if args .disk .endswith ('.qcow2' ) else 'raw'
@@ -290,10 +292,10 @@ def get_qemu_cmd(name, enable_uefi, disk_img, raid=None, iso_img=None, tpm=False
290292 -netdev user,id=n1 -device virtio-net-pci,netdev=n1,mac={ macbase } :01,romfile="",host_mtu=1500 \
291293 -netdev user,id=n2 -device virtio-net-pci,netdev=n2,mac={ macbase } :02,romfile="",host_mtu=1500 \
292294 -netdev user,id=n3 -device virtio-net-pci,netdev=n3,mac={ macbase } :03,romfile="",host_mtu=1500 \
293- -netdev user,id=n4 -device virtio-net-pci ,netdev=n4,mac={ macbase } :04,romfile="" \
294- -netdev user,id=n5 -device virtio-net-pci ,netdev=n5,mac={ macbase } :05,romfile="" \
295- -netdev user,id=n6 -device virtio-net-pci ,netdev=n6,mac={ macbase } :06,romfile="" \
296- -netdev user,id=n7 -device virtio-net-pci ,netdev=n7,mac={ macbase } :07,romfile="" \
295+ -netdev user,id=n4 -device e1000e ,netdev=n4,mac={ macbase } :04,romfile="" \
296+ -netdev user,id=n5 -device e1000e ,netdev=n5,mac={ macbase } :05,romfile="" \
297+ -netdev user,id=n6 -device vmxnet3 ,netdev=n6,mac={ macbase } :06,romfile="" \
298+ -netdev user,id=n7 -device vmxnet3 ,netdev=n7,mac={ macbase } :07,romfile="" \
297299 -device virtio-scsi-pci,id=scsi0 \
298300 { cdrom } { nested_cdrom } \
299301 -drive format={ disk_format } ,file={ disk_img } ,if=none,media=disk,id=drive-hd1,readonly=off \
@@ -387,6 +389,8 @@ if args.test_image_update:
387389 _primary_modes .append ('--test-image-update' )
388390if args .tpmtest :
389391 _primary_modes .append ('--tpmtest' )
392+ if args .ifnametest :
393+ _primary_modes .append ('--ifnametest' )
390394if args .raid :
391395 _primary_modes .append ('--raid' )
392396if args .smoketest :
@@ -397,8 +401,8 @@ if args.sbtest:
397401 _primary_modes .append ('--sbtest' )
398402if len (_primary_modes ) > 1 :
399403 log .error ('Incompatible combination of testcase flags (%s): only one of '
400- '--cloud-init, --test-image-update, --tpmtest, --raid , --smoketest , '
401- '--configtest, --sbtest may be set.' , ', ' .join (_primary_modes ))
404+ '--cloud-init, --test-image-update, --tpmtest, --ifnametest , --raid , '
405+ '--smoketest, -- configtest, --sbtest may be set.' , ', ' .join (_primary_modes ))
402406 sys .exit (1 )
403407
404408if args .no_interfaces and not args .smoketest :
@@ -687,6 +691,31 @@ def basic_cli_tests(c):
687691 c .expect (f'set console_type="{ console_type } "' )
688692 c .expect (op_mode_prompt )
689693
694+ def verify_eth_mac_mapping (c , log ):
695+ """ NICs are attached with a mix of drivers (virtio/e1000e/vmxnet3, see
696+ get_qemu_cmd()) to cover different naming schemes. Regardless of
697+ driver, udev must always enumerate them in ascending order: eth0
698+ carries mac0, eth1 mac1, ... up to eth7 mac7 - never scrambled. """
699+ log .info ('Verify eth0..eth7 are enumerated in ascending MAC order' )
700+ c .sendline ('ip -json link show | jq -r \' .[] | select(.ifname|test("^eth[0-9]+$")) | "\(.ifname) \(.address)"\' ' )
701+ c .expect (op_mode_prompt )
702+ lines = [l .strip () for l in c .before .decode (errors = 'replace' ).splitlines () if l .strip ()]
703+
704+ macs = {}
705+ for line in lines :
706+ parts = line .split ()
707+ if len (parts ) == 2 and re .fullmatch (r'eth\d+' , parts [0 ]):
708+ macs [parts [0 ]] = parts [1 ].lower ()
709+
710+ for i in range (8 ):
711+ ifname = f'eth{ i } '
712+ expected_mac = f'{ macbase } :{ i :02x} ' .lower ()
713+ if ifname not in macs :
714+ raise Exception (f'Interface { ifname } not found on installed system' )
715+ if macs [ifname ] != expected_mac :
716+ raise Exception (f'Interface { ifname } has MAC { macs [ifname ]} , expected { expected_mac } - naming race?' )
717+ log .info ('eth0..eth7 MAC mapping verified' )
718+
690719def _image_update_cli_sequence (c , log , new_image_name , server_bind_host = '127.0.0.1' , use_vrf = False ):
691720 """One add-system-image/delete cycle for nested ISO over HTTP (optional Linux VRF + VyOS vrf arg)."""
692721 url = f'http://{ server_bind_host } :{ NESTED_HTTP_SERV_PORT } /{ NESTED_INNER_ISO_NAME } '
@@ -1067,6 +1096,11 @@ try:
10671096 log .info ('Basic CLI configuration mode test' )
10681097 basic_cli_tests (c )
10691098
1099+ #################################################
1100+ # Verify NIC driver mix did not scramble interface naming
1101+ #################################################
1102+ verify_eth_mac_mapping (c , log )
1103+
10701104 #################################################
10711105 # Verify /etc/os-release via lsb_release
10721106 #################################################
@@ -1274,6 +1308,48 @@ try:
12741308 c .sendline ('exit' )
12751309 c .expect (op_mode_prompt )
12761310
1311+ elif args .ifnametest :
1312+ # A missing/deleted hw-id binding, or a fully deleted interface
1313+ # config, must not change the eth0..eth7 <-> MAC mapping after
1314+ # the next reboot (regression check for the boot-time naming race).
1315+ log .info ('Running interface naming/hw-id persistence tests' )
1316+ del_idx , hwid_idx = random .sample (range (8 ), 2 )
1317+ log .info (f'Deleting eth{ del_idx } entirely, removing hw-id only on eth{ hwid_idx } ' )
1318+
1319+ c .sendline ('configure' )
1320+ c .expect (cfg_mode_prompt )
1321+ c .sendline (f'delete interfaces ethernet eth{ del_idx } ' )
1322+ c .expect (cfg_mode_prompt )
1323+ c .sendline (f'delete interfaces ethernet eth{ hwid_idx } hw-id' )
1324+ c .expect (cfg_mode_prompt )
1325+ c .sendline ('commit' )
1326+ c .expect (cfg_mode_prompt )
1327+ c .sendline ('save' )
1328+ c .expect (cfg_mode_prompt )
1329+ c .sendline ('exit' )
1330+ c .expect (op_mode_prompt )
1331+
1332+ log .info ('Rebooting to verify interface naming survives across reboot' )
1333+ c .sendline ('reboot now' )
1334+ waitForLogin (c , log )
1335+ loginVM (c , log )
1336+
1337+ log .info ('Collecting interface naming diagnostics' )
1338+ c .sendline ('show configuration commands | match "hw-id"' )
1339+ c .expect (op_mode_prompt )
1340+ c .sendline ('show interfaces ethernet' )
1341+ c .expect (op_mode_prompt )
1342+ c .sendline ('ip link show' )
1343+ c .expect (op_mode_prompt )
1344+ c .sendline ('show log | match "hw-id"' )
1345+ c .expect (op_mode_prompt )
1346+ c .sendline ('cat /run/vyos-net-name-resolve.json 2>/dev/null || true' )
1347+ c .expect (op_mode_prompt )
1348+ c .sendline ('show log kernel | match "eth"' )
1349+ c .expect (op_mode_prompt )
1350+
1351+ verify_eth_mac_mapping (c , log )
1352+
12771353 elif args .raid :
12781354 # Verify RAID subsystem - by deleting a disk and re-create the array
12791355 # from scratch
0 commit comments