diff --git a/library/network_connections.py b/library/network_connections.py index 2db3737..efb7b57 100755 --- a/library/network_connections.py +++ b/library/network_connections.py @@ -760,6 +760,43 @@ class ArgValidator_DictIP(ArgValidatorDict): raise ValidationError(name, '"dhcp4_send_hostname" is only valid if "dhcp4" is enabled') return result +class ArgValidator_DictEthernet(ArgValidatorDict): + def __init__(self): + ArgValidatorDict.__init__(self, + name = 'ethernet', + nested = [ + ArgValidatorBool('autoneg', default_value = None), + ArgValidatorNum ('speed', val_min = 0, val_max = 0xFFFFFFFF, default_value = 0), + ArgValidatorStr ('duplex', enum_values = ['half', 'full']), + ], + default_value = ArgValidator.MISSING, + ) + + def get_default_ethernet(self): + return { + 'autoneg': None, + 'speed': 0, + 'duplex': None, + } + + def _validate_post(self, value, name, result): + has_speed_or_duplex = result['speed'] != 0 \ + or result['duplex'] is not None + if result['autoneg'] is None: + if has_speed_or_duplex: + result['autoneg'] = False + elif result['autoneg']: + if has_speed_or_duplex: + raise ValidationError(name, 'cannot specify "%s" with "autoneg" enabled' % ('duplex' if result['duplex'] is not None else 'speed')) + else: + if not has_speed_or_duplex: + raise ValidationError(name, 'need to specify "duplex" and "speed" with "autoneg" enabled') + if ( has_speed_or_duplex \ + and ( result['speed'] == 0 \ + or result['duplex'] is None)): + raise ValidationError(name, 'need to specify both "speed" and "duplex" with "autoneg" disabled') + return result + class ArgValidator_DictBond(ArgValidatorDict): VALID_MODES = [ 'balance-rr', 'active-backup', 'balance-xor', 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb'] @@ -810,6 +847,7 @@ class ArgValidator_DictConnection(ArgValidatorDict): ArgValidatorStr ('infiniband_transport_mode', enum_values = ['datagram', 'connected']), ArgValidatorNum ('infiniband_p_key', val_min = -1, val_max = 0xFFFF, default_value = None), ArgValidator_DictIP(), + ArgValidator_DictEthernet(), ArgValidator_DictBond(), ], default_value = dict, @@ -939,6 +977,13 @@ class ArgValidator_DictConnection(ArgValidatorDict): if 'bond' in result: raise ValidationError(name + '.bond', '"bond" settings are not allowed for "type" "%s"' % (result['type'])) + if result['type'] in ['ethernet', 'vlan', 'bridge', 'bond', 'team']: + if 'ethernet' not in result: + result['ethernet'] = self.nested['ethernet'].get_default_ethernet() + else: + if 'ethernet' in result: + raise ValidationError(name + '.ethernet', '"ethernet" settings are not allowed for "type" "%s"' % (result['type'])) + for k in VALID_FIELDS: if k in result: continue @@ -1179,6 +1224,15 @@ class IfcfgUtil: if connection['mtu']: ifcfg['MTU'] = str(connection['mtu']) + if 'ethernet' in connection: + if connection['ethernet']['autoneg'] is not None: + if connection['ethernet']['autoneg']: + s = 'autoneg on' + else: + s = 'autoneg off speed %s duplex %s' % (connection['ethernet']['speed'], + connection['ethernet']['duplex']) + ifcfg['ETHTOOL_OPTS'] = s + if connection['master'] is not None: m = ArgUtil.connection_find_master(connection['master'], connections, idx) if connection['slave_type'] == 'bridge': @@ -1553,6 +1607,13 @@ class NMUtil: else: raise MyError('unsupported type %s' % (connection['type'])) + if 'ethernet' in connection: + if connection['ethernet']['autoneg'] is not None: + s_wired = self.connection_ensure_setting(con, NM.SettingWired) + s_wired.set_property(NM.SETTING_WIRED_AUTO_NEGOTIATE, connection['ethernet']['autoneg']) + s_wired.set_property(NM.SETTING_WIRED_DUPLEX, connection['ethernet']['duplex']) + s_wired.set_property(NM.SETTING_WIRED_SPEED, connection['ethernet']['speed']) + if connection['mtu']: if connection['type'] == 'infiniband': s_infiniband = self.connection_ensure_setting(con, NM.SettingInfiniband) diff --git a/library/test_network_connections.py b/library/test_network_connections.py index 9587d26..0ee5376 100755 --- a/library/test_network_connections.py +++ b/library/test_network_connections.py @@ -249,6 +249,11 @@ class TestValidator(unittest.TestCase): 'dns': [], 'dns_search': [], }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -299,6 +304,11 @@ class TestValidator(unittest.TestCase): 'route_metric6': None, 'dhcp4_send_hostname': None, }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -367,6 +377,11 @@ class TestValidator(unittest.TestCase): 'rule_append_only': False, 'route': [], }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'state': 'up', 'mtu': 1450, 'check_iface_exists': True, @@ -431,6 +446,11 @@ class TestValidator(unittest.TestCase): 'gateway4': None, 'dns': [] }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'state': 'up', 'mtu': 1450, 'check_iface_exists': True, @@ -485,6 +505,11 @@ class TestValidator(unittest.TestCase): }, ], }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -561,6 +586,11 @@ class TestValidator(unittest.TestCase): 'rule_append_only': False, 'route': [], }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -596,6 +626,11 @@ class TestValidator(unittest.TestCase): 'gateway4': None, 'dns': [] }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -655,6 +690,11 @@ class TestValidator(unittest.TestCase): 'rule_append_only': False, 'route': [], }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -706,6 +746,11 @@ class TestValidator(unittest.TestCase): 'rule_append_only': False, 'route': [], }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -762,6 +807,11 @@ class TestValidator(unittest.TestCase): 'dns': [], 'dns_search': [], }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': 'aa:bb:cc:dd:ee:ff', 'mtu': None, 'zone': None, @@ -810,6 +860,11 @@ class TestValidator(unittest.TestCase): 'route_metric6': None, 'dhcp4_send_hostname': None, }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -856,6 +911,11 @@ class TestValidator(unittest.TestCase): 'route_metric6': None, 'dhcp4_send_hostname': None, }, + 'ethernet': { + 'autoneg': False, + 'duplex': 'half', + 'speed': 400, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -872,11 +932,34 @@ class TestValidator(unittest.TestCase): }, ], [ - { 'name': '5', - 'state': 'up', - 'type': 'ethernet', - 'ip': { - }, + { + 'name': '5', + 'state': 'up', + 'type': 'ethernet', + 'ip': { + }, + 'ethernet': { + 'duplex': 'half', + 'speed': 400, + }, + }, + ], + initscripts_dict_expected = [ + { + 'ifcfg': { + 'BOOTPROTO': 'dhcp', + 'ETHTOOL_OPTS': 'autoneg off speed 400 duplex half', + 'IPV6INIT': 'yes', + 'IPV6_AUTOCONF': 'yes', + 'NM_CONTROLLED': 'no', + 'ONBOOT': 'yes', + 'TYPE': 'Ethernet', + }, + 'keys': None, + 'route': None, + 'route6': None, + 'rule': None, + 'rule6': None, }, ], ) @@ -919,6 +1002,11 @@ class TestValidator(unittest.TestCase): 'route_metric6': None, 'dhcp4_send_hostname': None, }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': None, @@ -1018,6 +1106,11 @@ class TestValidator(unittest.TestCase): 'route_metric6': None, 'dhcp4_send_hostname': None, }, + 'ethernet': { + 'autoneg': None, + 'duplex': None, + 'speed': 0, + }, 'mac': None, 'mtu': None, 'zone': 'external',