diff options
Diffstat (limited to 'builder')
31 files changed, 1335 insertions, 0 deletions
diff --git a/builder/INSTALL b/builder/INSTALL new file mode 100644 index 00000000..e52f0c39 --- /dev/null +++ b/builder/INSTALL @@ -0,0 +1,19 @@ +This subfolder is dedicated to dracut related files. + +The folder 'dnbd3-qcow2-rootfs' is a dracut module used to setup +a client's rootfs based on a (read-only) dnbd3 export made writable +through the qcow2 container format of the qemu ecosystem. + +To use this module within dracut, you simply need to copy/link/mount +the above mentioned folder to '/usr/lib/dracut/modules.d' prefixed +with a numerical priority. We used '90' so far, so the module was linked +to '/usr/lib/dracut/modules.d/90dnbd3-qcow2-rootfs'. + +Once the module has been integrated in dracut's modules, you can build +the initramfs with the following command: + + dracut --no-hostonly --modules "dnbd3-qcow2-rootfs" <target_path> + +Note that the '--no-hostonly' is critical if the machine dracut is +executed on and the clients supposed to boot the resulting initramfs +differ. diff --git a/builder/dnbd3-qcow2-rootfs/TODO b/builder/dnbd3-qcow2-rootfs/TODO new file mode 100644 index 00000000..2e92a1d0 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/TODO @@ -0,0 +1,5 @@ +* Dynamic building of dnbd3 / nbd kernel modules +* Dynamic building of dnbd3-client / systemd-preserver-process-marker + +* Research whether the dracut busybox module satisfies our requirements +* If not, compiling busybox should be done somehow diff --git a/builder/dnbd3-qcow2-rootfs/binaries/busybox b/builder/dnbd3-qcow2-rootfs/binaries/busybox Binary files differnew file mode 100755 index 00000000..7eceacf7 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/binaries/busybox diff --git a/builder/dnbd3-qcow2-rootfs/binaries/dnbd3-client b/builder/dnbd3-qcow2-rootfs/binaries/dnbd3-client Binary files differnew file mode 100755 index 00000000..76924b49 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/binaries/dnbd3-client diff --git a/builder/dnbd3-qcow2-rootfs/binaries/systemd-preserve-process-marker b/builder/dnbd3-qcow2-rootfs/binaries/systemd-preserve-process-marker Binary files differnew file mode 100755 index 00000000..45beb51d --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/binaries/systemd-preserve-process-marker diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/busybox b/builder/dnbd3-qcow2-rootfs/debugging_tools/busybox Binary files differnew file mode 100755 index 00000000..1212ae20 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/busybox diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/endless b/builder/dnbd3-qcow2-rootfs/debugging_tools/endless Binary files differnew file mode 100755 index 00000000..de7a8c0a --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/endless diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/endless.c b/builder/dnbd3-qcow2-rootfs/debugging_tools/endless.c new file mode 100644 index 00000000..22c9bc50 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/endless.c @@ -0,0 +1,10 @@ +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +int main(int argc, char *argv[]) { + // Provides a dummy worker. + while (1) + sleep(999999999); +} diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/nbd-client b/builder/dnbd3-qcow2-rootfs/debugging_tools/nbd-client Binary files differnew file mode 100755 index 00000000..7bb2207f --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/nbd-client diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/nbd-debugging.ko b/builder/dnbd3-qcow2-rootfs/debugging_tools/nbd-debugging.ko Binary files differnew file mode 100644 index 00000000..47cf6cd6 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/nbd-debugging.ko diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/network.functions b/builder/dnbd3-qcow2-rootfs/debugging_tools/network.functions new file mode 100644 index 00000000..89ca5a20 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/network.functions @@ -0,0 +1,23 @@ +wait_for_iface() { + local DEVICE=$1 + local TIMEOUT=10 + echo -n "Waiting for interface $DEVICE: " + # Some systems don't have operstate. Seems to be hardware dependent + [ ! -e "/sys/class/net/${DEVICE}/operstate" ] && usleep 10000 + if [ -e "/sys/class/net/${DEVICE}/operstate" ]; then + while true; do + # check linkstate + [ "x$(cat "/sys/class/net/${DEVICE}/operstate")" == "xup" ] && break + TIMEOUT=$(( $TIMEOUT - 1 )) # don't wait forever, the pcnet iface of vmware will never be "up" although it's working + [ "$TIMEOUT" -le 0 ] && break + # else + echo -n "." + usleep 500000 + done + else + # we really don't have a operstate .. then just wait a sec and hope for the best. + sleep 1 + fi + echo ".$(cat "/sys/class/net/${DEVICE}/operstate" 2>/dev/null)" +} +true diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/qemu-nbd b/builder/dnbd3-qcow2-rootfs/debugging_tools/qemu-nbd Binary files differnew file mode 100755 index 00000000..b34a3ff7 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/qemu-nbd diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/qemu-nbd-systemd-mark b/builder/dnbd3-qcow2-rootfs/debugging_tools/qemu-nbd-systemd-mark Binary files differnew file mode 100755 index 00000000..03c2a6b1 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/qemu-nbd-systemd-mark diff --git a/builder/dnbd3-qcow2-rootfs/debugging_tools/setup-network.sh b/builder/dnbd3-qcow2-rootfs/debugging_tools/setup-network.sh new file mode 100644 index 00000000..3b32a68c --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/debugging_tools/setup-network.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +echo "Setting up network..." + +echo "Main MAC address is '$MAC'" + +# setup network +source /inc/network.functions + +# set up loopback networking +echo "Setting up loopback" +ip link set dev lo up 2>/dev/null +ip addr add 127.0.0.1/8 dev lo 2>/dev/null + +echo "Setting up bridge" +BRIDGE="br0" + +# Following was supposed to prevent scripts from getting confused by multiple interfaces with same MAC - does not work though +## Flip mac address of original interface - this var is not local so init sees the changes too +#MAC="$(echo "$MAC" | awk -F ':' '{printf $1 ":" $2 ":" $5 ":" $3 ":" $6 ":" $4}')" +#ip link set addr "$MAC" "$SLAVE" + +mkdir -p "${FUTURE_ROOT}/etc/udev/rules.d" + +#IP_OUT=$(ip a | sed -r ':a;N;$!ba;s/: ([a-z0-9]+): /####\1####/g;s/ether ([a-f0-9:]+) /####\1####/g'| grep -E -o '####[^ ]+####' | sed 's/#//g' | grep -B 1 ':') +IP_OUT=$(ip a | grep -B 1 "/ether" | sed -r '/^--$/d;$!N;s#^[0-9]+: ([a-z0-9\.:]+): .*?/ether ([0-9a-fA-Z:]+) .*$#\1==\2#') + +if ! echo "$IP_OUT" | grep -q -- "$MAC"; then + drop_shell "Boot interface not found in interface list. NIC driver missing?" +fi + +for LINE in $IP_OUT; do + IFACE=$(echo "$LINE" | awk -F '==' '{printf $1}') + IFMAC=$(echo "$LINE" | awk -F '==' '{printf $2}' | tr '[A-Z]' '[a-z]') # udev requires mac addesses to be lowercase (a-f), see http://www.debianhelp.co.uk/udev.htm + echo "${IFACE} = ${IFMAC}" + + if [ "x$IFMAC" == "x$MAC" ]; then + brctl addbr "$BRIDGE" || drop_shell "Could not create bridge $BRIDGE" + brctl stp "$BRIDGE" 0 + brctl setfd "$BRIDGE" 0.000000000001 + ip link set addr "$IFMAC" "$BRIDGE" || drop_shell "Could not set mac of $BRIDGE" + ip link set dev "$IFACE" up + wait_for_iface "$IFACE" + brctl addif "$BRIDGE" "$IFACE" || drop_shell "Could not add $IFACE to $BRIDGE" + + # save our variables for retry on fail + echo "IFACE=$IFACE" > /run/network.conf + + # analyze ip information from the kernel command line and put parts + # of it into several variables + if [ -n "$CLIENTIP" ] ; then + # set static ip address + ip addr add "$CLIENTIP/$(ipcalc -s -p "$CLIENTIP" "$SUBNET_MASK" | sed "s/.*=//")" broadcast "$BROADCAST_ADDRESS" dev "$BRIDGE" + ip link set dev "$BRIDGE" up + [ -n "$GATEWAY" ] && ip route add default via "$GATEWAY" dev "$BRIDGE" + else + NOIPYET="yes" + fi + # Ignore this device later on when systemd handles network interfaces (see hacked 99-systemd.rules in systemd data dir) + echo "SUBSYSTEM==\"net\", ACTION==\"add\", KERNEL==\"eth*\", ATTR{address}==\"$IFMAC\", TAG+=\"openslxignore\"" >> "${FUTURE_ROOT}/etc/udev/rules.d/01-ignore-boot-interface.rules" + fi + # youdev + echo "SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"?*\", ATTR{address}==\"$IFMAC\", ATTR{dev_id}==\"0x0\", ATTR{type}==\"1\", KERNEL==\"eth*\", NAME=\"$IFACE\"" >> "${FUTURE_ROOT}/etc/udev/rules.d/70-net-boot-nic-name.rules" + # continue... + IFACE="" +done + +wait_for_iface "$BRIDGE" + +# udhcpc +PARAM= +if [ -n "$CLIENTIP" ]; then + PARAM="-r $CLIENTIP" +fi +echo -n "$CLIENTIP" > "/run/firstip" +echo -n "$GATEWAY" > "/run/firstgw" + +# save our variables for retry on fail ff. +echo "CLIENTIP=$CLIENTIP" >> /run/network.conf +echo "GATEWAY=$GATEWAY" >> /run/network.conf +echo "BRIDGE=$BRIDGE" >> /run/network.conf + +udhcpc $PARAM -O domain -O nissrv -O nisdomain -O wpad -O search -t 5 -T 2 -s "/inc/udhcpc-trigger" -f -n -q -i "$BRIDGE" +# udhcpc return value will be return value of this script diff --git a/builder/dnbd3-qcow2-rootfs/hooks/cmdline/enable-sysrq.sh b/builder/dnbd3-qcow2-rootfs/hooks/cmdline/enable-sysrq.sh new file mode 100755 index 00000000..f779aa7a --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/cmdline/enable-sysrq.sh @@ -0,0 +1,2 @@ +# enables magic sysrq keys +echo 1 > /proc/sys/kernel/sysrq diff --git a/builder/dnbd3-qcow2-rootfs/hooks/cmdline/expand-kcl-ip.sh b/builder/dnbd3-qcow2-rootfs/hooks/cmdline/expand-kcl-ip.sh new file mode 100755 index 00000000..9f617b09 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/cmdline/expand-kcl-ip.sh @@ -0,0 +1,36 @@ +command -v warn >/dev/null || . /lib/dracut-lib.sh + +# fakes the cmdline to fix the ip parsing in darcut's net-lib.sh +[ -d /fake ] || mkdir /fake + +# need to be a tmpfs for the hack to work +mount -t tmpfs tmpfs /fake + +for parameter in $(getargs ip=); do + local temp="$parameter:" + set -- + while [ -n "$temp" ]; do + set -- "$@" "${temp%%:*}" + temp=${temp#*:} + done + + [ -n "$1" ] && ip=$1 + [ -n "$2" ] && server_ip=$2 + [ -n "$3" ] && gateway_ip=$3 + [ -n "$4" ] && net_mask=$4 + + warn "PXE given net configuration: ip: $ip server_ip: $server_ip gateway_ip: $gateway_ip net_mask: $net_mask" + local final_dracut_ip_config="$ip::$gateway_ip:$net_mask:hiwi-test-28:eno1:none" + warn "Final dracut ip config: $final_dracut_ip_config" + sed --regexp-extended "s/ip=[^ ]*/ip=$final_dracut_ip_config/g" /proc/cmdline > /fake/cmdline +done + + +# bind mount it. Can we trust mount return codes here? +# # if so, we should check what we get in $? +mount -o bind /fake/cmdline /proc/cmdline + +# check if it worked +if ! grep 'hiwi-test-28:eno1:none' /proc/cmdline; then + warn 'Haxing cmdline did not work :( sad pandaz...' +fi diff --git a/builder/dnbd3-qcow2-rootfs/hooks/cmdline/mark-root-device.sh b/builder/dnbd3-qcow2-rootfs/hooks/cmdline/mark-root-device.sh new file mode 100755 index 00000000..b7282521 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/cmdline/mark-root-device.sh @@ -0,0 +1,9 @@ +# set rootok and root as dracut expects them to be set by +# the module preparing the root filesystem. +# +# Once the root filesystem is mounted per dnbd3 and +# exported as qcow2 per nbd, /dev/root will be a symlink +# to /dev/nbd0 as this is then our rootfs-device +rootok=1 +root=block:/dev/root + diff --git a/builder/dnbd3-qcow2-rootfs/hooks/mount/mount-root-device.sh b/builder/dnbd3-qcow2-rootfs/hooks/mount/mount-root-device.sh new file mode 100755 index 00000000..d02f9002 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/mount/mount-root-device.sh @@ -0,0 +1,4 @@ +# this rudimentary script just mounts the rootfs device that was symlinked to +# /dev/root to dracut's $NEWROOT (usually /sysroot). + +mount /dev/root $NEWROOT diff --git a/builder/dnbd3-qcow2-rootfs/hooks/pre-mount/fetch-config.sh b/builder/dnbd3-qcow2-rootfs/hooks/pre-mount/fetch-config.sh new file mode 100755 index 00000000..a90481c8 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/pre-mount/fetch-config.sh @@ -0,0 +1,39 @@ +# load dracut functions +command -v getarg >/dev/null || . /lib/dracut-lib.sh + +# read SLX_SERVER and SLX_BASE from the kernel command line +SLX_SERVER=$(getarg slxsrv) +SLX_BASE=$(getarg slxbase) +SLX_CONFIG_DIR="/opt/openslx" +SLX_CONFIG_FILE="/opt/openslx/config" + +if [ -z "$SLX_SERVER" ]; then + warn "No 'slxsrv' parameter found in the kernel command line!" + warn "Skipping OpenSLX configuration..." + return 1 +fi +if [ -z "$SLX_BASE" ]; then + warn "No 'slxbase' parameter found in the kernel command line!" + warn "Skipping OpenSLX configuration..." + return 1 +fi + +info "Getting configuration from OPENSLX-Server..." +WGET="$(busybox which wget)" +if [ -z $WGET ]; then + # do nothing + warn "'wget' not found. Skipping openslx configuration..." + return 1 +fi + +# ok then we are ready to download the config +mkdir -p "${SLX_CONFIG_DIR}" +$WGET -T 5 -q "http://${SLX_SERVER}/${SLX_BASE}/config" -O "${SLX_CONFIG_FILE}" +RET="$?" +if [ $RET -ne 0 ]; then + warn "Downloading OpenSLX configuration from ${SLX_SERVER}/${SLX_BASE} failed: $RET" + emergency_shell -n "$0" + return 1 +else + return 0 +fi diff --git a/builder/dnbd3-qcow2-rootfs/hooks/pre-mount/mount-qcow.sh b/builder/dnbd3-qcow2-rootfs/hooks/pre-mount/mount-qcow.sh new file mode 100755 index 00000000..d70492bf --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/pre-mount/mount-qcow.sh @@ -0,0 +1,49 @@ +############################################################################### +# CHECKS +# + +SETUP_ROOTFS_SCRIPT="/sbin/setup-qcow2" + +if [ ! -e "${SETUP_ROOTFS_SCRIPT}" ]; then + warn "No such file of directory: ${SETUP_ROOTFS_SCRIPT}" + emergency_shell -n "Error in $0" + return 1 +fi + +if [ ! -x "${SETUP_ROOTFS_SCRIPT}" ]; then + warn "Cannot execute: ${SETUP_ROOTFS_SCRIPT}" + emergency_shell -n "Error in $0" + return 1 +fi + +# +# END CHECKS +############################################################################### + +############################################################################### +# MAIN CODE +# + +# ok, let's source the setup script +if ! . ${SETUP_ROOTFS_SCRIPT} ; then + warn "Could not source: ${SETUP_ROOTFS_SCRIPT}" + emergency_shell -n "Error in $0" + return 1 +fi + +# just go over the functions in the right order ;-) +for fun in connect_dnbd3 create_qcow export_qcow connect_qcow; do + if ! $fun; then + # something failed, drop a shell for debugging + warn "'$fun' failed with: $?" + emergency_shell -n "Error in $fun" + return 1 + fi +done + +# all good, we are done +return 0 + +# +# END MAIN CODE +############################################################################### diff --git a/builder/dnbd3-qcow2-rootfs/hooks/pre-pivot/mount-tmp.sh b/builder/dnbd3-qcow2-rootfs/hooks/pre-pivot/mount-tmp.sh new file mode 100755 index 00000000..3e702064 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/pre-pivot/mount-tmp.sh @@ -0,0 +1,40 @@ +# This script only checks if we found a usable partition for the +# future /tmp. The discovery of that partition is done by udev during +# the initqueue. If a valid partition is found (either GPT with the label +# OPENSLX_TMP or MBR with the type 0x44) its path will be written to +# /tmp/openslx.tmpdisk +OPENSLX_TMP_DISK_FLAG="/tmp/openslx.disk.tmp" + +if [ ! -e "$OPENSLX_TMP_DISK_FLAG" ]; then + warn "'$OPENSLX_TMP_DISK_FLAG' not found!" + warn "Systemd will manage $NEWROOT/tmp on its own." + # no partition for the future /tmp found, just + # let systemd manage it then (probably a tmpfs) + return 1 +fi + +# in /tmp/openslx.disk.tmp is the name of the device +# to mount as /tmp in the real system +# meaning we need to mount it to /sysroot/tmp here. + +OPENSLX_TMP_DISK_DEV="$(cat $OPENSLX_TMP_DISK_FLAG)" + +# sanity check: is the content a block device? +if [ ! -b "$OPENSLX_TMP_DISK_DEV" ]; then + warn "'$OPENSLX_TMP_DISK_DEV' appears not to be a block device!" + warn "Systemd will manage $NEWROOT/tmp on its own." + return 1 +fi + +# all good, keep on +if ! mount -t auto "$OPENSLX_TMP_DISK_DEV" $NEWROOT/tmp; then + # something else went wrong :( + warn "Mounting '$OPENSLX_TMP_DISK_DEV' to '$NEWROOT/tmp' failed with: $!" + warn "Systemd will manage $NEWROOT/tmp on its own." + return 1 +fi + +# still here? mount worked wohoo +# set permissions +chmod a+rwxt $NEWROOT/tmp +return 0 diff --git a/builder/dnbd3-qcow2-rootfs/hooks/pre-udev/lo-setup.sh b/builder/dnbd3-qcow2-rootfs/hooks/pre-udev/lo-setup.sh new file mode 100755 index 00000000..9103afd9 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/pre-udev/lo-setup.sh @@ -0,0 +1 @@ +/sbin/initqueue --name ifup-lo --unique --onetime /sbin/ifup lo -m diff --git a/builder/dnbd3-qcow2-rootfs/hooks/pre-udev/load-dnbd3-nbd-modules.sh b/builder/dnbd3-qcow2-rootfs/hooks/pre-udev/load-dnbd3-nbd-modules.sh new file mode 100755 index 00000000..29f9f210 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/hooks/pre-udev/load-dnbd3-nbd-modules.sh @@ -0,0 +1,32 @@ +# include dracut-lib.sh to use 'warn' +command -v warn >/dev/null || . /lib/dracut-lib.sh + +NBD_MOD_PATH="/usr/lib/modules/current/extra/nbd.ko" +DNBD3_MOD_PATH="/usr/lib/modules/current/extra/dnbd3.ko" + +# do we actually have our modules? +if [ ! -e "${NBD_MOD_PATH}" ]; then + warn "No such file of directory: ${NBD_MOD_PATH}" + emergency_shell -n "Error in $0" + return 1 +fi +if [ ! -e "${DNBD3_MOD_PATH}" ]; then + warn "No such file of directory: ${DNBD3_MOD_PATH}" + emergency_shell -n "Error in $0" + return 1 +fi + +# load the kernel modules for dnbd3 and nbd +if ! insmod "${DNBD3_MOD_PATH}"; then + warn "Failed to load DNBD3 kernel module..." + emergency_shell -n "Error in $0" + return 1 +fi + +if ! insmod "${NBD_MOD_PATH}"; then + warn "Failed to load NBD kernel module..." + emergency_shell -n "Error in $0" + return 1 +fi + +return 0 diff --git a/builder/dnbd3-qcow2-rootfs/kernel_modules/dnbd3.ko b/builder/dnbd3-qcow2-rootfs/kernel_modules/dnbd3.ko Binary files differnew file mode 100755 index 00000000..d7b7ce3b --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/kernel_modules/dnbd3.ko diff --git a/builder/dnbd3-qcow2-rootfs/kernel_modules/nbd.ko b/builder/dnbd3-qcow2-rootfs/kernel_modules/nbd.ko Binary files differnew file mode 100644 index 00000000..e8c3f40f --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/kernel_modules/nbd.ko diff --git a/builder/dnbd3-qcow2-rootfs/module-setup.sh b/builder/dnbd3-qcow2-rootfs/module-setup.sh new file mode 100644 index 00000000..b3f14c1d --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/module-setup.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +check() { + if ! type -f qemu-nbd >/dev/null; then + echo "'qemu-nbd' not found on this system, install it to use this module." + return 1 + fi + if ! type -f qemu-img >/dev/null; then + echo "'qemu-img' not found on this system, install it to use this module." + return 1 + fi + if ! type -f nbd-client >/dev/null; then + echo "'nbd-client' not found on this system, install it to use this module." + return 1 + else + # check version + local nbd_client_version="$(nbd-client --help 2>&1 | grep -oE '3\.[0-9]+')" + local nbd_client_version_major="${nbd_client_version%.*}" + local nbd_client_version_minor="${nbd_client_version#*.}" + if [ "${nbd_client_version_major}" -ne 3 ]; then + # nbd-client probably too old + echo "The nbd-client major version is not 3, meaning its probably too old." + return 1 + fi + if [ "${nbd_client_version_minor}" -gt 8 ]; then + # TODO check if ver = 3.9 has the same new syntax... + echo "The nbd-client minor version is greater than 8. A new syntax has been + introduced starting with 3.10 which we do not support." + return 1 + fi + return 0 + fi +} + +depends() { + echo base network bash kernel-modules + return 0 +} + +installkernel() { + instmods ext4 +} + +install() { + ### BINARIES + # + # busybox: cause we want lightweight tools + inst "$moddir/binaries/busybox" /usr/bin/busybox + # dnbd3-client: needed to mount remote dnbd3 filesystem. + inst "$moddir/binaries/dnbd3-client" /usr/bin/dnbd3-client + # A generic wrapper program to prepend a "@" to each process + # spawned by given nested programs. + inst "$moddir/binaries/systemd-preserve-process-marker" \ + /usr/bin/systemd-preserve-process-marker + + # NOTE: These modules are build again Kernel: 3.10.0-229.1.2.el7.x86_64 + # TODO: build these in check() ! + inst "$moddir/kernel_modules/dnbd3.ko" \ + /usr/lib/modules/current/extra/dnbd3.ko + inst "$moddir/kernel_modules/nbd.ko" \ + /usr/lib/modules/current/extra/nbd.ko + + ### HOOKS + ## HOOK cmdline + # enables sysrq-shortcuts + inst_hook cmdline 00 "$moddir/hooks/cmdline/enable-sysrq.sh" + + # expands the ip parameter in the kernel command line to + # make it dracut-compatible + # TODO: dracut still parses this incorrectly... + inst_hook cmdline 10 "$moddir/hooks/cmdline/expand-kcl-ip.sh" + + # sets environment variables to tell dracut which device + # holds the future root filesystem + inst_hook cmdline 90 "$moddir/hooks/cmdline/mark-root-device.sh" + + ## HOOK pre-udev + # loads the dnbd3/nbd kernel modules + inst_hook pre-udev 00 "$moddir/hooks/pre-udev/load-dnbd3-nbd-modules.sh" + + # setup loopback device + inst_hook pre-udev 00 "$moddir/hooks/pre-udev/lo-setup.sh" + + ## HOOK pre-mount + # this is the configuration hook where the config stuff is wget'ed + inst_hook pre-mount 00 "$moddir/hooks/pre-mount/fetch-config.sh" + + # this is the main hook where all the magic is triggered + inst_hook pre-mount 10 "$moddir/hooks/pre-mount/mount-qcow.sh" + + ## HOOK mount + # this simply mounts the prepared /dev/root to $NEWROOT + # aka "the dracut way" + inst_hook mount 10 "$moddir/hooks/mount/mount-root-device.sh" + + ## HOOK pre-pivot + # this checks whether we found a partition suitable for + # the future /tmp and if so, mounts it + inst_hook pre-pivot 00 "$moddir/hooks/pre-pivot/mount-tmp.sh" + + ### SCRIPTS + # the main magic script containing all the functions needed + # to prepare the qcow2-based root filesystem + inst "$moddir/scripts/setup-qcow2" /sbin/setup-qcow2 + + # the script triggered by udev upon finding the right partitions + inst "$moddir/scripts/prepare-disks" /sbin/prepare-disks + + # udev rules detecting 44, 45, 46 partitions and running + # 'prepare-disks' to do then format/mount/use them + inst "$moddir/udev/70-openslx-disk.rules" /etc/udev/rules.d/70-openslx-disk.rules + + # Debugging Uncomment this version if you need some useful debugging tools + # in your iniramfs. + inst_multiple lsblk ping ip ifconfig sshd htop tail head cat vim \ + touch sed lsmod insmod qemu-img sleep route wget find lsof strace \ + chroot switch_root pivot_root qemu-nbd mount nbd-client fdisk mkfs.xfs + # Production: + # inst_multiple insmod qemu-img qemu-nbd + return 0 +} diff --git a/builder/dnbd3-qcow2-rootfs/readme.md b/builder/dnbd3-qcow2-rootfs/readme.md new file mode 100644 index 00000000..9ad0f968 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/readme.md @@ -0,0 +1,472 @@ +# TODO --ommit npd +# TODO Testmenue PW: t + +h1. Evaluation, dissection and modification of Linux remote boot + +h2. Inhalt + +{{toc}} + +#h2. Begriffe + +#* qcow - qemu copy-on-write +#* stage3.1 initramfs +#* stage4 finales System + +h2. Aufgabe + +Es wird ein Skript bzw. Dracut-Modul benötigt, dass ein initramfs basierend auf +systemd baut. Das resultierende initramfs muss Netzwerk-Support bereitstellen, +ein dnbd3 Blockdevice mounten können und einen "switch_root" auf das zuvor +gemountete Dateisystem umsetzen. Das Framework sollte möglichst +Distributionsunabhängig konstruiert sein. Es soll bereits vor dem +"switch_root", also bevor das eigentliche Zielsystem im Root-Verzeichnis +eingebunden werden systemd als init-System zum Einsatz kommen. Die Kernaufgabe +eine initramfs ist es alle nötigen Anwendungen bereitzustellen, die benötigt +werden, um dass finale Zielsystem einzubinden. In dieser konkreten +Aufgabenstellung muss, dass initramfs ein nicht schreibbares Blockgerät +eingebunden werden und eine schreibbare Zwischenshicht (Overlayfilesystem) +zusätzlich eingebunden werden. + +h2. Möglicher Technologien für das Overlaykonzept + +* Dateibasierte Overlay-FS (Union-FS, Alternat-Union-FS, Overlay-FS) + - Funktioniert derzeit nicht auf jedem Zielsystem (Kernel) + - Nicht für den Linux-Kernel zertifiziert oder lässt sich nicht über das + Root-System legen. + - Bei wenigen Änderungen in einer großen Datei muss komplette Datei in + der schreibbaren Schicht gespeichert werden. +* Blockorientierte Overlay-FS (Network-Block-Device, DNBD3, Qemu-Copy-On-Write-Image) + - NBD ist für den Linux-Kernel zertifiziert + - Weniger Netzwerkverkehr nötig, da nur geänderte Blöcke übertragen werden + müssen, statt ganze Dateien zu kopieren. + - DNBD3 hat Failover-Strategien, verzichtet auf komplexe Strategien zum + Schreiben in geänderte Blöcke über das Netzwerk + - Das verfügbare qcow2-Format bietet eine Technologie, um blockorientiert + Änderungen in einer zusätzlichen Dateisystemschicht zu speichern. + +h2. Zielablauf + +Der generelle Ablauf vor bzw. während des Ladens des initramfs und deren +Minilinux-System: + +# Boot PXE +> # Laden des initramfs images +> # Laden des Kernels +# Ausführen des iniramfs +> # Ausführen von Systemd +> > # Bereitstellen aller benötigten Dienste und Hardware (Netzwerk hochbringen) +> > # Mounten des finalen Dateisystems als Wurzel +> > # Wechsel (switch_root) in die finale Distribution +> > # Starten / Weiterausführen von Systemd als Init-System + +h2. Benötigte Pakete zum bauen des initramfs + +> * dracut +# TODO + +h2. Benötigte Pakete innerhalb des resultierenden initramfs + +> * systemd +> * quemu-img +> * quemu-nbd +> * nbd-client +> * dnbd3-client + +h2. Aufsetzen einer Test-Arbeitsumgebung für CentOS + +h3. CentOS7/ArchLinux/RedHat/Ubuntu + VirtualBox + VirtualBoxGuestAdditions + +Aufsetzen des *Dynamic Kernel Module Support*, um einfach neue VBox-Kernel-Module zu aktualisieren: + +> # Aktualisiere Paketdatenbank: @yum update@ +> # Intalliere C-Compiler: @yum install gcc@ +> # Lade erweitertes rpmforfe Repository: @wget http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el7.rf.x86_64.rpm@ +> # Installiere Repository: @rpm -Uvh rpmforge-release-0.5.3-1.el7.rf.x86_64.rpm@ +> # Lade das DKMS-Paket: @wget ftp://rpmfind.net/linux/epel/5/x86_64/dkms-2.2.0.3-29.el5.noarch.rpm@ +> # Installiere DKMS-Paket: @yum localinstall dkms-2.2.0.3-25.el7.noarch.rpm --nogpgcheck@ +> # Aktiviere rpmforge Repository: @yum --enablerepo rpmforge install dkms@ +> # Installiere Entwicklertools zum bauen von Paketen: @yum groupinstall "Development Tools"@ +> # Installiere Metainformation zum Kernel: @yum install kernel-devel@ + +Installieren der VirtualBox-GuestAddtion: + +> # Lege die VirtualBox-GuestAddition-CD ein. +> # Mounte CD: @mount /dev/sr0 /mnt/ && cd /mnt/ && ./VBoxLinuxAdditions.run && reboot@ + +Erstellen eines Testboot Eintrags für Grub2: + +> - Füge in ??/etc/grub.d/40_custom?? den folgenden Inhalt hinzu: + +<pre> +menuentry 'test' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-123.el7.x86_64-advanced-6c06919a-389a-4a50-8c6b-b086e65db9b0' { + load_video + set gfxpayload=keep + insmod gzio + insmod part_msdos + insmod xfs + set root='hd0,msdos1' + if [ x$feature_platform_search_hint = xy ]; then + search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1 --hint='hd0,msdos1' 4236333a-a808-4f6b-b4a6-d963f4a69a25 + else + search --no-floppy --fs-uuid --set=root 4236333a-a808-4f6b-b4a6-d963f4a69a25 + fi + linux16 /vmlinuz-3.10.0-123.el7.x86_64 root=UUID=6c06919a-389a-4a50-8c6b-b086e65db9b0 ro rd.lvm.lv=centos/swap crashkernel=auto rd.lvm.lv=centos/root vconsole.font=latarcyrheb-sun16 vconsole.keymap=de rhgb quiet + initrd16 /initramfs-test.img +} +</pre> + +> - Füge neuen Menüeintrag in die automatisch generierte Grub2 Konfigurations2-Datei hinzu: @grub2-mkconfig -o /boot/grub2/grub.cfg@ + +h2. Bauen eines Test-Initramfs + +Mache ein Backup vom aktuellen initamfs: +@cp -p /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.BACKUP@ + +Der folgende Befehl ersetzt das aktuelle Standart Initramfs mit dem neu +gebauten und berücksichtigt dabei alle bisher editierten Dracut-Module in +??/usr/lib/dracut/modules.d??. Die durchschnittliche Größe des resultierenden +initramfs beträgt komprimiert: zwischen 11 und 16 MB. + +<pre><code class="bash"> +dracut --verbose --force /boot/initramfs-3.10.0-123.el7.x86_64.test.img \ + 3.10.0-123.el7.x86_64 +</code></pre> + +h3. Konfiguration + +TODO + +# Configure "cmdline" from "Chapter 8. DRACUT.CMDLINE(7)" in virtualbox in "/boot/startup.sh" for uefi or in "/boot/grub2/grub.cfg" for grub2 + +h2. Bauen eines minimalen individuellen Test-Initramfs + +<pre><code class="bash"> +dracut --verbose --hostonly --force + /boot/initramfs-3.10.0-123.el7.x86_64.test.img \ + 3.10.0-123.el7.x86_64 +</code></pre> + +Die Option "hostonly" veranlasst Dracut dazu nur alle nötigen Abhängigkeiten +für das aktuelle System in das resultierende Initramfs zu installieren. +Andernfalls fügt dracut viele zusätzliche Treiber hinzu, die die Größe der +resultierenden Datei erheblich vergrößern und damit größer wird als nötig. +Die durchschnittliche Größe des resultierenden initramfs beträgt +komprimiert: zwischen 11 und 16 MB. + +h2. Erstellen eines eigenen Dracut-Moduls + +Alle vorhanden Module befinden sich in ??/usr/lib/dracut/modules.d??. + +> # Erstelle ein neues Modul: @mkdir /usr/lib/dracut/modules.d/91test@ +> # Baue neues initramfs und achte darauf, dass ??\*\*Including module: test\*\*?? mit der Command-Line-Option ??--verbose?? ausgegeben wird. +> # Alle Modul-Installations-Informationen sind in der Datei ??module-setup.sh??: @touch /usr/lib/dracut/modules.d/91test/module-setup.sh@ +> # Konfiguriere Modul: +> > # Als erstes erstellt man eine ??check??-Funktion, die lediglich ??0?? + zurückgibt. Diese Funktion wird aufgerufen, wenn entschieden wird, welche + Dracutmodule geladen werden sollen. Durch zurückgeben der ??0?? wird das + Modul beim nächsten bauen eines Initramfs automatisch hinzugefügt ohne + das man es etwas in der ??/etc/dracut.conf?? oder per Command-Line-Option + "--add" angeben muss. Wenn die @$hostonly@ Variable gesetzt ist, dann + wird das Modul auch im "hostonly" Modus geladen. In diesem Fall sollte + die Funktion nur dann ??0?? zurückgeben, wenn das Modul auch wircklich + für den aktuellen Host benötigt wird. Wenn 255 zurückgegeben wird, wird + das Modul nur dann geladen, wenn es von einem andren Modul als + Abhängigkeit deklariert wurde. + +<pre><code class="bash"> +check() { + return 0 +} +</code></pre> + +Als nächstes wird eine ??install??-Funktion erstellt. Die ??install??-Funktion +wird aufgerufen, wenn alle nicht Kernel spezifischen Ressourcen installiert +werden sollen. Es können Binärdateien, Skripte und andere statischen Dateien +installiert werden. Um einen Datei im aktuellen Modul-Ordner zu addressieren +sollte die Variable "$moddir" als Prefix eingesetzt werden. +Eine solche Funktion kann beispielsweise einen ??Command-Line-Hook?? triggern, +der modulespezifische ??Kernel-Command-Line-Optionen?? verarbeitet während das +initiale Minilinux bootet. Im folgenden Beispiel werden ??Command-Line-Optionen +mit Priorität 20 vom Shell-Skript ??parse-insmodpost.sh?? gelesen und +ausgewertet. Dadurch muss natürlich auch das entsprechende Skript in das +initramfs kopiert werden. Dies wird durch den Aufruf der Funktion +??inst_simple?? erreicht. + +<pre><code class="bash"> +install() { + inst_hook cmdline 20 "$moddir/parse-cmdline.sh" + inst_simple "$moddir/parse-cmdline.sh" /sbin/insmodpost.sh +} +</code></pre> + +Die ??parse-cmdline.sh?? parst die Kernel-Command-Line für die Argumente +??rd.driver.post??, verhindert, dass die Module automatisch geladen werden und +installiert den Hook ??hook.sh?? in der ??initqueue/settled??. Der Inhalt von +??parse-cmdline.sh?? könnte wie folgt aussehen: + +<pre><code class="bash"> +for p in $(getargs rd.driver.post=); do + echo "blacklist $p" >> /etc/modprobe.d/initramfsblacklist.conf +done +</code></pre> + +In einer ??depends??-Funktion können andere Dracut-Module als Abhängigkeit +deklariert werden. Diese müssen einfach per "echo" als String Leerzeichen +getrennt ausgegeben werden. + +<pre><code class="bash"> +depends() { + echo 'debug virtfs' +} +</code></pre> + +Mit dieser Funktion können zusätzliche benötigte Kernel-Command-Line-Argumente +ausgegeben werden, die benötigt werden um die aktuelle Maschine zu booten. +Die Ausgabe sollte mit einem Leerzeichen beginnen und keine neuen Zeilen +ausgeben. + +<pre><code class="bash"> +cmdline() { + echo 'TODO' +} +</code></pre> + +Mit der Funktion ??installkernel?? sollen alle kernelspezifischen Dateien +installiert werden. Siehe hierzu auch den Abschnitt +??Hilfsfunktionen zur Installation?? + +<pre><code class="bash"> +installkernel() { + TODO +} +</code></pre> + +h2. Hilfsfunktionen zur Installation + +??inst_multiple?? installiert mehrere Binärdateien. Sollten ausführbare Dateien +ohne entsprechendem Pfad ausgewählt werden, wird dracut folgende die Pfade +??/usr/sbin??, ??/sbin??, ??/usr/bin??, ??/bin?? durchsuchen, um den Pfad der +zugehörigen ausführbaren Datei zu ermitteln. Bei dem Kommando-Zeilen-Argument +??-o?? als erster Parameter werden Fehler bei nicht auffindbaren Dateien +unterdrückt. + +??inst_multiple [-o] <file> [ <file> …]?? + +??inst?? installiert eine referenzierte Datei an den korrespondierenden Ort im +??initramfs??. Die Datei wird innerhalb des ??initramfs?? am gleichen Ort zu +finden sein wie auf der Referenzmaschine. Optional kann als zweites Argument +ein anderer Ort für das temporäre Dateisystem angegeben werden. + +??inst <src> [<dst>]?? + +??inst_hook?? installiert eine ausführbare Datei im Pfad ??<src>?? im +Dracut-hook-Ordner ??<hookdir>??. Der Einstiegspunkt wird mit Priorität +??<prio>?? zur Laufzeit des initialen Mini-Linux-Systems ausgeführt. + +??inst_hook <hookdir> <prio> <src>?? + +??inst_rules?? installiert einen oder mehrere udev-Regeln. Nicht-existente +udev-Regeln werden beim bauen des initramfs gemeldet, führen aber nicht zum +Abbruch. + +??inst_rules <udevrule> [ <udevrule> …]?? + +??instmods?? installiert einen oder mehrere Kernel-Module in das initramfs. +??<kernelmodule>?? kann auch ein komplettes Subsystem darstellen, wenn es mit +dem Prefiy "=" beginnt (z.B. "=drivers/net/team"). ??instmods?? sollte nur +innerhalb der ??installkernel()??-Funktion verwendet werden. Ist ??$hostonly?? +gesetzt und das aktuelle Modul nicht im Referenzsystem geladen und wird +demnach nicht in ??/sys/…/uevent MODALIAS?? verwendet, wird dieses nicht in +das initramfs integriert. Soll das Modul in jedem Fall geladen werden, kann +folgende Syntax verwendet werden: + +<pre><code class="bash"> +installkernel() { + hostonly='' instmods <kernelmodule> +} +</code></pre> + +??instmods <kernelmodule> [ <kernelmodule> … ]?? + +h3. Konfiguration + +TODO + +h3. Debugging + +TODO + +h2. Build kernel specific dnbd3 kernel module + +> # Installiere Metainformation zum Kernel: @yum install kernel-devel@ +> # Installiere cmake zum Bauen von dnbd3: @yum install cmake@ +> # Installiere zlib-devel zum Bauen von dnbd3: @yum install zlib-devel@ + +Die kernel header Dateien liegen in: + +/usr/lib/modules/3.10.0-229.1.2.el7.x86_64 + +TOOD + +dnbd3-client -h 132.230.4.1 -i stage4/torben/test -r 1 + +> # Installiere qemu-img: @yum install qemu-img@ +> # Installiere nbd: @wget http://dl.fedoraproject.org/pub/epel/6/x86_64/nbd-2.9.20-7.el6.x86_64.rpm && rpm -Uvh nbd-2.9.20-7.el6.x86_64.rpm@ + +NOTE: Disable NetworkManager to avoid reloading network on boot: systemctl disable NetworkManager + +h2. CentOS7 @rpmbuild@ + +First "Set up RPM build env":http://wiki.centos.org/HowTos/SetupRpmBuildEnvironment +Now in that user's home, e.g. @/home/builder@: +<pre> +# install yumdownloader +yum install yum-utils + +# download source in /home/builder/ +yumdownloader --source systemd + +# should have now have a file ~/systemd-208-20.el7_1.2.src.rpm +# "install" it in ~/rpmbuild +rpm -ivh systemd-208-20.el7_1.2.src.rpm + +# install building deps +yum-builddep ~/rpmbuild/SPECS/systemd.spec + +# now check if everything is working, by running the simple %prep% phase +# (unpacks source & applies patches) +rpmbuild -bp ~/rpmbuild/SPECS/systemd.spec + +# if it worked, we can probably compile +# this runs %prep% and %build% +rpmbuild -bp ~/rpmbuild/SPECS/systemd.spec +</pre> + +dmesg + 998 ping 8.8.8.8 + 999 shutdown .h now + 1000 shutdown -h now + 1001 y search yumdownloader + 1002 y install yum-utils + 1003 yumdownloader --help + 1004 yumdownloader --source glib2 qemu + 1005 yumdownloader --source qemu-img + 1006 ls + 1007 mkdir glib2_source + 1008 cd glib2 + 1009 cd glib2_source/ + 1010 rpm2cpio ../glib2-2.40.0-4.el7.src.rpm | cpio -idmv + 1011 ls + 1012 tar xf glib-2.40.0.tar.xz + 1013 ls + 1014 cd glib-2.40.0 + 1015 ls + 1016 ./configure --enable-static + 1017 make + 1018 ls + 1019 make + 1020 y search zlib + 1021 y search zlib-static + 1022 y install zlib-static + 1023 ./configure --enable-static + 1024 y search libffi + 1025 y install libffi + 1026 y install libffi-devel + 1027 ./configure --enable-static + 1028 make + 1029 ls + 1030 cd build/ + 1031 ls + 1032 .. + 1033 l + 1034 ls + 1035 make install + 1036 .. + 1037 l + 1038 .. + 1039 l + 1040 mkdir qemu-kvm + 1041 cd qemu-kvm + 1042 ls + 1043 rpm2cpio ../qemu-kvm-1.5.3-86.el7_1.1.src.rpm | cpio -idmv + 1044 l + 1045 pwd + 1046 ls + 1047 l + 1048 ls + 1049 .. + 1050 l + 1051 git clone git://git.qemu.org/qemu.git qemu + 1052 ls + 1053 rm qemu-kvm -rf + 1054 rm qemu-kvm-1.5.3-86.el7_1.1.src.rpm + 1055 l + 1056 cd qemu/ + 1057 l + 1058 ./configure --static --target-list=x86_64-linux-user + 1059 y install zlib + 1060 y install zlib-devel + 1061 y install zlib2 + 1062 y search zlib + 1063 y install zlib-static + 1064 y install zlib + 1065 ll /lib64/libz.a + 1066 ./configure --static --target-list=x86_64-linux-user + 1067 make clean + 1068 ./configure --static + 1069 y search zlib + 1070 y search zlib-static..x86_64 + 1071 y search zlib-static.x86_64 + 1072 y install zlib-static.x86_64 + 1073 ldconfig + 1074 ./configure --static + 1075 ./configure --help + 1076 ./configure --help | grep zlib + 1077 y search zlib + 1078 y install zlib-devel zlib-static zlib + 1079 y deinstall zlib + 1080 y remove zlib + 1081 y search libz + 1082 ls + 1083 git submodule update --init dtc + 1084 ./configure --static --target-list=x86_64-linux-user + 1085 find / -name zlib + 1086 find / -name libz + 1087 find / -name *libz* + 1088 find / -name *zlib* + 1089 ls + 1090 ./configure --disable-zlib-test --static --target-list=x86_64-linux-user + 1091 y search base-devel + 1092 y search devel + 1093 yum groupinstall "Development Tools" + 1094 yum groups mark install + 1095 yum group mark install + 1096 yum groupinstall "Development Tools" + 1097 yum groups mark install + 1098 yum grouplist + 1099 y search zlibrary + 1100 y search glibc + 1101 y install glibc-static + 1102 ls + 1103 ./configure --static --target-list=x86_64-linux-user + 1104 y search glib + 1105 y search glib-2 + 1106 y search glib2-devel + 1107 y install glib2-devel + 1108 ./configure --static --target-list=x86_64-linux-user + 1109 git submodule update --init pixman + 1110 ./configure --static --target-list=x86_64-linux-user + 1111 ls + 1112 ll + 1113 make -j3 + 1114 l + 1115 ldd qemu-nbd + 1116 history + +h2. Quellen + +* "Main Page Dracut on kerne.org":https://dracut.wiki.kernel.org/index.php/Main_Page +* "Documentation on kernel.org":https://www.kernel.org/pub/linux/utils/boot/dracut/dracut.html +* "Enable addional repository":http://www.tecmint.com/enable-rpmforge-repository +* "Getting Dynamic Kernel Module Support":http://rpmfind.net/linux/rpm2html/search.php?query=dkms diff --git a/builder/dnbd3-qcow2-rootfs/scripts/prepare-disks b/builder/dnbd3-qcow2-rootfs/scripts/prepare-disks new file mode 100755 index 00000000..2e68dd9e --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/scripts/prepare-disks @@ -0,0 +1,211 @@ +#!/bin/bash +############################################################################### +# GLOBALS +# + +# flag file containing pids of running instances for concurrency checks +declare -rg OPENSLX_DISK_FLAG="/tmp/openslx.disk" +# file that will contain the name of the device used for the /tmp partition +# - label 'OPENSLX_TMP' in GPT / type '0x44' in MBR +declare -rg OPENSLX_TMP_MARKER="/tmp/openslx.disk.tmp" +# file that will contain the name of the device used for storing qcow2 +# - label 'OPENSLX_SYS' in GPT / type '0x46' in MBR +declare -rg OPENSLX_SYS_MARKER="/tmp/openslx.disk.sys" +# mount point for system partition +declare -rg OPENSLX_SYS_MOUNT="/opt/openslx/system" + +# +# END GLOBALS +############################################################################### + +############################################################################### +# FUNCTION DEFINITIONS +# +# helper to mount the OPENSLX_SYS partition to /opt/openslx/system +# Usage: mount_sys_part <path_to_sys_partition> +mount_sys_part() { + if [ ! -b "$1" ]; then + warn "($$) Refusing to mount '$1' as its not a block device!" + return 1 + fi + + local OPENSLX_SYS_DEVICE="$1" + mkdir -p ${OPENSLX_SYS_MOUNT} + if ! mount -t auto "${OPENSLX_SYS_DEVICE}" "${OPENSLX_SYS_MOUNT}"; then + warn "($$) Mounting '${OPENSLX_SYS_DEVICE}' to '${OPENSLX_SYS_MOUNT}' failed." + return 1 + fi + return 0 + +} +# +# generic helper to format the given partition with the given filesystem or +# from the prefdefined list of xfs, ext4, ... +# Usage: format_disk <dev> <fs> +# e.g. format_disk /dev/sda1 xfs +format_disk () { + local TARGET_DEVICE="$1" + local fslist="xfs ext4" + # if we have a second arguments, its the filesystem of choice + local fs + [ $# -ge 2 ] && fslist="$2" + for fs in $fslist ; do + unset found + local MKFS="$(busybox which mkfs.$fs)" + if [ -n $MKFS ]; then + found=yes + case "mkfs.$fs" in + mkfs.xfs) fopt="-fq" ;; + mkfs.ext4) fopt="-Fq" ;; + esac + info "($$) Formatting $TARGET_DEVICE as $fs" + return $(${MKFS} ${fopt} "${TARGET_DEVICE}") + fi + [ -n "$found" ] && break + done + # still here? then we didn't find a proper formatter... + warn "($$) Could not format $PART_DEV as $fs." + return 1 +} + +# +# END FUNCTION DEFINITIONS +############################################################################### + +############################################################################### +# MAIN CODE +# + +command -v warn >/dev/null || . /lib/dracut-lib.sh + +# let check the arguments +if [ "$#" -ne 2 ]; then + warn "($$) '$0' need 2 arguments: '$0 [OPENSLX_SYS|OPENSLX_TMP] <dev_path>'" + exit 1 +fi +# $1 sane? +if [ "x$1" != "xOPENSLX_SYS" ] && [ "x$1" != "xOPENSLX_TMP" ]; then + warn "($$) First arg needs to be either 'OPENSLX_SYS' or 'OPENSLX_TMP', given: $1" + exit 1 +fi +# $2 sane? +if [ ! -b "/dev/$2" ]; then + warn "($$) Second arg appears not to be a block device!" + exit 1 +fi + +# ok all seems well, set the arguments +PART_TYPE="$1" +PART_DEV="/dev/$2" + +unset OPENSLX_TMP_DEVICE +unset OPENSLX_SYS_DEVICE + +# lets check if we are already running +INSTANCES="$(grep "$PART_TYPE" "$OPENSLX_DISK_FLAG" | busybox wc -l)" +if [ "$INSTANCES" -ge 1 ]; then + # uhoh we are not alone! Need to check + # if the other instance actually did its job + warn "($$) '$0' already running for $PART_TYPE on $PART_DEV... checking state." + # here two/three cases depending on which PART_TYPE we are + # currently processing. + for timeout in 1 1 2; do + # always give the other instance time to finish + # but only check 3 times overall + sleep $timeout + case "$PART_TYPE" in + OPENSLX_TMP) + # was the tmp partition marker created with a device? + if [ ! -f "${OPENSLX_TMP_MARKER}" ]; then + info "($$) Invalid state: no marker for $PART_TYPE" + continue + fi + # it was, is it a valid block device? + OPENSLX_TMP_DEVICE="$(cat ${OPENSLX_TMP_MARKER})" + if [ -z $OPENSLX_TMP_DEVICE -o ! -b $OPENSLX_TMP_DEVICE ]; then + info "($$) Invalid state: no for device $OPENSLX_TMP_DEVICE" + continue + fi + # its detected, its a block device and as mounting is + # done later, we can not check for more at this point + info "($$) Valid state for $OPENSLX_TMP_DEVICE as $PART_TYPE" + exit 0 + ;; + OPENSLX_SYS) + # was the system partition marker created with a device? + if [ ! -f "${OPENSLX_SYS_MARKER}" ]; then + info "($$) Invalid state: no marker for $PART_TYPE" + continue + fi + # it was, is it a valid block device? + OPENSLX_SYS_DEVICE="$(cat ${OPENSLX_SYS_MARKER})" + if [ -z $OPENSLX_SYS_DEVICE -o ! -b $OPENSLX_SYS_DEVICE ]; then + info "($$) Invalid state: no for device $OPENSLX_SYS_DEVICE" + continue + fi + # its detected, its a block device, is it mounted? + if mount | grep -qE "^$OPENSLX_SYS_DEVICE\ on $OPENSLX_SYS_MOUNT"; then + info "($$) Valid state for $OPENSLX_SYS_DEVICE as $PART_TYPE" + exit 0 + fi + # if its not mounted, we want to keep on, so no exit! + ;; + *) + # weird case which should never happen + warn "($$) If you see this, then $0 was called with a bad PART_TYPE: $@" + continue + ;; + esac + done + warn "($$) Timeout reached!" + warn "($$) '$PART_TYPE' was found but not device was associated to it!" +fi + +# We write our pid to $OPENSLX_DISK_FLAG in order to make sure +# we are the only instance of this script running. +info "($$) Processing: $PART_TYPE -> $PART_DEV ($$)" +echo "$PART_TYPE.$$" >> "$OPENSLX_DISK_FLAG" + +# if we are still here, then we can go on and process the partition +if [ "$PART_TYPE" = "OPENSLX_TMP" ]; then + # always format /tmp partition + if ! format_disk "${PART_DEV}" xfs; then + # error while formatting, cleanup + warn "($$) Error formatting $PART_DEV ... removing $$" + sed -i "/^${PART_TYPE}\.$$/d" "${OPENSLX_DISK_FLAG}" + exit 1 + fi + info "($$) Using '$PART_DEV' as '$PART_TYPE'" + # mark it for later: in pre-pivot we will check this file + # and mount it as $NEWROOT/tmp + echo "$PART_DEV" > "$OPENSLX_TMP_MARKER" + # remove our pid from OPENSLX_DISK_FLAG + sed -i "/^${PART_TYPE}\.$$/d" "${OPENSLX_DISK_FLAG}" + exit 0 +fi +if [ "$PART_TYPE" = "OPENSLX_SYS" ]; then + # TODO make the formatting of the system partition configurable + if ! format_disk "${PART_DEV}" xfs; then + # error while formatting, cleanup + warn "($$) Error formatting $PART_DEV ... removing $$" + sed -i "/^${PART_TYPE}\.$$/d" "${OPENSLX_DISK_FLAG}" + exit 1 + fi + # mark it + # mount it now, since qemu-nbd needs it asap! + if mount_sys_part "$PART_DEV"; then + # mount worked, mark it as done + info "($$) Using '$PART_DEV' as '$PART_TYPE'" + echo "$PART_DEV" > "$OPENSLX_SYS_MARKER" + sed -i "/^${PART_TYPE}\.$$/d" "${OPENSLX_DISK_FLAG}" + exit 0 + else + warn "($$) 'mount_sys_part' failed in $0" + sed -i "/^${PART_TYPE}\.$$/d" "${OPENSLX_DISK_FLAG}" + exit 1 + fi +fi + +# +# END MAIN CODE +############################################################################### diff --git a/builder/dnbd3-qcow2-rootfs/scripts/setup-qcow2 b/builder/dnbd3-qcow2-rootfs/scripts/setup-qcow2 new file mode 100755 index 00000000..70babc17 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/scripts/setup-qcow2 @@ -0,0 +1,137 @@ +#!/usr/bin/bash + +# dracut-lib to use debugging functions +command -v warn >/dev/null || . /lib/dracut-lib.sh +command -v emergency_shell >/dev/null || . /lib/dracut-lib.sh + +############################################################################### +# GLOBALS +# +# TODO make this configurable +[ -f /opt/openslx/config ] && . /opt/openslx/config +[ -z $SLX_DNBD3_SERVER ] && SLX_DNBD3_SERVER="132.230.4.1" +[ -z $SLX_STAGE4 ] && SLX_STAGE4="stage4/joe/centos7" +[ -z $SLX_STAGE4_RID ] && SLX_STAGE4_RID="4" +declare -rg DNBD3_SERVER="$SLX_DNBD3_SERVER" +declare -rg DNBD3_IMAGE="$SLX_STAGE4" +declare -rg DNBD3_RID="$SLX_STAGE4_RID" +declare -rg DNBD3_DEVICE="/dev/dnbd0" +declare -rg QCOW_CONTAINER="/opt/openslx/system/system.qcow2" +# +# END GLOBALS +############################################################################### + +############################################################################### +# FUNCTION DEFINITIONS +# +# helper to do some sanity checks +check_dnbd3() { + if [ ! command -v "dnbd3-client" >/dev/null ]; then + warn "No 'dnbd3-client' found. Was the initramfs built correctly?" + emergency_shell -n "Error in $0" + return 1 + fi + return 0 +} + +# helper to connect to the dnbd3-server +connect_dnbd3() { + # check if it already connected + local current_image_name="$(cat /sys/block/${DNBD3_DEVICE#/dev/}/net/image_name)" + [ "x${current_image_name}" != "x(null)" ] && return 0 + + # not connected yet, do it + if ! dnbd3-client -h "${DNBD3_SERVER}" \ + -i "${DNBD3_IMAGE}" \ + -r "${DNBD3_RID}" \ + -d "${DNBD3_DEVICE}" ; then + warn "Failed to mount $DNBD3_IMAGE from $DNBD3_SERVER to $DNBD3_DEVICE" + emergency_shell -n "Error in $0" + return 1 + fi + return 0 +} + +# helper to create the qcow2 container file using +# DNBD3_DEVICE as the base of the filesystem +# QCOW_CONTAINER as the writable file +# (our future rootfs) +create_qcow() { + # check if we already created the qcow2-container + [ -e "$QCOW_CONTAINER" ] && return 0 + + # check if we have our target directory, if not create it + [ ! -d "$(busybox dirname $QCOW_CONTAINER)" ] && \ + mkdir -p "$(busybox dirname $QCOW_CONTAINER)" + + # we did not, let's create it + if ! qemu-img create -f qcow2 -o \ + backing_file="$DNBD3_DEVICE",backing_fmt=qcow2 "$QCOW_CONTAINER"; then + warn "Failed to create qcow2-Container from $DNBD3_DEVICE" + emergency_shell -n "Error in $0" + rm -f -- "$QCOW_CONTAINER" + return 1 + fi + return 0 +} +# helper to start qemu-nbd on localhost:2000 +# use our wrapper to set argv[0][0] to '@' +# this keeps qemu-nbd running after switching root +export_qcow() { + # check if we already have a qemu-nbd + if [ -e /tmp/qemu-nbd.pid ]; then + kill -0 $(cat /tmp/qemu-nbd.pid) && return 0 + fi + # since we use the wrapper, we need a little more logic to see if it runs + /usr/bin/systemd-preserve-process-marker \ + /usr/bin/qemu-nbd -t -p 2000 "$QCOW_CONTAINER" & + # the wrapper returns 255 if the qemu-nbd binary is missing + local qemu_nbd_pid="$!" + for i in 0.5 1 2; do + sleep $i + if ! kill -0 $qemu_nbd_pid; then + # not running + wait $qemu_nbd_pid + local ret_wrapper="$?" + if [ "${ret_wrapper}" -eq 127 ]; then + # wrapper was not found by bash + warn "No such file or directory: /usr/bin/systemd-preserve-process-marker" + elif [ "${ret_wrapper}" -eq 255 ]; then + # qemu-nbd was not found + warn "No such file or directory: /usr/bin/qemu-nbd" + fi + emergency_shell -n "Error in $0" + return 1 + else + # all good, qemu-nbd is running, remember its pid + echo $qemu_nbd_pid > /tmp/qemu-nbd.pid + return 0 + fi + done + # fallback + return 1 +} +# helper to mount the qcow2-container per nbd +connect_qcow() { + # try to mount the locally exported qcow2-container using nbd-client + if /usr/bin/systemd-preserve-process-marker \ + nbd-client --persist 127.0.0.1 2000 /dev/nbd0; then + # it worked, lets set the symlink to /dev/root as dracut needs it + # later on to mount that device to the future root (/sysroot) + ln -sf /dev/nbd0 /dev/root + return 0 + else + # this is pretty bad, dracut would spawn an emergency later on + # since there is no /dev/root to mount. + # For debugging purposes, we drop an emergency shell ourselves + # if the mount fails. + warn "Could not mount /dev/nbd0 from 127.0.0.1:2000." + emergency_shell -n "Error in $0" + return 1 + fi +} +# +# END FUNCTION DEFINITIONS +############################################################################### + +# No main, use functions! diff --git a/builder/dnbd3-qcow2-rootfs/systemd-preserve-process-marker.c b/builder/dnbd3-qcow2-rootfs/systemd-preserve-process-marker.c new file mode 100644 index 00000000..8f0fc108 --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/systemd-preserve-process-marker.c @@ -0,0 +1,33 @@ +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +void print_array(int argc, char *argv[]) { + // Helper function to print given array with given length. + int i = 0; + int j = 0; + for (i = 0; i < argc; i ++) { + j = 0; + while(argv[i][j] != '\0') + printf("%c", argv[i][j++]); + printf(" "); + } + printf("\n"); +} +int main(int argc, char *argv[]) { + int count; + // Last item acts as null pointer. + char **copy = calloc(sizeof(char *), argc); + // Slice first given command line argument. + for (count = 0; count < argc - 1; count++) + copy[count] = strdup(argv[count + 1]); + // Adding systemd indicator to preserve wrapped process during changing + // root filesystem. We mark wrapper and child process. + argv[0][0] = '@'; + copy[0][0] = '@'; + if (-1 == execvp(argv[1], copy)) { + perror("Executing child process failed."); + return -1; + } +} diff --git a/builder/dnbd3-qcow2-rootfs/udev/70-openslx-disk.rules b/builder/dnbd3-qcow2-rootfs/udev/70-openslx-disk.rules new file mode 100644 index 00000000..3f5e382f --- /dev/null +++ b/builder/dnbd3-qcow2-rootfs/udev/70-openslx-disk.rules @@ -0,0 +1,8 @@ +# GPT rules +KERNEL=="sd?[0-9]" SUBSYSTEM=="block" ENV{ID_PART_TABLE_TYPE}=="gpt" ENV{ID_PART_ENTRY_NAME}=="OPENSLX_TMP" RUN+="/sbin/prepare-disks %E{ID_PART_ENTRY_NAME} %k" +KERNEL=="sd?[0-9]" SUBSYSTEM=="block" ENV{ID_PART_TABLE_TYPE}=="gpt" ENV{ID_PART_ENTRY_NAME}=="OPENSLX_SYS" RUN+="/sbin/prepare-disks %E{ID_PART_ENTRY_NAME} %k" + +# MBR rules +KERNEL=="sd?[0-9]" SUBSYSTEM=="block" ENV{ID_PART_TABLE_TYPE}=="dos" ENV{ID_PART_ENTRY_TYPE}=="0x44" RUN+="/sbin/prepare-disks OPENSLX_TMP %k" +KERNEL=="sd?[0-9]" SUBSYSTEM=="block" ENV{ID_PART_TABLE_TYPE}=="dos" ENV{ID_PART_ENTRY_TYPE}=="0x46" RUN+="/sbin/prepare-disks OPENSLX_SYS %k" + |
