Merge pull request #30021 from yuwata/dhcp-client-id-cleanups

dhcp-server: save bound leases to persistent storage, and load it on start
This commit is contained in:
Yu Watanabe
2024-03-12 13:40:09 +09:00
committed by GitHub
28 changed files with 769 additions and 212 deletions
+7
View File
@@ -888,8 +888,10 @@ class NetworkdClientTest(ClientTestBase, unittest.TestCase):
set -eu
mkdir -p /run/systemd/network
mkdir -p /run/systemd/netif
mkdir -p /var/lib/systemd/network
mount -t tmpfs none /run/systemd/network
mount -t tmpfs none /run/systemd/netif
mount -t tmpfs none /var/lib/systemd/network
[ ! -e /run/dbus ] || mount -t tmpfs none /run/dbus
# create router/client veth pair
cat <<EOF >/run/systemd/network/50-test.netdev
@@ -917,6 +919,10 @@ DNS=192.168.5.1
{dhopts}
EOF
# For the networkd instance invoked below cannot support varlink connection.
# Hence, 'networkctl persistent-storage yes' cannot be used.
touch /run/systemd/netif/persistent-storage-ready
# run networkd as in systemd-networkd.service
exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=//; s/^[@+-]//; s/^!*//; p}}')
'''.format(ifr=self.if_router,
@@ -930,6 +936,7 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=/
'-p', 'InaccessibleDirectories=-/etc/systemd/network',
'-p', 'InaccessibleDirectories=-/run/systemd/network',
'-p', 'InaccessibleDirectories=-/run/systemd/netif',
'-p', 'InaccessibleDirectories=-/var/lib/systemd/network',
'--service-type=notify', script])
# wait until devices got created
+116 -90
View File
@@ -51,18 +51,17 @@ radvd_pid_file = '/run/networkd-ci/test-radvd.pid'
systemd_lib_paths = ['/usr/lib/systemd', '/lib/systemd']
which_paths = ':'.join(systemd_lib_paths + os.getenv('PATH', os.defpath).lstrip(':').split(':'))
systemd_source_dir = None
networkd_bin = shutil.which('systemd-networkd', path=which_paths)
resolved_bin = shutil.which('systemd-resolved', path=which_paths)
timesyncd_bin = shutil.which('systemd-timesyncd', path=which_paths)
udevd_bin = shutil.which('systemd-udevd', path=which_paths)
wait_online_bin = shutil.which('systemd-networkd-wait-online', path=which_paths)
networkctl_bin = shutil.which('networkctl', path=which_paths)
resolvectl_bin = shutil.which('resolvectl', path=which_paths)
timedatectl_bin = shutil.which('timedatectl', path=which_paths)
udevadm_bin = shutil.which('udevadm', path=which_paths)
systemd_udev_rules_build_dir = None
build_dir = None
source_dir = None
use_valgrind = False
valgrind_cmd = ''
@@ -365,12 +364,13 @@ def clear_networkd_conf_dropins():
rm_rf(networkd_conf_dropin_dir)
def setup_systemd_udev_rules():
if not systemd_udev_rules_build_dir:
if not build_dir:
return
mkdir_p(udev_rules_dir)
for path in [systemd_udev_rules_build_dir, os.path.join(systemd_source_dir, "rules.d")]:
for path in [build_dir, source_dir]:
path = os.path.join(path, "rules.d")
print(f"Copying udev rules from {path} to {udev_rules_dir}")
for rule in os.listdir(path):
@@ -378,6 +378,9 @@ def setup_systemd_udev_rules():
continue
cp(os.path.join(path, rule), udev_rules_dir)
def clear_networkd_state_files():
rm_rf('/var/lib/systemd/network/')
def copy_udev_rule(*rules):
"""Copy udev rules"""
mkdir_p(udev_rules_dir)
@@ -412,11 +415,12 @@ def create_unit_dropin(unit, contents):
f.write('\n'.join(contents))
def create_service_dropin(service, command, additional_settings=None):
drop_in = [
'[Service]',
'ExecStart=',
f'ExecStart=!!{valgrind_cmd}{command}',
]
drop_in = ['[Service]']
if command:
drop_in += [
'ExecStart=',
f'ExecStart=!!{valgrind_cmd}{command}',
]
if enable_debug:
drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
if asan_options:
@@ -444,6 +448,87 @@ def create_service_dropin(service, command, additional_settings=None):
create_unit_dropin(f'{service}.service', drop_in)
def setup_system_units():
if build_dir:
mkdir_p('/run/systemd/system/')
for unit in [
'systemd-networkd.service',
'systemd-networkd.socket',
'systemd-networkd-persistent-storage.service',
'systemd-resolved.service',
'systemd-timesyncd.service',
'systemd-udevd.service',
]:
for path in [build_dir, source_dir]:
fullpath = os.path.join(os.path.join(path, "units"), unit)
if os.path.exists(fullpath):
print(f"Copying unit file from {fullpath} to /run/systemd/system/")
cp(fullpath, '/run/systemd/system/')
break
create_service_dropin('systemd-networkd', networkd_bin,
['[Service]',
'Restart=no',
'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
'[Unit]',
'StartLimitIntervalSec=0'])
create_service_dropin('systemd-resolved', resolved_bin)
create_service_dropin('systemd-timesyncd', timesyncd_bin)
# TODO: also run udevd with sanitizers, valgrind, or coverage
create_unit_dropin(
'systemd-udevd.service',
[
'[Service]',
'ExecStart=',
f'ExecStart=!!@{udevadm_bin} systemd-udevd',
]
)
create_unit_dropin(
'systemd-networkd.socket',
[
'[Unit]',
'StartLimitIntervalSec=0',
]
)
create_unit_dropin(
'systemd-networkd-persistent-storage.service',
[
'[Unit]',
'StartLimitIntervalSec=0',
'[Service]',
'ExecStart=',
f'ExecStart={networkctl_bin} persistent-storage yes',
'ExecStop=',
f'ExecStop={networkctl_bin} persistent-storage no'
]
)
check_output('systemctl daemon-reload')
print(check_output('systemctl cat systemd-networkd.service'))
print(check_output('systemctl cat systemd-networkd-persistent-storage.service'))
print(check_output('systemctl cat systemd-resolved.service'))
print(check_output('systemctl cat systemd-timesyncd.service'))
print(check_output('systemctl cat systemd-udevd.service'))
check_output('systemctl restart systemd-resolved.service')
check_output('systemctl restart systemd-timesyncd.service')
check_output('systemctl restart systemd-udevd.service')
def clear_system_units():
def rm_unit(name):
rm_f(f'/run/systemd/system/{name}')
rm_rf(f'/run/systemd/system/{name}.d')
rm_unit('systemd-networkd.service')
rm_unit('systemd-networkd.socket')
rm_unit('systemd-networkd-persistent-storage.service')
rm_unit('systemd-resolved.service')
rm_unit('systemd-timesyncd.service')
rm_unit('systemd-udevd.service')
check_output('systemctl daemon-reload')
check_output('systemctl restart systemd-udevd.service')
def link_exists(link):
return call_quiet(f'ip link show {link}') == 0
@@ -830,6 +915,7 @@ def tear_down_common():
# 6. remove configs
clear_network_units()
clear_networkd_conf_dropins()
clear_networkd_state_files()
# 7. flush settings
flush_fou_ports()
@@ -843,6 +929,7 @@ def setUpModule():
clear_network_units()
clear_networkd_conf_dropins()
clear_networkd_state_files()
clear_udev_rules()
setup_systemd_udev_rules()
@@ -855,58 +942,18 @@ def setUpModule():
save_routing_policy_rules()
save_timezone()
create_service_dropin('systemd-networkd', networkd_bin,
['[Service]',
'Restart=no',
'Environment=SYSTEMD_NETWORK_TEST_MODE=yes',
'[Unit]',
'StartLimitIntervalSec=0'])
create_service_dropin('systemd-resolved', resolved_bin)
create_service_dropin('systemd-timesyncd', timesyncd_bin)
# TODO: also run udevd with sanitizers, valgrind, or coverage
#create_service_dropin('systemd-udevd', udevd_bin,
# f'{udevadm_bin} control --reload --timeout 0')
create_unit_dropin(
'systemd-udevd.service',
[
'[Service]',
'ExecStart=',
f'ExecStart=!!@{udevd_bin} systemd-udevd',
]
)
create_unit_dropin(
'systemd-networkd.socket',
[
'[Unit]',
'StartLimitIntervalSec=0',
]
)
check_output('systemctl daemon-reload')
print(check_output('systemctl cat systemd-networkd.service'))
print(check_output('systemctl cat systemd-resolved.service'))
print(check_output('systemctl cat systemd-timesyncd.service'))
print(check_output('systemctl cat systemd-udevd.service'))
check_output('systemctl restart systemd-resolved.service')
check_output('systemctl restart systemd-timesyncd.service')
check_output('systemctl restart systemd-udevd.service')
setup_system_units()
def tearDownModule():
rm_rf(networkd_ci_temp_dir)
clear_udev_rules()
clear_network_units()
clear_networkd_conf_dropins()
clear_networkd_state_files()
restore_timezone()
rm_rf('/run/systemd/system/systemd-networkd.service.d')
rm_rf('/run/systemd/system/systemd-networkd.socket.d')
rm_rf('/run/systemd/system/systemd-resolved.service.d')
rm_rf('/run/systemd/system/systemd-timesyncd.service.d')
rm_rf('/run/systemd/system/systemd-udevd.service.d')
check_output('systemctl daemon-reload')
check_output('systemctl restart systemd-udevd.service')
clear_system_units()
restore_active_units()
class Utilities():
@@ -5687,8 +5734,20 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'NTP: 192.168.5.1\n *192.168.5.11')
output = networkctl_status('veth-peer')
print(output)
self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
networkctl_reconfigure('veth-peer')
self.wait_online('veth-peer:routable')
for _ in range(10):
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
if 'Offered DHCP leases: 192.168.5.' in output:
break
time.sleep(.2)
else:
self.fail()
def test_dhcp_server_null_server_address(self):
copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
start_networkd()
@@ -7464,15 +7523,6 @@ if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
parser.add_argument('--source-dir', help='Path to source dir/git tree', dest='source_dir')
parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
parser.add_argument('--timesyncd', help='Path to systemd-timesyncd', dest='timesyncd_bin')
parser.add_argument('--udevd', help='Path to systemd-udevd', dest='udevd_bin')
parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
parser.add_argument('--timedatectl', help='Path to timedatectl', dest='timedatectl_bin')
parser.add_argument('--udevadm', help='Path to udevadm', dest='udevadm_bin')
parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
@@ -7482,45 +7532,21 @@ if __name__ == '__main__':
ns, unknown_args = parser.parse_known_args(namespace=unittest)
if ns.build_dir:
if ns.networkd_bin or ns.resolved_bin or ns.timesyncd_bin or ns.udevd_bin or \
ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin or ns.udevadm_bin:
print('WARNING: --networkd, --resolved, --timesyncd, --udevd, --wait-online, --networkctl, --resolvectl, --timedatectl, or --udevadm options are ignored when --build-dir is specified.')
networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
timesyncd_bin = os.path.join(ns.build_dir, 'systemd-timesyncd')
udevd_bin = os.path.join(ns.build_dir, 'udevadm')
wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
udevadm_bin = os.path.join(ns.build_dir, 'udevadm')
systemd_udev_rules_build_dir = os.path.join(ns.build_dir, 'rules.d')
else:
if ns.networkd_bin:
networkd_bin = ns.networkd_bin
if ns.resolved_bin:
resolved_bin = ns.resolved_bin
if ns.timesyncd_bin:
timesyncd_bin = ns.timesyncd_bin
if ns.udevd_bin:
udevd_bin = ns.udevd_bin
if ns.wait_online_bin:
wait_online_bin = ns.wait_online_bin
if ns.networkctl_bin:
networkctl_bin = ns.networkctl_bin
if ns.resolvectl_bin:
resolvectl_bin = ns.resolvectl_bin
if ns.timedatectl_bin:
timedatectl_bin = ns.timedatectl_bin
if ns.udevadm_bin:
udevadm_bin = ns.udevadm_bin
build_dir = ns.build_dir
if ns.source_dir:
systemd_source_dir = ns.source_dir
source_dir = ns.source_dir
else:
systemd_source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
if not os.path.exists(os.path.join(systemd_source_dir, "meson_options.txt")):
raise RuntimeError(f"{systemd_source_dir} doesn't appear to be a systemd source tree")
source_dir = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../"))
assert os.path.exists(os.path.join(source_dir, "meson_options.txt")), f"{source_dir} doesn't appear to be a systemd source tree."
use_valgrind = ns.use_valgrind
enable_debug = ns.enable_debug