summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--interface-definitions/dns-domain-name.xml68
-rw-r--r--interface-definitions/host-name.xml26
-rwxr-xr-xsrc/conf_mode/host_name.py144
4 files changed, 172 insertions, 70 deletions
diff --git a/Makefile b/Makefile
index dd3d2d00f..73c307403 100644
--- a/Makefile
+++ b/Makefile
@@ -42,10 +42,6 @@ clean:
rm -rf $(TMPL_DIR)/*
rm -rf $(OP_TMPL_DIR)/*
-.PHONY: test
-test:
- PYTHONPATH=python/ python3 -m "nose" --with-xunit src --with-coverage --cover-erase --cover-xml --cover-package src/conf_mode,src/op_mode,src/completion,src/helpers,src/validators,src/tests --verbose
-
.PHONY: sonar
sonar:
sonar-scanner -X -Dsonar.login=${SONAR_TOKEN}
diff --git a/interface-definitions/dns-domain-name.xml b/interface-definitions/dns-domain-name.xml
new file mode 100644
index 000000000..7b8497c09
--- /dev/null
+++ b/interface-definitions/dns-domain-name.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<!-- host-name configuration -->
+<interfaceDefinition>
+ <node name="system">
+ <children>
+ <leafNode name="name-server">
+ <properties>
+ <help>Domain Name Server (DNS)</help>
+ <priority>400</priority>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>Domain Name Server (DNS) address</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>Domain Name Server (DNS) address</description>
+ </valueHelp>
+ <multi/>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="host-name" owner="${vyos_conf_scripts_dir}/host_name.py">
+ <properties>
+ <help>System host name (default: vyos)</help>
+ <constraint>
+ <regex>[A-Za-z0-9][-.A-Za-z0-9]*[A-Za-z0-9]</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="domain-name" owner="${vyos_conf_scripts_dir}/host_name.py">
+ <properties>
+ <help>System domain name</help>
+ <constraint>
+ <regex>[A-Za-z0-9][-.A-Za-z0-9]*</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="domain-search" owner="${vyos_conf_scripts_dir}/host_name.py">
+ <properties>
+ <help>Domain Name Server (DNS) domain completion order</help>
+ <priority>400</priority>
+ </properties>
+ <children>
+ <leafNode name="domain">
+ <properties>
+ <help>DNS domain completion order</help>
+ <constraint>
+ <regex>^[-a-zA-Z0-9.]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid domain name</constraintErrorMessage>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="disable-dhcp-nameservers" owner="${vyos_conf_scripts_dir}/host_name.py">
+ <properties>
+ <help>Disable DHCP updates of DNS settings</help>
+ <priority>300</priority>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/interface-definitions/host-name.xml b/interface-definitions/host-name.xml
deleted file mode 100644
index bbe679607..000000000
--- a/interface-definitions/host-name.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- host-name configuration -->
-
-<interfaceDefinition>
- <node name="system">
- <children>
- <leafNode name="host-name" owner="${vyos_conf_scripts_dir}/host_name.py">
- <properties>
- <help>System host name (default: vyos)</help>
- <constraint>
- <regex>[A-Za-z0-9][-.A-Za-z0-9]*[A-Za-z0-9]</regex>
- </constraint>
- </properties>
- </leafNode>
- <leafNode name="domain-name" owner="${vyos_conf_scripts_dir}/host_name.py">
- <properties>
- <help>System domain name</help>
- <constraint>
- <regex>[A-Za-z0-9][-.A-Za-z0-9]*</regex>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
-</interfaceDefinition>
diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py
index 030735215..4b6ce76b0 100755
--- a/src/conf_mode/host_name.py
+++ b/src/conf_mode/host_name.py
@@ -24,81 +24,146 @@ import os
import re
import sys
import subprocess
+import copy
+import jinja2
+import glob
from vyos.config import Config
from vyos import ConfigError
+config_file_hosts = '/etc/hosts'
+config_file_resolv = '/etc/resolv.conf'
-hosts_file = '/etc/hosts'
-hostname_regex = re.compile("^[A-Za-z0-9][-.A-Za-z0-9]*[A-Za-z0-9]$")
-local_addr = '127.0.1.1' # NOSONAR
+config_tmpl_hosts = """
+### Autogenerated by host_name.py ###
+127.0.0.1 localhost {{ hostname }}{% if domain_name %}.{{ domain_name }}{% endif %}
+# The following lines are desirable for IPv6 capable hosts
+::1 localhost ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+"""
+
+config_tmpl_resolv = """
+### Autogenerated by host_name.py ###
+{% for ns in nameserver -%}
+nameserver {{ ns }}
+{% endfor -%}
+
+{%- if domain_name %}
+domain {{ domain_name }}
+{%- endif %}
+
+{%- if domain_search %}
+search {{ domain_search | join(" ") }}
+{%- endif %}
+
+"""
+
+# borrowed from: https://github.com/donjajo/py-world/blob/master/resolvconfReader.py, THX!
+def get_resolvers(file):
+ resolvers = []
+ try:
+ with open(file, 'r') as resolvconf:
+ for line in resolvconf.readlines():
+ line = line.split('#',1)[0];
+ line = line.rstrip();
+ if 'nameserver' in line:
+ resolvers.append(line.split()[1])
+ return resolvers
+ except IOError:
+ return []
+
+default_config_data = {
+ 'hostname': 'vyos',
+ 'domain_name': '',
+ 'domain_search': [],
+ 'nameserver': [],
+ 'no_dhcp_ns': False
+}
def get_config():
"""Get configuration"""
conf = Config()
+ hosts = copy.deepcopy(default_config_data)
- hostname = conf.return_value("system host-name")
- domain = conf.return_value("system domain-name")
+ hosts['hostname'] = conf.return_value("system host-name")
+ hosts['domain_name'] = conf.return_value("system domain-name")
- # No one likes fixups, but we really don't want VyOS fail to boot
- # if hostname is not in the config
- if not hostname:
- hostname = "vyos"
+ if hosts['domain_name']:
+ hosts['domain_search'].append(hosts['domain_name'])
- if domain:
- fqdn = "{0}.{1}".format(hostname, domain)
- else:
- fqdn = hostname
-
- return {"hostname": hostname, "domain": domain, "fqdn": fqdn}
+ hosts['domain_search'] = conf.return_values("system domain-search domain")
+ hosts['nameserver'] = conf.return_values("system name-server")
+ hosts['no_dhcp_ns'] = conf.exists('system disable-dhcp-nameservers')
+ return hosts
def verify(config):
"""Verify configuration"""
# check for invalid host
# pattern $VAR(@) "^[[:alnum:]][-.[:alnum:]]*[[:alnum:]]$" ; "invalid host name $VAR(@)"
- if not hostname_regex.match(config["hostname"]):
+ hostname_regex = re.compile("^[A-Za-z0-9][-.A-Za-z0-9]*[A-Za-z0-9]$")
+ if not hostname_regex.match(config['hostname']):
raise ConfigError('Invalid host name ' + config["hostname"])
# pattern $VAR(@) "^.{1,63}$" ; "invalid host-name length"
- length = len(config["hostname"])
+ length = len(config['hostname'])
if length < 1 or length > 63:
raise ConfigError(
'Invalid host-name length, must be less than 63 characters')
- return None
+ # The search list is currently limited to six domains with a total of 256 characters.
+ # https://linux.die.net/man/5/resolv.conf
+ if len(config['domain_search']) > 6:
+ raise ConfigError('The search list is currently limited to six domains')
+ tmp = ' '.join(config['domain_search'])
+ if len(tmp) > 256:
+ raise ConfigError('The search list is currently limited to 256 characters')
+
+ return None
def generate(config):
"""Generate configuration files"""
- # read the hosts file
- with open(hosts_file, 'r') as f:
- hosts = f.read()
-
- # get the current hostname
- old_hostname = subprocess.check_output(['hostname']).decode().strip()
-
- # replace the local host line
- vyos_host_line_re = re.compile(r"({}\s+{}.*)".format(local_addr, old_hostname))
- vyos_host_line = "{}\t{} # VyOS entry\n".format(local_addr, config["fqdn"])
- if re.search(vyos_host_line_re, hosts):
- hosts = re.sub(vyos_host_line_re, vyos_host_line, hosts)
- else:
- # On boot (or after errors), the /etc/hosts file has no line for vyos hostname,
- # so we have to add it
- hosts = "{0}\n{1}".format(hosts, vyos_host_line)
-
- with open(hosts_file, 'w') as f:
- f.write(hosts)
+ if config is None:
+ return None
+
+ # If "system disable-dhcp-nameservers" is __configured__ all DNS resolvers
+ # received via dhclient should not be added into the final 'resolv.conf'.
+ #
+ # We iterate over every resolver file and retrieve the received nameservers
+ # for later adjustment of the system nameservers
+ dhcp_ns = []
+ for file in glob.glob('/etc/resolv.conf.dhclient-new*'):
+ for r in get_resolvers(file):
+ dhcp_ns.append(r)
+
+ if not config['no_dhcp_ns']:
+ config['nameserver'] += dhcp_ns
+
+ tmpl = jinja2.Template(config_tmpl_hosts)
+ config_text = tmpl.render(config)
+ with open(config_file_hosts, 'w') as f:
+ f.write(config_text)
+
+ tmpl = jinja2.Template(config_tmpl_resolv)
+ config_text = tmpl.render(config)
+ with open(config_file_resolv, 'w') as f:
+ f.write(config_text)
return None
-
def apply(config):
"""Apply configuration"""
- os.system("hostnamectl set-hostname --static {0}".format(config["fqdn"]))
+ fqdn = config['hostname']
+ if config['domain_name']:
+ fqdn += '.' + config['domain_name']
+
+ os.system("hostnamectl set-hostname --static {0}".format(fqdn))
# Restart services that use the hostname
os.system("systemctl restart rsyslog.service")
@@ -109,7 +174,6 @@ def apply(config):
return None
-
if __name__ == '__main__':
try:
c = get_config()