diff options
-rw-r--r-- | data/templates/chrony/chrony.conf.j2 | 10 | ||||
-rw-r--r-- | interface-definitions/service_dhcpv6-server.xml.in | 2 | ||||
-rw-r--r-- | interface-definitions/service_ntp.xml.in | 36 | ||||
-rw-r--r--[-rwxr-xr-x] | op-mode-definitions/generate-system-login-user.xml.in | 0 | ||||
-rw-r--r-- | python/vyos/qos/base.py | 50 | ||||
-rw-r--r-- | python/vyos/utils/network.py | 2 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_service_dhcp-server.py | 13 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_service_ntp.py | 33 | ||||
-rwxr-xr-x | src/conf_mode/service_dhcp-server.py | 21 | ||||
-rwxr-xr-x | src/conf_mode/service_ntp.py | 4 |
10 files changed, 134 insertions, 37 deletions
diff --git a/data/templates/chrony/chrony.conf.j2 b/data/templates/chrony/chrony.conf.j2 index d02fbf71d..e3f078fdc 100644 --- a/data/templates/chrony/chrony.conf.j2 +++ b/data/templates/chrony/chrony.conf.j2 @@ -21,7 +21,17 @@ ntsdumpdir /run/chrony pidfile {{ config_file | replace('.conf', '.pid') }} # Determine when will the next leap second occur and what is the current offset +{% if leap_second is vyos_defined('timezone') %} leapsectz right/UTC +{% elif leap_second is vyos_defined('ignore') %} +leapsecmode ignore +{% elif leap_second is vyos_defined('smear') %} +leapsecmode slew +maxslewrate 1000 +smoothtime 400 0.001024 leaponly +{% elif leap_second is vyos_defined('system') %} +leapsecmode system +{% endif %} user {{ user }} diff --git a/interface-definitions/service_dhcpv6-server.xml.in b/interface-definitions/service_dhcpv6-server.xml.in index b838f42e0..a64da83ae 100644 --- a/interface-definitions/service_dhcpv6-server.xml.in +++ b/interface-definitions/service_dhcpv6-server.xml.in @@ -11,7 +11,7 @@ #include <include/generic-disable-node.xml.i> <node name="global-parameters"> <properties> - <help>Additional global parameters for DHCPv6 server</help> + <help>Global options sent to all clients</help> </properties> <children> #include <include/name-server-ipv6.xml.i> diff --git a/interface-definitions/service_ntp.xml.in b/interface-definitions/service_ntp.xml.in index 65a45d7a1..c057b62b5 100644 --- a/interface-definitions/service_ntp.xml.in +++ b/interface-definitions/service_ntp.xml.in @@ -9,6 +9,38 @@ <priority>900</priority> </properties> <children> + #include <include/allow-client.xml.i> + #include <include/generic-interface.xml.i> + #include <include/listen-address.xml.i> + #include <include/interface/vrf.xml.i> + <leafNode name="leap-second"> + <properties> + <help>Leap second behavior</help> + <completionHelp> + <list>ignore smear system timezone</list> + </completionHelp> + <valueHelp> + <format>ignore</format> + <description>No correction is applied to the clock for the leap second</description> + </valueHelp> + <valueHelp> + <format>smear</format> + <description>Correct served time slowly be slewing instead of stepping</description> + </valueHelp> + <valueHelp> + <format>system</format> + <description>Kernel steps the system clock forward or backward</description> + </valueHelp> + <valueHelp> + <format>timezone</format> + <description>Use UTC timezone database to determine when will the next leap second occur</description> + </valueHelp> + <constraint> + <regex>(ignore|smear|system|timezone)</regex> + </constraint> + </properties> + <defaultValue>timezone</defaultValue> + </leafNode> <tagNode name="server"> <properties> <help>Network Time Protocol (NTP) server</help> @@ -56,10 +88,6 @@ </leafNode> </children> </tagNode> - #include <include/allow-client.xml.i> - #include <include/generic-interface.xml.i> - #include <include/listen-address.xml.i> - #include <include/interface/vrf.xml.i> </children> </node> </children> diff --git a/op-mode-definitions/generate-system-login-user.xml.in b/op-mode-definitions/generate-system-login-user.xml.in index 6f65c12b3..6f65c12b3 100755..100644 --- a/op-mode-definitions/generate-system-login-user.xml.in +++ b/op-mode-definitions/generate-system-login-user.xml.in diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py index d8bbfe970..a22039e52 100644 --- a/python/vyos/qos/base.py +++ b/python/vyos/qos/base.py @@ -1,4 +1,4 @@ -# Copyright 2022-2023 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2022-2024 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -14,6 +14,7 @@ # License along with this library. If not, see <http://www.gnu.org/licenses/>. import os +import jmespath from vyos.base import Warning from vyos.utils.process import cmd @@ -166,14 +167,17 @@ class QoSBase: } if rate == 'auto' or rate.endswith('%'): - speed = 10 + speed = 1000 + default_speed = speed # Not all interfaces have valid entries in the speed file. PPPoE # interfaces have the appropriate speed file, but you can not read it: # cat: /sys/class/net/pppoe7/speed: Invalid argument try: speed = read_file(f'/sys/class/net/{self._interface}/speed') if not speed.isnumeric(): - Warning('Interface speed cannot be determined (assuming 10 Mbit/s)') + Warning('Interface speed cannot be determined (assuming 1000 Mbit/s)') + if int(speed) < 1: + speed = default_speed if rate.endswith('%'): percent = rate.rstrip('%') speed = int(speed) * int(percent) // 100 @@ -223,6 +227,9 @@ class QoSBase: if 'mark' in match_config: mark = match_config['mark'] filter_cmd += f' handle {mark} fw' + if 'vif' in match_config: + vif = match_config['vif'] + filter_cmd += f' basic match "meta(vlan mask 0xfff eq {vif})"' for af in ['ip', 'ipv6']: tc_af = af @@ -298,23 +305,28 @@ class QoSBase: filter_cmd += f' flowid {self._parent:x}:{cls:x}' self._cmd(filter_cmd) + vlan_expression = "match.*.vif" + match_vlan = jmespath.search(vlan_expression, cls_config) + if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config): - filter_cmd += f' action police' - - if 'exceed' in cls_config: - action = cls_config['exceed'] - filter_cmd += f' conform-exceed {action}' - if 'not_exceed' in cls_config: - action = cls_config['not_exceed'] - filter_cmd += f'/{action}' - - if 'bandwidth' in cls_config: - rate = self._rate_convert(cls_config['bandwidth']) - filter_cmd += f' rate {rate}' - - if 'burst' in cls_config: - burst = cls_config['burst'] - filter_cmd += f' burst {burst}' + # For "vif" "basic match" is used instead of "action police" T5961 + if not match_vlan: + filter_cmd += f' action police' + + if 'exceed' in cls_config: + action = cls_config['exceed'] + filter_cmd += f' conform-exceed {action}' + if 'not_exceed' in cls_config: + action = cls_config['not_exceed'] + filter_cmd += f'/{action}' + + if 'bandwidth' in cls_config: + rate = self._rate_convert(cls_config['bandwidth']) + filter_cmd += f' rate {rate}' + + if 'burst' in cls_config: + burst = cls_config['burst'] + filter_cmd += f' burst {burst}' cls = int(cls) filter_cmd += f' flowid {self._parent:x}:{cls:x}' self._cmd(filter_cmd) diff --git a/python/vyos/utils/network.py b/python/vyos/utils/network.py index c3c419a61..b58e02d91 100644 --- a/python/vyos/utils/network.py +++ b/python/vyos/utils/network.py @@ -155,7 +155,9 @@ def is_wwan_connected(interface): """ Determine if a given WWAN interface, e.g. wwan0 is connected to the carrier network or not """ import json + from vyos.utils.dict import dict_search from vyos.utils.process import cmd + from vyos.utils.process import is_systemd_service_active if not interface.startswith('wwan'): raise ValueError(f'Specified interface "{interface}" is not a WWAN interface') diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 91ae901cd..9e8196d7a 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020-2021 VyOS maintainers and contributors +# Copyright (C) 2020-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -203,12 +203,19 @@ class TestServiceDHCPServer(VyOSUnitTestSHIM.TestCase): client_base += 1 # cannot have mappings with duplicate IP addresses + self.cli_set(pool + ['static-mapping', 'dupe1', 'mac-address', '00:50:00:00:fe:ff']) + self.cli_set(pool + ['static-mapping', 'dupe1', 'ip-address', inc_ip(subnet, 10)]) with self.assertRaises(ConfigSessionError): - self.cli_set(pool + ['static-mapping', 'dupe1', 'mac', '00:50:00:00:00:01']) - self.cli_set(pool + ['static-mapping', 'dupe1', 'ip-address', inc_ip(subnet, 10)]) self.cli_commit() self.cli_delete(pool + ['static-mapping', 'dupe1']) + # cannot have mappings with duplicate MAC addresses + self.cli_set(pool + ['static-mapping', 'dupe2', 'mac-address', '00:50:00:00:00:10']) + self.cli_set(pool + ['static-mapping', 'dupe2', 'ip-address', inc_ip(subnet, 120)]) + with self.assertRaises(ConfigSessionError): + self.cli_commit() + self.cli_delete(pool + ['static-mapping', 'dupe2']) + # commit changes self.cli_commit() diff --git a/smoketest/scripts/cli/test_service_ntp.py b/smoketest/scripts/cli/test_service_ntp.py index 5e385d5ad..ae45fe2f4 100755 --- a/smoketest/scripts/cli/test_service_ntp.py +++ b/smoketest/scripts/cli/test_service_ntp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2019-2023 VyOS maintainers and contributors +# Copyright (C) 2019-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -43,7 +43,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): self.assertFalse(process_named_running(PROCESS_NAME)) - def test_01_ntp_options(self): + def test_base_options(self): # Test basic NTP support with multiple servers and their options servers = ['192.0.2.1', '192.0.2.2'] options = ['nts', 'noselect', 'prefer'] @@ -77,7 +77,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): for pool in pools: self.assertIn(f'pool {pool} iburst', config) - def test_02_ntp_clients(self): + def test_clients(self): # Test the allowed-networks statement listen_address = ['127.0.0.1', '::1'] for listen in listen_address: @@ -107,7 +107,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): for listen in listen_address: self.assertIn(f'bindaddress {listen}', config) - def test_03_ntp_interface(self): + def test_interface(self): interfaces = ['eth0'] for interface in interfaces: self.cli_set(base_path + ['interface', interface]) @@ -124,7 +124,7 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): for interface in interfaces: self.assertIn(f'binddevice {interface}', config) - def test_04_ntp_vrf(self): + def test_vrf(self): vrf_name = 'vyos-mgmt' self.cli_set(['vrf', 'name', vrf_name, 'table', '12345']) @@ -142,5 +142,28 @@ class TestSystemNTP(VyOSUnitTestSHIM.TestCase): self.cli_delete(['vrf', 'name', vrf_name]) + def test_leap_seconds(self): + servers = ['time1.vyos.net', 'time2.vyos.net'] + for server in servers: + self.cli_set(base_path + ['server', server]) + + self.cli_commit() + + # Check generated client address configuration + # this file must be read with higher permissions + config = cmd(f'sudo cat {NTP_CONF}') + self.assertIn('leapsectz right/UTC', config) # CLI default + + for mode in ['ignore', 'system', 'smear']: + self.cli_set(base_path + ['leap-second', mode]) + self.cli_commit() + config = cmd(f'sudo cat {NTP_CONF}') + if mode != 'smear': + self.assertIn(f'leapsecmode {mode}', config) + else: + self.assertIn(f'leapsecmode slew', config) + self.assertIn(f'maxslewrate 1000', config) + self.assertIn(f'smoothtime 400 0.001024 leaponly', config) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py index 8d849b1e6..341b31dd7 100755 --- a/src/conf_mode/service_dhcp-server.py +++ b/src/conf_mode/service_dhcp-server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2023 VyOS maintainers and contributors +# Copyright (C) 2018-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -22,6 +22,7 @@ from netaddr import IPAddress from netaddr import IPRange from sys import exit +from vyos.base import DeprecationWarning from vyos.config import Config from vyos.template import render from vyos.utils.dict import dict_search @@ -149,9 +150,15 @@ def verify(dhcp): shared_networks = len(dhcp['shared_network_name']) disabled_shared_networks = 0 + common_deprecation_msg = 'are subject of removal in VyOS 1.5! Please raise a feature request for proper CLI nodes!' + if 'global_parameters' in dhcp: + DeprecationWarning(f'Additional global parameters {common_deprecation_msg}') # A shared-network requires a subnet definition for network, network_config in dhcp['shared_network_name'].items(): + if 'shared_network_parameters' in network_config: + DeprecationWarning(f'Additional shared network parameters in "{network}" {common_deprecation_msg}') + if 'disable' in network_config: disabled_shared_networks += 1 @@ -160,6 +167,9 @@ def verify(dhcp): 'lease subnet must be configured.') for subnet, subnet_config in network_config['subnet'].items(): + if 'subnet_parameters' in subnet_config: + DeprecationWarning(f'Additional subnet parameters in "{subnet}" {common_deprecation_msg}') + # All delivered static routes require a next-hop to be set if 'static_route' in subnet_config: for route, route_option in subnet_config['static_route'].items(): @@ -215,6 +225,7 @@ def verify(dhcp): if 'static_mapping' in subnet_config: # Static mappings require just a MAC address (will use an IP from the dynamic pool if IP is not set) used_ips = [] + used_mac = [] for mapping, mapping_config in subnet_config['static_mapping'].items(): if 'ip_address' in mapping_config: if ip_address(mapping_config['ip_address']) not in ip_network(subnet): @@ -226,10 +237,14 @@ def verify(dhcp): f'within shared-network "{network}, {subnet}"!') if mapping_config['ip_address'] in used_ips: - raise ConfigError(f'Configured IP address for static mapping "{mapping}" exists on another static mapping') - + raise ConfigError(f'Configured IP address for static mapping "{mapping}" already exists on another static mapping') used_ips.append(mapping_config['ip_address']) + if 'mac_address' in mapping_config: + if mapping_config['mac_address'] in used_mac: + raise ConfigError(f'Configured MAC address for static mapping "{mapping}" already exists on another static mapping') + used_mac.append(mapping_config['mac_address']) + # There must be one subnet connected to a listen interface. # This only counts if the network itself is not disabled! if 'disable' not in network_config: diff --git a/src/conf_mode/service_ntp.py b/src/conf_mode/service_ntp.py index 1cc23a7df..f11690ee6 100755 --- a/src/conf_mode/service_ntp.py +++ b/src/conf_mode/service_ntp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2023 VyOS maintainers and contributors +# Copyright (C) 2018-2024 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -42,7 +42,7 @@ def get_config(config=None): if not conf.exists(base): return None - ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) + ntp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, with_defaults=True) ntp['config_file'] = config_file ntp['user'] = user_group |