1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
| | #! /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@gibson.dropbear.id.au>
import contextlib
import ipaddress
import os
from tasst import Tasst, TasstSubData
from tasst.address import IpiAllocator, TEST_NET6_TASST_A
from tasst.nstool import UnshareSite
from tasst.site import Site
from tasst.typing import typecheck
class Dhcpv6TasstInfo:
def __init__(self, site, ifname, addr):
self.site = typecheck(site, Site)
self.ifname = typecheck(ifname, str)
self.addr = typecheck(addr, ipaddress.IPv6Address)
site.require_cmds('ip')
class BaseDhcpv6Tasst(Tasst):
"""
Test DHCPv6 behaviour.
:avocado: disable
"""
DHCLIENT = '/sbin/dhclient'
def setup_dhcpv6(self):
raise NotImplementedError("{} must implement setup_dhcpv6() method".format(type(self).__name__))
@contextlib.contextmanager
def dhclientv6(self):
with self.setup_dhcpv6() as d6ti:
d6ti.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
d6ti.site.fg('{} -6 -v -nc -pf {} -lf {} {}'
.format(self.DHCLIENT, pidfile, leasefile, d6ti.ifname), sudo=True)
yield d6ti
pid = int(d6ti.site.output('cat {}'.format(pidfile)))
d6ti.site.fg('kill {}'.format(pid))
def test_addr(self):
with self.dhclientv6() as d6ti:
addrs = [a.ip for a in d6ti.site.addrs(d6ti.ifname, family='inet6', scope='global')]
self.assertIn(d6ti.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
@contextlib.contextmanager
def setup_dhcpv6(self):
ifname = 'clientif'
server_ifname = 'serverif'
with UnshareSite(type(self).__name__ + '.client', '-Un') as client, \
UnshareSite(type(self).__name__ + '.server', '-n',
parent=client, sudo=True) as server:
server.require_cmds(self.DHCPD)
client.veth(ifname, server_ifname, server)
# Allocate IPs, and sort out link local addressing
ipa = IpiAllocator(self.SUBNET)
(server_ip6,) = ipa.next_ipis()
(client_ip6,) = ipa.next_ipis()
server.ifup('lo')
server.ifup(server_ifname, server_ip6)
client.ifup('lo')
client.ifup(ifname)
(server_ip6_ll,) = 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))
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, pidpath))
server.fg('{} -t {}'.format(self.DHCPD, opts)) # test config
with server.bg('{} {}'.format(self.DHCPD, opts), sudo=True) as dhcpd:
yield Dhcpv6TasstInfo(client, ifname, client_ip6.ip)
pid = int(open(pidpath).read())
server.fg('kill {}'.format(pid))
status = dhcpd.wait()
|