set -eu -o pipefail
vmname="coreos-nettest"
butane_common=\
'variant: fcos
version: 1.0.0
passwd:
users:
- name: core
ssh_authorized_keys:
- $sshpubkey
systemd:
units:
- name: serial-getty@ttyS0.service
dropins:
- name: autologin-core.conf
contents: |
[Service]
# Override Execstart in main unit
ExecStart=
# Add new Execstart with `-` prefix to ignore failure
ExecStart=-/usr/sbin/agetty --autologin core --noclear %I $TERM
TTYVTDisallocate=no
storage:
files:
# pulling from a remote verifies we have networking in the initramfs
- path: /home/core/remotefile
mode: 0600
user:
name: core
group:
name: core
contents:
source: https://raw.githubusercontent.com/coreos/fedora-coreos-config/8b08bd030ef3968d00d4fea9a0fa3ca3fbabf852/COPYING
verification:
hash: sha512-d904690e4fc5defb804c2151e397cbe2aeeea821639995610aa377bb2446214c3433616a8708163776941df585b657648f20955e50d4b011ea2a96e7d8e08c66
- path: /etc/sysctl.d/20-silence-audit.conf
contents:
inline: |
# Raise console message logging level from DEBUG (7) to WARNING (4)
kernel.printk=4'
ignitionhostname='ignitionhost'
butane_hostname='
- path: /etc/hostname
mode: 0644
contents:
inline: |
${ignitionhostname}'
butane_static_nic0_ifcfg='
- path: /etc/sysconfig/network-scripts/ifcfg-${nic0}
mode: 0600
contents:
inline: |
TYPE=Ethernet
BOOTPROTO=none
IPADDR=${ip}
PREFIX=${prefix}
GATEWAY=${gateway}
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
NAME=ethernet-${nic0}
DEVICE=${nic0}
ONBOOT=yes
DNS1=${nameserverstatic}
- path: /etc/sysconfig/network-scripts/ifcfg-${nic1}
mode: 0600
contents:
inline: |
TYPE=Ethernet
BOOTPROTO=none
NAME=ethernet-${nic1}
DEVICE=${nic1}
ONBOOT=no'
butane_static_nic0='
- path: /etc/NetworkManager/system-connections/${nic0}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=${nic0}
type=ethernet
interface-name=${nic0}
[ipv4]
address1=${ip}/${prefix},${gateway}
dns=${nameserverstatic};
dns-search=
may-fail=false
method=manual
[ipv6]
method=disabled
- path: /etc/NetworkManager/system-connections/${nic1}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=${nic1}
type=ethernet
interface-name=${nic1}
[ipv4]
method=disabled
[ipv6]
method=disabled'
butane_static_team0='
- path: /etc/NetworkManager/system-connections/team0.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=team0
type=team
interface-name=team0
[team]
config={"runner": {"name": "activebackup"}, "link_watch": {"name": "ethtool"}}
[ipv4]
address1=${ip}/${prefix},${gateway}
dns=${nameserverstatic};
dns-search=
may-fail=false
method=manual
- path: /etc/NetworkManager/system-connections/team0-slave-${nic0}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=team0-slave-${nic0}
type=ethernet
interface-name=${nic0}
master=team0
slave-type=team
[team-port]
config={"prio": 100}
- path: /etc/NetworkManager/system-connections/team0-slave-${nic1}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=team0-slave-${nic1}
type=ethernet
interface-name=${nic1}
master=team0
slave-type=team
[team-port]
config={"prio": 100}'
butane_static_bond0='
- path: /etc/NetworkManager/system-connections/bond0.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=bond0
type=bond
interface-name=bond0
[bond]
miimon=100
mode=active-backup
[ipv4]
address1=${ip}/${prefix},${gateway}
dns=${nameserverstatic};
dns-search=
may-fail=false
method=manual
- path: /etc/NetworkManager/system-connections/bond0-slave-${nic0}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=bond0-slave-${nic0}
type=ethernet
interface-name=${nic0}
master=bond0
slave-type=bond
- path: /etc/NetworkManager/system-connections/bond0-slave-${nic1}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=bond0-slave-${nic1}
type=ethernet
interface-name=${nic1}
master=bond0
slave-type=bond'
butane_static_br0='
- path: /etc/NetworkManager/system-connections/br0.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=br0
type=bridge
interface-name=br0
[bridge]
[ipv4]
address1=${ip}/${prefix},${gateway}
dns=${nameserverstatic};
dns-search=
may-fail=false
method=manual
- path: /etc/NetworkManager/system-connections/br0-slave-${nic0}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=br0-slave-${nic0}
type=ethernet
interface-name=${nic0}
master=br0
slave-type=bridge
[bridge-port]
- path: /etc/NetworkManager/system-connections/br0-slave-${nic1}.nmconnection
mode: 0600
contents:
inline: |
[connection]
id=br0-slave-${nic1}
type=ethernet
interface-name=${nic1}
master=br0
slave-type=bridge
[bridge-port]'
check_requirement() {
req=$1
if ! which "$req" &>/dev/null; then
echo "No $req. Can't continue" 1>&2
return 1
fi
}
check_requirements() {
reqs=(
chcon
envsubst
butane
jq
ssh
ssh-keygen
virsh
virt-cat
virt-install
virt-ls
)
for req in "${reqs[@]}"; do
check_requirement "$req"
done
}
start_vm() {
echo "Starting domain: $vmname"
local disk=$1; shift
local ignitionfile=$1; shift
local kernel=$1; shift
local initramfs=$1; shift
local kernel_args=$*
virt-install --name $vmname --ram 3096 --vcpus 2 --graphics=none --noautoconsole \
--quiet --network bridge=virbr0 --network bridge=virbr0 \
--disk size=20,backing_store="${disk}" \
--install kernel="${kernel}",initrd="${initramfs}",kernel_args_overwrite=yes,kernel_args="${kernel_args}" \
--qemu-commandline="-fw_cfg name=opt/com.coreos/config,file=$ignitionfile"
}
check_vm() {
local dhcp=$1
local interfaces=$2
local numkeyfiles=$3
local ip=$4
local dev=$5
local hostname=$6
local nameserver=$7
local sshkeyfile=$8
local ssh_config=' -o CheckHostIP=no'
ssh_config+=' -o UserKnownHostsFile=/dev/null'
ssh_config+=' -o StrictHostKeyChecking=no'
ssh_config+=" -i $sshkeyfile"
if [ "$dhcp" == 'dhcp' ]; then
macinfo=$(virsh dumpxml $vmname | grep 'mac address' | head -n 1)
macregex='(..:..:..:..:..:..)'
if ! [[ $macinfo =~ $macregex ]]; then
echo -e "\nCould not detect MAC in $macinfo" 2>&1
return 1
fi
mac="${BASH_REMATCH[1]}"
echo "Using DHCP.. Detected MAC address is ${mac}"
echo "Waiting a bit to give networking some time"
sleep 30
ip=$(ip -j neighbor show dev virbr0 | jq -r ".[] | select(.lladdr == \"${mac}\").dst")
echo "Detected IP address is '${ip}'"
if [ -z "$ip" ]; then
echo -e "\nCould not detect DHCP ipv4 address" 2>&1
return 1
fi
fi
export SSH_AUTH_SOCK=
local ssh="ssh -q $ssh_config -l core $ip"
try=10
echo 'waiting on ssh connection to come up'
while true; do
$ssh /usr/bin/true && echo && break
echo -n '.'
sleep 5
((try--))
if [ $try -eq 0 ]; then
echo -e "\nTimeout while trying to reach $ip" 2>&1
return 1
fi
done
ipinfo=$($ssh ip -j -4 -o address show up)
hostnameinfo=$($ssh hostnamectl | grep 'Static hostname')
resolvedotconf=$($ssh cat /etc/resolv.conf)
keyfiles=$($ssh ls /etc/NetworkManager/system-connections/)
detectedkeyfiles=$($ssh ls /etc/NetworkManager/system-connections/ | wc -l)
rc=0
if [[ ! $hostnameinfo =~ "Static hostname: $hostname" ]]; then
rc=1
echo "ERROR: Hostname information was not what was expected" 1>&2
fi
if [ "${detectedkeyfiles}" != "${numkeyfiles}" ]; then
rc=1
echo "ERROR: Expected ${numkeyfiles} NM keyfiles, found ${detectedkeyfiles}" 1>&2
fi
if grep systemd-resolved &>/dev/null <<< "$resolvedotconf"; then
nameserverinfo=$($ssh resolvectl dns)
else
nameserverinfo="$resolvedotconf"
fi
if ! grep "$nameserver" &>/dev/null <<< "$nameserverinfo"; then
rc=1
echo "ERROR: Nameserver information was not what was expected" 1>&2
fi
if [ "$(jq length <<< "$ipinfo")" != "$((interfaces+1))" ]; then
rc=1
echo "ERROR: More interfaces up than expected" 1>&2
fi
if [ "$(jq -r .[0].addr_info[0].dev <<< "$ipinfo")" != 'lo' ]; then
rc=1
echo "ERROR: The first active interface is not 'lo'" 1>&2
fi
if [ "$(jq -r .[1].addr_info[0].dev <<< "$ipinfo")" != "${dev}" ]; then
rc=1
echo "ERROR: The second active interface is not ${dev}" 1>&2
fi
if [ "$(jq -r .[1].addr_info[0].local <<< "$ipinfo")" != "${ip}" ]; then
rc=1
echo "ERROR: The second active interface does not have expected ip" 1>&2
fi
if [ "$rc" != '0' ]; then
echo "$hostnameinfo"
echo "$nameserverinfo"
echo "$keyfiles"
jq -r .[].addr_info[].dev 1>&2 <<< "$ipinfo"
jq -r .[].addr_info[].local 1>&2 <<< "$ipinfo"
true
else
echo "Check for ${hostname} + dns:${nameserver} + ${dev}/${ip} passed!"
fi
return $rc
}
reboot_vm() {
echo "Rebooting domain: $vmname"
virsh shutdown $vmname 1>/dev/null
sleep 10
virsh start $vmname 1>/dev/null
}
destroy_vm() {
echo "Destroying domain: $vmname"
virsh dominfo $vmname &>/dev/null || return 0
virsh destroy $vmname 1>/dev/null
virsh undefine --nvram --remove-all-storage $vmname 1>/dev/null
}
create_ignition_file() {
local butaneconfig=$1
local ignitionfile=$2
echo "$butaneconfig" | butane --strict --output "$ignitionfile"
chcon --verbose unconfined_u:object_r:svirt_home_t:s0 "$ignitionfile" &>/dev/null
}
main() {
qcow=$1
local ip='192.168.122.111'
local netmask='255.255.255.0'
local prefix='24'
local gateway='192.168.122.1'
local nameserverdhcp='192.168.122.1'
local nameserverstatic='208.67.222.222'
local initramfshostname='initrdhost'
local kernel="${PWD}/coreos-nettest-kernel"
local initramfs="${PWD}/coreos-nettest-initramfs"
local sshkeyfile="${PWD}/coreos-nettest-sshkey"
local sshpubkeyfile="${PWD}/coreos-nettest-sshkey.pub"
local ignitionfile="${PWD}/coreos-nettest-config.ign"
local sshpubkey
local butane
check_requirements
rm -f "$sshkeyfile" "$sshpubkeyfile"
ssh-keygen -N '' -C '' -f "$sshkeyfile" &>/dev/null
sshpubkey="$(cat "$sshpubkeyfile")"
partition=$(guestfish --ro -a "$qcow" <<EOF
run
findfs-label boot
exit
EOF
)
files=$(virt-ls -a "$qcow" -m "$partition" -R /ostree/)
for f in $files; do
if [[ "${f}" =~ hmac$ ]]; then
true
elif [[ "${f}" =~ img$ ]]; then
virt-cat -a "$qcow" -m "$partition" "/ostree/${f}" > "$initramfs"
elif [[ "${f}" =~ '/vmlinuz' ]]; then
virt-cat -a "$qcow" -m "$partition" "/ostree/${f}" > "$kernel"
fi
done
if [[ $qcow =~ 'rhcos' ]]; then
rhcos=1
nic0=ens2
nic1=ens3
bls_file=ostree-1-rhcos.conf
else
rhcos=0
nic0=ens2
nic1=ens3
bls_file=ostree-1-fedora-coreos.conf
fi
nics="${nic0},${nic1}"
common_args=$(virt-cat -a "$qcow" -m "$partition" "/loader.1/entries/${bls_file}" | \
grep -P '^options' | \
sed -e 's/options //' | \
sed -e 's/$ignition_firstboot//')
common_args+=' ignition.firstboot'
devname=eth0
x="${common_args} rd.neednet=1 ip=${devname}:dhcp"
initramfs_dhcp_eth0=$x
devname=$nic0
x="${common_args} rd.neednet=1 ip=${devname}:dhcp"
initramfs_dhcp_nic0=$x
devname=$nic0
x="${common_args} rd.neednet=1 ip=${nic0}:dhcp ip=${nic1}:dhcp"
initramfs_dhcp_nic0nic1=$x
devname=$nic0
x="${common_args} rd.neednet=1 ip=${nic1}:off"
x+=" ip=${ip}::${gateway}:${netmask}:${initramfshostname}:${devname}:none:${nameserverstatic}"
initramfs_static_nic0=$x
devname=bond0
x="${common_args} rd.neednet=1"
x+=" ip=${ip}::${gateway}:${netmask}:${initramfshostname}:${devname}:none:${nameserverstatic}"
x+=" bond=${devname}:${nics}:mode=active-backup,miimon=100"
initramfs_static_bond0=$x
devname=team0
x="${common_args} rd.neednet=1"
x+=" ip=${ip}::${gateway}:${netmask}:${initramfshostname}:${devname}:none:${nameserverstatic}"
x+=" team=${devname}:${nics}"
initramfs_static_team0=$x
devname=br0
x="${common_args} rd.neednet=1"
x+=" ip=${ip}::${gateway}:${netmask}:${initramfshostname}:${devname}:none:${nameserverstatic}"
x+=" bridge=${devname}:${nics}"
initramfs_static_br0=$x
export ip prefix nameserverstatic gateway sshpubkey ignitionhostname nic0 nic1
butane_none=$(echo "${butane_common}" | envsubst)
butane_static_nic0=$(echo "${butane_common}${butane_hostname}${butane_static_nic0}" | envsubst)
butane_static_bond0=$(echo "${butane_common}${butane_hostname}${butane_static_bond0}" | envsubst)
butane_static_team0=$(echo "${butane_common}${butane_hostname}${butane_static_team0}" | envsubst)
butane_static_br0=$(echo "${butane_common}${butane_hostname}${butane_static_br0}" | envsubst)
butane_static_nic0_ifcfg=$(echo "${butane_common}${butane_hostname}${butane_static_nic0_ifcfg}" | envsubst)
destroy_vm || true
if [ "$rhcos" == 1 ]; then
echo -e "\n###### Testing ifcfg file via Ignition disables initramfs propagation\n"
create_ignition_file "$butane_static_nic0_ifcfg" $ignitionfile
start_vm $qcow $ignitionfile $kernel $initramfs "$initramfs_static_bond0"
check_vm 'none' 1 0 $ip $nic0 $ignitionhostname $nameserverstatic $sshkeyfile
reboot_vm
check_vm 'none' 1 0 $ip $nic0 $ignitionhostname $nameserverstatic $sshkeyfile
destroy_vm
fi
echo -e "\n###### Testing coreos.no_persist_ip disables initramfs propagation\n"
create_ignition_file "$butane_none" $ignitionfile
start_vm $qcow $ignitionfile $kernel $initramfs "${initramfs_static_nic0} coreos.no_persist_ip"
check_vm 'dhcp' 2 0 $ip $nic0 'n/a' $nameserverdhcp $sshkeyfile
reboot_vm
check_vm 'dhcp' 2 0 $ip $nic0 'n/a' $nameserverdhcp $sshkeyfile
destroy_vm
echo -e "\n###### Testing coreos.force_persist_ip forces initramfs propagation\n"
create_ignition_file "$butane_static_nic0" $ignitionfile
start_vm $qcow $ignitionfile $kernel $initramfs "${initramfs_static_bond0} coreos.force_persist_ip"
check_vm 'none' 1 3 $ip bond0 $ignitionhostname $nameserverstatic $sshkeyfile
reboot_vm
check_vm 'none' 1 3 $ip bond0 $ignitionhostname $nameserverstatic $sshkeyfile
destroy_vm
echo -e "\n###### Testing initramfs nameserver= option\n"
create_ignition_file "$butane_none" $ignitionfile
start_vm $qcow $ignitionfile $kernel $initramfs "nameserver=${nameserverstatic} ${initramfs_dhcp_nic0nic1}"
check_vm 'dhcp' 2 2 $ip $nic0 'n/a' $nameserverstatic $sshkeyfile
reboot_vm
check_vm 'dhcp' 2 2 $ip $nic0 'n/a' $nameserverstatic $sshkeyfile
destroy_vm
echo -e "\n###### Testing net.ifnames=0 gives us legacy NIC naming\n"
create_ignition_file "$butane_none" $ignitionfile
start_vm $qcow $ignitionfile $kernel $initramfs "${initramfs_dhcp_eth0} net.ifnames=0"
check_vm 'dhcp' 2 1 $ip 'eth0' 'n/a' $nameserverdhcp $sshkeyfile
destroy_vm
initramfsloop=(
dhcp_nic0
static_nic0
static_bond0
static_team0
static_br0
)
butaneloop=(
none
static_nic0
static_bond0
static_team0
static_br0
)
for initramfsnet in "${initramfsloop[@]}"; do
for butanenet in "${butaneloop[@]}"; do
method='none'; interfaces=1;
nameserver=${nameserverstatic}
numkeyfiles=3
if [ "${butanenet}" == 'none' ]; then
devname=${initramfsnet##*_}
hostname=${initramfshostname}
if [ "${initramfsnet}" == 'dhcp_nic0' ]; then
method='dhcp'
interfaces=2
numkeyfiles=1
hostname='n/a'
nameserver=${nameserverdhcp}
fi
if [ "${initramfsnet}" == 'static_nic0' ]; then
numkeyfiles=2
fi
else
devname=${butanenet##*_}
hostname=${ignitionhostname}
if [ "${butanenet}" == 'static_nic0' ]; then
numkeyfiles=2
fi
fi
[ $devname == "nic0" ] && devname=${nic0}
butanevar="butane_${butanenet}"
butaneconfig=${!butanevar}
initramfsvar="initramfs_${initramfsnet}"
kernel_args=${!initramfsvar}
echo -e "\n###### Testing initramfs: ${initramfsnet} + ignition/butane: ${butanenet}\n"
create_ignition_file "$butaneconfig" $ignitionfile
start_vm $qcow $ignitionfile $kernel $initramfs "${kernel_args}"
check_vm $method $interfaces $numkeyfiles $ip $devname $hostname $nameserver $sshkeyfile
reboot_vm
check_vm $method $interfaces $numkeyfiles $ip $devname $hostname $nameserver $sshkeyfile
destroy_vm
done
done
for file in $kernel $initramfs $sshkeyfile $sshpubkeyfile $ignitionfile; do
rm -f $file
done
}
main "$@"