
The connectivity with the host depends on getting a DHCP lease from the host. With the latest commit's customizations, the virtual NIC is not managed by NM so it is not configured. Keep it unmanaged so debuging NetworkManager doesn't affect to this virtual NIC. Use dhclient to get a DHCP lease from the host. Assign a fixed interface name (host_net) to match it from NM and dhclient config files.
362 lines
12 KiB
Bash
Executable File
362 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -e
|
|
|
|
###############################################################################
|
|
# Script to create a virtual machine for testing NetworkManager.
|
|
#
|
|
# Commands:
|
|
# - build: build a new VM, named "$VM" ("nm").
|
|
# - run: start the VM.
|
|
# - exec: run bash inside the VM, connecting via ssh (this is the default).
|
|
# - stop: stop the VM.
|
|
# - reexec: stop and exec.
|
|
# - clean: stop the VM and delete the image.
|
|
#
|
|
# Commands exec and reexec accepts extra arguments, which are the command to
|
|
# execute in the VM instead of opening an ssh session.
|
|
#
|
|
# 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:
|
|
# $ VM=nm2 nm-in-vm build
|
|
# $ VM=nm2 nm-in-vm exec
|
|
# $ VM=nm2 nm-in-vm 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.
|
|
# $ OS_VERSION=debian-12 nm-in-vm build
|
|
# $ 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:=10G}
|
|
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
|
|
|
|
# compute some values that depends on user selectable variables
|
|
basedir_vm_image=$(readlink -f "$BASEDIR_VM_IMAGE")
|
|
vm_image_file="$VM.qcow2"
|
|
os_variant=${OS_VERSION//-/} # virt-install --os-variant value, deduced from OS_VERSION
|
|
os_variant=${os_variant/centosstream/centos-stream}
|
|
datadir="$BASEDIR_NM/tools/nm-guest-data"
|
|
|
|
##############################################################################
|
|
|
|
do_build() {
|
|
local t=$'\t'
|
|
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"
|
|
)
|
|
|
|
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 $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 =~ centos* ]]; 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)
|
|
|
|
install_pkgs=(
|
|
--run "$BASEDIR_NM/contrib/fedora/REQUIRED_PACKAGES"
|
|
--run-command "dnf install -y --skip-broken ${extra_pkgs[*]}"
|
|
--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
|
|
install_pkgs=(
|
|
--run "$BASEDIR_NM/contrib/debian/REQUIRED_PACKAGES"
|
|
)
|
|
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: $IMAGE_SIZE"
|
|
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"
|
|
|
|
virt-builder "$OS_VERSION" \
|
|
--output "$basedir_vm_image/$vm_image_file" \
|
|
--size "$IMAGE_SIZE" \
|
|
--format qcow2 \
|
|
--arch x86_64 \
|
|
--hostname "$VM" \
|
|
--root-password password:nm \
|
|
--ssh-inject root \
|
|
--append-line "/etc/ssh/sshd_config:PermitRootLogin yes" \
|
|
--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[@]}" \
|
|
--update \
|
|
"${install_pkgs[@]}" \
|
|
--run-command "pip3 install --user behave_html_formatter" \
|
|
--mkdir "/etc/systemd/system/NetworkManager.service.d" \
|
|
"${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" \
|
|
--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 [-h|--help] build|run|exec|stop|reexec|clean"
|
|
}
|
|
|
|
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
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
*)
|
|
[[ $cmd != "" ]] && break
|
|
echo "Invalid argument '$1'" >&2
|
|
echo $(usage) >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ $cmd == "" ]]; then
|
|
cmd=exec
|
|
fi
|
|
|
|
if [[ $UID == 0 ]]; then
|
|
die "cannot run as root"
|
|
fi
|
|
|
|
if [[ $cmd != exec && $cmd != reexec && $# != 0 ]]; then
|
|
die "Extra arguments are only allowed with exec|reexec command"
|
|
fi
|
|
|
|
do_$cmd "$@"
|