diff --git a/src/linuxmuster_base7/functions/network.py b/src/linuxmuster_base7/functions/network.py index fe959c0..9f89354 100644 --- a/src/linuxmuster_base7/functions/network.py +++ b/src/linuxmuster_base7/functions/network.py @@ -5,7 +5,7 @@ # network interface helpers # Signed-off by: thomas@linuxmuster.net # Assisted by : Claude -# Date : 20260921 +# Date : 20261001 # import csv @@ -19,6 +19,10 @@ import environment import netifaces +# reserved in hardware group names: separates school and group in the +# school-qualified LINBO group id (+) +LINBO_GROUP_SEPARATOR = '+' + # test if ip matches subnet def ipMatchSubnet(ip, subnet): @@ -111,7 +115,9 @@ def validateDeviceRow(row, school='default-school'): Validate and parse a device row from devices.csv. Extracts device fields, applies hostname transformation for non-default - schools, and validates hostname, MAC address, and IP address. + schools, and validates group, hostname, MAC address, and IP address. + A group containing '+' is invalid, because '+' separates school and + group in the school-qualified LINBO group id. Args: row: CSV row as list of fields @@ -136,6 +142,11 @@ def validateDeviceRow(row, school='default-school'): ip = row[4] pxe = row[10] + # '+' is reserved for the school-qualified LINBO group id + # (+), so a group must not contain it + if LINBO_GROUP_SEPARATOR in group: + return False, None + # Validate hostname and MAC address if not isValidHostname(hostname) or not isValidMac(mac): return False, None diff --git a/tests/test_functions_network.py b/tests/test_functions_network.py index 357728d..245df03 100644 --- a/tests/test_functions_network.py +++ b/tests/test_functions_network.py @@ -5,7 +5,7 @@ # validation. # Signed-off by: thomas@linuxmuster.net # Assisted by : Claude -# Date : 20260921 +# Date : 20261001 # """ Regression tests for linuxmuster_base7.functions.network.isValidHostIpv4(). @@ -23,7 +23,7 @@ pytest.importorskip('environment', reason='requires linuxmuster-common (environment.py) on sys.path') -from linuxmuster_base7.functions import isValidHostIpv4 # noqa: E402 +from linuxmuster_base7.functions import isValidHostIpv4, validateDeviceRow # noqa: E402 @pytest.mark.parametrize('ip, expected', [ @@ -77,3 +77,26 @@ def test_subnet_aware_network_broadcast_check(ip, subnet, expected): def test_malformed_subnet_rejects(): assert isValidHostIpv4('10.0.0.1', 'not-a-subnet') is False + + +def _device_row(group='raum101', hostname='pc01', mac='AA:BB:CC:DD:EE:01', ip='10.0.0.11'): + return ['room', hostname, group, mac, ip, '', '', '', '', '', '1'] + + +@pytest.mark.parametrize('school', ['default-school', 'abc']) +def test_valid_group_is_accepted(school): + is_valid, device = validateDeviceRow(_device_row('raum101'), school) + assert is_valid is True + assert device['group'] == 'raum101' + + +@pytest.mark.parametrize('group', ['abc+raum101', 'raum+101', '+raum101', 'raum101+']) +@pytest.mark.parametrize('school', ['default-school', 'abc']) +def test_group_with_plus_is_rejected(group, school): + assert validateDeviceRow(_device_row(group), school) == (False, None) + + +def test_plus_in_group_does_not_modify_the_row(): + row = _device_row('abc+raum101') + validateDeviceRow(row, 'abc') + assert row == _device_row('abc+raum101')