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"