From b41e83bb923f0cfdd414255270bd51f75b17c3fd Mon Sep 17 00:00:00 2001 From: Jonathan Bauer Date: Fri, 8 Apr 2016 17:35:15 +0200 Subject: more stuff for packaging new "--export" & "--update", see help --- packager/.gitignore | 2 +- packager/openslx | 15 +- packager/openslx.functions | 592 ++++++++++++++++++++++++--------------------- 3 files changed, 318 insertions(+), 291 deletions(-) (limited to 'packager') 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 " - 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 [--syncdir ]" - pinfo "\t\tIf not specified, --syncdir = './builds//stage4'" + pinfo "\t\tIf not specified, --syncdir = './clones//stage4'" pinfo "" - pinfo "PACKAGING:" - pinfo "$ARG0 --package --syncdir --container " - pinfo "$ARG0 --package --host --container " - pinfo "\t\tIf is specified, --syncdir = './builds//stage4'" - pinfo "$ARG0 --package --host --syncdir --container " - pinfo "\t\tIf both are specified, --syncdir is used." + pinfo "PACKAGING: pack local rsync directory as qcow2-container" + pinfo "$ARG0 --package --host [--container ] [--syncdir ]" + pinfo "\t\tIf is not specified, --container = './clones//stage4.qcow2'" + pinfo "\t\tIf is not specified, --syncdir = './clones//stage4'" + pinfo "" + pinfo "EXPORTING: rsync remote host to a new qcow2-container" + pinfo "$ARG0 --export --host [--container ] [--syncdir ]" + pinfo "\t\tIf is not specified, --container = './clones//stage4.qcow2'" + pinfo "" + pinfo "UPDATING: rsync remote host to an existing qcow2-container" + pinfo "$ARG0 --export --host [--container ]" + pinfo "\t\tIf is not specified, --container = './clones//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 -# 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 -# 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 -# 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 +# 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 +# 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 +# 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 +} + + -- cgit v1.2.3-55-g7522