diff options
Diffstat (limited to 'src/conf_mode/dhcp_server.py')
-rwxr-xr-x | src/conf_mode/dhcp_server.py | 240 |
1 files changed, 18 insertions, 222 deletions
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index 39cc72574..3d75414f5 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018-2019 VyOS maintainers and contributors +# Copyright (C) 2018-2020 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 @@ -14,16 +14,17 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import sys import os -import jinja2 -import socket -import struct - -import vyos.validate from ipaddress import ip_address, ip_network +from jinja2 import FileSystemLoader, Environment +from socket import inet_ntoa +from struct import pack +from sys import exit + from vyos.config import Config +from vyos.defaults import directories as vyos_data_dir +from vyos.validate import is_subnet_connected from vyos import ConfigError config_file = r'/etc/dhcp/dhcpd.conf' @@ -31,215 +32,6 @@ lease_file = r'/config/dhcpd.leases' pid_file = r'/var/run/dhcpd.pid' daemon_config_file = r'/etc/default/isc-dhcpv4-server' -# Please be careful if you edit the template. -config_tmpl = """ -### Autogenerated by dhcp_server.py ### - -# For options please consult the following website: -# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html -# -# log-facility local7; - -{% if hostfile_update %} -on release { - set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name); - set ClientIp = binary-to-ascii(10, 8, ".",leased-address); - set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6)); - set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!"); - execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain); -} - -on expiry { - set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name); - set ClientIp = binary-to-ascii(10, 8, ".",leased-address); - set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6)); - set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!"); - execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain); -} -{% endif %} -{%- if host_decl_name %} -use-host-decl-names on; -{%- endif %} -ddns-update-style {% if ddns_enable -%} interim {%- else -%} none {%- endif %}; -{% if static_route -%} -option rfc3442-static-route code 121 = array of integer 8; -option windows-static-route code 249 = array of integer 8; -{%- endif %} -{% if wpad -%} -option wpad-url code 252 = text; -{% endif %} - -{%- if global_parameters %} -# The following {{ global_parameters | length }} line(s) were added as global-parameters in the CLI and have not been validated -{%- for param in global_parameters %} -{{ param }} -{%- endfor -%} -{%- endif %} - -# Failover configuration -{% for network in shared_network %} -{%- if not network.disabled -%} -{%- for subnet in network.subnet %} -{%- if subnet.failover_name -%} -failover peer "{{ subnet.failover_name }}" { -{%- if subnet.failover_status == 'primary' %} - primary; - mclt 1800; - split 128; -{%- elif subnet.failover_status == 'secondary' %} - secondary; -{%- endif %} - address {{ subnet.failover_local_addr }}; - port 520; - peer address {{ subnet.failover_peer_addr }}; - peer port 520; - max-response-delay 30; - max-unacked-updates 10; - load balance max seconds 3; -} -{% endif -%} -{% endfor -%} -{% endif -%} -{% endfor %} - -# Shared network configration(s) -{% for network in shared_network %} -{%- if not network.disabled -%} -shared-network {{ network.name }} { - {%- if network.authoritative %} - authoritative; - {%- endif %} - {%- if network.network_parameters %} - # The following {{ network.network_parameters | length }} line(s) were added as shared-network-parameters in the CLI and have not been validated - {%- for param in network.network_parameters %} - {{ param }} - {%- endfor %} - {%- endif %} - {%- for subnet in network.subnet %} - subnet {{ subnet.address }} netmask {{ subnet.netmask }} { - {%- if subnet.dns_server %} - option domain-name-servers {{ subnet.dns_server | join(', ') }}; - {%- endif %} - {%- if subnet.domain_search %} - option domain-search {{ subnet.domain_search | join(', ') }}; - {%- endif %} - {%- if subnet.ntp_server %} - option ntp-servers {{ subnet.ntp_server | join(', ') }}; - {%- endif %} - {%- if subnet.pop_server %} - option pop-server {{ subnet.pop_server | join(', ') }}; - {%- endif %} - {%- if subnet.smtp_server %} - option smtp-server {{ subnet.smtp_server | join(', ') }}; - {%- endif %} - {%- if subnet.time_server %} - option time-servers {{ subnet.time_server | join(', ') }}; - {%- endif %} - {%- if subnet.wins_server %} - option netbios-name-servers {{ subnet.wins_server | join(', ') }}; - {%- endif %} - {%- if subnet.static_route %} - option rfc3442-static-route {{ subnet.static_route }}{% if subnet.rfc3442_default_router %}, {{ subnet.rfc3442_default_router }}{% endif %}; - option windows-static-route {{ subnet.static_route }}; - {%- endif %} - {%- if subnet.ip_forwarding %} - option ip-forwarding true; - {%- endif -%} - {%- if subnet.default_router %} - option routers {{ subnet.default_router }}; - {%- endif -%} - {%- if subnet.server_identifier %} - option dhcp-server-identifier {{ subnet.server_identifier }}; - {%- endif -%} - {%- if subnet.domain_name %} - option domain-name "{{ subnet.domain_name }}"; - {%- endif -%} - {%- if subnet.subnet_parameters %} - # The following {{ subnet.subnet_parameters | length }} line(s) were added as subnet-parameters in the CLI and have not been validated - {%- for param in subnet.subnet_parameters %} - {{ param }} - {%- endfor -%} - {%- endif %} - {%- if subnet.tftp_server %} - option tftp-server-name "{{ subnet.tftp_server }}"; - {%- endif -%} - {%- if subnet.bootfile_name %} - option bootfile-name "{{ subnet.bootfile_name }}"; - filename "{{ subnet.bootfile_name }}"; - {%- endif -%} - {%- if subnet.bootfile_server %} - next-server {{ subnet.bootfile_server }}; - {%- endif -%} - {%- if subnet.time_offset %} - option time-offset {{ subnet.time_offset }}; - {%- endif -%} - {%- if subnet.wpad_url %} - option wpad-url "{{ subnet.wpad_url }}"; - {%- endif -%} - {%- if subnet.client_prefix_length %} - option subnet-mask {{ subnet.client_prefix_length }}; - {%- endif -%} - {% if subnet.lease %} - default-lease-time {{ subnet.lease }}; - max-lease-time {{ subnet.lease }}; - {%- endif -%} - {%- for host in subnet.static_mapping %} - {% if not host.disabled -%} - host {% if host_decl_name -%} {{ host.name }} {%- else -%} {{ network.name }}_{{ host.name }} {%- endif %} { - {%- if host.ip_address %} - fixed-address {{ host.ip_address }}; - {%- endif %} - hardware ethernet {{ host.mac_address }}; - {%- if host.static_parameters %} - # The following {{ host.static_parameters | length }} line(s) were added as static-mapping-parameters in the CLI and have not been validated - {%- for param in host.static_parameters %} - {{ param }} - {%- endfor -%} - {%- endif %} - } - {%- endif %} - {%- endfor %} - {%- if subnet.failover_name %} - pool { - failover peer "{{ subnet.failover_name }}"; - deny dynamic bootp clients; - {%- for range in subnet.range %} - range {{ range.start }} {{ range.stop }}; - {%- endfor %} - } - {%- else %} - {%- for range in subnet.range %} - range {{ range.start }} {{ range.stop }}; - {%- endfor %} - {%- endif %} - } - {%- endfor %} - on commit { - set shared-networkname = "{{ network.name }}"; - {% if hostfile_update -%} - set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name); - set ClientIp = binary-to-ascii(10, 8, ".", leased-address); - set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6)); - set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!"); - execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "commit", ClientName, ClientIp, ClientMac, ClientDomain); - {%- endif %} - } -} -{%- endif %} -{% endfor %} -""" - -daemon_tmpl = """ -### Autogenerated by dhcp_server.py ### - -# sourced by /etc/init.d/isc-dhcpv4-server - -DHCPD_CONF={{ config_file }} -DHCPD_PID={{ pid_file }} -OPTIONS="-4 -lf {{ lease_file }}" -INTERFACES="" -""" - default_config_data = { 'lease_file': lease_file, 'disabled': False, @@ -459,7 +251,7 @@ def get_config(): if conf.exists('client-prefix-length'): # snippet borrowed from https://stackoverflow.com/questions/33750233/convert-cidr-to-subnet-mask-in-python host_bits = 32 - int(conf.return_value('client-prefix-length')) - subnet['client_prefix_length'] = socket.inet_ntoa(struct.pack('!I', (1 << 32) - (1 << host_bits))) + subnet['client_prefix_length'] = inet_ntoa(pack('!I', (1 << 32) - (1 << host_bits))) # Default router IP address on the client's subnet if conf.exists('default-router'): @@ -778,7 +570,7 @@ def verify(dhcp): # There must be one subnet connected to a listen interface. # This only counts if the network itself is not disabled! if not network['disabled']: - if vyos.validate.is_subnet_connected(subnet['network'], primary=True): + if is_subnet_connected(subnet['network'], primary=True): listen_ok = True # Subnets must be non overlapping @@ -810,9 +602,13 @@ def generate(dhcp): print('Warning: DHCP server will be deactivated because it is disabled') return None - tmpl = jinja2.Template(config_tmpl) - config_text = tmpl.render(dhcp) + # Prepare Jinja2 template loader from files + tmpl_path = os.path.join(vyos_data_dir['data'], 'templates', 'dhcp-server') + fs_loader = FileSystemLoader(tmpl_path) + env = Environment(loader=fs_loader) + tmpl = env.get_template('dhcpd.conf.tmpl') + config_text = tmpl.render(dhcp) # Please see: https://phabricator.vyos.net/T1129 for quoting of the raw parameters # we can pass to ISC DHCPd config_text = config_text.replace(""",'"') @@ -820,7 +616,7 @@ def generate(dhcp): with open(config_file, 'w') as f: f.write(config_text) - tmpl = jinja2.Template(daemon_tmpl) + tmpl = env.get_template('daemon.tmpl') config_text = tmpl.render(dhcp) with open(daemon_config_file, 'w') as f: f.write(config_text) @@ -852,4 +648,4 @@ if __name__ == '__main__': apply(c) except ConfigError as e: print(e) - sys.exit(1) + exit(1) |