summaryrefslogtreecommitdiff
path: root/src/conf_mode/dhcp_server.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/dhcp_server.py')
-rwxr-xr-xsrc/conf_mode/dhcp_server.py240
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("&quot;",'"')
@@ -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)