Merge #1 Adds tests according to the CI
This commit is contained in:
commit
b58246c039
20
tests/ipv4-tests/dhcpcd-domain-dns.conf
Normal file
20
tests/ipv4-tests/dhcpcd-domain-dns.conf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
debug
|
||||||
|
# Inform the DHCP server of our hostname for DDNS.
|
||||||
|
hostname
|
||||||
|
|
||||||
|
# Use the hardware address of the interface for the Client ID.
|
||||||
|
clientid
|
||||||
|
|
||||||
|
# Persist interface configuration when dhcpcd exits.
|
||||||
|
persistent
|
||||||
|
|
||||||
|
# A list of options to request from the DHCP server.
|
||||||
|
option domain_name_servers, domain_name, domain_search, host_name
|
||||||
|
option classless_static_routes
|
||||||
|
# Most distributions have NTP support.
|
||||||
|
option ntp_servers
|
||||||
|
# Respect the network MTU. This is applied to DHCP routes.
|
||||||
|
option interface_mtu
|
||||||
|
|
||||||
|
# A ServerID is required by RFC2131.
|
||||||
|
require dhcp_server_identifier
|
20
tests/ipv4-tests/dhcpcd-mtu.conf
Normal file
20
tests/ipv4-tests/dhcpcd-mtu.conf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
debug
|
||||||
|
# Inform the DHCP server of our hostname for DDNS.
|
||||||
|
hostname
|
||||||
|
|
||||||
|
# Use the hardware address of the interface for the Client ID.
|
||||||
|
clientid
|
||||||
|
|
||||||
|
# Persist interface configuration when dhcpcd exits.
|
||||||
|
persistent
|
||||||
|
|
||||||
|
# A list of options to request from the DHCP server.
|
||||||
|
option domain_name_servers, domain_name, domain_search, host_name
|
||||||
|
option classless_static_routes
|
||||||
|
# Most distributions have NTP support.
|
||||||
|
option ntp_servers
|
||||||
|
# Respect the network MTU. This is applied to DHCP routes.
|
||||||
|
option interface_mtu
|
||||||
|
|
||||||
|
# A ServerID is required by RFC2131.
|
||||||
|
require dhcp_server_identifier
|
213
tests/ipv4-tests/dhcpcd-tests.py
Executable file
213
tests/ipv4-tests/dhcpcd-tests.py
Executable file
@ -0,0 +1,213 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
# ~~~
|
||||||
|
# Description: Tests for dhcpcd - a DHCP client
|
||||||
|
#
|
||||||
|
# Author: Susant Sahani <susant@redhat.com>
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
# ~~~
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
import subprocess
|
||||||
|
import signal
|
||||||
|
import shutil
|
||||||
|
import psutil
|
||||||
|
import socket
|
||||||
|
from pyroute2 import IPRoute
|
||||||
|
|
||||||
|
DHCPCD_CI_DIR="/var/run/dhcpcd-ci"
|
||||||
|
DHCPCD_LOG_FILE='/var/run/dhcpcd-ci/dhcpcd-test-log'
|
||||||
|
DHCPCD_PID_FILE='/var/run/dhcpcd.pid'
|
||||||
|
|
||||||
|
DHCPCD_TCP_DUMP_FILE='/tmp/dhcpcd-tcp-dump.pcap'
|
||||||
|
|
||||||
|
DNSMASQ_PID_FILE='/var/run/dhcpcd-ci/test-dnsmasq.pid'
|
||||||
|
DNSMASQ_LOG_FILE='/var/run/dhcpcd-ci/dnsmasq-log-file'
|
||||||
|
|
||||||
|
def setUpModule():
|
||||||
|
"""Initialize the environment, and perform sanity checks on it."""
|
||||||
|
|
||||||
|
if shutil.which('dhcpcd') is None:
|
||||||
|
raise OSError(errno.ENOENT, 'dhcpcd not found')
|
||||||
|
|
||||||
|
if shutil.which('dnsmasq') is None:
|
||||||
|
raise OSError(errno.ENOENT, 'dnsmasq not found')
|
||||||
|
|
||||||
|
|
||||||
|
def tearDownModule():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class GenericUtilities():
|
||||||
|
"""Provide a set of utility functions start stop daemons. write config files etc """
|
||||||
|
|
||||||
|
def StartDnsMasq(self, conf):
|
||||||
|
"""Start DnsMasq"""
|
||||||
|
|
||||||
|
conf_file=os.path.join(DHCPCD_CI_DIR, conf)
|
||||||
|
|
||||||
|
subprocess.check_output(['dnsmasq', '-8', DNSMASQ_LOG_FILE, '--log-dhcp', '--pid-file=/var/run/dhcpcd-ci/test-dnsmasq.pid',
|
||||||
|
'-C', conf_file, '-i', 'veth-peer', '-R'])
|
||||||
|
|
||||||
|
def StartDhcpcd(self, conf):
|
||||||
|
""" Start dnsmaq """
|
||||||
|
|
||||||
|
conf_file=os.path.join(DHCPCD_CI_DIR, conf)
|
||||||
|
subprocess.check_output(['dhcpcd', '-4', '-M', '-d', '--logfile', DHCPCD_LOG_FILE, '-f', conf_file, 'veth-test'])
|
||||||
|
|
||||||
|
def StopDaemon(self, pid_file):
|
||||||
|
|
||||||
|
with open(pid_file, 'r') as f:
|
||||||
|
pid = f.read().rstrip(' \t\r\n\0')
|
||||||
|
os.kill(int(pid), signal.SIGTERM)
|
||||||
|
|
||||||
|
os.remove(pid_file)
|
||||||
|
|
||||||
|
def findTextInDaemonLogs(self, log_file, **kwargs):
|
||||||
|
"""dnsmasq server logs."""
|
||||||
|
|
||||||
|
if kwargs is not None:
|
||||||
|
with open (log_file, 'rt') as in_file:
|
||||||
|
contents = in_file.read()
|
||||||
|
for key in kwargs:
|
||||||
|
self.assertRegex(contents, kwargs[key])
|
||||||
|
|
||||||
|
def FindProtocolFieldsinTCPDump(self, **kwargs):
|
||||||
|
"""Look attributes in tcpdump."""
|
||||||
|
|
||||||
|
contents = subprocess.check_output(['tcpdump', '-vv', '-r', DHCPCD_TCP_DUMP_FILE]).rstrip().decode('utf-8')
|
||||||
|
if kwargs is not None:
|
||||||
|
for key in kwargs:
|
||||||
|
self.assertRegex(contents, kwargs[key])
|
||||||
|
|
||||||
|
def SetupVethInterface(self):
|
||||||
|
"""Setup veth interface"""
|
||||||
|
|
||||||
|
ip = IPRoute()
|
||||||
|
|
||||||
|
ip.link('add', ifname='veth-test', peer='veth-peer', kind='veth')
|
||||||
|
idx_veth_test = ip.link_lookup(ifname='veth-test')[0]
|
||||||
|
idx_veth_peer = ip.link_lookup(ifname='veth-peer')[0]
|
||||||
|
|
||||||
|
ip.link('set', index=idx_veth_test, address='02:01:02:03:04:08')
|
||||||
|
ip.link('set', index=idx_veth_peer, address='02:01:02:03:04:09')
|
||||||
|
ip.link('set', index=idx_veth_test, state='up')
|
||||||
|
ip.link('set', index=idx_veth_peer, state='up')
|
||||||
|
ip.addr('add', index=idx_veth_peer, address='192.168.111.50')
|
||||||
|
ip.close()
|
||||||
|
|
||||||
|
def TearDownVethInterface(self):
|
||||||
|
|
||||||
|
ip = IPRoute()
|
||||||
|
ip.link('del', index=ip.link_lookup(ifname='veth-test')[0])
|
||||||
|
ip.close()
|
||||||
|
|
||||||
|
def StartCaptureBOOTPPackets(self):
|
||||||
|
"""Start tcpdump to capture dhcp packets"""
|
||||||
|
|
||||||
|
subprocess.check_output(['systemctl','restart', 'tcpdumpd.service'])
|
||||||
|
|
||||||
|
def StopCapturingPackets(self):
|
||||||
|
subprocess.check_output(['systemctl', 'stop', 'tcpdumpd.service'])
|
||||||
|
time.sleep(3);
|
||||||
|
|
||||||
|
class DhcpcdTests(unittest.TestCase, GenericUtilities):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
""" setup veth and write radvd and dhcpv6configs """
|
||||||
|
self.SetupVethInterface()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.StopDaemon(DHCPCD_PID_FILE)
|
||||||
|
self.StopDaemon(DNSMASQ_PID_FILE)
|
||||||
|
|
||||||
|
self.TearDownVethInterface()
|
||||||
|
|
||||||
|
def test_dhcpcd_ipv4(self):
|
||||||
|
""" dhcpcd gets address """
|
||||||
|
|
||||||
|
self.StartDnsMasq('dnsmasq-ipv4.conf')
|
||||||
|
time.sleep(1)
|
||||||
|
self.StartDhcpcd('dhcpcd-domain-dns.conf')
|
||||||
|
|
||||||
|
time.sleep(5)
|
||||||
|
output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
|
||||||
|
# Address prefix
|
||||||
|
self.assertRegex(output, "192.168.111.*")
|
||||||
|
|
||||||
|
# Default route
|
||||||
|
output=subprocess.check_output(['ip','route', 'show', 'dev', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
self.assertRegex(output, "default via 192.168.1.1*")
|
||||||
|
|
||||||
|
def test_dhcpcd_dns_domain(self):
|
||||||
|
""" dhcpcd request DNS and domain name """
|
||||||
|
|
||||||
|
self.StartDnsMasq('dnsmasq-ipv4.conf')
|
||||||
|
time.sleep(1)
|
||||||
|
self.StartDhcpcd('dhcpcd-domain-dns.conf')
|
||||||
|
|
||||||
|
output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
|
||||||
|
# Address prefix
|
||||||
|
self.assertRegex(output, "192.168.111.*")
|
||||||
|
|
||||||
|
# Default route
|
||||||
|
output=subprocess.check_output(['ip','route', 'show', 'dev', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
self.assertRegex(output, "default via 192.168.1.1*")
|
||||||
|
|
||||||
|
# Dump the lease file
|
||||||
|
output=subprocess.check_output(['dhcpcd','-U', '-4', 'veth-test'], stderr=subprocess.STDOUT).rstrip().decode('utf-8')
|
||||||
|
self.assertRegex(output, 'domain_name=example-test.com')
|
||||||
|
self.assertRegex(output, 'domain_name_servers=\'8.8.8.8 8.8.4.4\'')
|
||||||
|
self.assertRegex(output, 'routers=192.168.1.1')
|
||||||
|
|
||||||
|
def test_dhcpcd_mtu(self):
|
||||||
|
""" dhcpcd gets MTU 1492 """
|
||||||
|
|
||||||
|
self.StartDnsMasq('dnsmasq-mtu.conf')
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
self.StartDhcpcd('dhcpcd-mtu.conf')
|
||||||
|
time.sleep(5)
|
||||||
|
output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
|
||||||
|
# Address prefix
|
||||||
|
self.assertRegex(output, "192.168.111.*")
|
||||||
|
|
||||||
|
# Dump the lease file
|
||||||
|
output=subprocess.check_output(['dhcpcd','-U', '-4', 'veth-test'], stderr=subprocess.STDOUT).rstrip().decode('utf-8')
|
||||||
|
self.assertRegex(output, 'interface_mtu=1492')
|
||||||
|
|
||||||
|
def test_dhcpcd_clientid_vendorclassid_userclass(self):
|
||||||
|
""" verify dhcpcd sends custom clientid vendor class id and userclass """
|
||||||
|
|
||||||
|
self.StartDnsMasq('dnsmasq-vendorclass.conf')
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
self.StartCaptureBOOTPPackets()
|
||||||
|
|
||||||
|
self.StartDhcpcd('dhcpcd-vendorclass.conf')
|
||||||
|
time.sleep(5)
|
||||||
|
self.StopCapturingPackets()
|
||||||
|
|
||||||
|
output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
# Address prefix
|
||||||
|
self.assertRegex(output, "192.168.111.*")
|
||||||
|
|
||||||
|
self.findTextInDaemonLogs(DNSMASQ_LOG_FILE, vendor_class='vendor class: Zeus_dhcpcd_vendorclass_id',
|
||||||
|
user_class='user class: AAAA BBBB CCCC DDDD',
|
||||||
|
host_name='client provides name: Zeus')
|
||||||
|
self.findTextInDaemonLogs(DHCPCD_LOG_FILE, client_id='using ClientID 00:11:11:12:12:13:13:14:14:15:15:16:16')
|
||||||
|
self.FindProtocolFieldsinTCPDump(vendor='Vendor-Option Option 43, length 11: 104.101.108.108.111.32.119.111.114.108.100',
|
||||||
|
user_class='instance#1:.*AAAA BBBB CCCC DDDD", length 19',
|
||||||
|
vendor_class='Vendor-Class Option 60, length 26:.*Zeus_dhcpcd_vendorclass_id',
|
||||||
|
host_name='Hostname.*Zeus')
|
||||||
|
os.remove(DHCPCD_TCP_DUMP_FILE)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
|
||||||
|
verbosity=3))
|
9
tests/ipv4-tests/dhcpcd-vendorclass.conf
Normal file
9
tests/ipv4-tests/dhcpcd-vendorclass.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
debug
|
||||||
|
hostname Zeus
|
||||||
|
# Use custom Client ID.
|
||||||
|
clientid 00:11:11:12:12:13:13:14:14:15:15:16:16
|
||||||
|
vendorclassid Zeus_dhcpcd_vendorclass_id
|
||||||
|
userclass "AAAA BBBB CCCC DDDD"
|
||||||
|
vendor ,"hello world"
|
||||||
|
# A ServerID is required by RFC2131.
|
||||||
|
require dhcp_server_identifier
|
15
tests/ipv4-tests/dnsmasq-ipv4.conf
Normal file
15
tests/ipv4-tests/dnsmasq-ipv4.conf
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
interface=veth-peer
|
||||||
|
bind-interfaces
|
||||||
|
|
||||||
|
# Optionally set a domain name
|
||||||
|
domain=example-test.com
|
||||||
|
|
||||||
|
# Set default gateway
|
||||||
|
dhcp-option=3,192.168.1.1
|
||||||
|
|
||||||
|
# Set DNS servers to announce
|
||||||
|
dhcp-option=6,8.8.8.8,8.8.4.4
|
||||||
|
|
||||||
|
# Dynamic range of IPs to make available to LAN PC and the lease time.
|
||||||
|
# Ideally set the lease time to 5m only at first to test everything works okay before you set long-lasting records.
|
||||||
|
dhcp-range=192.168.111.50,192.168.111.100,255.255.255.0,1h
|
18
tests/ipv4-tests/dnsmasq-mtu.conf
Normal file
18
tests/ipv4-tests/dnsmasq-mtu.conf
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
interface=veth-peer
|
||||||
|
bind-interfaces
|
||||||
|
|
||||||
|
# Optionally set a domain name
|
||||||
|
domain=example-test.com
|
||||||
|
|
||||||
|
# Set default gateway
|
||||||
|
dhcp-option=3,192.168.1.1
|
||||||
|
|
||||||
|
# Set DNS servers to announce
|
||||||
|
dhcp-option=6,8.8.8.8,8.8.4.4
|
||||||
|
|
||||||
|
# MTU
|
||||||
|
dhcp-option=26,1492
|
||||||
|
|
||||||
|
# Dynamic range of IPs to make available to LAN PC and the lease time.
|
||||||
|
# Ideally set the lease time to 5m only at first to test everything works okay before you set long-lasting records.
|
||||||
|
dhcp-range=192.168.111.50,192.168.111.100,255.255.255.0,1h
|
6
tests/ipv4-tests/dnsmasq-vendorclass.conf
Normal file
6
tests/ipv4-tests/dnsmasq-vendorclass.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
interface=veth-peer
|
||||||
|
bind-interfaces
|
||||||
|
|
||||||
|
# Dynamic range of IPs to make available to LAN PC and the lease time.
|
||||||
|
# Ideally set the lease time to 5m only at first to test everything works okay before you set long-lasting records.
|
||||||
|
dhcp-range=192.168.111.50,192.168.111.100,255.255.255.0,1h
|
63
tests/ipv4-tests/runtest.sh
Executable file
63
tests/ipv4-tests/runtest.sh
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
# ~~~
|
||||||
|
# runtest.sh of dhcpcd
|
||||||
|
# Description: a DHCP and DHCPv6 client. It's also an IPv4LL (aka ZeroConf) client.
|
||||||
|
#
|
||||||
|
# Author: Susant Sahani <susant@redhat.com>
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
# ~~~
|
||||||
|
|
||||||
|
# Include Beaker environment
|
||||||
|
. /usr/share/beakerlib/beakerlib.sh || exit 1
|
||||||
|
|
||||||
|
PACKAGE="dhcpcd"
|
||||||
|
DHCPCD_CI_DIR="/var/run/dhcpcd-ci"
|
||||||
|
|
||||||
|
SERVICE_UNITDIR="/var/run/systemd/system"
|
||||||
|
RESOLVE_CONF="/etc/resolv.conf"
|
||||||
|
|
||||||
|
rlJournalStart
|
||||||
|
rlPhaseStartSetup
|
||||||
|
rlAssertRpm $PACKAGE
|
||||||
|
rlRun "systemctl stop firewalld" 0,5
|
||||||
|
rlRun "setenforce 0" 0,1
|
||||||
|
|
||||||
|
rlFileBackup "$RESOLVE_CONF"
|
||||||
|
|
||||||
|
rlRun "[ -e /sys/class/net/veth-test ] && ip link del veth-test" 0,1
|
||||||
|
|
||||||
|
rlLog "Create work dir ..."
|
||||||
|
rlRun "mkdir -p $DHCPCD_CI_DIR"
|
||||||
|
rlRun "cp *.conf $DHCPCD_CI_DIR"
|
||||||
|
|
||||||
|
rlRun "cp tcpdumpd.service $SERVICE_UNITDIR"
|
||||||
|
rlRun "cp dhcpcd-tests.py /usr/bin/"
|
||||||
|
|
||||||
|
rlRun "systemctl daemon-reload"
|
||||||
|
rlPhaseEnd
|
||||||
|
|
||||||
|
rlPhaseStartTest
|
||||||
|
rlLog "Starting dhcpcd tests ..."
|
||||||
|
rlRun "/usr/bin/python3 /usr/bin/dhcpcd-tests.py"
|
||||||
|
rlPhaseEnd
|
||||||
|
|
||||||
|
rlPhaseStartCleanup
|
||||||
|
rlRun "rm /usr/bin/dhcpcd-tests.py"
|
||||||
|
rlRun "[ -e /sys/class/net/veth-test ] && ip link del veth-test" 0,1
|
||||||
|
|
||||||
|
rlFileRestore
|
||||||
|
|
||||||
|
rlLog "remove work dir"
|
||||||
|
rlRun "rm -rf $DHCPCD_CI_DIR"
|
||||||
|
|
||||||
|
rlRun "rm $SERVICE_UNITDIR/tcpdumpd.service"
|
||||||
|
rlRun "systemctl daemon-reload"
|
||||||
|
|
||||||
|
rlRun "setenforce 1" 0,1
|
||||||
|
rlLog "dhcpcd tests done"
|
||||||
|
rlPhaseEnd
|
||||||
|
rlJournalPrintText
|
||||||
|
rlJournalEnd
|
||||||
|
|
||||||
|
rlGetTestState
|
10
tests/ipv4-tests/tcpdumpd.service
Normal file
10
tests/ipv4-tests/tcpdumpd.service
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=TCPDumpd
|
||||||
|
After=multi-user.target network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/sbin/tcpdump -i veth-peer port 67 or port 68 -vvv -w "/tmp/dhcpcd-tcp-dump.pcap"
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
20
tests/ipv6-tests/dhcp6s.conf
Normal file
20
tests/ipv6-tests/dhcp6s.conf
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
option domain-name-servers 2001:888:0db8:1::a 2001:888:db8:1::d;
|
||||||
|
option domain-name "test.com";
|
||||||
|
|
||||||
|
host zeus {
|
||||||
|
duid 00:01:00:01:22:8a:88:26:08:00:27:87:00:7e;
|
||||||
|
address 2001:888:db8:1::b infinity;
|
||||||
|
};
|
||||||
|
|
||||||
|
host test {
|
||||||
|
duid 00:01:00:01:22:8d:cb:58:0a:00:27:00:00:00;
|
||||||
|
address 2001:888:db8:1::c infinity;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface veth-peer {
|
||||||
|
address-pool pool1 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
pool pool1 {
|
||||||
|
range 2001:888:0db8:1::1000 to 2001:888:0db8:1::2000;
|
||||||
|
};
|
8
tests/ipv6-tests/dhcpcd-test.conf
Normal file
8
tests/ipv6-tests/dhcpcd-test.conf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
debug
|
||||||
|
interface veth-test
|
||||||
|
# Inform the DHCP server of our hostname for DDNS.
|
||||||
|
hostname
|
||||||
|
dhcp6
|
||||||
|
ipv6only
|
||||||
|
# Persist interface configuration when dhcpcd exits.
|
||||||
|
persistent
|
192
tests/ipv6-tests/dhcpcd-tests.py
Executable file
192
tests/ipv6-tests/dhcpcd-tests.py
Executable file
@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
# ~~~
|
||||||
|
# Description: DHCPv6 Tests for dhcpcd - a DHCP client
|
||||||
|
#
|
||||||
|
# Author: Susant Sahani <susant@redhat.com>
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
# ~~~
|
||||||
|
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
import subprocess
|
||||||
|
import signal
|
||||||
|
import shutil
|
||||||
|
import psutil
|
||||||
|
import socket
|
||||||
|
from pyroute2 import IPRoute
|
||||||
|
|
||||||
|
DHCPCD_CI_DIR="/var/run/dhcpcd-ci"
|
||||||
|
DHCPCD_LOG_FILE='/var/run/dhcpcd-ci/dhcpcd-test-log'
|
||||||
|
DHCPCD_CONF_FILE='/var/run/dhcpcd-ci/dhcpcd-test.conf'
|
||||||
|
|
||||||
|
DHCPCD_PID_FILE='/var/run/dhcpcd.pid'
|
||||||
|
DHCPCD_DUID_FILE='/etc/dhcpcd.duid'
|
||||||
|
|
||||||
|
RADVD_LOG_FILE ='/var/run/dhcpcd-ci/radvd.log'
|
||||||
|
RADVD_CONFIG_FILE ='/var/run/dhcpcd-ci/radvd-ci.conf'
|
||||||
|
RADVD_PID_FILE='/var/run/dhcpcd-ci/radvd.pid'
|
||||||
|
|
||||||
|
DHCP6S_CONFIG_FILE='/var/run/dhcpcd-ci/dhcp6s.conf'
|
||||||
|
DHCP6S_PID_FILE='/var/run/dhcp6s.pid'
|
||||||
|
|
||||||
|
RESOLVE_CONF='/etc/resolv.conf'
|
||||||
|
|
||||||
|
def setUpModule():
|
||||||
|
"""Initialize the environment, and perform sanity checks on it."""
|
||||||
|
|
||||||
|
if shutil.which('dhcpcd') is None:
|
||||||
|
raise OSError(errno.ENOENT, 'dhcpcd not found')
|
||||||
|
if shutil.which('dhcp6s') is None:
|
||||||
|
raise OSError(errno.ENOENT, 'dhcdp6s not found')
|
||||||
|
if shutil.which('radvd') is None:
|
||||||
|
raise OSError(errno.ENOENT, 'radvd not found')
|
||||||
|
|
||||||
|
def tearDownModule():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class GenericUtilities():
|
||||||
|
"""Provide a set of utility functions start stop daemons. write config files etc """
|
||||||
|
|
||||||
|
def StartDhcp6s(self):
|
||||||
|
"""Start dhcp6s"""
|
||||||
|
|
||||||
|
subprocess.check_output(['dhcp6s', '-c', DHCP6S_CONFIG_FILE, 'veth-peer', '-dD'])
|
||||||
|
|
||||||
|
def StartRadvd(self):
|
||||||
|
"""Start radvd"""
|
||||||
|
|
||||||
|
subprocess.check_output(['radvd', '-d5', '-C', RADVD_CONFIG_FILE, '-p', RADVD_PID_FILE, '-l', RADVD_LOG_FILE])
|
||||||
|
|
||||||
|
def StartDhcpcd(self):
|
||||||
|
subprocess.check_output(['dhcpcd', '-6', '-M', '-d', '--logfile', DHCPCD_LOG_FILE, '-f', DHCPCD_CONF_FILE, 'veth-test'])
|
||||||
|
|
||||||
|
def StopDaemon(self, pid_file):
|
||||||
|
|
||||||
|
with open(pid_file, 'r') as f:
|
||||||
|
pid = f.read().rstrip(' \t\r\n\0')
|
||||||
|
os.kill(int(pid), signal.SIGTERM)
|
||||||
|
|
||||||
|
os.remove(pid_file)
|
||||||
|
|
||||||
|
def WriteConfigFile(self, path, contents):
|
||||||
|
"""Write a config file, and queue it to be removed."""
|
||||||
|
|
||||||
|
with open(path, 'w') as unit:
|
||||||
|
unit.write(contents)
|
||||||
|
|
||||||
|
self.addCleanup(os.remove, path)
|
||||||
|
|
||||||
|
def findTextInDaemonLogs(self, log_file, **kwargs):
|
||||||
|
"""dnsmasq server logs."""
|
||||||
|
|
||||||
|
if kwargs is not None:
|
||||||
|
with open (log_file, 'rt') as in_file:
|
||||||
|
contents = in_file.read()
|
||||||
|
for key in kwargs:
|
||||||
|
self.assertRegex(contents, kwargs[key])
|
||||||
|
|
||||||
|
def SetupVethInterface(self):
|
||||||
|
"""Setup veth interface"""
|
||||||
|
|
||||||
|
ip = IPRoute()
|
||||||
|
|
||||||
|
ip.link('add', ifname='veth-test', peer='veth-peer', kind='veth')
|
||||||
|
idx_veth_test = ip.link_lookup(ifname='veth-test')[0]
|
||||||
|
idx_veth_peer = ip.link_lookup(ifname='veth-peer')[0]
|
||||||
|
|
||||||
|
ip.link('set', index=idx_veth_test, address='02:01:02:03:04:08')
|
||||||
|
ip.link('set', index=idx_veth_peer, address='02:01:02:03:04:09')
|
||||||
|
ip.link('set', index=idx_veth_test, state='up')
|
||||||
|
ip.link('set', index=idx_veth_peer, state='up')
|
||||||
|
ip.addr('add', index=idx_veth_peer, address='192.168.111.50')
|
||||||
|
|
||||||
|
ip.close()
|
||||||
|
|
||||||
|
def TearDownVethInterface(self):
|
||||||
|
|
||||||
|
ip = IPRoute()
|
||||||
|
ip.link('del', index=ip.link_lookup(ifname='veth-test')[0])
|
||||||
|
ip.close()
|
||||||
|
|
||||||
|
class DhcpcdTests(unittest.TestCase, GenericUtilities):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
""" setup veth and write radvd and dhcpv6configs """
|
||||||
|
self.SetupVethInterface()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.StopDaemon(DHCPCD_PID_FILE)
|
||||||
|
self.StopDaemon(RADVD_PID_FILE)
|
||||||
|
self.StopDaemon(DHCP6S_PID_FILE)
|
||||||
|
|
||||||
|
self.TearDownVethInterface()
|
||||||
|
|
||||||
|
def test_dhcp6s_gets_rdnss_dnssl(self):
|
||||||
|
""" DHCP6c gets the RDNSS DNSSL """
|
||||||
|
|
||||||
|
self.StartDhcp6s()
|
||||||
|
self.StartRadvd()
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
self.WriteConfigFile(DHCPCD_DUID_FILE, '''00:01:00:01:22:8a:88:26:08:00:27:87:00:7e\n''')
|
||||||
|
|
||||||
|
self.StartDhcpcd()
|
||||||
|
|
||||||
|
time.sleep(10)
|
||||||
|
output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
|
||||||
|
# Address prefix
|
||||||
|
self.assertRegex(output, "2001:888:db8:1::b")
|
||||||
|
self.findTextInDaemonLogs(RESOLVE_CONF,
|
||||||
|
dns1='2001:888:db8:1::a',
|
||||||
|
dns2='2001:888:db8:1::d',
|
||||||
|
search='test.com')
|
||||||
|
|
||||||
|
def test_dhcp6s_assigns_static_address_using_duid2(self):
|
||||||
|
""" DHCP6c gets the (static) addresses to hosts using known DUID value 00:01:00:01:22:8d:cb:58:0a:00:27:00:00:00 """
|
||||||
|
|
||||||
|
self.StartDhcp6s()
|
||||||
|
self.StartRadvd()
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
self.WriteConfigFile(DHCPCD_DUID_FILE, '''00:01:00:01:22:8d:cb:58:0a:00:27:00:00:00\n''')
|
||||||
|
|
||||||
|
self.StartDhcpcd()
|
||||||
|
time.sleep(10)
|
||||||
|
output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
|
||||||
|
# Address
|
||||||
|
self.assertRegex(output, "2001:888:db8:1::c")
|
||||||
|
self.findTextInDaemonLogs(RESOLVE_CONF,
|
||||||
|
dns='2001:888:db8:1::a',
|
||||||
|
search='test.com')
|
||||||
|
|
||||||
|
def test_dhcp6s_assigns_static_address_using_duid1(self):
|
||||||
|
""" DHCP6c gets the (static) addresses to hosts using known DUID value 00:01:00:01:22:8a:88:26:08:00:27:87:00:7e """
|
||||||
|
|
||||||
|
self.StartDhcp6s()
|
||||||
|
self.StartRadvd()
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
self.WriteConfigFile(DHCPCD_DUID_FILE, '''00:01:00:01:22:8a:88:26:08:00:27:87:00:7e\n''')
|
||||||
|
|
||||||
|
self.StartDhcpcd()
|
||||||
|
time.sleep(10)
|
||||||
|
output=subprocess.check_output(['ip','address', 'show', 'veth-test']).rstrip().decode('utf-8')
|
||||||
|
|
||||||
|
# Address prefix
|
||||||
|
self.assertRegex(output, "2001:888:db8:1::b")
|
||||||
|
self.findTextInDaemonLogs(RESOLVE_CONF,
|
||||||
|
dns='2001:888:db8:1::a',
|
||||||
|
search='test.com')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout,
|
||||||
|
verbosity=3))
|
9
tests/ipv6-tests/radvd-ci.conf
Normal file
9
tests/ipv6-tests/radvd-ci.conf
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
interface veth-peer
|
||||||
|
{
|
||||||
|
AdvSendAdvert on;
|
||||||
|
AdvManagedFlag on;
|
||||||
|
AdvOtherConfigFlag on;
|
||||||
|
prefix 2001:888:0db8:1::/64 {
|
||||||
|
AdvAutonomous off;
|
||||||
|
};
|
||||||
|
};
|
59
tests/ipv6-tests/runtest.sh
Executable file
59
tests/ipv6-tests/runtest.sh
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
# ~~~
|
||||||
|
# runtest.sh of dhcpcd
|
||||||
|
# Description: a DHCP and DHCPv6 client. It's also an IPv4LL (aka ZeroConf) client.
|
||||||
|
#
|
||||||
|
# Author: Susant Sahani <susant@redhat.com>
|
||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
# ~~~
|
||||||
|
|
||||||
|
# Include Beaker environment
|
||||||
|
. /usr/share/beakerlib/beakerlib.sh || exit 1
|
||||||
|
|
||||||
|
PACKAGE="dhcpcd"
|
||||||
|
|
||||||
|
DHCPCD_CI_DIR="/var/run/dhcpcd-ci"
|
||||||
|
DHCPCD_DUID_FILE="/etc/dhcpcd.duid"
|
||||||
|
|
||||||
|
RESOLVE_CONF="/etc/resolv.conf"
|
||||||
|
|
||||||
|
rlJournalStart
|
||||||
|
rlPhaseStartSetup
|
||||||
|
rlAssertRpm $PACKAGE
|
||||||
|
|
||||||
|
rlRun "systemctl stop firewalld" 0,5
|
||||||
|
rlRun "setenforce 0" 0,1
|
||||||
|
|
||||||
|
rlFileBackup "$RESOLVE_CONF"
|
||||||
|
rlFileBackup "$DHCPCD_DUID_FILE"
|
||||||
|
rlRun "[ -e /sys/class/net/veth-test ] && ip link del veth-test" 0,1
|
||||||
|
|
||||||
|
rlLog "Create work dir ..."
|
||||||
|
rlRun "mkdir -p $DHCPCD_CI_DIR"
|
||||||
|
rlRun "cp *.conf $DHCPCD_CI_DIR"
|
||||||
|
|
||||||
|
rlRun "cp dhcpcd-tests.py /usr/bin/"
|
||||||
|
rlPhaseEnd
|
||||||
|
|
||||||
|
rlPhaseStartTest
|
||||||
|
rlLog "Starting dhcpcd tests ..."
|
||||||
|
rlRun "/usr/bin/python3 /usr/bin/dhcpcd-tests.py"
|
||||||
|
rlPhaseEnd
|
||||||
|
|
||||||
|
rlPhaseStartCleanup
|
||||||
|
rlRun "rm /usr/bin/dhcpcd-tests.py"
|
||||||
|
rlRun "[ -e /sys/class/net/veth-test ] && ip link del veth-test" 0,1
|
||||||
|
|
||||||
|
rlFileRestore
|
||||||
|
|
||||||
|
rlLog "remove work dir"
|
||||||
|
rlRun "rm -rf $DHCPCD_CI_DIR"
|
||||||
|
|
||||||
|
rlRun "setenforce 1" 0,1
|
||||||
|
rlLog "dhcpcd tests done"
|
||||||
|
rlPhaseEnd
|
||||||
|
rlJournalPrintText
|
||||||
|
rlJournalEnd
|
||||||
|
|
||||||
|
rlGetTestState
|
18
tests/tests.yml
Normal file
18
tests/tests.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
- hosts: localhost
|
||||||
|
roles:
|
||||||
|
- role: standard-test-beakerlib
|
||||||
|
tags:
|
||||||
|
- classic
|
||||||
|
tests:
|
||||||
|
- ipv4-tests
|
||||||
|
- ipv6-tests
|
||||||
|
required_packages:
|
||||||
|
- radvd
|
||||||
|
- dnsmasq
|
||||||
|
- python3
|
||||||
|
- tcpdump
|
||||||
|
- systemd
|
||||||
|
- iproute
|
||||||
|
- dhcpcd
|
||||||
|
- wide-dhcpv6
|
||||||
|
- python3-pyroute2
|
Loading…
Reference in New Issue
Block a user