From c5b7ea987a2e209bc553a89c340d0a208f926c71 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 28 Apr 2020 19:24:53 +0200 Subject: dhclient: T2393: migrate from SysVinit to systemd --- data/templates/dhcp-client/daemon-options.tmpl | 1 + data/templates/dhcp-client/ipv4.tmpl | 2 +- python/vyos/ifconfig/dhcp.py | 53 ++++++++------------------ src/systemd/dhclient@.service | 19 +++++++++ 4 files changed, 36 insertions(+), 39 deletions(-) create mode 100644 data/templates/dhcp-client/daemon-options.tmpl create mode 100644 src/systemd/dhclient@.service diff --git a/data/templates/dhcp-client/daemon-options.tmpl b/data/templates/dhcp-client/daemon-options.tmpl new file mode 100644 index 000000000..ec74a4234 --- /dev/null +++ b/data/templates/dhcp-client/daemon-options.tmpl @@ -0,0 +1 @@ +DHCLIENT_OPTS="-nw -cf /run/dhclient/{{ ifname }}.conf -pf /run/dhclient/{{ ifname }}.pid -lf /run/dhclient/{{ ifname }}.leases {{ ifname }}" diff --git a/data/templates/dhcp-client/ipv4.tmpl b/data/templates/dhcp-client/ipv4.tmpl index 43f273077..ab772b5f6 100644 --- a/data/templates/dhcp-client/ipv4.tmpl +++ b/data/templates/dhcp-client/ipv4.tmpl @@ -1,4 +1,4 @@ -# generated by ifconfig.py +# generated by dhcp.py option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; timeout 60; retry 300; diff --git a/python/vyos/ifconfig/dhcp.py b/python/vyos/ifconfig/dhcp.py index 3122147a3..b045727a2 100644 --- a/python/vyos/ifconfig/dhcp.py +++ b/python/vyos/ifconfig/dhcp.py @@ -19,23 +19,22 @@ from vyos.dicts import FixedDict from vyos.ifconfig.control import Control from vyos.template import render - class _DHCP (Control): - client_base = r'/var/lib/dhcp/dhclient_' - - def __init__(self, ifname, version, **kargs): + def __init__(self, ifname, **kargs): super().__init__(**kargs) - self.version = version self.file = { 'ifname': ifname, - 'conf': self.client_base + ifname + '.' + version + 'conf', - 'pid': self.client_base + ifname + '.' + version + 'pid', - 'lease': self.client_base + ifname + '.' + version + 'leases', + 'conf': self.client_base + f'{ifname}.conf', + 'options': self.client_base + f'{ifname}.options', + 'pid': self.client_base + f'{ifname}.pid', + 'lease': self.client_base + f'{ifname}.leases', } class _DHCPv4 (_DHCP): + client_base = r'/run/dhclient/' + def __init__(self, ifname): - super().__init__(ifname, '') + super().__init__(ifname) self.options = FixedDict(**{ 'ifname': ifname, 'hostname': '', @@ -62,18 +61,10 @@ class _DHCPv4 (_DHCP): with open('/etc/hostname', 'r') as f: self.options['hostname'] = f.read().rstrip('\n') + render(self.file['options'], 'dhcp-client/daemon-options.tmpl', self.options) render(self.file['conf'], 'dhcp-client/ipv4.tmpl' ,self.options) - cmd = 'start-stop-daemon' - cmd += ' --start' - cmd += ' --oknodo' - cmd += ' --quiet' - cmd += ' --pidfile {pid}' - cmd += ' --exec /sbin/dhclient' - cmd += ' --' - # now pass arguments to dhclient binary - cmd += ' -4 -nw -cf {conf} -pf {pid} -lf {lease} {ifname}' - return self._cmd(cmd.format(**self.file)) + return self._cmd('systemctl restart dhclient@{ifname}.service'.format(**self.file)) def delete(self): """ @@ -90,32 +81,18 @@ class _DHCPv4 (_DHCP): self._debug_msg('No DHCP client PID found') return None - # with open(self.file['pid'], 'r') as f: - # pid = int(f.read()) - - # stop dhclient, we need to call dhclient and tell it should release the - # aquired IP address. tcpdump tells me: - # 172.16.35.103.68 > 172.16.35.254.67: [bad udp cksum 0xa0cb -> 0xb943!] BOOTP/DHCP, Request from 00:50:56:9d:11:df, length 300, xid 0x620e6946, Flags [none] (0x0000) - # Client-IP 172.16.35.103 - # Client-Ethernet-Address 00:50:56:9d:11:df - # Vendor-rfc1048 Extensions - # Magic Cookie 0x63825363 - # DHCP-Message Option 53, length 1: Release - # Server-ID Option 54, length 4: 172.16.35.254 - # Hostname Option 12, length 10: "vyos" - # - cmd = '/sbin/dhclient -cf {conf} -pf {pid} -lf {lease} -r {ifname}' - self._cmd(cmd.format(**self.file)) + return self._cmd('systemctl stop dhclient@{ifname}.service'.format(**self.file)) # cleanup old config files - for name in ('conf', 'pid', 'lease'): + for name in ('conf', 'options', 'pid', 'lease'): if os.path.isfile(self.file[name]): os.remove(self.file[name]) - class _DHCPv6 (_DHCP): + client_base = r'/run/dhclient6/' + def __init__(self, ifname): - super().__init__(ifname, 'v6') + super().__init__(ifname) self.options = FixedDict(**{ 'ifname': ifname, 'dhcpv6_prm_only': False, diff --git a/src/systemd/dhclient@.service b/src/systemd/dhclient@.service new file mode 100644 index 000000000..5e185908b --- /dev/null +++ b/src/systemd/dhclient@.service @@ -0,0 +1,19 @@ +[Unit] +Description=DHCP client on %i (IPv4) +Documentation=man:dhclient(8) +RequiresMountsFor=/run +ConditionPathExists=/run/dhclient/%i.conf +ConditionPathExists=/run/dhclient/%i.options +After=vyos-router.service + +[Service] +WorkingDirectory=/run/dhclient +Type=simple +EnvironmentFile=-/run/dhclient/%i.options +PIDFile=/run/dhclient/%i.pid +ExecStart=/sbin/dhclient -4 $DHCLIENT_OPTS +ExecStop=/sbin/dhclient -4 $DHCLIENT_OPTS -r +Restart=always + +[Install] +WantedBy=multi-user.target -- cgit v1.2.3 From 3e7b8515cafb6f6e015c765021ecd38203d87a4d Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 28 Apr 2020 20:05:20 +0200 Subject: dhclient6: T2393: T2394: migrate from SysVinit to systemd --- data/templates/dhcp-client/daemon-options.tmpl | 2 +- data/templates/dhcp-client/ipv6.tmpl | 2 +- python/vyos/ifconfig/dhcp.py | 55 ++++++++------------------ src/systemd/dhclient6@.service | 18 +++++++++ src/systemd/dhclient@.service | 4 +- 5 files changed, 38 insertions(+), 43 deletions(-) create mode 100644 src/systemd/dhclient6@.service diff --git a/data/templates/dhcp-client/daemon-options.tmpl b/data/templates/dhcp-client/daemon-options.tmpl index ec74a4234..8f995ab62 100644 --- a/data/templates/dhcp-client/daemon-options.tmpl +++ b/data/templates/dhcp-client/daemon-options.tmpl @@ -1 +1 @@ -DHCLIENT_OPTS="-nw -cf /run/dhclient/{{ ifname }}.conf -pf /run/dhclient/{{ ifname }}.pid -lf /run/dhclient/{{ ifname }}.leases {{ ifname }}" +DHCLIENT_OPTS="-nw -cf {{ config_path }}/{{ ifname }}.conf -pf {{ config_path }}/{{ ifname }}.pid -lf {{ config_path }}/{{ ifname }}.leases {{ '-S' if dhcpv6_prm_only }} {{ '-T' if dhcpv6_temporary }} {{ ifname }}" diff --git a/data/templates/dhcp-client/ipv6.tmpl b/data/templates/dhcp-client/ipv6.tmpl index 83db40c5f..be0235add 100644 --- a/data/templates/dhcp-client/ipv6.tmpl +++ b/data/templates/dhcp-client/ipv6.tmpl @@ -1,4 +1,4 @@ -# generated by ifconfig.py +# generated by dhcp.py interface "{{ ifname }}" { request routers, domain-name-servers, domain-name; } diff --git a/python/vyos/ifconfig/dhcp.py b/python/vyos/ifconfig/dhcp.py index b045727a2..b9862652a 100644 --- a/python/vyos/ifconfig/dhcp.py +++ b/python/vyos/ifconfig/dhcp.py @@ -24,19 +24,20 @@ class _DHCP (Control): super().__init__(**kargs) self.file = { 'ifname': ifname, - 'conf': self.client_base + f'{ifname}.conf', - 'options': self.client_base + f'{ifname}.options', - 'pid': self.client_base + f'{ifname}.pid', - 'lease': self.client_base + f'{ifname}.leases', + 'conf': self.client_base + f'/{ifname}.conf', + 'options': self.client_base + f'/{ifname}.options', + 'pid': self.client_base + f'/{ifname}.pid', + 'lease': self.client_base + f'/{ifname}.leases', } class _DHCPv4 (_DHCP): - client_base = r'/run/dhclient/' + client_base = r'/run/dhclient' def __init__(self, ifname): super().__init__(ifname) self.options = FixedDict(**{ 'ifname': ifname, + 'config_path': self.client_base, 'hostname': '', 'client_id': '', 'vendor_class_id': '' @@ -62,7 +63,7 @@ class _DHCPv4 (_DHCP): self.options['hostname'] = f.read().rstrip('\n') render(self.file['options'], 'dhcp-client/daemon-options.tmpl', self.options) - render(self.file['conf'], 'dhcp-client/ipv4.tmpl' ,self.options) + render(self.file['conf'], 'dhcp-client/ipv4.tmpl', self.options) return self._cmd('systemctl restart dhclient@{ifname}.service'.format(**self.file)) @@ -89,12 +90,13 @@ class _DHCPv4 (_DHCP): os.remove(self.file[name]) class _DHCPv6 (_DHCP): - client_base = r'/run/dhclient6/' + client_base = r'/run/dhclient6' def __init__(self, ifname): super().__init__(ifname) self.options = FixedDict(**{ 'ifname': ifname, + 'config_path': self.client_base, 'dhcpv6_prm_only': False, 'dhcpv6_temporary': False, }) @@ -111,7 +113,7 @@ class _DHCPv6 (_DHCP): >>> from vyos.ifconfig import Interface >>> j = Interface('eth0') - >>> j.set_dhcpv6() + >>> j.dhcp.v6.set() """ # better save then sorry .. should be checked in interface script @@ -120,29 +122,13 @@ class _DHCPv6 (_DHCP): raise Exception( 'DHCPv6 temporary and parameters-only options are mutually exclusive!') + render(self.file['options'], 'dhcp-client/daemon-options.tmpl', self.options) render(self.file['conf'], 'dhcp-client/ipv6.tmpl', self.options) # no longer accept router announcements on this interface self._write_sysfs(self.file['accept_ra'], 0) - # assemble command-line to start DHCPv6 client (dhclient) - cmd = 'start-stop-daemon' - cmd += ' --start' - cmd += ' --oknodo' - cmd += ' --quiet' - cmd += ' --pidfile {pid}' - cmd += ' --exec /sbin/dhclient' - cmd += ' --' - # now pass arguments to dhclient binary - cmd += ' -6 -nw -cf {conf} -pf {pid} -lf {lease}' - # add optional arguments - if self.options['dhcpv6_prm_only']: - cmd += ' -S' - if self.options['dhcpv6_temporary']: - cmd += ' -T' - cmd += ' {ifname}' - - return self._cmd(cmd.format(**self.file)) + return self._cmd('systemctl restart dhclient6@{ifname}.service'.format(**self.file)) def delete(self): """ @@ -153,33 +139,24 @@ class _DHCPv6 (_DHCP): >>> from vyos.ifconfig import Interface >>> j = Interface('eth0') - >>> j.del_dhcpv6() + >>> j.dhcp.v6.delete() """ if not os.path.isfile(self.file['pid']): self._debug_msg('No DHCPv6 client PID found') return None - # with open(self.file['pid'], 'r') as f: - # pid = int(f.read()) - - # stop dhclient - cmd = 'start-stop-daemon' - cmd += ' --stop' - cmd += ' --oknodo' - cmd += ' --quiet' - cmd += ' --pidfile {pid}' - self._cmd(cmd.format(**self.file)) + return self._cmd('systemctl stop dhclient6@{ifname}.service'.format(**self.file)) # accept router announcements on this interface self._write_sysfs(self.file['accept_ra'], 1) # cleanup old config files - for name in ('conf', 'pid', 'lease'): + for name in ('conf', 'options', 'pid', 'lease'): if os.path.isfile(self.file[name]): os.remove(self.file[name]) -class DHCP (object): +class DHCP(object): def __init__(self, ifname): self.v4 = _DHCPv4(ifname) self.v6 = _DHCPv6(ifname) diff --git a/src/systemd/dhclient6@.service b/src/systemd/dhclient6@.service new file mode 100644 index 000000000..d871e7354 --- /dev/null +++ b/src/systemd/dhclient6@.service @@ -0,0 +1,18 @@ +[Unit] +Description=DHCPv6 client on %i +Documentation=man:dhclient(8) +RequiresMountsFor=/run +ConditionPathExists=/run/dhclient6/%i.conf +ConditionPathExists=/run/dhclient6/%i.options +After=vyos-router.service + +[Service] +WorkingDirectory=/run/dhclient6 +Type=exec +EnvironmentFile=-/run/dhclient6/%i.options +PIDFile=/run/dhclient6/%i.pid +ExecStart=/sbin/dhclient -6 $DHCLIENT_OPTS +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/src/systemd/dhclient@.service b/src/systemd/dhclient@.service index 5e185908b..c6f1e4be1 100644 --- a/src/systemd/dhclient@.service +++ b/src/systemd/dhclient@.service @@ -1,5 +1,5 @@ [Unit] -Description=DHCP client on %i (IPv4) +Description=DHCP client on %i Documentation=man:dhclient(8) RequiresMountsFor=/run ConditionPathExists=/run/dhclient/%i.conf @@ -8,7 +8,7 @@ After=vyos-router.service [Service] WorkingDirectory=/run/dhclient -Type=simple +Type=forking EnvironmentFile=-/run/dhclient/%i.options PIDFile=/run/dhclient/%i.pid ExecStart=/sbin/dhclient -4 $DHCLIENT_OPTS -- cgit v1.2.3 From fc3ceac0160db30d49d9b8bb2417934c9b399db4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 28 Apr 2020 20:43:15 +0200 Subject: dhclient: T2393: remove intermediate _DHCP helper class The intermedite class only held the path to the configuration files - thus its existence was doubtworthy. For better readability and a clean inheritance graph that class has been dropped. --- data/templates/dhcp-client/daemon-options.tmpl | 2 +- python/vyos/ifconfig/dhcp.py | 76 +++++++++++--------------- src/systemd/dhclient@.service | 2 +- 3 files changed, 35 insertions(+), 45 deletions(-) diff --git a/data/templates/dhcp-client/daemon-options.tmpl b/data/templates/dhcp-client/daemon-options.tmpl index 8f995ab62..b5a10c3b8 100644 --- a/data/templates/dhcp-client/daemon-options.tmpl +++ b/data/templates/dhcp-client/daemon-options.tmpl @@ -1 +1 @@ -DHCLIENT_OPTS="-nw -cf {{ config_path }}/{{ ifname }}.conf -pf {{ config_path }}/{{ ifname }}.pid -lf {{ config_path }}/{{ ifname }}.leases {{ '-S' if dhcpv6_prm_only }} {{ '-T' if dhcpv6_temporary }} {{ ifname }}" +DHCLIENT_OPTS="-nw -cf {{ conf_file }} -pf {{ pid_file }} -lf {{ lease_file }} {{ '-S' if dhcpv6_prm_only }} {{ '-T' if dhcpv6_temporary }} {{ ifname }}" diff --git a/python/vyos/ifconfig/dhcp.py b/python/vyos/ifconfig/dhcp.py index b9862652a..d3e75c292 100644 --- a/python/vyos/ifconfig/dhcp.py +++ b/python/vyos/ifconfig/dhcp.py @@ -19,28 +19,20 @@ from vyos.dicts import FixedDict from vyos.ifconfig.control import Control from vyos.template import render -class _DHCP (Control): - def __init__(self, ifname, **kargs): - super().__init__(**kargs) - self.file = { - 'ifname': ifname, - 'conf': self.client_base + f'/{ifname}.conf', - 'options': self.client_base + f'/{ifname}.options', - 'pid': self.client_base + f'/{ifname}.pid', - 'lease': self.client_base + f'/{ifname}.leases', - } - -class _DHCPv4 (_DHCP): - client_base = r'/run/dhclient' +class _DHCPv4 (Control): def __init__(self, ifname): - super().__init__(ifname) + super().__init__() + config_base = r'/run/dhclient' self.options = FixedDict(**{ 'ifname': ifname, - 'config_path': self.client_base, 'hostname': '', 'client_id': '', - 'vendor_class_id': '' + 'vendor_class_id': '', + 'conf_file': config_base + f'/{ifname}.conf', + 'options_file': config_base + f'/{ifname}.options', + 'pid_file': config_base + f'/{ifname}.pid', + 'lease_file': config_base + f'/{ifname}.leases', }) # replace dhcpv4/v6 with systemd.networkd? @@ -55,17 +47,16 @@ class _DHCPv4 (_DHCP): >>> j = Interface('eth0') >>> j.dhcp.v4.set() """ - if not self.options['hostname']: # read configured system hostname. # maybe change to vyos hostd client ??? with open('/etc/hostname', 'r') as f: self.options['hostname'] = f.read().rstrip('\n') - render(self.file['options'], 'dhcp-client/daemon-options.tmpl', self.options) - render(self.file['conf'], 'dhcp-client/ipv4.tmpl', self.options) + render(self.options['options_file'], 'dhcp-client/daemon-options.tmpl', self.options) + render(self.options['conf_file'], 'dhcp-client/ipv4.tmpl', self.options) - return self._cmd('systemctl restart dhclient@{ifname}.service'.format(**self.file)) + return self._cmd('systemctl restart dhclient@{ifname}.service'.format(**self.options)) def delete(self): """ @@ -78,31 +69,30 @@ class _DHCPv4 (_DHCP): >>> j = Interface('eth0') >>> j.dhcp.v4.delete() """ - if not os.path.isfile(self.file['pid']): + if not os.path.isfile(self.options['pid_file']): self._debug_msg('No DHCP client PID found') return None - return self._cmd('systemctl stop dhclient@{ifname}.service'.format(**self.file)) + self._cmd('systemctl stop dhclient@{ifname}.service'.format(**self.options)) # cleanup old config files - for name in ('conf', 'options', 'pid', 'lease'): - if os.path.isfile(self.file[name]): - os.remove(self.file[name]) - -class _DHCPv6 (_DHCP): - client_base = r'/run/dhclient6' + for name in ('conf_file', 'options_file', 'pid_file', 'lease_file'): + if os.path.isfile(self.options[name]): + os.remove(self.options[name]) +class _DHCPv6 (Control): def __init__(self, ifname): - super().__init__(ifname) + super().__init__() + config_base = r'/run/dhclient6' self.options = FixedDict(**{ 'ifname': ifname, - 'config_path': self.client_base, + 'conf_file': config_base + f'/{ifname}.conf', + 'options_file': config_base + f'/{ifname}.options', + 'pid_file': config_base + f'/{ifname}.pid', + 'lease_file': config_base + f'/{ifname}.leases', 'dhcpv6_prm_only': False, 'dhcpv6_temporary': False, }) - self.file.update({ - 'accept_ra': f'/proc/sys/net/ipv6/conf/{ifname}/accept_ra', - }) def set(self): """ @@ -122,13 +112,13 @@ class _DHCPv6 (_DHCP): raise Exception( 'DHCPv6 temporary and parameters-only options are mutually exclusive!') - render(self.file['options'], 'dhcp-client/daemon-options.tmpl', self.options) - render(self.file['conf'], 'dhcp-client/ipv6.tmpl', self.options) + render(self.options['options_file'], 'dhcp-client/daemon-options.tmpl', self.options) + render(self.options['conf_file'], 'dhcp-client/ipv6.tmpl', self.options) # no longer accept router announcements on this interface - self._write_sysfs(self.file['accept_ra'], 0) + self._write_sysfs('/proc/sys/net/ipv6/conf/{ifname}/accept_ra'.format(**self.options), 0) - return self._cmd('systemctl restart dhclient6@{ifname}.service'.format(**self.file)) + return self._cmd('systemctl restart dhclient6@{ifname}.service'.format(**self.options)) def delete(self): """ @@ -141,19 +131,19 @@ class _DHCPv6 (_DHCP): >>> j = Interface('eth0') >>> j.dhcp.v6.delete() """ - if not os.path.isfile(self.file['pid']): + if not os.path.isfile(self.options['pid_file']): self._debug_msg('No DHCPv6 client PID found') return None - return self._cmd('systemctl stop dhclient6@{ifname}.service'.format(**self.file)) + self._cmd('systemctl stop dhclient6@{ifname}.service'.format(**self.options)) # accept router announcements on this interface - self._write_sysfs(self.file['accept_ra'], 1) + self._write_sysfs('/proc/sys/net/ipv6/conf/{ifname}/accept_ra'.format(**self.options), 1) # cleanup old config files - for name in ('conf', 'options', 'pid', 'lease'): - if os.path.isfile(self.file[name]): - os.remove(self.file[name]) + for name in ('conf_file', 'options_file', 'pid_file', 'lease_file'): + if os.path.isfile(self.options[name]): + os.remove(self.options[name]) class DHCP(object): diff --git a/src/systemd/dhclient@.service b/src/systemd/dhclient@.service index c6f1e4be1..1040ce2b2 100644 --- a/src/systemd/dhclient@.service +++ b/src/systemd/dhclient@.service @@ -8,7 +8,7 @@ After=vyos-router.service [Service] WorkingDirectory=/run/dhclient -Type=forking +Type=exec EnvironmentFile=-/run/dhclient/%i.options PIDFile=/run/dhclient/%i.pid ExecStart=/sbin/dhclient -4 $DHCLIENT_OPTS -- cgit v1.2.3