mirror of
https://salsa.debian.org/kernel-team/initramfs-tools.git
synced 2026-01-27 01:44:25 +00:00
Merge branch 'bring-up-networking' into 'master'
Bring up networking if ip is specified on cmdline See merge request kernel-team/initramfs-tools!77
This commit is contained in:
commit
00c8e8553b
266
debian/tests/check-log
vendored
Executable file
266
debian/tests/check-log
vendored
Executable file
@ -0,0 +1,266 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
"""Run given checks on the log output."""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
import shlex
|
||||
import sys
|
||||
import typing
|
||||
|
||||
|
||||
class Check:
|
||||
"""The Check class contains all the checks that the caller can run."""
|
||||
|
||||
def __init__(self, log: str) -> None:
|
||||
self.errors = 0
|
||||
self.command_outputs = self._extract_command_outputs_from_log(log)
|
||||
|
||||
def _error(self, msg: str) -> None:
|
||||
print("ERROR: " + msg)
|
||||
self.errors += 1
|
||||
|
||||
@staticmethod
|
||||
def _extract_command_outputs_from_log(log: str) -> dict[str, str]:
|
||||
"""Extract command outputs from the given log output.
|
||||
|
||||
The output must be framed by a header and a footer line. The header
|
||||
line contains the key surrounded by 10 # characters. The footer
|
||||
line consist of 40 # characters. Returns a mapping from the output
|
||||
key to the command output.
|
||||
"""
|
||||
marker = "#" * 10
|
||||
footer = "#" * 40
|
||||
matches = re.findall(
|
||||
f"^{marker} ([^#]+) {marker}\n(.*?\n){footer}$",
|
||||
log,
|
||||
flags=re.DOTALL | re.MULTILINE,
|
||||
)
|
||||
return {m[0]: m[1] for m in matches}
|
||||
|
||||
def get_commands_from_ps_output(self) -> list[str]:
|
||||
"""Get list of command from `ps -ww aux` output."""
|
||||
ps_output = self.command_outputs["ps -ww aux"]
|
||||
lines = ps_output.strip().split("\n")[1:]
|
||||
commands = []
|
||||
for line in lines:
|
||||
columns = re.split(r"\s+", line, maxsplit=10)
|
||||
commands.append(columns[10])
|
||||
return commands
|
||||
|
||||
def get_ip_addr(self) -> list[typing.Any]:
|
||||
"""Get IP address information from `ip addr` JSON output."""
|
||||
ip_addr = json.loads(self.command_outputs["ip -json addr"])
|
||||
assert isinstance(ip_addr, list)
|
||||
return ip_addr
|
||||
|
||||
def get_ip_route(self) -> list[typing.Any]:
|
||||
"""Get IP route information from `ip route` JSON output."""
|
||||
ip_route = json.loads(self.command_outputs["ip -json route"])
|
||||
assert isinstance(ip_route, list)
|
||||
return ip_route
|
||||
|
||||
def run_checks(self, args: list[str]) -> int:
|
||||
"""Run the checks and return the number of errors found.
|
||||
|
||||
The methods of this class can be u
|
||||
"""
|
||||
if not args:
|
||||
return self.errors
|
||||
|
||||
try:
|
||||
check = getattr(self, args[0])
|
||||
except AttributeError:
|
||||
self._error(f"Check '{args[0]}' not found.")
|
||||
return self.errors
|
||||
check_args = []
|
||||
|
||||
for arg in args[1:]:
|
||||
if not hasattr(self, arg):
|
||||
check_args.append(arg)
|
||||
continue
|
||||
|
||||
check(*check_args)
|
||||
check = getattr(self, arg)
|
||||
check_args = []
|
||||
|
||||
check(*check_args)
|
||||
return self.errors
|
||||
|
||||
def _check_is_subset(self, actual: set[str], expected: set[str]) -> None:
|
||||
"""Check that the first dictionary is a subset of the second one.
|
||||
|
||||
Log errors if the sets are different.
|
||||
"""
|
||||
unexpected = actual - expected
|
||||
if unexpected:
|
||||
self._error(f"Not expected entries: {unexpected}")
|
||||
|
||||
def _check_is_subdict(
|
||||
self,
|
||||
expected_dict: dict[str, str],
|
||||
actual_dict: dict[str, str],
|
||||
log_prefix: str,
|
||||
) -> None:
|
||||
"""Check that the first dictionary is a subset of the second one.
|
||||
|
||||
Log errors if differences are found.
|
||||
"""
|
||||
missing_keys = set(expected_dict.keys()) - set(actual_dict.keys())
|
||||
if missing_keys:
|
||||
self._error(f"{log_prefix}Missing keys: {missing_keys}")
|
||||
for key, expected_value in sorted(expected_dict.items()):
|
||||
actual_value = actual_dict.get(key, "")
|
||||
if expected_value != actual_value:
|
||||
self._error(
|
||||
f"{log_prefix}Value for key '{key}' differs:"
|
||||
f" '{expected_value}' expected, but got '{actual_value}'"
|
||||
)
|
||||
|
||||
# Below are all checks that the user might call.
|
||||
|
||||
def has_hostname(self, hostname_pattern: str) -> None:
|
||||
"""Check that the hostname matches the given regular expression."""
|
||||
hostname = self.command_outputs["hostname"].strip()
|
||||
if re.fullmatch(hostname_pattern, hostname):
|
||||
print(f"hostname '{hostname}' matches pattern '{hostname_pattern}'")
|
||||
return
|
||||
self._error(
|
||||
f"hostname '{hostname}' does not match"
|
||||
f" expected pattern '{hostname_pattern}'"
|
||||
)
|
||||
|
||||
def has_interface_mtu(self, device_pattern: str, expected_mtu: str) -> None:
|
||||
"""Check that a matching network device has the expected MTU set."""
|
||||
for device in self.get_ip_addr():
|
||||
if not re.fullmatch(device_pattern, device["ifname"]):
|
||||
continue
|
||||
if str(device["mtu"]) == expected_mtu:
|
||||
print(f"device {device['ifname']} has MTU {device['mtu']}")
|
||||
return
|
||||
self._error(
|
||||
f"device {device['ifname']} has MTU {device['mtu']}"
|
||||
f" but expected {expected_mtu}"
|
||||
)
|
||||
return
|
||||
self._error(f"no link found that matches '{device_pattern}'")
|
||||
|
||||
def has_ip_addr(self, family: str, addr_pattern: str, device_pattern: str) -> None:
|
||||
"""Check that a matching network device has a matching IP address."""
|
||||
for device in self.get_ip_addr():
|
||||
if not re.fullmatch(device_pattern, device["ifname"]):
|
||||
continue
|
||||
for addr in device["addr_info"]:
|
||||
if addr["family"] != family or addr["scope"] != "global":
|
||||
continue
|
||||
address = f"{addr['local']}/{addr['prefixlen']}"
|
||||
if re.fullmatch(addr_pattern, address):
|
||||
print(f"found addr {address} for {device['ifname']}: {addr}")
|
||||
return
|
||||
self._error(
|
||||
f"addr {address} for {device['ifname']}"
|
||||
f" does not match {addr_pattern}: {addr}"
|
||||
)
|
||||
return
|
||||
name = {"inet": "IPv4", "inet6": "IPv6"}[family]
|
||||
self._error(
|
||||
f"no link found that matches '{device_pattern}' and has an {name} address"
|
||||
)
|
||||
|
||||
def has_ipv4_addr(self, addr_pattern: str, device_pattern: str) -> None:
|
||||
"""Check that a matching network device has a matching IPv4 address."""
|
||||
self.has_ip_addr("inet", addr_pattern, device_pattern)
|
||||
|
||||
def has_ipv6_addr(self, addr_pattern: str, device_pattern: str) -> None:
|
||||
"""Check that a matching network device has a matching IPv6 address."""
|
||||
self.has_ip_addr("inet6", addr_pattern, device_pattern)
|
||||
|
||||
def has_ipv4_default_route(self, gateway_pattern: str, device_pattern: str) -> None:
|
||||
"""Check that the IPv4 default route is via a matching gateway and device."""
|
||||
for route in self.get_ip_route():
|
||||
if route["dst"] != "default":
|
||||
continue
|
||||
if not re.fullmatch(gateway_pattern, route["gateway"]) or not re.fullmatch(
|
||||
device_pattern, route["dev"]
|
||||
):
|
||||
self._error(
|
||||
f"Default IPv4 route does not match expected gateway pattern"
|
||||
f" '{gateway_pattern}' or dev pattern '{device_pattern}': {route}"
|
||||
)
|
||||
continue
|
||||
print(
|
||||
f"found IPv4 default route via {route['gateway']}"
|
||||
f" for {route['dev']}: {route}"
|
||||
)
|
||||
return
|
||||
self._error("no IPv4 default route found")
|
||||
|
||||
def has_net_conf(self, min_expected_files: str, *expected_net_confs: str) -> None:
|
||||
"""Compare the /run/net*.conf files.
|
||||
|
||||
There must be at least `min_expected_files` /run/net*.conf files
|
||||
in the log output and no unexpected one. The format for
|
||||
`expected_net_confs` is `<file name>=<expected content>`.
|
||||
"""
|
||||
|
||||
expected = dict(nc.split("=", maxsplit=1) for nc in expected_net_confs)
|
||||
prog = re.compile(r"/run/net[^#]+\.conf")
|
||||
got = {
|
||||
key: value for key, value in self.command_outputs.items() if prog.match(key)
|
||||
}
|
||||
|
||||
if len(got) < int(min_expected_files):
|
||||
self._error(
|
||||
f"Expected at least {min_expected_files} /run/net*.conf files,"
|
||||
f" but got only {len(got)}: {set(got.keys())}"
|
||||
)
|
||||
self._check_is_subset(set(got.keys()), set(expected.keys()))
|
||||
|
||||
for net_dev in sorted(got.keys()):
|
||||
log_prefix = f"{net_dev}: "
|
||||
expected_net_conf = parse_net_conf(expected.get(net_dev, ""))
|
||||
actual_net_conf = parse_net_conf(got.get(net_dev, ""))
|
||||
self._check_is_subdict(expected_net_conf, actual_net_conf, log_prefix)
|
||||
print(f"compared {len(expected_net_conf)} items from {net_dev}")
|
||||
|
||||
def has_no_running_processes(self) -> None:
|
||||
"""Check that there are no remaining running processes from the initrd."""
|
||||
processes = drop_kernel_processes(self.get_commands_from_ps_output())
|
||||
if len(processes) == 2:
|
||||
print(f"found only expected init and ps process: {processes}")
|
||||
return
|
||||
self._error(
|
||||
f"Expected only init and ps process, but got {len(processes)}: {processes}"
|
||||
)
|
||||
|
||||
|
||||
def drop_kernel_processes(processes: list[str]) -> list[str]:
|
||||
"""Return a list of processes with the kernel processes dropped."""
|
||||
return [p for p in processes if not p.startswith("[") or not p.endswith("]")]
|
||||
|
||||
|
||||
def parse_net_conf(net_conf: str) -> dict[str, str]:
|
||||
"""Parse /run/net*.conf file and return a key to value mapping."""
|
||||
items = shlex.split(net_conf)
|
||||
return dict(item.split("=", maxsplit=1) for item in items)
|
||||
|
||||
|
||||
def main(arguments: list[str]) -> int:
|
||||
"""Run given checks on the log output. Return number of errors."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("log_file", metavar="log-file")
|
||||
parser.add_argument("checks", metavar="check", nargs="+")
|
||||
args = parser.parse_args(arguments)
|
||||
|
||||
log = pathlib.Path(args.log_file).read_text(encoding="ascii")
|
||||
check = Check(log)
|
||||
return check.run_checks(args.checks)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
16
debian/tests/control
vendored
16
debian/tests/control
vendored
@ -37,3 +37,19 @@ Depends: genext2fs,
|
||||
seabios [ppc64el],
|
||||
zstd,
|
||||
@
|
||||
|
||||
Tests: qemu-net
|
||||
Architecture: amd64 arm64 armhf ppc64el s390x
|
||||
Depends: genext2fs,
|
||||
iproute2,
|
||||
ipxe-qemu,
|
||||
klibc-utils,
|
||||
linux-image-generic,
|
||||
procps,
|
||||
python3,
|
||||
qemu-efi-aarch64 [arm64],
|
||||
qemu-efi-arm [armhf],
|
||||
qemu-kvm,
|
||||
seabios [ppc64el],
|
||||
zstd,
|
||||
@
|
||||
|
||||
17
debian/tests/hooks/drop-hostname
vendored
Executable file
17
debian/tests/hooks/drop-hostname
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
PREREQ=""
|
||||
|
||||
prereqs()
|
||||
{
|
||||
echo "$PREREQ"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
prereqs)
|
||||
prereqs
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -f "${DESTDIR}/etc/hostname"
|
||||
32
debian/tests/hooks/persistent-net
vendored
Executable file
32
debian/tests/hooks/persistent-net
vendored
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
|
||||
PREREQ=""
|
||||
|
||||
prereqs()
|
||||
{
|
||||
echo "$PREREQ"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
prereqs)
|
||||
prereqs
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
persist_net() {
|
||||
name="$1"
|
||||
mac="$2"
|
||||
|
||||
mkdir -p "${DESTDIR}/etc/systemd/network"
|
||||
cat >"${DESTDIR}/etc/systemd/network/10-persistent-${name}.link" <<EOF
|
||||
[Match]
|
||||
MACAddress=${mac}
|
||||
|
||||
[Link]
|
||||
Name=${name}
|
||||
EOF
|
||||
}
|
||||
|
||||
persist_net lan0 "52:54:00:65:43:21"
|
||||
persist_net lan1 "52:54:00:12:34:56"
|
||||
1
debian/tests/qemu-ata-only
vendored
1
debian/tests/qemu-ata-only
vendored
@ -21,3 +21,4 @@ build_initramfs
|
||||
build_rootfs_ext2
|
||||
|
||||
run_qemu
|
||||
check_no_network_configuration
|
||||
|
||||
1
debian/tests/qemu-busybox
vendored
1
debian/tests/qemu-busybox
vendored
@ -15,6 +15,7 @@ lsinitramfs "${INITRAMFS}" | grep -qw busybox
|
||||
build_rootfs_ext2
|
||||
|
||||
run_qemu
|
||||
check_no_network_configuration
|
||||
|
||||
# Check that fsck ran
|
||||
grep -q "^/dev/${ROOTDISK_LINUX_NAME}: clean," "${OUTPUT}"
|
||||
|
||||
1
debian/tests/qemu-klibc
vendored
1
debian/tests/qemu-klibc
vendored
@ -15,6 +15,7 @@ build_initramfs
|
||||
build_rootfs_ext2
|
||||
|
||||
run_qemu
|
||||
check_no_network_configuration
|
||||
|
||||
# Check that fsck ran
|
||||
grep -q "^/dev/${ROOTDISK_LINUX_NAME}: clean," "${OUTPUT}"
|
||||
|
||||
128
debian/tests/qemu-net
vendored
Executable file
128
debian/tests/qemu-net
vendored
Executable file
@ -0,0 +1,128 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
# Some simple tests of the initramfs network configuration.
|
||||
|
||||
# The basic idea is to make an ext2 root image that only ships a /sbin/init to
|
||||
# just gather some data and shutdown again and boot it in qemu system
|
||||
# emulation (not KVM, so it can be run in the autopkgtest architecture without
|
||||
# hoping nested kvm works). Currently it only sets up qemu user networking
|
||||
# which limits our ability to be clever. In the long run we should set up a
|
||||
# tun and a bridge and specify the mac address of the NICs in the emulated
|
||||
# system and run dnsmasq on it so we test ipv6 and can control which ips which
|
||||
# nics get and so on -- but this is still better than nothing.
|
||||
|
||||
# TODO: Add a test case for classless static routes. This needs support in
|
||||
# Qemu first. Following patch should be refreshed:
|
||||
# https://lore.kernel.org/all/20180314190814.22631-1-benjamin.drung@profitbricks.com/
|
||||
|
||||
SUPPORTED_FLAVOURS='amd64 arm64 armmp powerpc64 s390x generic'
|
||||
ROOTDISK_QEMU_IF=virtio
|
||||
ROOTDISK_LINUX_NAME=vda
|
||||
. debian/tests/test-common
|
||||
|
||||
cat >>"${CONFDIR}/initramfs.conf" <<EOF
|
||||
MODULES=list
|
||||
BUSYBOX=n
|
||||
FSTYPE=ext2
|
||||
EOF
|
||||
cat >"${CONFDIR}/modules" <<EOF
|
||||
ext2
|
||||
virtio_pci
|
||||
virtio_blk
|
||||
virtio_net
|
||||
EOF
|
||||
install -m 755 debian/tests/hooks/drop-hostname "${CONFDIR}/hooks/drop-hostname"
|
||||
install -m 755 debian/tests/hooks/persistent-net "${CONFDIR}/hooks/persistent-net"
|
||||
build_initramfs
|
||||
|
||||
prepare_network_dumping_rootfs
|
||||
build_rootfs_ext2
|
||||
|
||||
EXPECTED_DHCP_LAN0="
|
||||
DEVICE='lan0'
|
||||
PROTO='dhcp'
|
||||
IPV4ADDR='10.0.3.15'
|
||||
IPV4BROADCAST='10.0.3.255'
|
||||
IPV4NETMASK='255.255.255.0'
|
||||
IPV4GATEWAY='10.0.3.2'
|
||||
IPV4DNS0='10.0.3.3'
|
||||
HOSTNAME='pizza'
|
||||
DNSDOMAIN='example.com'
|
||||
ROOTSERVER='10.0.3.2'
|
||||
filename='/path/to/bootfile2'
|
||||
DOMAINSEARCH='test.'
|
||||
"
|
||||
EXPECTED_DHCP_LAN1="
|
||||
DEVICE='lan1'
|
||||
PROTO='dhcp'
|
||||
IPV4ADDR='10.0.2.15'
|
||||
IPV4BROADCAST='10.0.2.255'
|
||||
IPV4NETMASK='255.255.255.0'
|
||||
IPV4GATEWAY='10.0.2.2'
|
||||
IPV4DNS0='10.0.2.3'
|
||||
HOSTNAME='goulash'
|
||||
DNSDOMAIN='test'
|
||||
ROOTSERVER='10.0.2.2'
|
||||
filename='/path/to/bootfile'
|
||||
DOMAINSEARCH='example. example.net.'
|
||||
"
|
||||
|
||||
run_qemu "ip=dhcp"
|
||||
check_output "Begin: Waiting up to 180 secs for any network device to become available"
|
||||
./debian/tests/check-log "${OUTPUT}" has_no_running_processes \
|
||||
has_hostname "goulash|pizza" \
|
||||
has_interface_mtu "lan[01]" 1500 \
|
||||
has_ipv4_addr "10\.0\.[23]\.15/24" "lan[01]" \
|
||||
has_ipv4_default_route "10\.0\.[23]\.2" "lan[01]" \
|
||||
has_net_conf 1 "/run/net-lan0.conf=${EXPECTED_DHCP_LAN0}" "/run/net-lan1.conf=${EXPECTED_DHCP_LAN1}"
|
||||
|
||||
# Test _set_netdev_from_ip_param
|
||||
run_qemu "ip=:::::lan1:dhcp"
|
||||
check_output "Begin: Waiting up to 180 secs for lan1 to become available"
|
||||
./debian/tests/check-log "${OUTPUT}" has_no_running_processes \
|
||||
has_hostname "goulash" \
|
||||
has_interface_mtu "lan1" 1500 \
|
||||
has_ipv4_addr "10\.0\.2\.15/24" "lan1" \
|
||||
has_ipv4_default_route "10\.0\.2\.2" "lan1" \
|
||||
has_net_conf 1 "/run/net-lan1.conf=${EXPECTED_DHCP_LAN1}"
|
||||
|
||||
# Test setting the IP address manually
|
||||
run_qemu "ip=10.0.2.100::10.0.2.2:255.0.0.0:lasagne:lan1:"
|
||||
check_output "Begin: Waiting up to 180 secs for lan1 to become available"
|
||||
./debian/tests/check-log "${OUTPUT}" has_no_running_processes \
|
||||
has_hostname "lasagne" \
|
||||
has_interface_mtu "lan1" 1500 \
|
||||
has_ipv4_addr "10\.0\.2\.100/8" "lan1" \
|
||||
has_ipv4_default_route "10\.0\.2\.2" "lan1" \
|
||||
has_net_conf 1 "/run/net-lan1.conf=DEVICE='lan1'
|
||||
PROTO='none'
|
||||
IPV4ADDR='10.0.2.100'
|
||||
IPV4BROADCAST='10.255.255.255'
|
||||
IPV4NETMASK='255.0.0.0'
|
||||
IPV4GATEWAY='10.0.2.2'
|
||||
IPV4DNS0='0.0.0.0'
|
||||
HOSTNAME='lasagne'
|
||||
DNSDOMAIN=''
|
||||
ROOTSERVER='0.0.0.0'
|
||||
filename=''
|
||||
DOMAINSEARCH=''"
|
||||
|
||||
# Test DHCP configuration with BOOTIF specified
|
||||
run_qemu "BOOTIF=01-52-54-00-12-34-56 ip=dhcp"
|
||||
check_output "Begin: Waiting up to 180 secs for device with address 52:54:00:12:34:56 to become available"
|
||||
./debian/tests/check-log "${OUTPUT}" has_no_running_processes \
|
||||
has_hostname "goulash" \
|
||||
has_interface_mtu "lan1" 1500 \
|
||||
has_ipv4_addr "10\.0\.2\.15/24" "lan1" \
|
||||
has_ipv4_default_route "10\.0\.2\.2" "lan1" \
|
||||
has_net_conf 1 "/run/net-lan1.conf=${EXPECTED_DHCP_LAN1}"
|
||||
|
||||
run_qemu "ip=on"
|
||||
check_output "Begin: Waiting up to 180 secs for any network device to become available"
|
||||
./debian/tests/check-log "${OUTPUT}" has_no_running_processes \
|
||||
has_hostname "goulash|pizza" \
|
||||
has_interface_mtu "lan[01]" 1500 \
|
||||
has_ipv4_addr "10\.0\.[23]\.15/24" "lan[01]" \
|
||||
has_ipv4_default_route "10\.0\.[23]\.2" "lan[01]" \
|
||||
has_net_conf 1 "/run/net-lan0.conf=${EXPECTED_DHCP_LAN0}" "/run/net-lan1.conf=${EXPECTED_DHCP_LAN1}"
|
||||
3
debian/tests/qemu-panic-shell
vendored
3
debian/tests/qemu-panic-shell
vendored
@ -20,13 +20,16 @@ build_initramfs
|
||||
build_rootfs_ext2
|
||||
|
||||
run_qemu_nocheck
|
||||
check_no_network_configuration
|
||||
grep -qF "ALERT! /dev/nonexistent does not exist. Dropping to a shell!" "${OUTPUT}"
|
||||
grep -qF "(initramfs) " "${OUTPUT}"
|
||||
|
||||
run_qemu_nocheck "panic=-1"
|
||||
check_no_network_configuration
|
||||
grep -qF "Rebooting automatically due to panic= boot argument" "${OUTPUT}"
|
||||
! grep -qF "(initramfs) " "${OUTPUT}"
|
||||
|
||||
run_qemu_nocheck "panic=0"
|
||||
check_no_network_configuration
|
||||
grep -qF "Halting automatically due to panic= boot argument" "${OUTPUT}"
|
||||
! grep -qF "(initramfs) " "${OUTPUT}"
|
||||
|
||||
1
debian/tests/qemu-separate-usr
vendored
1
debian/tests/qemu-separate-usr
vendored
@ -30,6 +30,7 @@ build_rootfs_ext2
|
||||
build_fs_ext2 "${USRDIR}" "${USRDISK}"
|
||||
|
||||
run_qemu
|
||||
check_no_network_configuration
|
||||
|
||||
# Check that fsck ran on both devices
|
||||
grep -q "^/dev/${ROOTDISK_LINUX_NAME}: clean," "${OUTPUT}"
|
||||
|
||||
1
debian/tests/qemu-virtio-only
vendored
1
debian/tests/qemu-virtio-only
vendored
@ -20,3 +20,4 @@ build_initramfs
|
||||
build_rootfs_ext2
|
||||
|
||||
run_qemu
|
||||
check_no_network_configuration
|
||||
|
||||
80
debian/tests/test-common
vendored
80
debian/tests/test-common
vendored
@ -46,6 +46,7 @@ CONFDIR="${BASEDIR}/config"
|
||||
mkdir -p "${CONFDIR}"
|
||||
cp conf/initramfs.conf "${CONFDIR}/initramfs.conf"
|
||||
echo "RESUME=none" >>"${CONFDIR}/initramfs.conf"
|
||||
mkdir "${CONFDIR}/hooks"
|
||||
touch "${CONFDIR}/modules"
|
||||
mkdir "${CONFDIR}/scripts"
|
||||
|
||||
@ -62,10 +63,10 @@ test -n "${ROOTDISK_LINUX_NAME}" || ROOTDISK_LINUX_NAME=vda
|
||||
# Create a root fs with a trivial userspace
|
||||
ROOTDIR="${BASEDIR}/rootdir"
|
||||
INIT_MESSAGE='root fs init system started successfully'
|
||||
for subdir in "" dev proc run sys usr usr/bin usr/lib usr/sbin; do
|
||||
for subdir in "" dev proc run sys usr usr/bin usr/lib usr/lib64 usr/sbin; do
|
||||
mkdir "${ROOTDIR}/${subdir}"
|
||||
done
|
||||
for subdir in bin lib sbin; do
|
||||
for subdir in bin lib lib64 sbin; do
|
||||
ln -s "usr/$subdir" "${ROOTDIR}/${subdir}"
|
||||
done
|
||||
cat >"${ROOTDIR}/sbin/init" <<EOF
|
||||
@ -86,6 +87,55 @@ cp "$(dpkg -L libklibc | grep '/klibc-.*\.so$')" "${ROOTDIR}/lib/"
|
||||
# VM output file
|
||||
OUTPUT="${BASEDIR}/output.log"
|
||||
|
||||
prepare_network_dumping_rootfs() {
|
||||
local root_dir="${1-$ROOTDIR}"
|
||||
cat >"${root_dir}/usr/sbin/init" <<EOF
|
||||
#!/bin/sh
|
||||
echo "I: Executing /usr/sbin/init from root fs"
|
||||
# Stop the kernel from spamming the output
|
||||
current_printk=\$(sysctl kernel.printk)
|
||||
sysctl -w kernel.printk="4 4 1 7"
|
||||
# Run twice, once for the human, once for the test harness
|
||||
echo "I: ip addr"
|
||||
ip addr
|
||||
echo "I: ip route"
|
||||
ip route
|
||||
echo "I: ip -6 route"
|
||||
ip -6 route
|
||||
for file in /run/net*.conf; do
|
||||
[ -f \$file ] || continue;
|
||||
echo "########## \$file ##########"
|
||||
cat \$file
|
||||
echo "########################################"
|
||||
done
|
||||
echo "########## hostname ##########"
|
||||
cat /proc/sys/kernel/hostname
|
||||
echo "########################################"
|
||||
echo "########## ip -json addr ##########"
|
||||
ip -json addr
|
||||
echo "########################################"
|
||||
echo "########## ip -json route ##########"
|
||||
ip -json route
|
||||
echo "########################################"
|
||||
echo "########## ip -json -6 route ##########"
|
||||
ip -json -6 route
|
||||
echo "########################################"
|
||||
echo "########## ps -ww aux ##########"
|
||||
ps -ww aux
|
||||
echo "########################################"
|
||||
sysctl -w "\${current_printk}"
|
||||
echo '${INIT_MESSAGE}'
|
||||
poweroff
|
||||
EOF
|
||||
|
||||
. /usr/share/initramfs-tools/hook-functions
|
||||
verbose=y
|
||||
DESTDIR="$root_dir"
|
||||
for binary in /usr/bin/cat /usr/bin/ip /usr/bin/ps /usr/sbin/sysctl; do
|
||||
copy_exec "$binary"
|
||||
done
|
||||
}
|
||||
|
||||
build_initramfs() {
|
||||
echo "build_initramfs: /usr/sbin/mkinitramfs -d ${CONFDIR} -o ${INITRAMFS} ${KVER}"
|
||||
/usr/sbin/mkinitramfs -d "${CONFDIR}" -o "${INITRAMFS}" "${KVER}"
|
||||
@ -150,6 +200,10 @@ _run_qemu() {
|
||||
-device virtio-rng-pci,rng=rng0 -object rng-random,filename=/dev/urandom,id=rng0 \
|
||||
-nographic -no-reboot -kernel /boot/vmlinu*-"${KVER}" -initrd "${INITRAMFS}" \
|
||||
-nodefaults -chardev stdio,id=char0 -serial chardev:char0 \
|
||||
-device "virtio-net-pci,netdev=lan0,mac=52:54:00:65:43:21" \
|
||||
-netdev "user,id=lan0,net=10.0.3.0/24,ipv6-net=fec7::/48,hostname=pizza,dnssearch=test,domainname=example.com,bootfile=/path/to/bootfile2" \
|
||||
-device "virtio-net-pci,netdev=lan1,mac=52:54:00:12:34:56" \
|
||||
-netdev "user,id=lan1,hostname=goulash,dnssearch=example,dnssearch=example.net,domainname=test,bootfile=/path/to/bootfile" \
|
||||
-append "root=/dev/${ROOTDISK_LINUX_NAME} ro console=${console:-ttyS0},115200 ${extra_params}" | tee "${OUTPUT}"
|
||||
}
|
||||
|
||||
@ -159,6 +213,26 @@ run_qemu_nocheck() {
|
||||
}
|
||||
|
||||
run_qemu() {
|
||||
_run_qemu "panic=-1"
|
||||
_run_qemu "panic=-1 $*"
|
||||
grep -qF "${INIT_MESSAGE}" "${OUTPUT}"
|
||||
}
|
||||
|
||||
check_no_output() {
|
||||
local msg="$1"
|
||||
if grep -qF "${msg}" "${OUTPUT}"; then
|
||||
echo >&2 "E: Message '${msg}' found in log output '${OUTPUT}."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_output() {
|
||||
local msg="$1"
|
||||
if ! grep -qF "${msg}" "${OUTPUT}"; then
|
||||
echo >&2 "E: Message '${msg}' not found in log output '${OUTPUT}."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_no_network_configuration() {
|
||||
check_no_output "Waiting up to 180 secs for"
|
||||
}
|
||||
|
||||
6
init
6
init
@ -262,6 +262,12 @@ mount_bottom
|
||||
nfs_bottom
|
||||
local_bottom
|
||||
|
||||
case "$IP" in
|
||||
""|none|off) ;; # Do nothing
|
||||
*)
|
||||
configure_networking
|
||||
esac
|
||||
|
||||
maybe_break bottom
|
||||
[ "$quiet" != "y" ] && log_begin_msg "Running /scripts/init-bottom"
|
||||
# We expect udev's init-bottom script to move /dev to ${rootmnt}/dev
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user