diff options
| author | Michael Biebl <biebl@debian.org> | 2024-05-05 00:07:30 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2024-05-05 00:07:30 +0200 |
| commit | 34bb501be08aa2b313d88e67d6e0a7e0a3f9cfa6 (patch) | |
| tree | 4e6220877828be4c6f261de09ec0cb2d80e32389 /tools | |
| parent | bba2e4b4de668db525cbfdfc35292e5a0b51671a (diff) | |
New upstream version 1.47.90 upstream/1.47.90
Diffstat (limited to 'tools')
25 files changed, 1557 insertions, 22 deletions
diff --git a/tools/enums-to-docbook.pl b/tools/enums-to-docbook.pl index 8439eaee..d21cafa2 100755 --- a/tools/enums-to-docbook.pl +++ b/tools/enums-to-docbook.pl @@ -27,7 +27,7 @@ my $nm = shift @ARGV or die "Missing title"; print <<END; <?xml version='1.0'?> <?xml-stylesheet type="text/xsl" href="http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl"?> -<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> <refentry id="$id"> <refmeta> diff --git a/tools/meson-dist-data.sh b/tools/meson-dist-data.sh index 1b5d3b03..da1e5e66 100755 --- a/tools/meson-dist-data.sh +++ b/tools/meson-dist-data.sh @@ -13,10 +13,6 @@ ensure_var_path() { fi } -copy_from_build() { - cp -Tr "$MESON_BUILD_ROOT/$1" "$MESON_DIST_ROOT/$1" -} - if [ "$MESON_BUILD_ROOT" = "" ]; then if [ "$1" = "--build-root" ]; then MESON_BUILD_ROOT="$2" @@ -25,11 +21,9 @@ fi ensure_var_path "MESON_DIST_ROOT" ensure_var_path "MESON_BUILD_ROOT" -ensure_var_path "MESON_SOURCE_ROOT" ninja -C "$MESON_BUILD_ROOT" all libnm-doc NetworkManager-doc -mkdir -p "$MESON_DIST_ROOT/docs/" -copy_from_build /docs/api/ -copy_from_build /docs/libnm/ -copy_from_build /man/ +cp -Tr "$MESON_BUILD_ROOT/docs/api/html" "$MESON_DIST_ROOT/docs/api/html" +cp -Tr "$MESON_BUILD_ROOT/docs/libnm/html" "$MESON_DIST_ROOT/docs/libnm/html" +cp "$MESON_BUILD_ROOT/man/"*.[1-8] "$MESON_DIST_ROOT/man" diff --git a/tools/nm-guest-data/.gitignore b/tools/nm-guest-data/.gitignore new file mode 100644 index 00000000..f87048d7 --- /dev/null +++ b/tools/nm-guest-data/.gitignore @@ -0,0 +1,2 @@ +/data-* +/containerfile diff --git a/tools/nm-guest-data/README.md b/tools/nm-guest-data/README.md new file mode 100644 index 00000000..2b6aac3a --- /dev/null +++ b/tools/nm-guest-data/README.md @@ -0,0 +1,10 @@ +Run NetworkManager in Container for Development +=============================================== + +Run `nm-in-container` script for running NetworkManager in a non-root, +privileged podman container. Run `nm-in-vm` to do it in a vitual machine. +This is useful for testing NetworkManager for development. + +Try `./nm-in-container --help` or `./nm-in-vm --help` to see options. Inside +the container, see the bash history for useful commands and read the hints in +"/etc/motd". diff --git a/tools/nm-guest-data/bin-_nm-in-container-setup.sh.in b/tools/nm-guest-data/bin-_nm-in-container-setup.sh.in new file mode 100644 index 00000000..837daf77 --- /dev/null +++ b/tools/nm-guest-data/bin-_nm-in-container-setup.sh.in @@ -0,0 +1,13 @@ +#!/bin/bash -e + +if [ ! -d /tmp/sys2 ]; then + # `ip -netns t exec ...` will try to mount sysfs. But kernel rejects that in + # the container, unless a writable sysfs is already mounted. Due to --priviledged, + # we have /sys mounted rw, however, ip will first unmount /sys before trying to + # remount it. We thus need it mounted as rw one additional time. + # + # Let's do this setup step once, and never clean it up. + # https://github.com/containers/podman/issues/11887#issuecomment-938706628 + mkdir /tmp/sys2 + mount -t sysfs --make-private sysfs /tmp/sys2 +fi diff --git a/tools/nm-guest-data/bin-nm-deploy.sh.in b/tools/nm-guest-data/bin-nm-deploy.sh.in new file mode 100644 index 00000000..9b5385b1 --- /dev/null +++ b/tools/nm-guest-data/bin-nm-deploy.sh.in @@ -0,0 +1,14 @@ +#!/bin/bash + +set -ex + +cd /NetworkManager + +if [ -f ./config.log ] ; then + make -j 5 install +else + meson install -C build +fi + +systemctl daemon-reload +systemctl restart NetworkManager.service diff --git a/tools/nm-guest-data/bin-nm-env-prepare.sh.in b/tools/nm-guest-data/bin-nm-env-prepare.sh.in new file mode 100644 index 00000000..e26fb167 --- /dev/null +++ b/tools/nm-guest-data/bin-nm-env-prepare.sh.in @@ -0,0 +1,163 @@ +#!/bin/bash + +set -e + +die() { + printf '%s\n' "$*" + exit 1 +} + +do_cleanup() { + local IDX="$1" + local NAME_PREFIX="${2:-net}" + local PEER_PREFIX="${3:-d_}" + local NETNS_PREFIX="${4:-tt}" + + logger --id "nm-env-prepare-$IDX" "cleanup start # $@" + + pkill -F "/tmp/nm-dnsmasq-$PEER_PREFIX$IDX.pid" dnsmasq &>/dev/null || : + rm -rf "/tmp/nm-dnsmasq-$PEER_PREFIX$IDX.pid" + + pkill -F "/tmp/nm-radvd-$PEER_PREFIX$IDX.pid" radvd &>/dev/null || : + rm -rf "/tmp/nm-radvd-$PEER_PREFIX$IDX.pid" + + pkill -F "/tmp/nm-pppoe-$PEER_PREFIX$IDX.pid" pppoe-server &>/dev/null || : + rm -rf "/tmp/nm-pppoe-$PEER_PREFIX$IDX.pid" + rm -rf "/tmp/nm-pppoe-allip-$PEER_PREFIX$IDX" + + rm -rf "/tmp/nm-radvd-$PEER_PREFIX$IDX.conf" + + ip -netns "$NETNS_PREFIX$IDX" link del "$PEER_PREFIX$IDX" &>/dev/null || : + + ip netns del "$NETNS_PREFIX$IDX" &>/dev/null || : + + logger --id "nm-env-prepare-$IDX" "cleanup complete # $@" +} + +do_setup() { + local IDX="$1" + local NAME_PREFIX="${2:-net}" + local PEER_PREFIX="${3:-d_}" + local NETNS_PREFIX="${4:-tt}" + + logger --id "nm-env-prepare-$IDX" "setup start # $@" + + ip netns add "$NETNS_PREFIX$IDX" + ip -netns "$NETNS_PREFIX$IDX" link set name lo up + + ip -netns "$NETNS_PREFIX$IDX" link add "$NAME_PREFIX$IDX" type veth peer "$PEER_PREFIX$IDX" + ip -netns "$NETNS_PREFIX$IDX" link set name "$NAME_PREFIX$IDX" addr aa:0f:f1:ce:00:$(printf '%02x' $IDX) + ip -netns "$NETNS_PREFIX$IDX" link set name "$PEER_PREFIX$IDX" addr cc:0f:f1:ce:00:$(printf '%02x' $IDX) + ip -netns "$NETNS_PREFIX$IDX" link set name "$PEER_PREFIX$IDX" up + + ip -netns "$NETNS_PREFIX$IDX" addr add "192.168.$((120 + IDX)).1/23" dev "$PEER_PREFIX$IDX" + ip -netns "$NETNS_PREFIX$IDX" addr add "192:168:$((120 + IDX))::1/64" dev "$PEER_PREFIX$IDX" + + # PPPoE inside the rootless container is not actually working, because + # /dev/ppp is not accessible. Still start it, so that we at least can + # test how far it goes... + echo "192.168.$((120 + $IDX)).180-200" > "/tmp/nm-pppoe-allip-$PEER_PREFIX$IDX" + ip netns exec "$NETNS_PREFIX$IDX" \ + pppoe-server \ + -X "/tmp/nm-pppoe-$PEER_PREFIX$IDX.pid" \ + -S isp \ + -C isp \ + -L "192.168.$((120 + IDX)).1" \ + -p "/tmp/nm-pppoe-allip-$PEER_PREFIX$IDX" \ + -I "$PEER_PREFIX$IDX" \ + & + + ip netns exec "$NETNS_PREFIX$IDX" \ + dnsmasq \ + --conf-file=/dev/null \ + --pid-file="/tmp/nm-dnsmasq-$PEER_PREFIX$IDX.pid" \ + --no-hosts \ + --keep-in-foreground \ + --bind-interfaces \ + --log-debug \ + --log-queries \ + --log-dhcp \ + --except-interface=lo \ + --clear-on-reload \ + --listen-address="192.168.$((120 + $IDX)).1" \ + --listen-address="192:168:$((120 + IDX))::1" \ + --dhcp-range="192.168.$((120 + $IDX)).100,192.168.$((120 + $IDX)).150,2m" \ + --dhcp-range="192:168:$((120 + IDX))::1:1000,192:168:$((120 + IDX))::1:2000,64,2m" \ + --dhcp-option=option:dns-server,8.8.8.8,8.8.4.4 \ + --dhcp-option=option6:dns-server \ + --no-ping \ + & + + cat <<EOF > "/tmp/nm-radvd-$PEER_PREFIX$IDX.conf" +interface $PEER_PREFIX$IDX +{ + AdvSendAdvert on; + AdvManagedFlag on; + prefix 192:168:$((120 + IDX))::/64 + { + AdvOnLink on; + }; + +}; +EOF + ip netns exec "$NETNS_PREFIX$IDX" \ + radvd \ + --config "/tmp/nm-radvd-$PEER_PREFIX$IDX.conf" \ + --pidfile "/tmp/nm-radvd-$PEER_PREFIX$IDX.pid" \ + --logmethod syslog \ + -d 5 \ + & + + ip -netns ""$NETNS_PREFIX$IDX"" link set name "$NAME_PREFIX$IDX" netns $$ + + logger --id "nm-env-prepare-$IDX" "setup complete: netns=$NETNS_PREFIX$IDX, iface=$NAME_PREFIX$IDX, peer=$PEER_PREFIX$IDX # $@" +} + +do_redo() { + do_cleanup "$@" + do_setup "$@" +} + +############################################################################### + +IDX=1 +NAME_PREFIX=net +PEER_PREFIX= +CMD=redo +for (( i=1 ; i<="$#" ; )) ; do + c="${@:$i:1}" + i=$((i+1)) + case "$c" in + redo|setup|cleanup) + CMD="$c" + ;; + --prefix|-p) + NAME_PREFIX="${@:$i:1}" + test -n "$NAME_PREFIX" || die "missing argument to --prefix" + i=$((i+1)) + ;; + --peer-prefix) + PEER_PREFIX="${@:$i:1}" + test -n "$PEER_PREFIX" || die "missing argument to --peer-prefix" + i=$((i+1)) + ;; + --idx|-i) + test $i -le "$#" || die "missing argument to --idx" + IDX="${@:$i:1}" + i=$((i+1)) + ;; + *) + die "invalid argument" + ;; + esac +done + +if [ -z "$PEER_PREFIX" ]; then + if [ "$NAME_PREFIX" = net ]; then + PEER_PREFIX=d_ + else + PEER_PREFIX="d_${NAME_PREFIX}_" + fi +fi + +do_$CMD "$IDX" "$NAME_PREFIX" "$PEER_PREFIX" diff --git a/tools/nm-guest-data/etc-bashrc-motd.my.in b/tools/nm-guest-data/etc-bashrc-motd.my.in new file mode 100644 index 00000000..510d30f8 --- /dev/null +++ b/tools/nm-guest-data/etc-bashrc-motd.my.in @@ -0,0 +1,4 @@ +if test "$SHOW_MOTD" != 0; then + cat /etc/motd + export SHOW_MOTD=0 +fi diff --git a/tools/nm-guest-data/etc-bashrc.my.in b/tools/nm-guest-data/etc-bashrc.my.in new file mode 100644 index 00000000..88f2f9aa --- /dev/null +++ b/tools/nm-guest-data/etc-bashrc.my.in @@ -0,0 +1,45 @@ +alias m="make -j 8" +alias n="ninja -C build" + +alias l='ls -l --color=auto' + +ulimit -c unlimited + +export G_DEBUG=fatal-warnings + +unset DEBUGINFOD_URLS + +export NMCI_DEBUG=1 + +export TERM=xterm + +Clean() { + systemctl stop NetworkManager + rm -i -rf /run/NetworkManager + nm-env-prepare.sh +} + +Cat-Timestamp() { + while IFS=$'\n' read line; do + printf "[%s]: %s\n" "1689938340.403232924" "$line" + done +} + +Journald-clear() { + rm -rf /var/log/journal/????????????????????????????????/* + systemctl restart systemd-journald +} + +nm_run_gdb() { + systemctl stop NetworkManager.service + gdb --args "${1:-/opt/test/sbin/NetworkManager}" --debug +} + +nm_run_normal() { + systemctl stop NetworkManager.service + "${1:-/opt/test/sbin/NetworkManager}" --debug 2>&1 | tee /tmp/nm-log.txt +} + +. /usr/share/git-core/contrib/completion/git-prompt.sh +PS1="\[\033[01;36m\]\u@\h\[\033[00m\]:\t:\[\033[01;34m\]\w\$(__git_ps1 \" \[\033[01;36m\](%s)\[\033[00m\]\")\[\033[00m\]$ " +export GIT_PS1_SHOWDIRTYSTATE=1 diff --git a/tools/nm-guest-data/etc-motd-container.in b/tools/nm-guest-data/etc-motd-container.in new file mode 100644 index 00000000..131f815c --- /dev/null +++ b/tools/nm-guest-data/etc-motd-container.in @@ -0,0 +1,75 @@ +*** nm-in-container: + +find NetworkManager bind mounted at {{BASEDIR_NM}} +run `nm-env-prepare.sh setup --idx 1` to setup test interfaces + +Coredumps: coredumps are not namespaced, so by default they will +be sent to coredumpctl of the outer host, which has no idea where +to get the debugging symbols from. A possible workaround is setting + + $ echo '/tmp/core.%e.%p' | sudo tee /proc/sys/kernel/core_pattern + +so that core dumps get written to file. Afterwards, restore with + + $ echo '|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h' | sudo tee /proc/sys/kernel/core_pattern + +from /usr/lib/sysctl.d/50-coredump.conf. + +For example, configure NetworkManager with + $ ./configure \ + --enable-address-sanitizer=no \ + --enable-compile-warnings=yes \ + --enable-concheck \ + --enable-config-plugin-ibft=yes \ + --enable-gtk-doc \ + --enable-ifcfg-rh=yes \ + --enable-ifcfg-suse \ + --enable-ifnet \ + --enable-ifupdown=yes \ + --enable-introspection \ + --enable-json-validation=yes \ + --enable-maintainer-mode \ + --enable-more-logging \ + --enable-more-warnings=error \ + --enable-ovs=yes \ + --enable-polkit=yes \ + --enable-teamdctl=yes \ + --enable-undefined-sanitizer=no \ + --enable-vala=yes \ + --enable-wimax \ + --localstatedir=/var \ + --prefix=/opt/test \ + --sysconfdir=/etc \ + --with-config-dhcp-default=internal \ + --with-config-dns-rc-manager-default=auto \ + --with-consolekit=yes \ + --with-consolekit=yes \ + --with-crypto=nss \ + --with-dhclient=yes \ + --with-dhcpcanon=yes \ + --with-dhcpcd=yes \ + --with-iwd=yes \ + --with-libnm-glib=yes \ + --with-modem-manager-1 \ + --with-netconfig=/bin/nowhere/netconfig \ + --with-nm-cloud-setup=yes \ + --with-nmcli=yes \ + --with-nmtui=yes \ + --with-ofono=yes \ + --with-resolvconf=/bin/nowhere/resolvconf \ + --with-session-tracking=systemd \ + --with-suspend-resume=systemd \ + --with-systemd-logind=yes \ + --with-valgrind=yes \ + --enable-tests="${NM_BUILD_TESTS:-yes}" \ + --with-more-asserts="${NM_BUILD_MORE_ASSERTS:-1000}" \ + "${NM_CONFIGURE_OTPS[@]}" +Test with: + $ systemctl stop NetworkManager; /opt/test/sbin/NetworkManager --debug 2>&1 | tee -a /tmp/nm-log.txt + +Or better, configure with `contrib/fedora/rpm/configure-for-system.sh`, +subsequent `make && make install` will overwrite your system's NetworkManager, +and you can test it with `systemctl daemon-reload ; systemctl restart NetworkManager`. + +Run NM-ci tests after creating eth1 with +`nm-env-prepare.sh --prefix eth -i 1 && sleep 1 && nmcli device connect eth1`. diff --git a/tools/nm-guest-data/etc-motd-vm.in b/tools/nm-guest-data/etc-motd-vm.in new file mode 100644 index 00000000..10ae1f9f --- /dev/null +++ b/tools/nm-guest-data/etc-motd-vm.in @@ -0,0 +1,63 @@ +*** nm-in-vm: + +find NetworkManager bind mounted at {{BASEDIR_NM}} +run `nm-env-prepare.sh setup --idx 1` to setup test interfaces + +For example, configure NetworkManager with + $ ./configure \ + --enable-address-sanitizer=no \ + --enable-compile-warnings=yes \ + --enable-concheck \ + --enable-config-plugin-ibft=yes \ + --enable-gtk-doc \ + --enable-ifcfg-rh=yes \ + --enable-ifcfg-suse \ + --enable-ifnet \ + --enable-ifupdown=yes \ + --enable-introspection \ + --enable-json-validation=yes \ + --enable-maintainer-mode \ + --enable-more-logging \ + --enable-more-warnings=error \ + --enable-ovs=yes \ + --enable-polkit=yes \ + --enable-teamdctl=yes \ + --enable-undefined-sanitizer=no \ + --enable-vala=yes \ + --enable-wimax \ + --localstatedir=/var \ + --prefix=/opt/test \ + --sysconfdir=/etc \ + --with-config-dhcp-default=internal \ + --with-config-dns-rc-manager-default=auto \ + --with-consolekit=yes \ + --with-consolekit=yes \ + --with-crypto=nss \ + --with-dhclient=yes \ + --with-dhcpcanon=yes \ + --with-dhcpcd=yes \ + --with-iwd=yes \ + --with-libnm-glib=yes \ + --with-modem-manager-1 \ + --with-netconfig=/bin/nowhere/netconfig \ + --with-nm-cloud-setup=yes \ + --with-nmcli=yes \ + --with-nmtui=yes \ + --with-ofono=yes \ + --with-resolvconf=/bin/nowhere/resolvconf \ + --with-session-tracking=systemd \ + --with-suspend-resume=systemd \ + --with-systemd-logind=yes \ + --with-valgrind=yes \ + --enable-tests="${NM_BUILD_TESTS:-yes}" \ + --with-more-asserts="${NM_BUILD_MORE_ASSERTS:-1000}" \ + "${NM_CONFIGURE_OTPS[@]}" +Test with: + $ systemctl stop NetworkManager; /opt/test/sbin/NetworkManager --debug 2>&1 | tee -a /tmp/nm-log.txt + +Or better, if using Fedora, configure with `contrib/fedora/rpm/configure-for-system.sh`, +subsequent `make && make install` will overwrite your system's NetworkManager, +and you can test it with `systemctl daemon-reload ; systemctl restart NetworkManager`. + +Run NM-ci tests after creating eth1 with +`nm-env-prepare.sh --prefix eth -i 1 && sleep 1 && nmcli device connect eth1`. diff --git a/tools/nm-guest-data/etc-rc.local.in b/tools/nm-guest-data/etc-rc.local.in new file mode 100644 index 00000000..d59a8d76 --- /dev/null +++ b/tools/nm-guest-data/etc-rc.local.in @@ -0,0 +1,8 @@ +#!/bin/bash -e + +/usr/bin/_nm-in-container-setup.sh + +cat <<EOF > /etc/resolv.conf +# Generated by /etc/rc.d/rc.local +nameserver 8.8.8.8 +EOF diff --git a/tools/nm-guest-data/home-bash_history.in b/tools/nm-guest-data/home-bash_history.in new file mode 100644 index 00000000..d97c561e --- /dev/null +++ b/tools/nm-guest-data/home-bash_history.in @@ -0,0 +1,31 @@ +NM-log +NM-log /tmp/nm-log.txt +behave -f html --stop -t ipv4_method_static_with_IP ./features/scenarios/ipv4.feature +behave -f html --stop ./features/scenarios/vrf.feature +cd {{BASEDIR_NM}} +cd /NetworkManager-ci +cd /NetworkManager +for i in {1..9}; do nm-env-prepare.sh --prefix eth -i $i; done +Journald-clear +journalctl | NM-log +journalctl --since '3 min ago' | NM-log +m +make +make install +n +nm-deploy.sh +nm-env-prepare.sh +nm-env-prepare.sh --prefix eth -i 1 +nm-env-prepare.sh --prefix eth -i 1 && sleep 1 && nmcli device connect eth1 +nm_run_gdb +nm_run_normal +gdb /usr/sbin/NetworkManager /tmp/core.NetworkManager. +nmcli connection add type pppoe con-name ppp-net1 ifname ppp-net1 pppoe.parent net1 service isp username test password networkmanager autoconnect no +nmcli device connect eth1 +systemctl stop NetworkManager; /opt/test/sbin/NetworkManager --debug 2>&1 | tee -a ./nm-log.txt +systemctl stop NetworkManager; gdb -ex run --args /opt/test/sbin/NetworkManager --debug +systemctl stop NetworkManager +systemctl daemon-reload ; systemctl restart NetworkManager +(cd /NetworkManager && n && n install && systemctl daemon-reload && systemctl restart NetworkManager.service) +(cd /NetworkManager && m && m install && systemctl daemon-reload && systemctl restart NetworkManager.service) +systemctl status NetworkManager diff --git a/tools/nm-guest-data/home-behaverc.in b/tools/nm-guest-data/home-behaverc.in new file mode 100644 index 00000000..ebb49fa7 --- /dev/null +++ b/tools/nm-guest-data/home-behaverc.in @@ -0,0 +1,2 @@ +[behave.formatters] +html = behave_html_formatter:HTMLFormatter diff --git a/tools/nm-guest-data/home-gdb_history.in b/tools/nm-guest-data/home-gdb_history.in new file mode 100644 index 00000000..39fb9abf --- /dev/null +++ b/tools/nm-guest-data/home-gdb_history.in @@ -0,0 +1,2 @@ +run +run --debug 2>&1 | tee /tmp/nm-log.txt diff --git a/tools/nm-guest-data/home-gdbinit.in b/tools/nm-guest-data/home-gdbinit.in new file mode 100644 index 00000000..f55a669a --- /dev/null +++ b/tools/nm-guest-data/home-gdbinit.in @@ -0,0 +1,2 @@ +set history save +set history filename ~/.gdb_history diff --git a/tools/nm-guest-data/nm-90-my.conf.in b/tools/nm-guest-data/nm-90-my.conf.in new file mode 100644 index 00000000..72aeb110 --- /dev/null +++ b/tools/nm-guest-data/nm-90-my.conf.in @@ -0,0 +1,19 @@ +[main] +no-auto-default=* +debug=RLIMIT_CORE,fatal-warnings + +[logging] +level=TRACE +domains=ALL,VPN_PLUGIN:TRACE + +[device-managed-0] +match-device=interface-name:d_*,interface-name:tap* +managed=0 + +[device-managed-1] +match-device=interface-name:net*,interface-name:eth* +managed=1 + +[device-host-net] +match-device=interface-name:host_net +managed=0 diff --git a/tools/nm-guest-data/nm-95-user.conf.in b/tools/nm-guest-data/nm-95-user.conf.in new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tools/nm-guest-data/nm-95-user.conf.in diff --git a/tools/nm-guest-data/systemd-10-host-net.link.in b/tools/nm-guest-data/systemd-10-host-net.link.in new file mode 100644 index 00000000..6425d926 --- /dev/null +++ b/tools/nm-guest-data/systemd-10-host-net.link.in @@ -0,0 +1,6 @@ +[Match] +Driver=virtio_net + +[Link] +NamePolicy= +Name=host_net diff --git a/tools/nm-guest-data/systemd-20-nm.override.in b/tools/nm-guest-data/systemd-20-nm.override.in new file mode 100644 index 00000000..d8f251e4 --- /dev/null +++ b/tools/nm-guest-data/systemd-20-nm.override.in @@ -0,0 +1,5 @@ +# Overrides for NetworkManager.service with nm-in-container +# Placed in /etc/systemd/system/NetworkManager.service.d/ +[Service] +Environment=G_DEBUG=fatal-warnings +Environment=NM_OBFUSCATE_PTR=0 diff --git a/tools/nm-guest-data/systemd-dhcp-host.service.in b/tools/nm-guest-data/systemd-dhcp-host.service.in new file mode 100644 index 00000000..844e766c --- /dev/null +++ b/tools/nm-guest-data/systemd-dhcp-host.service.in @@ -0,0 +1,14 @@ +[Unit] +Description=dhclient service to get connectivity with the host +After=network.target + +[Service] +Type=simple +Restart=on-failure +User=root +RestartSec=5 +ExecStart=dhclient -d host_net +ExecStop=dhclient -r host_net + +[Install] +WantedBy=multi-user.target diff --git a/tools/nm-in-container b/tools/nm-in-container new file mode 100755 index 00000000..7ff073d0 --- /dev/null +++ b/tools/nm-in-container @@ -0,0 +1,520 @@ +#!/bin/bash + +set -e + +############################################################################### +# Script to create a podman container for testing NetworkManager. +# +# Commands: +# - build: build a new image, named "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" ("nm:nm"). +# - run: start the container and tag it "$CONTAINER_NAME_NAME" ("nm"). +# - exec: run bash inside the container (this is the default). +# - journal|j: print the journal from inside the container. +# - stop: stop the container. +# - reset: stop and delete the container. +# - clean: stop and delete the container and the image. +# +# Options: +# --no-cleanup: don't delete the CONTAINERFILE and other artifacts +# --stop: only has effect with "run". It will stop the container afterwards. +# -- [EXTRA_ARGS]: +# - with command "exec", provide a command and arguments to run in the container. +# Defaults to "bash". +# - with command "journal", additional arguments that are passed to journalctl. +# +# It bind mounts the current working directory inside the container. +# You can run `make install` and run tests. +# There is a script nm-env-prepare.sh to generate a net1 interface for testing. +# +# This will bind-mount the NetworkManager working tree inside the container (and symlink +# from /NetworkManager). Create a file ".git/nm-in-container-host" to bind mount the host's +# "/" to "/Host". +# +# Create a symlink ./.git/NetworkManager-ci, to also bind-mount the CI directory. +# Create additional symlinks ./.git/nm-guest-link-*, to bind mount additional +# directories. +# +# Currently NM-ci requires a working eth1. +# Hence call `nm-env-prepare.sh --prefix eth -i 1 && sleep 1 && nmcli device connect eth1` before +# running a CI test. +############################################################################### + +if [ -z "$BASE_IMAGE" ]; then + if grep -q "^ID=fedora$" /etc/os-release 2>/dev/null ; then + BASE_IMAGE="$(sed -n 's/^VERSION_ID=\([0-9]\+\)$/fedora:\1/p' /etc/os-release)" + fi +fi +if [ -z "$BASE_IMAGE" ]; then + BASE_IMAGE=fedora:latest +fi + +BASEDIR_NM="$(readlink -f "$(dirname "$(readlink -f "$0")")/..")" +BASEDIR_DATA="$BASEDIR_NM/tools/nm-guest-data" + +SYMLINK_NAME=() +SYMLINK_TARGET=() +for d in $(ls -1d "$BASEDIR_NM/.git/NetworkManager-ci" "$BASEDIR_NM/.git/nm-guest-link-"* 2>/dev/null) ; do + NAME="${d##*/}" + NAME="${NAME##nm-guest-link-}" + TARGET="$(readlink -f "$d")" + test -e "$TARGET" + SYMLINK_NAME+=("$NAME") + SYMLINK_TARGET+=("$TARGET") +done + +CONTAINER_NAME_REPOSITORY=${CONTAINER_NAME_REPOSITORY:-nm} +CONTAINER_NAME_TAG=${CONTAINER_NAME_TAG:-nm} +CONTAINER_NAME_NAME=${CONTAINER_NAME_NAME:-nm} + +EXEC_ENV=() + +############################################################################### + +usage() { + cat <<EOF +$0: build|run|exec|stop|reset|reexec|clean|journal [--no-cleanup] [--stop] [-- EXTRA_ARGS] +EOF + echo + awk '/^####*$/{ if(on) exit; on=1} { if (on) { if (on==2) print(substr($0,3)); on=2; } }' "$BASH_SOURCE" + echo +} + +############################################################################### + +die() { + ( + echo -n -e "\033[31m" + printf "%s" "$*" + echo -e "\033[0m" + ) >&2 + exit 1 +} + +############################################################################### + +CLEANUP_FILES=() +DO_CLEANUP=1 +cleanup() { + test "$DO_CLEANUP" = 1 || return 0 + for f in "${CLEANUP_FILES[@]}" ; do + rm -rf "$f" + done +} + +trap cleanup EXIT + +############################################################################### + +tmp_file() { + cat > "$1" + CLEANUP_FILES+=( "$1" ) + test -z "$2" || chmod "$2" "$1" +} + +gen_file() { + local PERMS + [[ $1 =~ bin-* ]] && PERMS=755 || PERMS=644 + sed "s|{{BASEDIR_NM}}|$BASEDIR_NM|g" "$BASEDIR_DATA/$1.in" \ + | tmp_file "$BASEDIR_DATA/data-$1" $PERMS +} + +bind_files() { + VARIABLE_NAME="$1" + + ARR=() + H=~ + + ARR+=( -v "$BASEDIR_NM:$BASEDIR_NM" ) + + if [ -e "$BASEDIR_NM/.git/nm-in-container-host" ] ; then + ARR+=( -v /:/Host ) + fi + + for i in $(seq 1 ${#SYMLINK_TARGET[@]}) ; do + j=$((i - 1)) + ARR+=( -v "${SYMLINK_TARGET[$j]}:${SYMLINK_TARGET[$j]}" ) + done + + for f in ~/.gitconfig* ~/.vim* ; do + test -e "$f" || continue + f2="${f#$H/}" + [[ "$f2" = .viminf* ]] && continue + [[ "$f2" = *.tmp ]] && continue + [[ "$f2" = *~ ]] && continue + f2="/root/$f2" + ARR+=( -v "$f:$f2" ) + done + + eval "$VARIABLE_NAME=( \"\${ARR[@]}\" )" +} + +create_dockerfile() { + + local CONTAINERFILE="$1" + local BASE_IMAGE="$2" + local GEN_FILES="bin-nm-env-prepare.sh bin-nm-deploy.sh bin-_nm-in-container-setup.sh + etc-rc.local etc-motd-container etc-bashrc.my etc-bashrc-motd.my nm-90-my.conf + nm-95-user.conf home-bash_history home-gdbinit home-gdb_history home-behaverc + systemd-20-nm.override" + + cp "$BASEDIR_NM/contrib/scripts/NM-log" "$BASEDIR_DATA/data-bin-NM-log" + CLEANUP_FILES+=( "$BASEDIR_DATA/data-NM-log" ) + + for f in $GEN_FILES; do + gen_file "$f" + done + chmod 755 "$BASEDIR_DATA/data-etc-rc.local" + + RUN_LN_BASEDIR_NM= + if [ -n "$BASEDIR_NM" -a "$BASEDIR_NM" != "/NetworkManager" ] ; then + RUN_LN_BASEDIR_NM="RUN ln -snf \"$BASEDIR_NM\" /NetworkManager" + fi + + RUN_LN_SYMLINK_CMDS="" + for i in $(seq 1 ${#SYMLINK_NAME[@]}) ; do + j=$((i - 1)) + if [ -d "${SYMLINK_TARGET[$j]}" ] ; then + RUN_LN_SYMLINK_CMDS="$RUN_LN_SYMLINK_CMDS"$'\n'"RUN ln -snf \"${SYMLINK_TARGET[$j]}\" \"/${SYMLINK_NAME[$j]}\"" + fi + done + + cat <<EOF | tmp_file "$CONTAINERFILE" +FROM $BASE_IMAGE + +ENTRYPOINT ["/sbin/init"] + +RUN sed -i 's/^tsflags=.*/tsflags=/' /etc/dnf/dnf.conf + +RUN dnf install -y \\ + --skip-broken \\ + \\ + /usr/bin/python \\ + /usr/sbin/ausearch \\ + ModemManager-devel \\ + ModemManager-glib-devel \\ + NetworkManager \\ + audit-libs-devel \\ + bash-completion \\ + bind-utils \\ + black \\ + bluez-libs-devel \\ + ccache \\ + clang \\ + clang-tools-extra \\ + cscope \\ + dbus-devel \\ + dbus-x11 \\ + dhclient \\ + dnsmasq \\ + firewalld-filesystem \\ + gcc-c++ \\ + gdb \\ + gettext-devel \\ + git \\ + glib2-doc \\ + glibc-langpack-pl \\ + gnutls-devel \\ + gobject-introspection-devel \\ + gtk-doc \\ + intltool \\ + iproute \\ + iproute-tc \\ + iptables \\ + jansson-devel \\ + libasan \\ + libcurl-devel \\ + libndp-devel \\ + libpsl-devel \\ + libselinux-devel \\ + libtool \\ + libubsan \\ + libuuid-devel \\ + make \\ + meson \\ + mlocate \\ + mobile-broadband-provider-info-devel \\ + newt-devel \\ + nispor \\ + nmstate \\ + nss-devel \\ + polkit-devel \\ + ppp \\ + ppp-devel \\ + procps \\ + python3-behave \\ + python3-black \\ + python3-dbus \\ + python3-devel \\ + python3-gobject \\ + python3-pexpect \\ + python3-pip \\ + python3-pyte \\ + python3-pyyaml \\ + qt-devel \\ + radvd \\ + readline-devel \\ + rp-pppoe \\ + rpm-build \\ + strace \\ + systemd \\ + systemd-devel \\ + tcpdump \\ + teamd-devel \\ + vala \\ + vala-devel \\ + valgrind \\ + vim \\ + which \\ + \\ + 'dbus*' \\ + 'openvswitch2*' \\ + /usr/bin/pytest \\ + /usr/bin/debuginfo-install \\ + NetworkManager-openvpn \\ + NetworkManager-pptp \\ + NetworkManager-strongswan \\ + NetworkManager-vpnc \\ + NetworkManager-ovs \\ + NetworkManager-wifi \\ + NetworkManager-team \\ + NetworkManager-ppp \\ + cryptsetup \\ + dhcp-client \\ + dhcp-relay \\ + dhcp-server \\ + dnsmasq \\ + dracut-network \\ + ethtool \\ + firewalld \\ + gcc \\ + git \\ + hostapd \\ + iproute-tc \\ + ipsec-tools \\ + iputils \\ + iscsi-initiator-utils \\ + iw \\ + ldns \\ + libreswan \\ + libselinux-utils \\ + libyaml-devel \\ + logrotate \\ + lvm2 \\ + mdadm \\ + net-tools \\ + nfs-utils \\ + nmap-ncat \\ + nss-tools \\ + openvpn \\ + perl-IO-Pty-Easy \\ + perl-IO-Tty \\ + psmisc \\ + python3-dbus \\ + python3-gobject \\ + python3-netaddr \\ + qemu-kvm \\ + radvd \\ + rp-pppoe \\ + scsi-target-utils \\ + tcpdump \\ + tcpreplay \\ + tuned \\ + wireguard-tools \\ + wireshark-cli + +RUN dnf debuginfo-install --skip-broken NetworkManager \$(ldd /usr/sbin/NetworkManager | sed -n 's/.* => \\(.*\\) (0x[0-9A-Fa-f]*)$/\1/p' | xargs -n1 readlink -f) -y + +RUN dnf clean all + +RUN pip3 install --user behave_html_formatter || true + +RUN mkdir -p /etc/systemd/system/NetworkManager.service.d + +COPY data-bin-NM-log "/usr/bin/NM-log" +COPY data-bin-nm-env-prepare.sh "/usr/bin/nm-env-prepare.sh" +COPY data-bin-nm-deploy.sh "/usr/bin/nm-deploy.sh" +COPY data-bin-_nm-in-container-setup.sh "/usr/bin/_nm-in-container-setup.sh" +COPY data-etc-rc.local "/etc/rc.d/rc.local" +COPY data-etc-motd-container /etc/motd +COPY data-etc-bashrc.my /etc/bashrc.my +COPY data-etc-bashrc-motd.my /etc/bashrc-motd.my +COPY data-nm-90-my.conf /etc/NetworkManager/conf.d/90-my.conf +COPY data-nm-95-user.conf /etc/NetworkManager/conf.d/95-user.conf +COPY data-home-bash_history /root/.bash_history +COPY data-home-gdbinit /root/.gdbinit +COPY data-home-gdb_history /root/.gdb_history +COPY data-home-behaverc /root/.behaverc +COPY data-systemd-20-nm.override /etc/systemd/system/NetworkManager.service.d/20-nm.override + +RUN systemctl enable NetworkManager + +# Generate a stable machine id. +RUN echo "10001000100010001000100010001000" > /etc/machine-id + +RUN echo -e "# Default from the container image\nnameserver 8.8.8.8" > /etc/resolv.conf + +# Generate a fixed (version 1) secret key. +RUN mkdir -p /var/lib/NetworkManager +RUN chmod 700 /var/lib/NetworkManager +RUN echo -n "nm-in-container-secret-key" > /var/lib/NetworkManager/secret_key +RUN chmod 600 /var/lib/NetworkManager/secret_key + +RUN sed 's/.*RateLimitBurst=.*/RateLimitBurst=0/' /etc/systemd/journald.conf -i + +$RUN_LN_BASEDIR_NM +$RUN_LN_SYMLINK_CMDS + +RUN rm -rf /etc/NetworkManager/system-connections/* + +RUN echo -e '\n. /etc/bashrc.my\n' >> /etc/bashrc +RUN echo -e '\n. /etc/bashrc-motd.my\n' >> /etc/bashrc + +RUN updatedb +EOF +} + +############################################################################### + +container_image_exists() { + podman image exists "$1" || return 1 +} + +container_exists() { + podman container exists "$1" || return 1 +} + +container_is_running() { + test "$(podman ps --format "{{.ID}} {{.Names}}" | sed -n "s/ $1\$/\0/p")" != "" || return 1 +} + +############################################################################### + +do_reset() { + podman stop "$CONTAINER_NAME_NAME" || : + podman rm "$CONTAINER_NAME_NAME" || : +} + +do_clean() { + do_reset + podman rmi "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" || : +} + +do_build() { + container_image_exists "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" && return 0 + + CONTAINERFILE="$BASEDIR_DATA/containerfile" + create_dockerfile "$CONTAINERFILE" "$BASE_IMAGE" + podman build --squash-all --tag "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" -f "$CONTAINERFILE" +} + +do_run() { + do_build + + if container_is_running "$CONTAINER_NAME_NAME" ; then + return 0 + fi + + if container_exists "$CONTAINER_NAME_NAME" ; then + podman start "$CONTAINER_NAME_NAME" + return 0 + fi + + bind_files BIND_FILES + + podman run --privileged \ + --name "$CONTAINER_NAME_NAME" \ + --dns=none \ + --no-hosts \ + -d \ + "${BIND_FILES[@]}" \ + "$CONTAINER_NAME_REPOSITORY:$CONTAINER_NAME_TAG" +} + +do_exec() { + do_run + + local e + local EXTRA_ARGS=("$@") + if [ "${#EXTRA_ARGS[@]}" = 0 ]; then + EXTRA_ARGS=('bash') + fi + + local ENV=() + for e in "${EXEC_ENV[@]}" ; do + ENV+=(-e "$e") + done + + podman exec "${ENV[@]}" --workdir "$BASEDIR_NM" -it "$CONTAINER_NAME_NAME" "${EXTRA_ARGS[@]}" + + if [ "$DO_STOP" = 1 ]; then + do_stop + fi +} + +do_reexec() { + do_reset + do_exec "$@" +} + +do_journal() { + EXEC_ENV+=( "SYSTEMD_COLORS=0" ) + do_exec "journalctl" --no-pager "$@" +} + +do_stop() { + container_is_running "$CONTAINER_NAME_NAME" || return 0 + podman stop "$CONTAINER_NAME_NAME" +} + +############################################################################### + +DO_STOP=0 +CMD=exec +EXTRA_ARGS=() +for (( i=1 ; i<="$#" ; )) ; do + c="${@:$i:1}" + i=$((i+1)) + case "$c" in + --no-cleanup) + DO_CLEANUP=0 + ;; + --stop) + DO_STOP=1 + ;; + j) + CMD=journal + ;; + build|run|exec|stop|reset|reexec|clean|journal) + CMD=$c + ;; + --) + EXTRA_ARGS=( "${@:$i}" ) + break + ;; + -h|--help) + usage + exit 0 + ;; + *) + if [ "$CMD" = "journal" ]; then + EXTRA_ARGS=( "${@:$((i-1))}" ) + break; + else + usage + die "invalid argument: $c" + fi + ;; + esac +done + +############################################################################### + +test "$UID" != 0 || die "cannot run as root" + +if test "$CMD" != exec -a "$CMD" != journal -a "$CMD" != reexec -a "${#EXTRA_ARGS[@]}" != 0 ; then + die "Extra arguments are only allowed with exec|journal|reexec command" +fi + +############################################################################### + +do_$CMD "${EXTRA_ARGS[@]}" diff --git a/tools/nm-in-vm b/tools/nm-in-vm new file mode 100755 index 00000000..569c766c --- /dev/null +++ b/tools/nm-in-vm @@ -0,0 +1,441 @@ +#!/bin/bash + +set -e + +############################################################################### +# Script to create a virtual machine for testing NetworkManager. +# +# Commands: +# - build: build a new VM, named "$VM" ("nm") +# Args: the name of the OS to build, from `virt-builder --list` (Fedora by default) +# - run: start the VM. +# - exec: run bash inside the VM, connecting via ssh (this is the default). +# Args: command to execute inside the VM (then, ssh session won't open). +# - stop: stop the VM. +# - reexec: stop and exec. +# - clean: stop the VM and delete the image. +# +# Options (they MUST go before the command): +# -v | --vm: name of the VM to run the command on (by default 'nm'). +# This allows to have more than one VM at the same time +# -h | --help: show this text +# +# NetworkManager directories: +# +# The NetworkManager root directory is mounted in the VM as a filesystem share. +# You can run `make install` and run tests. +# +# Create a symlink ./.git/NetworkManager-ci, to share the CI directory too. +# +# Required packages: +# +# Your host needs libvirt, libvirt-nss and guestfs-tools. To access the VM with +# `ssh root@$VM`, configure /etc/nsswitch.conf as explained in +# https://libvirt.org/nss.html (otherwise, `nm-in-vm exec` won't work, either). +# +# Prepare for testing: +# +# There is a script nm-env-prepare.sh to generate a net1 interface for testing. +# Currently NM-ci requires a working eth1, so use this before running a CI test: +# `nm-env-prepare.sh --prefix eth -i 1 && sleep 1 && nmcli device connect eth1` +# +# Additional VMs: +# +# By default, the VM named 'nm' is created, but additional ones can be created: +# $ nm-in-vm -v nm2 build +# $ nm-in-vm -v nm2 exec +# $ nm-in-vm -v nm2 stop +# +# Choosing a different OS: +# +# By default Fedora is used, but you can choose a different OS. Most from the +# list `virt-builder --list` will work. +# $ nm-in-vm build debian-12 +# $ nm-in-vm exec +# $ nm-in-vm stop +############################################################################### + +# Check for libvirt +if ! (command -v virsh && command -v virt-builder && command -v virt-install) &>/dev/null; then + echo "libvirt and guestfs-tools are required" >&2 + exit 1 +fi + +# set defaults if user didn't define these values +VM=${VM:="nm"} +OS_VERSION=${OS_VERSION:=} +RAM=${RAM:=2048} +IMAGE_SIZE=${IMAGE_SIZE=20G} +ROOT_PASSWORD=${ROOT_PASSWORD:=nm} +LIBVIRT_POOL=${LIBVIRT_POOL:=default} # only useful if BASEDIR_VM_IMAGE is empty +BASEDIR_VM_IMAGE=${BASEDIR_VM_IMAGE:=} +BASEDIR_NM=${BASEDIR_NM:=} +BASEDIR_NM_CI=${BASEDIR_NM_CI:=} +HOST_BRIDGE=${HOST_BRIDGE:=virbr0} +SSH_LOG_LEVEL=${SSH_LOG_LEVEL:=ERROR} + +if [[ -z $OS_VERSION ]]; then + # if running Fedora, select same version, else select latest Fedora + if grep -q "^ID=fedora$" /etc/os-release 2>/dev/null ; then + OS_VERSION="$(sed -n 's/^VERSION_ID=\([0-9]\+\)$/fedora-\1/p' /etc/os-release)" + else + OS_VERSION=$(virt-builder --list | grep '^fedora' | sort | tail -n 1 | cut -d" " -f 1) + fi +fi + +if [[ -z $BASEDIR_NM ]]; then + BASEDIR_NM="$(readlink -f "$(dirname -- "$BASH_SOURCE")/..")" +fi + +if [[ -z $BASEDIR_NM_CI && -d "$BASEDIR_NM/.git/NetworkManager-ci" ]]; then + BASEDIR_NM_CI="$(readlink -f "$BASEDIR_NM/.git/NetworkManager-ci")" +fi + +if [[ -z $BASEDIR_VM_IMAGE ]]; then + libvirt_pool_path_xml=$(virsh pool-dumpxml $LIBVIRT_POOL | grep -F '<path>') + if [[ $libvirt_pool_path_xml =~ \<path\>(.*)\</path\> ]]; then + BASEDIR_VM_IMAGE=${BASH_REMATCH[1]} + else + BASEDIR_VM_IMAGE=$BASEDIR_NM + fi +fi + +############################################################################## + +do_build() { + local t=$'\t' + local ram + local size + local os_variant + local nm_ci_build_args + local nm_ci_install_args + local extra_pkgs + local install_pkgs + local install_files + local gen_files=( + "bin-nm-env-prepare.sh:/usr/bin/nm-env-prepare.sh" + "bin-nm-deploy.sh:/usr/bin/nm-deploy.sh" + "etc-motd-vm:/etc/motd" + "etc-bashrc.my:/etc/bashrc.my" + "nm-90-my.conf:/etc/NetworkManager/conf.d/90-my.conf" + "nm-95-user.conf:/etc/NetworkManager/conf.d/95-user.conf" + "home-bash_history:/root/.bash_history" + "home-gdbinit:/root/.gdbinit" + "home-gdb_history:/root/.gdb_history" + "home-behaverc:/root/.behaverc" + "systemd-10-host-net.link:/etc/systemd/network/10-host-net.link" + "systemd-dhcp-host.service:/etc/systemd/system/dhcp-host.service" + "systemd-20-nm.override:/etc/systemd/system/NetworkManager.service.d/20-nm.override" + ) + + (( $# > 1 )) && die "build only accepts one argument" + (( $# > 0 )) && OS_VERSION="$1" + os_variant=${OS_VERSION//-/} # virt-install --os-variant value, deduced from OS_VERSION + os_variant=${os_variant/centosstream/centos-stream} + + if [[ ! $VM =~ ^[a-zA-Z0-9\-]*$ ]]; then + echo "Invalid VM name '$VM', use only letters, numbers and '-' character" + return 1 + fi + + if vm_is_installed; then + echo "The virtual machine '$VM' is already installed, skiping build" >&2 + return 0 + fi + + if vm_image_exists; then + echo "The image '$basedir_vm_image/$vm_image_file' already exists, skiping build" >&2 + return 0 + fi + + if [[ -n $IMAGE_SIZE ]]; then + size=(--size "$IMAGE_SIZE") + fi + + if [[ -n $BASEDIR_NM_CI ]]; then + nm_ci_build_args=( + --mkdir "$BASEDIR_NM_CI" + --link "$BASEDIR_NM_CI:/NetworkManager-ci" + --append-line "/etc/fstab:/NM_CI${t}$BASEDIR_NM_CI${t}9p${t}trans=virtio,rw,_netdev${t}0${t}0" + ) + nm_ci_install_args=( + --filesystem "$BASEDIR_NM_CI,/NM_CI" + ) + fi + + if [[ $OS_VERSION =~ fedora || $OS_VERSION =~ centosstream ]]; then + extra_pkgs=(bash-completion bind-utils ccache clang-tools-extra cryptsetup cscope \'dbus\*\' + dhcp-client dhcp-relay dhcp-server dnsmasq dracut-network ethtool firewalld gcc gdb + glibc-langpack-pl hostapd intltool iproute ipsec-tools iputils iscsi-initiator-utils + iw ldns libreswan libselinux-utils libyaml-devel logrotate lvm2 mdadm mlocate net-tools + NetworkManager NetworkManager-openvpn NetworkManager-ovs NetworkManager-ppp + NetworkManager-pptp NetworkManager-strongswan NetworkManager-team NetworkManager-vpnc + NetworkManager-wifi nfs-utils nispor nmap-ncat nmstate nss-tools openvpn + \'openvswitch2\*\' perl-IO-Pty-Easy perl-IO-Tty procps psmisc python3-behave + python3-black python3-devel python3-netaddr python3-pip python3-pyte python3-pyyaml + qemu-kvm radvd rp-pppoe scsi-target-utils strace systemd tcpdump tcpreplay tuned + /usr/bin/debuginfo-install /usr/bin/pytest /usr/bin/python vim wireguard-tools + wireshark-cli) + + if [[ $OS_VERSION == centosstream-8 ]]; then + install_pkgs=( + --run-command "dnf -y copr enable nmstate/nm-build-deps" + --run-command "dnf config-manager -y --set-enabled powertools" + --install epel-release,epel-next-release + ) + elif [[ $OS_VERSION == centosstream-9 ]]; then + install_pkgs=( + --run-command "dnf -y copr enable nmstate/nm-build-deps" + --run-command "dnf config-manager -y --set-enabled crb" + --install epel-release,epel-next-release + ) + fi + + install_pkgs+=( + --update + --run "$BASEDIR_NM/contrib/fedora/REQUIRED_PACKAGES" + --run-command "dnf install -y --skip-broken ${extra_pkgs[*]}" + --run-command "pip3 install --user behave_html_formatter" + --run-command "dnf debuginfo-install -y --skip-broken NetworkManager \ + \$(ldd /usr/sbin/NetworkManager \ + | sed -n 's/.* => \(.*\) (0x[0-9A-Fa-f]*)\$/\1/p' \ + | xargs -n1 readlink -f)" + ) + elif [[ $OS_VERSION =~ debian || $OS_VERSION =~ ubuntu ]]; then + extra_pkgs=(bash-completion bind9-utils ccache clang-tools cryptsetup cscope \'dbus\*\' + isc-dhcp-client isc-dhcp-relay isc-dhcp-server dnsmasq dracut-network ethtool firewalld + gcc gdb hostapd intltool iproute2 \'iputils-\*\' iw libldns3 libreswan + libyaml-dev logrotate lvm2 mdadm mlocate net-tools network-manager + network-manager-openvpn network-manager-pptp network-manager-strongswan + network-manager-vpnc nfs-common ncat libnss3-tools openvpn \'openvswitch2\*\' procps + psmisc python3-behave black python3-dev python3-netaddr python3-pip python3-pyte + python3-pretty-yaml qemu-kvm radvd pppoe strace systemd tcpdump tcpreplay tuned + debian-goodies python3-pytest python3 vim wireguard-tools tshark) + + if [[ $OS_VERSION =~ debian ]]; then + install_pkgs=( + --run-command "echo deb http://deb.debian.org/debian-debug/ \$(lsb_release -cs)-debug main >> /etc/apt/sources.list.d/debug.list" + --run-command "echo deb http://deb.debian.org/debian-debug/ \$(lsb_release -cs)-proposed-updates-debug main >> /etc/apt/sources.list.d/debug.list" + ) + elif [[ $OS_VERSION =~ ubuntu ]]; then + install_pkgs=( + --run-command "echo deb http://ddebs.ubuntu.com \$(lsb_release -cs) main restricted universe multiverse >> /etc/apt/sources.list.d/debug.list" + --run-command "echo deb http://ddebs.ubuntu.com \$(lsb_release -cs)-updates main restricted universe multiverse >> /etc/apt/sources.list.d/debug.list" + --run-command "echo deb http://ddebs.ubuntu.com \$(lsb_release -cs)-proposed main restricted universe multiverse >> /etc/apt/sources.list.d/debug.list" + ) + fi + + install_pkgs+=( + --update + --upload "$BASEDIR_NM/contrib/debian/REQUIRED_PACKAGES:/tmp/REQUIRED_PACKAGES" + --run-command "/bin/bash /tmp/REQUIRED_PACKAGES" # using only --run fails + --run-command "apt-get install -y \$(find-dbgsym-packages NetworkManager 2>/dev/null)" + --edit "/etc/locale.gen:s/^# pl_PL.UTF-8/pl_PL.UTF-8/" + --run-command "locale-gen" + ) + + for p in "${extra_pkgs[@]}"; do + install_pkgs+=(--run-command "apt-get install -y $p || :") + done + fi + + install_files=(--upload "$BASEDIR_NM/contrib/scripts/NM-log:/usr/bin/NM-log") + for f in "${gen_files[@]}"; do + gen_file "${f%:*}" + install_files+=(--upload "$datadir/data-$f") + done + + echo "Creating VM" + echo " - VM NAME: $VM" + echo " - OS VERSION: $OS_VERSION" + echo " - SIZE: $([[ -n $IMAGE_SIZE ]] && echo "$IMAGE_SIZE" || echo "don't resize")" + echo " - RAM: $RAM" + echo " - ROOT PASSWORD: $ROOT_PASSWORD" + echo " - IMAGE PATH: $basedir_vm_image/$vm_image_file" + echo " - NM DIR: $BASEDIR_NM" + echo " - NM CI DIR: $([[ -n $BASEDIR_NM_CI ]] && echo "$BASEDIR_NM_CI" || echo '<none>')" + echo " - HOST BRIDGE: $HOST_BRIDGE" + + if [[ $OS_VERSION =~ centosstream ]]; then + echo "WARNING: NetworkManager repositories can't be shared with the guest" \ + "(CentOS Stream doesn't support 9P filesystem). You'll need to manually" \ + "share by NFS or make a new clone of the repository inside the guest." >&2 + fi + + virt-builder "$OS_VERSION" \ + --output "$basedir_vm_image/$vm_image_file" \ + "${size[@]}" \ + --format qcow2 \ + --arch x86_64 \ + --hostname "$VM" \ + --root-password password:nm \ + --ssh-inject root \ + --append-line "/etc/ssh/sshd_config:PermitRootLogin yes" \ + --run-command "ssh-keygen -A" \ + --mkdir "$BASEDIR_NM" \ + --link "$BASEDIR_NM:/NetworkManager" \ + --append-line "/etc/fstab:/NM${t}$BASEDIR_NM${t}9p${t}trans=virtio,rw,_netdev${t}0${t}0" \ + "${nm_ci_build_args[@]}" \ + "${install_pkgs[@]}" \ + --mkdir "/etc/systemd/system/NetworkManager.service.d" \ + --mkdir "/etc/systemd/network" \ + "${install_files[@]}" \ + --write "/var/lib/NetworkManager/secret_key:nm-in-container-secret-key" \ + --chmod "700:/var/lib/NetworkManager" \ + --chmod "600:/var/lib/NetworkManager/secret_key" \ + --edit "/etc/systemd/journald.conf:s/.*RateLimitBurst=.*/RateLimitBurst=0/" \ + --delete "/etc/NetworkManager/system-connections/*" \ + --append-line "/etc/bashrc:. /etc/bashrc.my" \ + --run-command "updatedb" \ + --append-line "/etc/dhcp/dhclient.conf:send host-name = gethostname();" \ + --firstboot-command "systemctl enable --now dhcp-host" + + virt-install \ + --name "$VM" \ + --ram "$RAM" \ + --disk "path=$basedir_vm_image/$vm_image_file,format=qcow2" \ + --os-variant "$os_variant" \ + --filesystem "$BASEDIR_NM,/NM" \ + "${nm_ci_install_args[@]}" \ + --network "bridge=$HOST_BRIDGE" \ + --import \ + --autoconsole none \ + --noreboot +} + +do_clean() { + vm_is_running && virsh shutdown "$VM" &>/dev/null + vm_is_installed && virsh undefine "$VM" &>/dev/null + rm -f "$basedir_vm_image/$vm_image_file" +} + +do_run() { + vm_is_installed || do_build + vm_is_running && return 0 + virsh start "$VM" >&2 +} + +do_exec() { + do_run + + local failed=0 + while ! ping -c 1 "$VM" &>/dev/null; do + failed=$((failed + 1)) + (( failed < 15 )) || die "Timeout trying to ping the VM" + sleep 1 + done + + ssh \ + -o StrictHostKeyChecking=no \ + -o UserKnownHostsFile=/dev/null \ + -o LogLevel="$SSH_LOG_LEVEL" \ + "root@$VM" "$@" +} + +do_reexec() { + vm_is_running && do_stop + + local waited=0 + while vm_is_running; do + waited=$((waited + 1)) + (( waited < 30 )) || die "Timeout waiting for VM shutdown" + sleep 1 + done + + do_exec "$@" +} + +do_stop() { + vm_is_running && virsh shutdown "$VM" >&2 +} + +############################################################################### + +vm_image_exists() { + [[ -f "$basedir_vm_image/$vm_image_file" ]] || return 1 +} + +vm_is_installed() { + virsh list --all --name | grep --fixed-strings --line-regexp "$VM" &>/dev/null || return 1 +} + +vm_is_running() { + virsh list --name | grep --fixed-strings --line-regexp "$VM" &>/dev/null || return 1 +} + +gen_file() { + sed "s|{{BASEDIR_NM}}|$BASEDIR_NM|g" "$datadir/$1.in" > "$datadir/data-$1" + if [[ $1 =~ ^bin- ]]; then + chmod 755 "$datadir/data-$1" + else + chmod 644 "$datadir/data-$1" + fi +} + +usage() { + echo "nm-in-vm [options] build|run|exec|stop|reexec|clean [command_args]" +} + +help() { + usage + echo + awk '/^####*$/ { if (on) exit; on=-1; } !/^####*$/ { if (on) print(substr($0,3)) }' "$BASH_SOURCE" + echo +} + +die() { + echo "$1" >&2 + exit 1 +} + +############################################################################### + +cmd= + +while (( $# > 0 )); do + case "$1" in + build|run|exec|reexec|stop|clean) + cmd="$1" + shift + ;; + -h|--help) + help + exit 0 + ;; + -v|--vm) + (( $# > 1 )) || die "--vm requires one argument" + VM="$2" + shift 2 + ;; + --) + shift + break + ;; + *) + [[ $cmd != "" ]] && break + echo "Invalid argument '$1'" >&2 + echo $(usage) >&2 + exit 1 + ;; + esac +done + +# compute some values that depends on user selectable variables +mkdir -p "$BASEDIR_VM_IMAGE" +basedir_vm_image=$(readlink -f "$BASEDIR_VM_IMAGE") +vm_image_file="$VM.qcow2" +datadir="$BASEDIR_NM/tools/nm-guest-data" + +if [[ $cmd == "" ]]; then + cmd=exec +fi + +if [[ $UID == 0 ]]; then + die "cannot run as root" +fi + +if [[ $cmd != exec && $cmd != reexec && $cmd != build && $# != 0 ]]; then + die "Extra arguments are only allowed with exec|reexec|build command" +fi + +do_$cmd "$@" diff --git a/tools/test-build.sh b/tools/test-build.sh new file mode 100755 index 00000000..04d33b6a --- /dev/null +++ b/tools/test-build.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +set -e + +die() { + echo "$@" + exit 1 +} + +if [[ "x$(LANG=C git clean -ndx)" != x ]]; then + die "The working directory is not clean. Refuse to run. Try \`git clean -dx -n\`" +fi +if [[ "x$(git status --porcelain)" != x ]]; then + die "The working directory has local changes. Refuse to run. Try \`git reset --hard\`" +fi + +build_out_of_tree() { + local TARGET="$1" + local C="$2" + local M="$3" + ( + git clean -fdx || return 1 + NOCONFIGURE=x ./autogen.sh || return 1 + mkdir -p x/y || return 1 + cd x/y || return 1 + ../../configure $C $NMTST_CONFIGURE || return 1 + make $TARGET $M $NMTST_MAKE || return 1 + ) +} + +TARGETS=("$@") +if [ "${#TARGETS}" -lt 1 ]; then + TARGETS=( + dispatcher/nm-dispatcher + src/core/NetworkManager + src/core/dhcp/nm-dhcp-helper + src/core/ndisc/tests/test-ndisc-linux + src/core/platform/tests/monitor + src/nm-online/nm-online + src/nmcli/nmcli + src/nmtui/nmtui + $(git grep -h '\.l\?a\>' Makefile.am | sed 's/[a-zA-Z.0-9_-/]\+/\n\0\n/g' | sort -u | grep '\.l\?a$') + ) +fi + +set -x + +cd "$(dirname "$(readlink -f "$0")")/.." + +IDX=($(seq 0 $((${#TARGETS[@]} - 1)))) +IDX=($(printf '%s\n' "${IDX[@]}" | sort -R)) +for idx in "${IDX[@]}"; do + TARGET="${TARGETS[$idx]}" + echo "### $idx: TARGET=$TARGET" + build_out_of_tree "$TARGET" "--enable-gtk-doc" "-j 5" +done diff --git a/tools/test-networkmanager-service.py b/tools/test-networkmanager-service.py index f274a460..b4268721 100755 --- a/tools/test-networkmanager-service.py +++ b/tools/test-networkmanager-service.py @@ -31,6 +31,37 @@ _DEFAULT_ARG = object() ############################################################################### +class AliasedProperty: + def __init__(self, old_name, new_name): + self.old_name = old_name + self.new_name = new_name + + +ALIASED_PROPERTIES = { + NM.SETTING_CONNECTION_SETTING_NAME: [ + AliasedProperty( + NM.SETTING_CONNECTION_MASTER, + "controller", # NM.SETTING_CONNECTION_CONTROLLER + ), + AliasedProperty( + NM.SETTING_CONNECTION_SLAVE_TYPE, + "port-type", # NM.SETTING_CONNECTION_PORT_TYPE + ), + AliasedProperty(NM.SETTING_CONNECTION_AUTOCONNECT_SLAVES, "autoconnect-ports"), + ], + NM.SETTING_WIRELESS_SETTING_NAME: [ + AliasedProperty( + NM.SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, "mac-address-denylist" + ) + ], + NM.SETTING_WIRED_SETTING_NAME: [ + AliasedProperty(NM.SETTING_WIRED_MAC_ADDRESS_BLACKLIST, "mac-address-denylist") + ], +} + +############################################################################### + + class Global: pass @@ -2118,6 +2149,13 @@ class Connection(ExportedObj): out_signature="a{sv}", ) def Update2(self, con_hash, flags, args): + for setting_name in ALIASED_PROPERTIES.keys(): + if setting_name in con_hash: + setting = con_hash[setting_name] + for pty in ALIASED_PROPERTIES[setting_name]: + if pty.new_name in setting and pty.old_name in setting: + del setting[pty.new_name] + self.update_connection(con_hash, True) return [] @@ -2329,12 +2367,16 @@ class IP4Config(ExportedObj): a = { "dest": Util.random_ip(seed, net="192.168.0.0/16")[0], "prefix": Util.random_int(seed, 17, 32), - "next-hop": None - if (Util.random_int(seed) % 3 == 0) - else Util.random_ip(seed, net="192.168.0.0/16")[0], - "metric": -1 - if (Util.random_int(seed) % 3 == 0) - else Util.random_int(seed, 0, 0xFFFFFFFF), + "next-hop": ( + None + if (Util.random_int(seed) % 3 == 0) + else Util.random_ip(seed, net="192.168.0.0/16")[0] + ), + "metric": ( + -1 + if (Util.random_int(seed) % 3 == 0) + else Util.random_int(seed, 0, 0xFFFFFFFF) + ), } routes.append(a) @@ -2527,12 +2569,16 @@ class IP6Config(ExportedObj): a = { "dest": Util.random_ip(seed, net="2001:a::/64")[0], "prefix": Util.random_int(seed, 65, 128), - "next-hop": None - if (Util.random_int(seed) % 3 == 0) - else Util.random_ip(seed, net="2001:a::/64")[0], - "metric": -1 - if (Util.random_int(seed) % 3 == 0) - else Util.random_int(seed, 0, 0xFFFFFFFF), + "next-hop": ( + None + if (Util.random_int(seed) % 3 == 0) + else Util.random_ip(seed, net="2001:a::/64")[0] + ), + "metric": ( + -1 + if (Util.random_int(seed) % 3 == 0) + else Util.random_int(seed, 0, 0xFFFFFFFF) + ), } routes.append(a) |