Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- avocado/tasst/dhcp.py | 133 ++++++++++++++++++++++++++++++++++++++++ avocado/tasst/dhcpv6.py | 123 +++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 avocado/tasst/dhcp.py create mode 100644 avocado/tasst/dhcpv6.py diff --git a/avocado/tasst/dhcp.py b/avocado/tasst/dhcp.py new file mode 100644 index 0000000..071fc20 --- /dev/null +++ b/avocado/tasst/dhcp.py @@ -0,0 +1,133 @@ +#! /usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-or-later +# +# tasst - Test A Simple Socket Transport +# library of test helpers for passt & pasta +# +# tasst/dhcp.py - Helpers for testing DHCP +# +# Copyright Red Hat +# Author: David Gibson <david(a)gibson.dropbear.id.au> + +import ipaddress +import os + +from tasst import Tasst, TasstSubData +from tasst.address import IpiAllocator, TEST_NET_1 +from tasst.nstool import UnshareSite + + +class BaseDhcpTasst(Tasst): + """ + Test DHCP behaviour. + + :avocado: disable + """ + + DHCLIENT = '/sbin/dhclient' + + def subsetup(self, site, ifname, addr, gw, mtu): + site.require_cmds(self.DHCLIENT) + + pidfile = os.path.join(self.workdir, 'dhclient.pid') + leasefile = os.path.join(self.workdir, 'dhclient.leases') + + # We need '-nc' because we may be running with capabilities + # but not UID 0. Without -nc dhclient drops capabilities + # before invoking dhclient-script, so it's unable to actually + # configure the interface + site.fg('{} -4 -v -nc -pf {} -lf {} {}' + .format(self.DHCLIENT, pidfile, leasefile, ifname), sudo=True) + + Tasst.subsetup(self, BaseDhcpTasst, + TasstSubData(site=site, ifname=ifname, addr=addr, gw=gw, mtu=mtu)) + + def test_addr(self): + sub = self.get_subsetup(BaseDhcpTasst) + (addr,) = sub.site.addrs(sub.ifname, family='inet', scope='global') + self.assertEquals(addr.ip, sub.addr) + + def test_route(self): + sub = self.get_subsetup(BaseDhcpTasst) + (defroute,) = sub.site.routes4(dst='default') + self.assertEquals(ipaddress.ip_address(defroute['gateway']), sub.gw) + + def test_mtu(self): + sub = self.get_subsetup(BaseDhcpTasst) + self.assertEquals(sub.site.mtu(sub.ifname), sub.mtu) + + +class MetaDhcpTasst(BaseDhcpTasst): + """Ugly workaround for + https://github.com/avocado-framework/avocado/issues/5680. + Explicitly apply the "meta" tag to inherited tests + + :avocado: disable + :avocado: tags=meta + + """ + + def test_addr(self): + super().test_addr() + + def test_route(self): + super().test_route() + + def test_mtu(self): + super().test_mtu() + + +class DhcpdTasst(MetaDhcpTasst): + DHCPD = 'dhcpd' + SUBNET = TEST_NET_1 + + def setUp(self): + super().setUp() + + ifname = 'clientif' + server_ifname = 'serverif' + + self.client = UnshareSite(self.__class__.__name__ + '.client', '-Un') + self.server = UnshareSite(self.__class__.__name__ + '.server', '-n', + parent=self.client, sudo=True) + + self.server.require_cmds(self.DHCPD) + + self.client.veth(ifname, server_ifname, self.server) + + # Configure the DHCP server + ipa = IpiAllocator(self.SUBNET) + (server_ip4,) = ipa.next_ipis() + (client_ip4,) = ipa.next_ipis() + + confpath = os.path.join(self.workdir, 'dhcpd.conf') + open(confpath, 'w').write(''' + subnet {} netmask {} {{ + option routers {}; + range {} {}; + }} + '''.format(self.SUBNET.network_address, self.SUBNET.netmask, + server_ip4.ip, client_ip4.ip, client_ip4.ip)) + self.pidpath = os.path.join(self.workdir, 'dhcpd.pid') + leasepath = os.path.join(self.workdir, 'dhcpd.leases') + open(leasepath, 'w').write('') + + self.server.ifup('lo') + self.server.ifup(server_ifname, server_ip4) + + opts = ('-f -d -4 -cf {} -lf {} -pf {}'.format(confpath, leasepath, self.pidpath)) + self.server.fg('{} -t {}'.format(self.DHCPD, opts)) # test config + self.dhcpd = self.server.bg('{} {}'.format(self.DHCPD, opts), sudo=True) + + # Configure the client + self.client.ifup('lo') + BaseDhcpTasst.subsetup(self, self.client, ifname, client_ip4.ip, server_ip4.ip, 1500) + + def tearDown(self): + pid = int(open(self.pidpath).read()) + self.server.fg('kill {}'.format(pid)) + status = self.dhcpd.wait() + self.server.close() + self.client.close() + super().tearDown() diff --git a/avocado/tasst/dhcpv6.py b/avocado/tasst/dhcpv6.py new file mode 100644 index 0000000..e885c06 --- /dev/null +++ b/avocado/tasst/dhcpv6.py @@ -0,0 +1,123 @@ +#! /usr/bin/python3 + +# SPDX-License-Identifier: GPL-2.0-or-later +# +# tasst - Test A Simple Socket Transport +# library of test helpers for passt & pasta +# +# tasst/dhcpv6.py - Helpers for testing DHCPv6 +# +# Copyright Red Hat +# Author: David Gibson <david(a)gibson.dropbear.id.au> + +import ipaddress +import os + +from tasst import Tasst, TasstSubData +from tasst.address import IpiAllocator, TEST_NET6_TASST_A +from tasst.nstool import UnshareSite + + +class BaseDhcpv6Tasst(Tasst): + """ + Test DHCPv6 behaviour. + + :avocado: disable + """ + + DHCLIENT = '/sbin/dhclient' + + def subsetup(self, site, ifname, addr): + site.require_cmds(self.DHCLIENT) + + pidfile = os.path.join(self.workdir, 'dhclient.pid') + leasefile = os.path.join(self.workdir, 'dhclient.leases') + + # We need '-nc' because we may be running with capabilities + # but not UID 0. Without -nc dhclient drops capabilities + # before invoking dhclient-script, so it's unable to actually + # configure the interface + site.fg('{} -6 -v -nc -pf {} -lf {} {}' + .format(self.DHCLIENT, pidfile, leasefile, ifname), sudo=True) + + Tasst.subsetup(self, BaseDhcpv6Tasst, + TasstSubData(site=site, ifname=ifname, addr=addr)) + + def test_addr(self): + sub = self.get_subsetup(BaseDhcpv6Tasst) + addrs = [a.ip for a in sub.site.addrs(sub.ifname, family='inet6', scope='global')] + self.assertIn(sub.addr, addrs) # Might also have a SLAAC address + + +class MetaDhcpv6Tasst(BaseDhcpv6Tasst): + """Ugly workaround for + https://github.com/avocado-framework/avocado/issues/5680. + Explicitly apply the "meta" tag to inherited tests + + :avocado: disable + :avocado: tags=meta + + """ + + def test_addr(self): + super().test_addr() + + +class Dhcpd6Tasst(MetaDhcpv6Tasst): + """ + :avocado: tags=meta + """ + + DHCPD = 'dhcpd' + SUBNET = TEST_NET6_TASST_A + + def setUp(self): + super().setUp() + + ifname = 'clientif' + server_ifname = 'serverif' + + self.client = UnshareSite(self.__class__.__name__ + '.client', '-Un') + self.server = UnshareSite(self.__class__.__name__ + '.server', '-n', + parent=self.client, sudo=True) + + self.server.require_cmds(self.DHCPD) + + self.client.veth(ifname, server_ifname, self.server) + + # Allocate IPs, and sort out link local addressing + ipa = IpiAllocator(self.SUBNET) + (server_ip6,) = ipa.next_ipis() + (client_ip6,) = ipa.next_ipis() + + self.server.ifup('lo') + self.server.ifup(server_ifname, server_ip6) + self.client.ifup('lo') + self.client.ifup(ifname) + (server_ip6_ll,) = self.server.addr_wait(server_ifname, family='inet6', scope='link') + + # Configure the DHCP server + confpath = os.path.join(self.workdir, 'dhcpd.conf') + open(confpath, 'w').write(''' + subnet6 {} {{ + range6 {} {}; + }} + '''.format(self.SUBNET, client_ip6.ip, client_ip6.ip)) + self.pidpath = os.path.join(self.workdir, 'dhcpd.pid') + leasepath = os.path.join(self.workdir, 'dhcpd.leases') + open(leasepath, 'w').write('') + + opts = ('-f -d -6 -cf {} -lf {} -pf {}'.format(confpath, leasepath, self.pidpath)) + self.server.fg('{} -t {}'.format(self.DHCPD, opts)) # test config + self.dhcpd = self.server.bg('{} {}'.format(self.DHCPD, opts), sudo=True) + + # Configure the client + BaseDhcpv6Tasst.subsetup(self, self.client, ifname, client_ip6.ip) + + def tearDown(self): + pid = int(open(self.pidpath).read()) + self.server.fg('kill {}'.format(pid)) + status = self.dhcpd.wait() + self.server.close() + self.client.close() + super().tearDown() -- 2.40.1