summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortorben2016-12-01 10:30:54 +0100
committertorben2016-12-01 10:30:54 +0100
commit09d5a988fdd0fc897c167e9d773dcc1293743955 (patch)
treea3c92fe7d77686e53850de55ed61deea34fc12ed
parenta (diff)
parentduh 2.0 (diff)
downloadsystemd-init-09d5a988fdd0fc897c167e9d773dcc1293743955.tar.gz
systemd-init-09d5a988fdd0fc897c167e9d773dcc1293743955.tar.xz
systemd-init-09d5a988fdd0fc897c167e9d773dcc1293743955.zip
Merge branch 'master' of git.openslx.org:openslx-ng/systemd-init
-rwxr-xr-xbuilder/bootnet-conf/module-setup.sh13
-rwxr-xr-xbuilder/bootnet-conf/scripts/gen-bootnet-conf.sh18
-rwxr-xr-xbuilder/build-initramfs.sh61
-rwxr-xr-xbuilder/conf-tgz/hooks/fetch-config-tgz.sh42
-rwxr-xr-xbuilder/conf-tgz/hooks/unpack-config-tgz.sh72
-rwxr-xr-xbuilder/conf-tgz/module-setup.sh46
-rwxr-xr-xbuilder/dnbd3-rootfs/hooks/fetch-config.sh4
-rwxr-xr-xbuilder/dns/module-setup.sh13
-rwxr-xr-xbuilder/dns/scripts/gen-resolv-conf.sh11
-rwxr-xr-xbuilder/ib-conf/module-setup.sh13
-rwxr-xr-xbuilder/ib-conf/scripts/gen-ib-conf.sh24
-rw-r--r--packager/.gitignore1
-rw-r--r--packager/blacklists/essential/linux-base2
-rw-r--r--packager/blacklists/essential/linux-extended4
-rwxr-xr-xpackager/openslx18
-rw-r--r--packager/openslx.config18
-rw-r--r--packager/openslx.functions640
17 files changed, 650 insertions, 350 deletions
diff --git a/builder/bootnet-conf/module-setup.sh b/builder/bootnet-conf/module-setup.sh
new file mode 100755
index 00000000..f1d6697f
--- /dev/null
+++ b/builder/bootnet-conf/module-setup.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+check() {
+ # Tell dracut that this module should only be included if it is required
+ # explicitly.
+ return 255
+}
+depends() {
+ echo dnbd3-rootfs
+}
+install() {
+ inst_hook pre-pivot 50 "$moddir/scripts/gen-bootnet-conf.sh"
+}
diff --git a/builder/bootnet-conf/scripts/gen-bootnet-conf.sh b/builder/bootnet-conf/scripts/gen-bootnet-conf.sh
new file mode 100755
index 00000000..ec82f143
--- /dev/null
+++ b/builder/bootnet-conf/scripts/gen-bootnet-conf.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh
+
+mac="$(getargs BOOTIF=)"
+mac="$(echo $mac | cut -c 4- | tr '-' ':')"
+if [ -n "${mac}" ]; then
+ cat << EOF >> "${NEWROOT}/etc/sysconfig/network-scripts/ifcfg-bootnet"
+DEVICE=bootnet
+NAME=bootnet
+HWADDR=${mac}
+BOOTPROTO=dhcp
+ONBOOT=yes
+NM_CONTROLLED=yes
+EOF
+else
+ echo "Could not parse MAC"
+fi
diff --git a/builder/build-initramfs.sh b/builder/build-initramfs.sh
index b705fece..dcab3b1d 100755
--- a/builder/build-initramfs.sh
+++ b/builder/build-initramfs.sh
@@ -43,9 +43,11 @@ if ! [[ -d "$_needed_location" ]]; then
'
git submodule update --remote
popd
- cp --recursive \
- "${_temporary_repository_location}/builder/dnbd3-rootfs" \
- "$_needed_location"
+ for mod in dnbd3-rootfs conf-tgz dns ib-conf bootnet-conf; do
+ cp --recursive \
+ "${_temporary_repository_location}/builder/${mod}" \
+ "$(dirname $_needed_location)/${mod}"
+ done
rm --recursive --force "$_temporary_repository_location"
fi
set +o errexit
@@ -203,9 +205,10 @@ parse_command_line() {
debug
>>> parse_command_line -p
+ +doc_test_capture_stderr
+doc_test_contains
- Error with given option "-p":
+doc_test_ellipsis
+ Error with given option "-p":
Traceback (most recent call first):
...
@@ -240,9 +243,10 @@ parse_command_line() {
yes
>>> parse_command_line -t; echo $?
+ +doc_test_capture_stderr
+doc_test_contains
- Error with given option "-t":
+doc_test_ellipsis
+ Error with given option "-t":
Traceback (most recent call first):
...
@@ -262,9 +266,10 @@ parse_command_line() {
vim htop
>>> parse_command_line --no-available-option; echo $?
+ +doc_test_capture_stderr
+doc_test_contains
- Error with given option "--no-available-option":
+doc_test_ellipsis
+ Error with given option "--no-available-option":
Traceback (most recent call first):
...
'
@@ -547,30 +552,33 @@ main() {
logging.info "Dracut isn't available yet loading it."
initialize_dracut
fi
-
- _dracut_modules_source='../../dnbd3-rootfs'
- _dracut_modules_target="$(dirname "${BASH_SOURCE[0]}")/dracut/modules.d/90dnbd3-rootfs"
- if [[ ! -L "$_dracut_modules_target" || "$(readlink \
- "$_dracut_modules_target")" != "$_dracut_modules_source" ]]
- then
- logging.info \
- "Link dnbd3 plugin into dracut modules folder ($_dracut_modules_source -> $_dracut_modules_target)."
- if ! ln --symbolic --force "$_dracut_modules_source" \
- "$_dracut_modules_target" 2>/dev/null; then
- logging.warn \
- "Linking \"$_dracut_modules_source\" to \"$_dracut_modules_target\" failed. We will copy them. So we have to recopy it every time to ensure that recompiled things take effect."
- cp --recursive --force --no-target-directory \
- "$(dirname "${BASH_SOURCE[0]}")/$(basename "$_dracut_modules_source")" \
- "$_dracut_modules_target"
+
+ declare -A _dracut_modules=( [dnbd3]='../../dnbd3-rootfs' [conf]='../../conf-tgz' [dns]='../../dns' [ib]='../../ib-conf' [bootnet]='../../bootnet-conf')
+ for mod in dnbd3 conf dns ib bootnet; do
+ _dracut_modules_source="${_dracut_modules[$mod]}"
+ _dracut_modules_target="$(dirname "${BASH_SOURCE[0]}")/dracut/modules.d/90$(basename "${_dracut_modules[$mod]}")"
+ if [[ ! -L "$_dracut_modules_target" || "$(readlink \
+ "$_dracut_modules_target")" != "$_dracut_modules_source" ]]
+ then
+ logging.info \
+ "Link ${mod} plugin into dracut modules folder ($_dracut_modules_source -> $_dracut_modules_target)."
+ if ! ln --symbolic --force "$_dracut_modules_source" \
+ "$_dracut_modules_target" 2>/dev/null; then
+ logging.warn \
+ "Linking \"$_dracut_modules_source\" to \"$_dracut_modules_target\" failed. We will copy them. So we have to recopy it every time to ensure that recompiled things take effect."
+ cp --recursive --force --no-target-directory \
+ "$(dirname "${BASH_SOURCE[0]}")/$(basename "$_dracut_modules_source")" \
+ "$_dracut_modules_target"
+ fi
fi
- fi
+ done
# endregion
# region prepare and perform final dracut call
_loglevel=''
if [ "$verbose" == 'yes' ]; then
_loglevel='--verbose'
fi
- _modules='dnbd3-rootfs'
+ _modules='dnbd3-rootfs conf-tgz dns ib-conf bootnet-conf'
if [ "$debug" == 'yes' ]; then
_loglevel="$_loglevel --stdlog 4"
_modules="$_modules i18n terminfo"
@@ -598,7 +606,8 @@ main() {
logging.set_commands_level debug
# shellcheck disable=SC2086
"$(dirname "${BASH_SOURCE[0]}")/dracut/dracut.sh" --local \
- $_loglevel --modules "$_modules" "${dracut_parameter[@]}" \
+ $_loglevel --modules "$_modules" --conf /etc/dracut.conf \
+ --confdir /etc/dracut.conf.d "${dracut_parameter[@]}" \
"$file_path"
_return_code=$?
logging.set_commands_level "$_commands_log_level_backup"
@@ -613,7 +622,9 @@ main() {
exceptions.deactivate
}
# endregion
-core.is_main && main "$@"
+if core.is_main; then
+ main "$@"
+fi
# region vim modline
# vim: set tabstop=4 shiftwidth=4 expandtab:
# vim: foldmethod=marker foldmarker=region,endregion:
diff --git a/builder/conf-tgz/hooks/fetch-config-tgz.sh b/builder/conf-tgz/hooks/fetch-config-tgz.sh
new file mode 100755
index 00000000..8c3bed23
--- /dev/null
+++ b/builder/conf-tgz/hooks/fetch-config-tgz.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8 -*-
+
+source '/usr/lib/rebash/core.sh'
+core.import exceptions
+core.import logging
+type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh
+
+exceptions.try
+{
+ logging.set_commands_level debug
+ logging.set_level debug
+ # NOTE: "getarg" raises an exception so deactivate exceptions for now.
+ exceptions.deactivate
+ slx_server="$(getarg slxsrv=)"
+ slx_server_base="$(getarg slxbase=)"
+ exceptions.activate
+
+ logging.info "Download config.tgz from '${slx_server}'..."
+ IFS_backup="$IFS"
+ IFS=','
+ for host in ${slx_server}; do
+ logging.info "Trying host \"$host\"."
+ if wget --timeout 5 \
+ "http://${host}/${slx_server_base}/config.tgz" \
+ --output-document "/etc/config.tgz"
+ then
+ break
+ fi
+ done
+ IFS="$IFS_backup"
+
+ if [[ ! -e "/etc/config.tgz" ]]; then
+ logging.warn "Downloading 'config.tgz' from '${slx_server}' failed. Return code: $return_code"
+ exit 1
+ fi
+}
+exceptions.catch
+{
+ logging.error "$exceptions_last_traceback"
+ emergency_shell "error in ${BASH_SOURCE[0]}"
+}
diff --git a/builder/conf-tgz/hooks/unpack-config-tgz.sh b/builder/conf-tgz/hooks/unpack-config-tgz.sh
new file mode 100755
index 00000000..083fbc6e
--- /dev/null
+++ b/builder/conf-tgz/hooks/unpack-config-tgz.sh
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8 -*-
+
+source '/usr/lib/rebash/core.sh'
+core.import exceptions
+core.import logging
+type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh
+
+# this module unpacks the config.tgz
+temporary_extract_directory="$(mktemp -d)"
+exceptions.try
+{
+ logging.set_commands_level debug
+ logging.set_level debug
+ exceptions.activate
+ if [[ -e "/etc/config.tgz" ]]; then
+ tar --extract --preserve-permissions \
+ --file="/etc/config.tgz" \
+ --directory="$temporary_extract_directory"
+ fi
+}
+exceptions.catch
+{
+ logging.error "Failed to extract '/etc/config.tgz' to '$temporary_extract_directory'."
+ logging.error "$exceptions_last_traceback"
+ emergency_shell "error in ${BASH_SOURCE[0]}"
+}
+# extracted to temporary directory, now check for SLX_LOCAL_CONFIGURATION
+source "/etc/openslx"
+exceptions.try
+{
+ logging.set_commands_level debug
+ logging.set_level debug
+ exceptions.activate
+ if [[ -z "$SLX_LOCAL_CONFIGURATION" ]]; then
+ logging.warn "SLX_LOCAL_CONFIGURATION is not set in '/etc/openslx'."
+ exit 0
+ fi
+ if [[ ! -d "${temporary_extract_directory}/openslx-configs/${SLX_LOCAL_CONFIGURATION}" ]]; then
+ logging.warn "SLX_LOCAL_CONFIGURATION is set but no corresponding folder found in '/etc/config.tgz'."
+ exit 0
+ fi
+ # still here? then process to merge the localized configuration files with the common files.
+ cd "${temporary_extract_directory}/openslx-configs/${SLX_LOCAL_CONFIGURATION}"
+ tar --create --preserve-permissions * | tar --extract --preserve-permissions --directory "${temporary_extract_directory}"
+}
+exceptions.catch
+{
+ # errors here are not critical, so no emergency shell
+ logging.error "Failed to merge local configuration files for '$SLX_LOCAL_CONFIGURATION'."
+ logging.error "$exceptions_last_traceback"
+ emergency_shell "error in ${BASH_SOURCE[0]}"
+}
+
+# now just copy everything from the temporary_extract_directory to the future root
+exceptions.try
+{
+ logging.set_commands_level debug
+ logging.set_level debug
+ exceptions.activate
+ cd "${temporary_extract_directory}"
+ # purge openslx-configs/
+ rm -rf "openslx-configs"
+ tar --create --preserve-permissions * | tar --extract --preserve-permissions --directory "${NEWROOT}"
+}
+exceptions.catch
+{
+ # errors here are not critical, so no emergency shell
+ logging.error "Failed to copy extracted configuration files to '$NEWROOT'."
+ logging.error "$exceptions_last_traceback"
+ emergency_shell "error in ${BASH_SOURCE[0]}"
+}
diff --git a/builder/conf-tgz/module-setup.sh b/builder/conf-tgz/module-setup.sh
new file mode 100755
index 00000000..946344bf
--- /dev/null
+++ b/builder/conf-tgz/module-setup.sh
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8 -*-
+
+source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd '../' && \
+ cd *'dnbd3-rootfs/scripts/rebash' && pwd)/core.sh"
+core.import exceptions
+core.import logging
+
+check() {
+ local __doc__='
+ Checks whether needed assumptions are satisfied.
+
+ Example:
+
+ `check`
+ '
+
+ # Here we could build our package file.
+
+ # Tell dracut that this module should only be included if it is required
+ # explicitly.
+ return 255
+}
+depends() {
+ local __doc__='
+ Outputs all dependent dracut modules to make this module work.
+
+ >>> depends
+ +doc_test_contains
+ base
+ '
+ echo base
+}
+install() {
+ local __doc__='
+ Copies all needed files into the initramfs image and registers all needed
+ dracut hooks.
+
+ Example:
+
+ `install`
+ '
+ inst_hook pre-mount 10 "$moddir/hooks/fetch-config-tgz.sh"
+ inst_hook pre-pivot 10 "$moddir/hooks/unpack-config-tgz.sh"
+ inst_multiple tar wget mktemp
+}
diff --git a/builder/dnbd3-rootfs/hooks/fetch-config.sh b/builder/dnbd3-rootfs/hooks/fetch-config.sh
index 7921a99f..23c5f004 100755
--- a/builder/dnbd3-rootfs/hooks/fetch-config.sh
+++ b/builder/dnbd3-rootfs/hooks/fetch-config.sh
@@ -29,7 +29,7 @@ IFS=','
for host in ${slx_server}; do
logging.info "Trying host \"$host\"."
if wget --timeout 5 \
- "http://${host}/${slx_server_base}${configuration_file_name}" \
+ "http://${host}/${slx_server_base}/${configuration_file_name}" \
--output-document '/etc/openslx'
then
break
@@ -38,7 +38,7 @@ done
IFS="$IFS_backup"
if [[ ! -e "/etc/openslx" ]]; then
- logging.warn "Downloading OpenSLX configuration file from any of the servers \"${slx_server}\" at location \"${slx_server_base}${configuration_file_name}\" failed. Return code: $return_code"
+ logging.warn "Downloading OpenSLX configuration file from any of the servers \"${slx_server}\" at location \"${slx_server_base}/${configuration_file_name}\" failed. Return code: $return_code"
exit 1
fi
}
diff --git a/builder/dns/module-setup.sh b/builder/dns/module-setup.sh
new file mode 100755
index 00000000..3bad6548
--- /dev/null
+++ b/builder/dns/module-setup.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+check() {
+ # Tell dracut that this module should only be included if it is required
+ # explicitly.
+ return 255
+}
+depends() {
+ echo base
+}
+install() {
+ inst_hook pre-mount 50 "$moddir/scripts/gen-resolv-conf.sh"
+}
diff --git a/builder/dns/scripts/gen-resolv-conf.sh b/builder/dns/scripts/gen-resolv-conf.sh
new file mode 100755
index 00000000..1601bb12
--- /dev/null
+++ b/builder/dns/scripts/gen-resolv-conf.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh
+
+CONFFILE="/etc/resolv.conf"
+
+DNS=$(getargs dns=)
+HOSTNAME=$(getargs hostname=)
+
+[ -n "$DNS" ] && echo "nameserver $DNS" >> "$CONFFILE"
+[ -n "$HOSTNAME" ] && echo "$HOSTNAME" >> /etc/hostname
diff --git a/builder/ib-conf/module-setup.sh b/builder/ib-conf/module-setup.sh
new file mode 100755
index 00000000..0a80d89e
--- /dev/null
+++ b/builder/ib-conf/module-setup.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+check() {
+ # Tell dracut that this module should only be included if it is required
+ # explicitly.
+ return 255
+}
+depends() {
+ echo dnbd3-rootfs
+}
+install() {
+ inst_hook pre-pivot 50 "$moddir/scripts/gen-ib-conf.sh"
+}
diff --git a/builder/ib-conf/scripts/gen-ib-conf.sh b/builder/ib-conf/scripts/gen-ib-conf.sh
new file mode 100755
index 00000000..2d4b7a27
--- /dev/null
+++ b/builder/ib-conf/scripts/gen-ib-conf.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+type emergency_shell >/dev/null 2>&1 || source /lib/dracut-lib.sh
+
+IPSUFFIX=$(
+ ip -4 -o addr show dev bootnet | \
+ awk '{split($4,a,"/");split(a[1],b,".");print b[3]"."b[4]}'
+)
+
+cat << EOF >> "${NEWROOT}/etc/sysconfig/network-scripts/ifcfg-ib0"
+DEVICE=ib0
+NAME=ib0
+TYPE=Infiniband
+BOOTPROTO=static
+IPADDR=10.12.${IPSUFFIX}
+BROADCAST=10.12.255.255
+NETWORK=10.12.0.0
+NETMASK=255.255.0.0
+ONBOOT=yes
+NM_CONTROLLED=yes
+CONNECTED_MODE=yes
+MTU=65520
+EOF
+
diff --git a/packager/.gitignore b/packager/.gitignore
new file mode 100644
index 00000000..f4ed9bc4
--- /dev/null
+++ b/packager/.gitignore
@@ -0,0 +1 @@
+clones
diff --git a/packager/blacklists/essential/linux-base b/packager/blacklists/essential/linux-base
index 54e9c294..2c3ec439 100644
--- a/packager/blacklists/essential/linux-base
+++ b/packager/blacklists/essential/linux-base
@@ -1,4 +1,3 @@
-- /boot/*
- /cdrom/*
- /dev/*
- /adm/*
@@ -68,6 +67,7 @@
#- /var/spool/**
+ /var/spool/torque/*
- /var/tmp/*
++ /var/tmp/*guestfs*
- /var/lib/nova/instances/*
- /var/lib/libvirt
diff --git a/packager/blacklists/essential/linux-extended b/packager/blacklists/essential/linux-extended
index 3d81000a..90dcdca7 100644
--- a/packager/blacklists/essential/linux-extended
+++ b/packager/blacklists/essential/linux-extended
@@ -1,5 +1,5 @@
- /home/*
- /root/*
-+ /root/.bash*
-+ /root/.ssh
+#+ /root/.bash*
+#+ /root/.ssh
- /etc/cups
diff --git a/packager/openslx b/packager/openslx
index 9613196b..6845f9ba 100755
--- a/packager/openslx
+++ b/packager/openslx
@@ -31,25 +31,13 @@ else
banner
fi
-# do we even have tools?
-for TOOL in qemu-img qemu-nbd mkfs.ext4; do
- which "${TOOL}" &>/dev/null || \
- perror "Could not find '${TOOL}'. Please install it."
-done
-
# setup trap
-trap cleanexit SIGINT SIGTERM
+trap do_cleanup SIGINT SIGTERM SIGUSR1 EXIT
# read params
read_params $@
# react to given action
-if [ "x$ACTION" == "xCLONE" ]; then
- clone_stage4 || perror "Cloning stage4 failed with: $?"
-elif [ "x$ACTION" == "xPACKAGE" ]; then
- pack_qcow2 || perror "Packing as QCoW2 failed with: $?"
-else
- pwarning "No action given."
- print_usage
-fi
+process_action
+exit 0
diff --git a/packager/openslx.config b/packager/openslx.config
index 00fc5501..985ace1a 100644
--- a/packager/openslx.config
+++ b/packager/openslx.config
@@ -1,8 +1,18 @@
+# default options for rsync
+DEFAULT_RSYNC_OPTS="--acls \
+ --hard-links \
+ --xattrs \
+ --archive \
+ --delete \
+ --delete-excluded \
+ --numeric-ids \
+ --verbose"
+
# default options used by rsync
-DEFAULT_RSYNC_OPTS="-e ssh -c arcfour -oStrictHostKeyChecking=no"
+DEFAULT_RSYNC_RSH="ssh -c aes256-ctr -o StrictHostKeyChecking=no"
-# default size for the qcow2-container (the one containing diffs)
-DEFAULT_QCOW_SIZE="10G"
+# default size for the qcow2-container
+DEFAULT_CONTAINER_SIZE="20G"
# default filesystem type for the qcow2-container
-DEFAULT_QCOW_FS="ext4"
+DEFAULT_CONTAINER_FILESYSTEM="xfs"
diff --git a/packager/openslx.functions b/packager/openslx.functions
index 7fe74e0a..fb32a231 100644
--- a/packager/openslx.functions
+++ b/packager/openslx.functions
@@ -44,28 +44,37 @@ perror() {
exit 1
}
+# accepts one argument to be printed as a warning before dumping the help
print_usage() {
+ [ -n "$1" ] && pwarning "$1"
pinfo "USAGE:"
pinfo "$ARG0 <action> <actions_params>"
- pinfo "\t\tActions: '--clone', '--package'"
+ pinfo "\t\tActions: '--clone', '--package', '--export', 'update'"
pinfo ""
- pinfo "CLONING:"
+ pinfo "CLONING: rsync remote host to local directory"
pinfo "$ARG0 --clone --host <host> [--syncdir <path>]"
- pinfo "\t\tIf not specified, --syncdir = './builds/<host>/stage4'"
+ pinfo "\t\tIf not specified, --syncdir = './clones/<host>/stage4'"
pinfo ""
- pinfo "PACKAGING:"
- pinfo "$ARG0 --package --syncdir <path> --container <path>"
- pinfo "$ARG0 --package --host <host> --container <path>"
- pinfo "\t\tIf <host> is specified, --syncdir = './builds/<host>/stage4'"
- pinfo "$ARG0 --package --host <host> --syncdir <path> --container <path>"
- pinfo "\t\tIf both are specified, --syncdir is used."
+ pinfo "PACKAGING: pack local rsync directory as qcow2-container"
+ pinfo "$ARG0 --package --host <host> [--container <path>] [--syncdir <dir>]"
+ pinfo "\t\tIf <container> is not specified, --container = './clones/<host>/stage4.qcow2'"
+ pinfo "\t\tIf <syncdir> is not specified, --syncdir = './clones/<host>/stage4'"
+ pinfo ""
+ pinfo "EXPORTING: rsync remote host to a new qcow2-container"
+ pinfo "$ARG0 --export --host <host> [--container <path>] [--syncdir <dir>]"
+ pinfo "\t\tIf <container> is not specified, --container = './clones/<host>/stage4.qcow2'"
+ pinfo ""
+ pinfo "UPDATING: rsync remote host to an existing qcow2-container"
+ pinfo "$ARG0 --update --host <host> [--container <path>]"
+ pinfo "\t\tIf <container> is not specified, --container = './clones/<host>/stage4.qcow2'"
kill "$SELF_PID"
exit 1
}
-#
+
# helper to parse the command line arguments and fill the environment
-# with the parameters given. Note that checks for validity happens
-# in the respective functions, we only parse here.
+# with the parameters given. Since the fallbacks for those variables
+# are only dependent on the selected action, we will also post-process
+# them to make sure they are set when leaving this function!
read_params() {
# initialize global variables
declare -g FORCE=0
@@ -85,10 +94,15 @@ read_params() {
--package)
declare -rg ACTION="PACKAGE"
;;
+ --export)
+ declare -rg ACTION="EXPORT"
+ ;;
+ --update)
+ declare -rg ACTION="UPDATE"
+ ;;
--host)
if [ -z "$1" ]; then
- pwarning "'--host' requires a host as parameter."
- print_usage && exit 1
+ print_usage "'--host' requires a host as parameter."
else
if [[ "$1" == --* ]]; then
local _msg="A host should not start with '--'."
@@ -102,8 +116,7 @@ read_params() {
;;
--container)
if [ -z "$1" ]; then
- pwarning "'--container' requires a path as parameter."
- print_usage && exit 1
+ print_usage "'--container' requires a path as parameter."
else
if [[ "$1" == --* ]]; then
local _msg="A path should not start with '--'."
@@ -117,8 +130,7 @@ read_params() {
;;
--syncdir)
if [ -z "$1" ]; then
- pwarning "'--syncdir' requires a path as parameter."
- print_usage && exit 1
+ print_usage "'--syncdir' requires a path as parameter."
else
if [[ "$1" == --* ]]; then
local _msg="A path should not start with '--'."
@@ -134,154 +146,101 @@ read_params() {
declare -rg FORCE=1
;;
*)
- pwarning "Unknown flag: $PARAM"
- print_usage && exit 1
+ print_usage "Unknown flag: $PARAM"
;;
esac
continue
fi
done
-}
-
-# helper function trapped on SIGTERM/SIGINT
-# Usage: do not use as is
-cleanexit() {
- trap '' SIGINT SIGTERM # from now on, ignore INT and TERM
- pwarning "SIGINT/SIGTERM triggered - cleaning up ..."
- [ -z "${_STATE}" ] && perror "'_STATE' not set, this is bad."
-
- case "${_STATE}" in
- SYNC_DONE|QCOW_DONE)
- # we are happy
- pwarning "SIGINT/SIGTERM received, but everything seems fine. Check it!"
- exit 0
- ;;
- BLACKLISTING)
- # creation of blacklists failed
- # TODO do what?
- ;;
- SYNCING)
- # error during rsync, create the .stage4 file again
- [ -z "${RSYNC_TARGET}" ] && \
- perror "RSYNC_TARGET not set, this should not happen."
- if [ ! -e "${RSYNC_TARGET}/.stage4" ]; then
- pwarning "'.stage4' flag was lost during rsync, restoring it."
- touch "${RSYNC_TARGET}/.stage4"
- fi
- ;;
- QCOW_CREATING)
- # qemu-img failed. Just remove the container if its there
- if [ -n "${CONTAINER_PATH}" -a -e "${CONTAINER_PATH}" ]; then
- rm -f "${CONTAINER_PATH}" || \
- pwarning "Could not remove '${CONTAINER_PATH}'."
- fi
- ;;
- QCOW_NBD_CONNECTING)
- # qemu-nbd failed
- if [ -n "${NBD_DEV}" ]; then
- qemu-nbd -d "${NBD_DEV}" && \
- pwarning "Could not disconnect '${NBD_DEV}'."
- fi
- ;;
- QCOW_FSING)
- # mkfs failed, disconnect and remove container
- if [ -n "${NBD_DEV}" ]; then
- qemu-nbd -d "${NBD_DEV}" && \
- pwarning "Could not disconnect '${NBD_DEV}'."
- fi
- if [ -n "${CONTAINER_PATH}" -a -e "${CONTAINER_PATH}" ]; then
- rm -f "${CONTAINER_PATH}" || \
- pwarning "Could not remove '${CONTAINER_PATH}'."
- fi
- ;;
- QCOW_MOUNTING)
- # mounting failed:
- # umount, disconnect and remove container and mount point
- if [ -n "${NBD_MNT}" ]; then
- umount "${NBD_MNT}" || pwarning "Could not umount '${NBD_MNT}'."
- rmdir "${NBD_MNT}" || pwarning "Could not rmdir '${NBD_MNT}'."
- fi
- if [ -n "${NBD_DEV}" ]; then
- qemu-nbd -d "${NBD_DEV}" && \
- perror "Could not disconnect '${NBD_DEV}'."
- fi
- if [ -n "${CONTAINER_PATH}" -a -e "${CONTAINER_PATH}" ]; then
- rm -f "${CONTAINER_PATH}" || \
- perror "Could not remove '${CONTAINER_PATH}'."
+ # done parsing the arguments, exit if no action given
+ [ -z "$ACTION" ] && \
+ print_usage "No action given"
+
+ # now check for existance of variables
+ # and use fallbacks when possible if they were not specified
+
+ # REMOTE_HOST valid? always needed!
+ [ -z "$REMOTE_HOST" ] && \
+ perror "REMOTE_HOST not set. Use '--host'."
+ check_host "$REMOTE_HOST" || \
+ perror "'$REMOTE_HOST' is neither an IP nor a known hostname."
+ # REMOTE_HOST valid - set build directory for the rest of the operations
+ declare -rg BUILD_DIR="${ROOT_DIR}/clones/${REMOTE_HOST}"
+ mkdir -p "${BUILD_DIR}" || perror "Could not create '${BUILD_DIR}'."
+
+ # RSYNC_TARGET needs special care
+ if [ -z "$RSYNC_TARGET" ]; then
+ # none given - use fallbacks
+ if [ "x$ACTION" == "xCLONE" ]; then
+ # use default path when cloning, no need for CONTAINER_MNT
+ pwarning "RSYNC_TARGET not set, using: '${BUILD_DIR}/stage4/'."
+ declare -rg RSYNC_TARGET="${BUILD_DIR}/stage4"
+ else
+ # we always want CONTAINER_MNT here
+ declare -rg CONTAINER_MNT="$(mktemp -d)"
+ [ -z "${CONTAINER_MNT}" ] && \
+ perror "Could not create temporary directory for mounting the container."
+ add_cleanup rmdir "${CONTAINER_MNT}"
+ # RSYNC_TARGET depends on the action at this point
+ if [ "x$ACTION" == "xPACKAGE" ]; then
+ # use default path when packaging
+ declare -rg RSYNC_TARGET="${BUILD_DIR}/stage4"
+ elif [ "x$ACTION" == "xUPDATE" -o "x$ACTION" == "xEXPORT" ]; then
+ # for action update/export, we want to sync to the mounted container
+ # so create a temporary directory for the mount point that we'll use later
+ declare -rg RSYNC_TARGET="${CONTAINER_MNT}"
fi
- ;;
- QCOW_COPYING)
- # rare case, should not happen
- ;;
- QCOW_CLEANING)
- # should also not happen
- ;;
- *)
- pwarning "Unknown state: ${_STATE}"
- esac
- # still here? then we ran into some error
- exit 1
-}
-
-
-
-# helper to validate an ip
-# Usage:
-# valid_ip <ip>
-# Returns 0 if valid, 1 otherwise.
-valid_ip() {
- local ip=$1
- local stat=1
+ fi
+ fi
- if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
- OIFS=$IFS
- IFS='.'
- ip=($ip)
- IFS=$OIFS
- [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
- && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
- stat=$?
- fi
- return $stat
+ # CONTAINER_PATH valid?
+ if [ -z "$CONTAINER_PATH" ]; then
+ # use default path: ${BUILD_DIR}/stage4.qcow2
+ pwarning "CONTAINER_PATH not set. Using '${BUILD_DIR}/stage4.qcow2'."
+ declare -rg CONTAINER_PATH="${BUILD_DIR}/stage4.qcow2"
+ fi
+ # so from now on REMOTE_HOST, RSYNC_TARGET, CONTAINER_PATH are set (and read-only).
}
-
-# helper to check whether a given host is valid
-# Usage:
-# check_host <hostname|ip>
-# Returns 0 if valid, 1 otherwise
-check_host() {
- local HOST="$1"
- [ -z "$HOST" ] && return 1
- # check if its a valid IP or a valid hostname
- valid_ip "$HOST" && return 0
- host -W 2 "$HOST" && return 0
- # still here? then fail
- return 1
+process_action() {
+ if [ "x$ACTION" == "xCLONE" ]; then
+ clone_host || perror "Cloning stage4 failed with: $?"
+ elif [ "x$ACTION" == "xPACKAGE" ]; then
+ pack_clone || perror "Packing as QCoW2 failed with: $?"
+ elif [ "x$ACTION" == "xEXPORT" ]; then
+ export_host || perror "Exporting failed with: $?"
+ elif [ "x$ACTION" == "xUPDATE" ]; then
+ update_container || perror "Updating failed with: $?"
+ else
+ print_usage "No action given."
+ fi
+ return 0
}
-# helper to check if a dir is empty or not
-# Usage:
-# dir_empty <dir>
-# Returns 0 if empty, 1 otherwise
-is_dir_empty() {
- [ $# -ne 1 ] && perror "$0 requires directory as paramter, none given."
- local _dir="$1"
- [ -d "$_dir" ] || return 1
- [ -n "$(ls -A $_dir)" ] && return 1 || return 0
+# wrapper to package a cloned stage4 as a qcow2 container
+# - creates empty container at CONTAINER_PATH
+# - mounts it to RSYNC_TARGET
+# - copy RSYNC_TARGET
+pack_clone() {
+ create_container
+ mount_container
+ copy_to_container
}
-
-# helper to ask user for confirmation
-# Usage:
-# user_confirm
-# Return 0 if confirmed, 1 otherwise
-user_confirm() {
- [ $# -ne 1 ] && perror "$0 requires the question as first argument."
- pinfo "$1 [Y/n]"
- local _input
- read _input
- [ "x${_input}" == "x" -o "x${_input}" == "xy" ] && return 0 || return 1
+# wrapper to update an existing container
+# - mounts it to RSYNC_TARGET
+# - clone host there
+update_container() {
+ mount_container
+ clone_host
+}
+# wrapper to export a host directly to a container
+# - create en empty qcow2 container at CONTAINER_PATH
+# - mount it to RSYNC_TARGET
+# - clone host there
+export_host() {
+ create_container
+ mount_container
+ clone_host
}
-
# ------------------------------------------------------------------------------
#
# Stage4 related functions
@@ -290,43 +249,21 @@ user_confirm() {
#
# Helper to generate a stage4 export for a remote machine per rsync.
# Usage:
-# clone_stage4
+# clone_host
#
# Note: this functions requires REMOTE_HOST and RSYNC_TARGET to be set.
-clone_stage4() {
- # REMOTE_HOST valid?
- [ -z "$REMOTE_HOST" ] && pwarning "REMOTE_HOST not set. Use '--host'." && print_usage
- if ! check_host "$REMOTE_HOST"; then
- # invalid, abort
- pwarning "'$REMOTE_HOST' is neither an IP nor a known hostname."
- print_usage
- fi
- # REMOTE_HOST is valid, use it as the base for our build files
- # set BUILD_DIR relative to the ROOT_DIR for the REMOTE_HOST
- declare -rg BUILD_DIR="${ROOT_DIR}/builds/$REMOTE_HOST"
- mkdir -p "${BUILD_DIR}"
-
- # RSYNC_TARGET set?
- if [ -z "$RSYNC_TARGET" ]; then
- pwarning "RSYNC_TARGET not set. Assuming local mode."
- pinfo "Using '${BUILD_DIR}/stage4'"
- declare -rg RSYNC_TARGET="${BUILD_DIR}/stage4"
- fi
-
+clone_host() {
# check if RSYNC_TARGET is valid
if [ -d "${RSYNC_TARGET}" ]; then
- # does it have the '.stage4' flag?
- [ ! -e "${RSYNC_TARGET}/.stage4" ] && \
- perror "'${RSYNC_TARGET}' exists, but no '.stage4' flag found." \
- "Refusing to rsync there."
+ # does it have the '.stage4' flag? skip this check when exporting directly
+ [ "x$ACTION" != "xEXPORT" ] && [ ! -e "${RSYNC_TARGET}/.stage4" ] && \
+ perror "'${RSYNC_TARGET}' exists, but no '.stage4' flag found. Refusing to rsync there."
+ #touch $RSYNC_TARGET/.stage4 && exit 0
else
# not a directory, create it and set the .stage4 flag
- mkdir -p "${RSYNC_TARGET}"
- touch "${RSYNC_TARGET}/.stage4"
+ mkdir -p "${RSYNC_TARGET}" || perror "Could not create '${RSYNC_TARGET}'."
fi
- # mark state
- _STATE='BLACKLISTING'
local EXCLUDE="$BUILD_DIR/exclude-stage4"
local INCLUDE="$BUILD_DIR/include-stage4"
@@ -339,155 +276,149 @@ clone_stage4() {
grep '^-' "$FILE" >> "$EXCLUDE"
grep '^+' "$FILE" >> "$INCLUDE"
done
- pinfo "Done."
- # prepare rsync's options
- if [ -z "$DEFAULT_RSYNC_OPTS" ]; then
- local RSYNC_OPTS="-e ssh -c arcfour -oStrictHostKeyChecking=no"
- else
- local RSYNC_OPTS="$DEFAULT_RSYNC_OPTS"
- fi
+ # prepare rsync's general options
+ local RSYNC_OPTS="${DEFAULT_RSYNC_OPTS}"
+ [ -z "${RSYNC_OPTS}" ] && \
+ RSYNC_OPTS="
+ --acls \
+ --hard-links \
+ --xattrs \
+ --archive \
+ --delete \
+ --delete-excluded \
+ --numeric-ids
+ "
+ # prepare rsync's remote shell options
+ local RSYNC_RSH="$DEFAULT_RSYNC_RSH"
+ [ -z "${RSYNC_RSH}" ] && RSYNC_RSH="ssh -c blowfish -oStrictHostKeyChecking=no"
local RSYNC_SOURCE="root@$REMOTE_HOST:/"
- _STATE='SYNCING'
+ # if something goes wrong during rsync, we need to recreate the .stage4 flag
+ add_cleanup touch ${RSYNC_TARGET}/.stage4
# run rsync with the exclude/include lists created earlier
cat "$INCLUDE" "$EXCLUDE" | \
- rsync --verbose \
- --archive \
- --delete \
- --delete-excluded \
- --numeric-ids \
+ rsync ${RSYNC_OPTS} \
--exclude-from=- \
- "${RSYNC_OPTS}" \
+ --rsh "${RSYNC_RSH}" \
"${RSYNC_SOURCE}" \
- "${RSYNC_TARGET}" \
- || perror "rsync from '${RSYNC_SOURCE}' to '${RSYNC_TARGET}' failed."
- ## TODO real exit code handling
- pinfo "Cloning '${REMOTE_HOST}' to '${RSYNC_TARGET}' succeeded."
- _STATE='SYNC_DONE'
+ "${RSYNC_TARGET}"
+
+ local -i rsync_ret=$?
+ if [ "x$rsync_ret" != "x0" ]; then
+ perror "rsync from '${RSYNC_SOURCE}' to '${RSYNC_TARGET}' failed."
+ fi
touch "${RSYNC_TARGET}/.stage4"
- return 0
+ # make sure everything gets flushed
+ sync
+ pinfo "Cloning '${REMOTE_HOST}' to '${RSYNC_TARGET}' succeeded."
}
-# helper to build a qcow2 container from a stage4 sync directory
-# Usage:
-# pack_qcow2
-#
-# Note that this requires CONTAINER_PATH to be set.
-# RSYNC_TARGET is either the path given through the option '--syncdir'
-# or the standard local path '$ROOT_DIR/builds/$REMOTE_HOST/stage4'
-# is assumed to be our rsync destination directory.
-pack_qcow2() {
+
+# Helper to create the empty container at CONTAINER_PATH
+create_container() {
# CONTAINER_PATH valid?
- [ -z "$CONTAINER_PATH" ] && \
- pwarning "CONTAINER_PATH not set. Use '--container'." && print_usage
- [ -d "$CONTAINER_PATH" ] && perror "Path to container can not be a directory!"
- if [ -f "$CONTAINER_PATH" ]; then
+ [ -d "${CONTAINER_PATH}" ] && perror "Path to container can not be a directory!"
+ if [ -f "${CONTAINER_PATH}" ]; then
if [ $FORCE -eq 0 ]; then
perror "Container file already exists. Use '--force' to overwrite it."
else
# force removal
- rm -f "$CONTAINER_PATH" || perror "Could not remove '$CONTAINER_PATH'"
- pinfo "Removed old '$CONTAINER_PATH'."
- fi
- fi
-
- # RSYNC_TARGET valid?
- if [ -z "$RSYNC_TARGET" ]; then
- # if not RSYNC_TARGET was specified, we must have REMOTE_HOST
- # or we do not know which stage4 we are supposed to package
- [ -z "$REMOTE_HOST" ] && \
- pwarning "Need either '--syncdir' or '--host'!" && \
- print_usage
- check_host "$REMOTE_HOST" || perror "Given host invalid."
- pwarning "RSYNC_TARGET not set. Assuming local mode."
- local RSYNC_TARGET_CANDIDATE="${ROOT_DIR}/builds/${REMOTE_HOST}/stage4"
- if [ ! -d "$RSYNC_TARGET_CANDIDATE" ]; then
- pwarning "Local stage4 sync not found at '${RSYNC_TARGET_CANDIDATE}'"
- pwarning "Did you sync with '--syncdir' set? Then use that :)"
- perror "Stage4 to package not found."
+ rm -f "${CONTAINER_PATH}" || perror "Could not remove '${CONTAINER_PATH}'"
+ pinfo "Removed old '${CONTAINER_PATH}'."
fi
- is_dir_empty "$RSYNC_TARGET_CANDIDATE" && \
- perror "Appears to be empty, did you clone?"
- pinfo "Found '$RSYNC_TARGET_CANDIDATE', using it."
- declare -rg RSYNC_TARGET="$RSYNC_TARGET_CANDIDATE"
- fi
-
- # more sanity checks
- [ ! -d "$RSYNC_TARGET" ] && perror "'$RSYNC_TARGET' not a directory!"
- is_dir_empty "$RSYNC_TARGET" && \
- perror "'$RSYNC_TARGET' appears to be empty. Did you clone?"
- # the ultimative check
- if [ ! -e "${RSYNC_TARGET}/.stage4" ]; then
- perror "No '.stage4' flag found in '${RSYNC_TARGET}'." \
- "Was this cloned properly?"
fi
# which size for the qcow2 container?
- if [ -z "$DEFAULT_QCOW_SIZE" ]; then
- local QCOW_SIZE="10G"
- else
- local QCOW_SIZE="$DEFAULT_QCOW_SIZE"
- fi
+ local CONTAINER_SIZE="${DEFAULT_CONTAINER_SIZE}"
+ [ -z "${CONTAINER_SIZE}" ] && CONTAINER_SIZE="20G"
+
# so far so good
- pinfo "Creating empty qcow2-container ..."
- _STATE='QCOW_CREATING'
- qemu-img create -f qcow2 "${CONTAINER_PATH}" "${QCOW_SIZE}" \
- || perror "qemu-img create failed with: $?"
- pinfo "Done."
+ pinfo "Creating qcow2-container '${CONTAINER_PATH}'"
+ qemu-img create -f qcow2 "${CONTAINER_PATH}" "${CONTAINER_SIZE}" || \
+ perror "qemu-img create failed with: $?"
+ # now expose it as a loop device
+ expose_container
+ # filesystem for the qcow2 container?
+ local CONTAINER_FILESYSTEM="${DEFAULT_CONTAINER_FILESYSTEM}"
+ [ -z "${CONTAINER_FILESYSTEM}" ] && CONTAINER_FILESYSTEM="xfs"
+ # check if we have that mkfs helper
+ which "mkfs.${CONTAINER_FILESYSTEM}" || \
+ perror "Could not find 'mkfs.${CONTAINER_FILESYSTEM}'. Install it and retry."
+
+ pinfo "Creating '${CONTAINER_FILESYSTEM}' filesystem on '${CONTAINER_PATH}'..."
+ mkfs."${CONTAINER_FILESYSTEM}" "${LOOPED_NBD_DEV}" || perror "mkfs failed with: $?"
+ return 0
+}
+# Helper exposing the container as a loop device
+expose_container() {
+ [ -z "${CONTAINER_PATH}" ] && \
+ perror "Internal error - CONTAINER_PATH not set but should be! Check read_params()"
+
# find usable nbd device
- pinfo "Looking for usable nbd device..."
- local NBD_DEV="$(find_free_nbd)"
- [ -z "${NBD_DEV}" ] && perror "Could not find usable NBD device."
+ declare -rg NBD_DEV="$(find_free_nbd)"
+ [ -n "${NBD_DEV}" ] || perror "Could not find usable NBD device."
[ -b "${NBD_DEV}" ] || perror "'${NBD_DEV}' is not a block device!"
- pinfo "Exporting '${CONTAINER_PATH}' using '${NBD_DEV}'..."
- _STATE='QCOW_NBD_CONNECTING'
+ pinfo "Connecting '${CONTAINER_PATH}' to '${NBD_DEV}'"
+
qemu-nbd -c "${NBD_DEV}" "${CONTAINER_PATH}" || \
perror "qemu-nbd failed with: $?"
- pinfo "Done."
+ add_cleanup disconnect_nbd
+ # expose as a loop device
+ declare -rg LOOPED_NBD_DEV="$(losetup --find)"
+ losetup "${LOOPED_NBD_DEV}" "${NBD_DEV}" || \
+ perror "Loop device setup for '${NBD_DEV}' failed with: $?"
+ add_cleanup disconnect_loop
+ return 0
+}
- # which filesystem for the qcow2 container?
- if [ -z "$DEFAULT_QCOW_FS" ]; then
- local QCOW_FS="ext4"
- else
- # check if we have mkfs helper
- which "mkfs.$DEFAULT_QCOW_FS" &>/dev/null || \
- perror "Could not find 'mkfs.$DEFAULT_QCOW_FS'."
- local QCOW_FS="$DEFAULT_QCOW_FS"
- fi
- pinfo "Creating '${QCOW_FS}' filesystem on '${CONTAINER_PATH}'..."
- _STATE='QCOW_FSING'
- mkfs."${QCOW_FS}" "${NBD_DEV}" || perror "mkfs failed with: $?"
- pinfo "Done."
+# Helper to mount CONTAINER_PATH to CONTAINER_MNT through expose_container
+mount_container() {
+ [ -z "${CONTAINER_MNT}" ] && \
+ perror "Internal error - CONTAINER_MNT not set but should be! Check read_params()"
+ # connect container to a loop device first, if it wasnt already done
+ [ -z "${LOOPED_NBD_DEV}" ] && expose_container
+ # lets be safe...
+ [ -z "${LOOPED_NBD_DEV}" ] && \
+ perror "Internal error - LOOPED_NBD_DEV not set but should be! Check expose_container()"
- # prepare NBD mount directory and check state to be safe
- local NBD_MNT="$(mktemp -d)"
- [ ! -d "${NBD_MNT}" ] && \
- perror "Making temporary dir for mounting '$NBD_DEV' failed."
- is_dir_empty ${NBD_MNT} || \
- perror "'${NBD_MNT}' not empty. Refusing to mount ${NBD_DEV} to it."
-
- pinfo "Mounting '${NBD_DEV}' to '${NBD_MNT}'..."
- _STATE='QCOW_MOUNTING'
- mount "${NBD_DEV}" "${NBD_MNT}" || perror "Mount failed with: $?"
- pinfo "Done."
+ # now we got everything, mount it
+ pinfo "Mounting '${LOOPED_NBD_DEV}' to '${CONTAINER_MNT}'..."
+ mount "${LOOPED_NBD_DEV}" "${CONTAINER_MNT}" \
+ || perror "Mount failed with: $?"
+ add_cleanup umount_container
+ return 0
+}
+# helper to copy the content of RSYNC_TARGET to CONTAINER_MNT
+copy_to_container() {
+ [ -z "${RSYNC_TARGET}" ] && \
+ perror "Internal error - RSYNC_TARGET not set but should be!"
+ [ -z "${CONTAINER_MNT}" ] && \
+ perror "Internal error - CONTAINER_MNT not set but should be!"
+ # sanity checks
+ is_dir_empty "$RSYNC_TARGET" && \
+ perror "'$RSYNC_TARGET' appears to be empty. Did you clone?"
+ # check for '.stage4' flag in the directory, indicating we cloned there
+ if [ ! -e "${RSYNC_TARGET}/.stage4" ]; then
+ perror "No '.stage4' flag found in '${RSYNC_TARGET}'." \
+ "Was this cloned properly?"
+ fi
# copy files from the stage4 directory to the mounted qcow2-container
- pinfo "Copying '${RSYNC_TARGET}' to '${NBD_MNT}'..."
- _STATE='QCOW_COPYING'
- cp -ra "${RSYNC_TARGET}"/* "${NBD_MNT}" || perror "Copying failed with: $?"
- pinfo "Done."
-
- pinfo "Cleaning up..."
- _STATE='QCOW_CLEANING'
- umount "${NBD_MNT}" || pwarning "Could not unmount '${NBD_MNT}'."
- rmdir "${NBD_MNT}" || pwarning "Could not remove '${NBD_MNT}'."
- qemu-nbd -d "${NBD_DEV}" || pwarning "Could not disconnect '${NBD_DEV}'."
- _STATE='QCOW_DONE'
- pinfo "Exporting '${RSYNC_TARGET}' to '${CONTAINER_PATH}' completed."
+ pinfo "Copying '${RSYNC_TARGET}' to '${CONTAINER_MNT}'..."
+ rsync -avAHX "${RSYNC_TARGET}"/ "${CONTAINER_MNT}"/ \
+ || perror "Rsync failed with: $?"
+ #make sure everything is flushed
+ sync && return 0
}
+
+###############################################################################
+#
+#
+#
+###############################################################################
# helper to find an unused nbd device
# Usage:
# find_free_nbd
@@ -503,3 +434,110 @@ find_free_nbd() {
echo ""
}
+# helper to validate an ip
+# Usage:
+# valid_ip <ip>
+# Returns 0 if valid, 1 otherwise.
+valid_ip() {
+ local ip=$1
+ local stat=1
+
+ if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
+ OIFS=$IFS
+ IFS='.'
+ ip=($ip)
+ IFS=$OIFS
+ [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
+ && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
+ stat=$?
+ fi
+ return $stat
+}
+
+# helper to check whether a given host is valid
+# Usage:
+# check_host <hostname|ip>
+# Returns 0 if valid, 1 otherwise
+check_host() {
+ local HOST="$1"
+ [ -z "$HOST" ] && return 1
+ # check if its a valid IP or a valid hostname
+ valid_ip "$HOST" && return 0
+ host -W 2 "$HOST" && return 0
+ # still here? then fail
+ return 1
+}
+
+# helper to check if a dir is empty or not
+# Usage:
+# dir_empty <dir>
+# Returns 0 if empty, 1 otherwise
+is_dir_empty() {
+ [ $# -ne 1 ] && perror "$0 requires directory as paramter, none given."
+ local _dir="$1"
+ [ -d "$_dir" ] || return 1
+ [ -n "$(ls -A $_dir)" ] && return 1 || return 0
+}
+
+# helper to ask user for confirmation
+# Usage:
+# user_confirm
+# Return 0 if confirmed, 1 otherwise
+user_confirm() {
+ [ $# -ne 1 ] && perror "$0 requires the question as first argument."
+ pinfo "$1 [Y/n]"
+ local _input
+ read _input
+ [ "x${_input}" == "x" -o "x${_input}" == "xy" ] && return 0 || return 1
+}
+
+###############################################################################
+#
+# CLEANUP FUNCTIONS
+#
+###############################################################################
+# indexed array for cleanup commands
+declare -ag cleanup_commands
+
+# function to add a cleanup command to be executed when the program exits
+add_cleanup() {
+ # get current command count
+ local -i count="${#cleanup_commands[*]}"
+ cleanup_commands[$count]="$*"
+}
+
+# function trapped to EXIT, SIGINT, SIGTERM
+# do the cleanup in FILO style
+do_cleanup() {
+ trap '' SIGINT SIGTERM EXIT # from now on, ignore INT and TERM
+ for i in $(seq $(( ${#cleanup_commands[*]} - 1 )) -1 0); do
+ eval ${cleanup_commands[$i]}
+ done
+}
+
+# Helper to umount + disconnect the container from all the devices
+umount_container() {
+ [ -z "${CONTAINER_MNT}" ] && \
+ perror "CONTAINER_MNT not set - is it really mounted?"
+ # sync?
+ umount -l "${CONTAINER_MNT}" || \
+ perror "Failed to umount '${CONTAINER_MNT}'."
+# rmdir "${CONTAINER_MNT}" || \
+# pwarning "Could not remove '${CONTAINER_MNT}'."
+}
+
+# Helper to disconnect from loop device
+disconnect_loop() {
+ [ -z "${LOOPED_NBD_DEV}" ] && \
+ perror "Container not connected to a loop device?"
+ losetup -d "${LOOPED_NBD_DEV}" ||\
+ perror "Could not disconnect loop device '${LOOPED_NBD_DEV}'."
+}
+# Helper to disconnect from nbd device
+disconnect_nbd() {
+ [ -z "${NBD_DEV}" ] && \
+ perror "Container does not seem to be connected to a NBD device?"
+ qemu-nbd -d "${NBD_DEV}" || \
+ perror "Could not disconnect '${CONTAINER_PATH}' from '${NBD_DEV}'."
+}
+