summaryrefslogtreecommitdiffstats
path: root/packager
diff options
context:
space:
mode:
authorJonathan Bauer2016-04-08 17:35:15 +0200
committerJonathan Bauer2016-04-08 17:35:15 +0200
commitb41e83bb923f0cfdd414255270bd51f75b17c3fd (patch)
tree55ec6c4b29c4113d003622a5bf33e3c280e5883a /packager
parent[conf-tgz] new dracut module for config tgz stuff [wip] (diff)
downloadsystemd-init-b41e83bb923f0cfdd414255270bd51f75b17c3fd.tar.gz
systemd-init-b41e83bb923f0cfdd414255270bd51f75b17c3fd.tar.xz
systemd-init-b41e83bb923f0cfdd414255270bd51f75b17c3fd.zip
more stuff for packaging
new "--export" & "--update", see help
Diffstat (limited to 'packager')
-rw-r--r--packager/.gitignore2
-rwxr-xr-xpackager/openslx15
-rw-r--r--packager/openslx.functions592
3 files changed, 318 insertions, 291 deletions
diff --git a/packager/.gitignore b/packager/.gitignore
index b205ba3d..ecb2b900 100644
--- a/packager/.gitignore
+++ b/packager/.gitignore
@@ -1 +1 @@
-builds/*
+clones/*
diff --git a/packager/openslx b/packager/openslx
index 9613196b..69102564 100755
--- a/packager/openslx
+++ b/packager/openslx
@@ -31,12 +31,6 @@ 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
@@ -44,12 +38,5 @@ trap cleanexit SIGINT SIGTERM
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
diff --git a/packager/openslx.functions b/packager/openslx.functions
index 3b5c4394..e38f88c5 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 --export --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,74 @@ 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."
+ # 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
-
- 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
-}
+ fi
+ fi
-# 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
+ # 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 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
+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
}
-
# ------------------------------------------------------------------------------
#
# Stage4 related functions
@@ -290,29 +222,10 @@ 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?
@@ -321,12 +234,9 @@ clone_stage4() {
"Refusing to rsync there."
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,7 +249,6 @@ clone_stage4() {
grep '^-' "$FILE" >> "$EXCLUDE"
grep '^+' "$FILE" >> "$INCLUDE"
done
- pinfo "Done."
# prepare rsync's options
if [ -z "$DEFAULT_RSYNC_OPTS" ]; then
@@ -367,22 +276,14 @@ clone_stage4() {
|| 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'
touch "${RSYNC_TARGET}/.stage4"
+ _STATE='SYNCED'
return 0
}
-# 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
if [ $FORCE -eq 0 ]; then
@@ -394,37 +295,6 @@ pack_qcow2() {
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."
- 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="25G"
@@ -432,70 +302,182 @@ pack_qcow2() {
local QCOW_SIZE="$DEFAULT_QCOW_SIZE"
fi
# 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."
-
- # 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."
- [ -b "${NBD_DEV}" ] || perror "'${NBD_DEV}' is not a block device!"
- pinfo "Exporting '${CONTAINER_PATH}' using '${NBD_DEV}'..."
- _STATE='QCOW_NBD_CONNECTING'
- qemu-nbd -c "${NBD_DEV}" "${CONTAINER_PATH}" || \
- perror "qemu-nbd failed with: $?"
- pinfo "Done."
-
- # expose as a loop device
- local NBD_LOOPED_DEV="$(losetup --find)"
- losetup "${NBD_LOOPED_DEV}" "${NBD_DEV}"
-
- # which filesystem for the qcow2 container?
+ pinfo "Creating qcow2-container '${CONTAINER_PATH}'"
+ qemu-img create -f qcow2 "${CONTAINER_PATH}" "${QCOW_SIZE}" || \
+ perror "qemu-img create failed with: $?"
+ # now expose it as a loop device
+ expose_container
+ _STATE='QCOW_EXPOSED'
+ # 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'."
+ perror "Could not find 'mkfs.$DEFAULT_QCOW_FS'. Install it and retry."
local QCOW_FS="$DEFAULT_QCOW_FS"
fi
pinfo "Creating '${QCOW_FS}' filesystem on '${CONTAINER_PATH}'..."
- _STATE='QCOW_FSING'
- mkfs."${QCOW_FS}" "${NBD_LOOPED_DEV}" || perror "mkfs failed with: $?"
- pinfo "Done."
+ mkfs."${QCOW_FS}" "${LOOPED_NBD_DEV}" || perror "mkfs failed with: $?"
+}
+# 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()"
- # 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_LOOPED_DEV}" "${NBD_MNT}" || perror "Mount failed with: $?"
- pinfo "Done."
+ # 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 "Connecting '${CONTAINER_PATH}' to '${NBD_DEV}'"
+ qemu-nbd -c "${NBD_DEV}" "${CONTAINER_PATH}" || \
+ perror "qemu-nbd failed with: $?"
+ _STATE='QCOW_EXPOSED_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: $?"
+}
+
+# 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()"
+
+ # now we got everything, mount it
+ pinfo "Mounting '${LOOPED_NBD_DEV}' to '${CONTAINER_MNT}'..."
+ mount "${LOOPED_NBD_DEV}" "${CONTAINER_MNT}" || perror "Mount failed with: $?"
+ _STATE='QCOW_MOUNTED'
+}
+
+# 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'
- rsync -avAHX "${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}'."
- losetup -d "${NBD_LOOPED_DEV}" || pwarning "Could not disconnect '${NBD_LOOPED_DEV}'."
- 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: $?"
+}
+
+# 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
+ umount_container
+}
+# wrapper to update an existing container
+# - mounts it to RSYNC_TARGET
+# - clone host there
+update_container() {
+ mount_container
+ clone_host
+ umount_container
+}
+# 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
+ umount_container
+}
+
+# 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 ..."
+
+ case "${_STATE}" in
+ 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
+ break;
+ ;;
+ QCOW_MOUNTED)
+ # container mounted
+ [ -z "${CONTAINER_PATH}" ] && \
+ perror "CONTAINER_PATH not set - should not be!"
+ [ -z "${CONTAINER_MNT}" ] && \
+ perror "CONTAINER_MNT not set - should not be!"
+ umount_container || \
+ perror "Could not umount '${CONTAINER_MNT}'."
+ ;;
+ QCOW_MOUNTED|QCOW_EXPOSED)
+ disconnect_container
+ break
+ ;;
+ # internal expose_container state
+ QCOW_EXPOSED_NBD)
+ disconnect_nbd
+ break
+ ;;
+ *)
+ [ -n "${_STATE}" ] && pwarning "Unknown state: ${_STATE}"
+ ;;
+ esac
+ # still here? then we ran into some error
+ exit 1
+}
+# Helper to umount + disconnect the container from all the devices
+umount_container() {
+ [ -z "${CONTAINER_MNT}" ] && \
+ perror "CONTAINER_MNT not set - is it really mounted?"
+ umount "${CONTAINER_MNT}" || \
+ perror "Failed to umount '${CONTAINER_MNT}'."
+ rmdir "${CONTAINER_MNT}" || \
+ pwarning "Could not remove '${CONTAINER_MNT}'."
+}
+
+# Wrapper to disconnect the container from all the devices
+disconnect_container() {
+ disconnect_loop
+ disconnect_nbd
}
+# 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}'."
+}
# helper to find an unused nbd device
# Usage:
# find_free_nbd
@@ -511,3 +493,61 @@ 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
+}
+
+