Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dhcp: T6611: add vendor support for ShoreTel IP phones #3884

Open
wants to merge 9 commits into
base: current
Choose a base branch
from
14 changes: 13 additions & 1 deletion data/templates/dhcp-server/kea-dhcp4.conf.j2
Original file line number Diff line number Diff line change
@@ -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 }},
Expand Down Expand Up @@ -48,7 +60,7 @@
"code": 1,
"type": "ipv4-address",
"space": "ubnt"
}
},
],
"hooks-libraries": [
{% if high_availability is vyos_defined %}
Expand Down
19 changes: 19 additions & 0 deletions interface-definitions/include/dhcp/option-v4.xml.i
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,25 @@
</leafNode>
</children>
</node>
<node name="shoretel">
<properties>
<help>ShoreTel-specfic parameters</help>
</properties>
<children>
<leafNode name="shoretel-server">
sarthurdev marked this conversation as resolved.
Show resolved Hide resolved
<properties>
<help>ShoreTel phone configuration</help>
<valueHelp>
<format>text</format>
<description>Comma-separated parameters for ShoreTel phone configuration</description>
</valueHelp>
<constraint>
<validator name="shoretel-server"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the validator and instead just put the regex in the XML instead.

Example here: https://github.com/vyos/vyos-1x/blob/current/interface-definitions/firewall.xml.in#L14

</constraint>
</properties>
</leafNode>
</children>
</node>
</children>
</node>
<leafNode name="wins-server">
Expand Down
22 changes: 22 additions & 0 deletions python/vyos/kea.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be optimised so the kea_client_classes is not needing to be called twice.

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

Expand All @@ -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'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be optimised so the kea_client_classes is not needing to be called twice.

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

Expand Down Expand Up @@ -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'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be optimised so the kea_client_classes is not needing to be called twice.

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

Expand Down
38 changes: 36 additions & 2 deletions python/vyos/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't these changes irrelevant to this PR?

# 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,
Expand Down Expand Up @@ -871,6 +871,32 @@ def kea_high_availability_json(config):

return dumps(data)

@register_filter('kea_client_classes')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As not used in any Jinja templates, this doesn't need to be registered.

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
Expand All @@ -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'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be optimised so the kea_client_classes is not needing to be called twice.

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'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be optimised so the kea_client_classes is not needing to be called twice.

network['subnet4'][-1]['require-client-classes'] = ['shoretel']

out.append(network)

return dumps(out, indent=4)
Expand Down
9 changes: 9 additions & 0 deletions smoketest/scripts/cli/test_service_dhcp-server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'])
Expand All @@ -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])
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions src/validators/shoretel-server
Original file line number Diff line number Diff line change
@@ -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"
Loading