diff --git a/data/templates/dhcp-server/kea-dhcp4.conf.j2 b/data/templates/dhcp-server/kea-dhcp4.conf.j2 index bf37b94f66..59f04e506d 100644 --- a/data/templates/dhcp-server/kea-dhcp4.conf.j2 +++ b/data/templates/dhcp-server/kea-dhcp4.conf.j2 @@ -1,5 +1,17 @@ { "Dhcp4": { + "client-classes": [ + { + "name": "shoretel", + "test": "option[60].hex == 'ShoreTel IP Phone'", + "only-if-required": true + }, + { + "name": "ubnt", + "test": "option[60].hex == 'ubnt'", + "only-if-required": true + } + ], "interfaces-config": { {% if listen_address is vyos_defined %} "interfaces": {{ listen_address | kea_address_json }}, @@ -48,7 +60,7 @@ "code": 1, "type": "ipv4-address", "space": "ubnt" - } + }, ], "hooks-libraries": [ {% if high_availability is vyos_defined %} diff --git a/interface-definitions/include/dhcp/option-v4.xml.i b/interface-definitions/include/dhcp/option-v4.xml.i index bd6fc6043b..41f4208e4b 100644 --- a/interface-definitions/include/dhcp/option-v4.xml.i +++ b/interface-definitions/include/dhcp/option-v4.xml.i @@ -232,6 +232,25 @@ + + + ShoreTel-specfic parameters + + + + + ShoreTel phone configuration + + text + Comma-separated parameters for ShoreTel phone configuration + + + + + + + + diff --git a/python/vyos/kea.py b/python/vyos/kea.py index addfdba496..7c52b67891 100644 --- a/python/vyos/kea.py +++ b/python/vyos/kea.py @@ -20,6 +20,7 @@ from vyos.template import is_ipv6 from vyos.template import isc_static_route from vyos.template import netmask_from_cidr +from vyos.template import kea_client_classes from vyos.utils.dict import dict_search_args from vyos.utils.file import file_permissions from vyos.utils.process import run @@ -114,6 +115,13 @@ def kea_parse_subnet(subnet, config): if 'bootfile_server' in config['option']: out['next-server'] = config['option']['bootfile_server'] + if kea_client_classes(config): + if kea_client_classes(config,'shoretel'): + if not('required-client-classes' in config): + config['require-client-classes'] = ['shoretel'] + else: + config['required-client-classes'].append('shoretel') + if 'ignore_client_id' in config: out['match-client-id'] = False @@ -138,6 +146,13 @@ def kea_parse_subnet(subnet, config): if 'bootfile_server' in range_config['option']: pool['next-server'] = range_config['option']['bootfile_server'] + if kea_client_classes(config): + if kea_client_classes(config,'shoretel'): + if not('required-client-classes' in config): + pool['require-client-classes'] = ['shoretel'] + else: + pool['require-client-classes'].append('shoretel') + pools.append(pool) out['pools'] = pools @@ -169,6 +184,13 @@ def kea_parse_subnet(subnet, config): if 'bootfile_server' in host_config['option']: reservation['next-server'] = host_config['option']['bootfile_server'] + if kea_client_classes(config): + if kea_client_classes(config,'shoretel'): + if not('required-client-classes' in config): + reservation['require-client-classes'] = ['shoretel'] + else: + reservation['require-client-classes'].append('shoretel') + reservations.append(reservation) out['reservations'] = reservations diff --git a/python/vyos/template.py b/python/vyos/template.py index be9f781a61..60f44c7519 100755 --- a/python/vyos/template.py +++ b/python/vyos/template.py @@ -48,9 +48,9 @@ def _get_environment(location=None): loc_loader=FileSystemLoader(location) env = Environment( # Don't check if template files were modified upon re-rendering - auto_reload=False, + auto_reload=True, # Cache up to this number of templates for quick re-rendering - cache_size=100, + cache_size=0, loader=loc_loader, trim_blocks=True, undefined=ChainableUndefined, @@ -871,6 +871,32 @@ def kea_high_availability_json(config): return dumps(data) +@register_filter('kea_client_classes') +def kea_client_classes(shared_networks, client_class=None): + """ Check shared_networks/subnets/ranges (pools)/static_mappings + for instances of client_class in vendor_option. If client_class + is falsey, return True if any vendor_options are present + """ + result = False + path = ['option','vendor_option'] + if client_class: + path.append(client_class) + + for name, config in shared_networks.items(): + if 'disable' in config: + continue + if result := dict_search_args(config, path): + break + + if 'subnet' in config: + for subnet, subnet_config in config['subnet'].items(): + if 'disable' in config: + continue + if result := dict_search_args(config, *path): + break + + return result + @register_filter('kea_shared_network_json') def kea_shared_network_json(shared_networks): from vyos.kea import kea_parse_options @@ -897,12 +923,20 @@ def kea_shared_network_json(shared_networks): if 'bootfile_server' in config['option']: network['next-server'] = config['option']['bootfile_server'] + if kea_client_classes(config): + if kea_client_classes(config, 'shoretel'): + network['require-client-classes'] = ['shoretel'] + if 'subnet' in config: for subnet, subnet_config in config['subnet'].items(): if 'disable' in subnet_config: continue network['subnet4'].append(kea_parse_subnet(subnet, subnet_config)) + if kea_client_classes(subnet_config): + if kea_client_classes(subnet_config, 'shoretel'): + network['subnet4'][-1]['require-client-classes'] = ['shoretel'] + out.append(network) return dumps(out, indent=4) diff --git a/smoketest/scripts/cli/test_service_dhcp-server.py b/smoketest/scripts/cli/test_service_dhcp-server.py index 46c4e25a1f..a430085808 100755 --- a/smoketest/scripts/cli/test_service_dhcp-server.py +++ b/smoketest/scripts/cli/test_service_dhcp-server.py @@ -170,6 +170,8 @@ def test_dhcp_single_pool_options(self): wpad = 'http://wpad.vyos.io/foo/bar' server_identifier = bootfile_server ipv6_only_preferred = '300' + shoretel_space = 'ShoreTel IP Phone' + shoretel_server = 'ftpservers=10.0.0.1,country=1,language=1,layer2tagging=1,vlanid=123' pool = base_path + ['shared-network-name', shared_net_name, 'subnet', subnet] self.cli_set(pool + ['subnet-id', '1']) @@ -193,6 +195,7 @@ def test_dhcp_single_pool_options(self): self.cli_set(pool + ['option', 'static-route', '10.0.0.0/24', 'next-hop', '192.0.2.1']) self.cli_set(pool + ['option', 'ipv6-only-preferred', ipv6_only_preferred]) self.cli_set(pool + ['option', 'time-zone', 'Europe/London']) + self.cli_set(pool + ['option', 'vendor-option', 'shoretel', 'shoretel-server', shoretel_server]) self.cli_set(pool + ['range', '0', 'start', range_0_start]) self.cli_set(pool + ['range', '0', 'stop', range_0_stop]) @@ -278,6 +281,12 @@ def test_dhcp_single_pool_options(self): ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], {'name': 'tcode', 'data': 'Europe/London'}) + # Vendor options + self.verify_config_object( + obj, + ['Dhcp4', 'shared-networks', 0, 'subnet4', 0, 'option-data'], + {'name': 'shoretel-server', 'data': shoretel_server, 'space': shoretel_space}) + # Verify pools self.verify_config_object( obj, diff --git a/src/validators/shoretel-server b/src/validators/shoretel-server new file mode 100644 index 0000000000..51ec67e08b --- /dev/null +++ b/src/validators/shoretel-server @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +### how far to get into vendor option validation? +### ftpservers=10.0.0.1,country=1,language=1,layer2tagging=1,vlanid=123 + +${vyos_libexec_dir}/validate-value --regex "[A-Za-z0-9,=\.]+" --value "$1"