From 3061412bc943e0e28d059e24eb28f9f08d0afae2 Mon Sep 17 00:00:00 2001 From: Jesse Gardner Date: Fri, 16 Jul 2021 15:19:25 -0700 Subject: [PATCH] Line length (CPL) bumped to 80 --- qq2clone | 922 ++++++++++----------- qq2clone_installer.bash | 1691 +++++++++++++++++++-------------------- qq2clone_noarchive | 498 ++++++------ 3 files changed, 1555 insertions(+), 1556 deletions(-) diff --git a/qq2clone b/qq2clone index d1167d4..438ce7f 100755 --- a/qq2clone +++ b/qq2clone @@ -55,13 +55,13 @@ NOMATCH="# No matching domain" #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# #---------------------------------------------------# -#=========================================================================# +#==============================================================================# check_pipe () # DESCRIPTION: See if pipe is open # INPUT: None # OUTPUT: Return value # PARAMETERS: None -#=========================================================================# +#==============================================================================# { local type type="$(file -bL /proc/self/fd/3)" @@ -70,13 +70,13 @@ if [[ ! "$type" =~ fifo ]]; then fi return 0 } -#=========================================================================# +#==============================================================================# open_pipe () # DESCRIPTION: Open a named pipe in read/write mode on fd3 # INPUT: None # OUTPUT: None # PARAMETERS: None -#=========================================================================# +#==============================================================================# { check_pipe && return local fifo_path @@ -87,7 +87,7 @@ mkfifo "$fifo_path/fifo" || fifo_error exec 3<>"$fifo_path/fifo" return 0 } -#=========================================================================# +#==============================================================================# read_pipe () # DESCRIPTION: Flushes the contents of the named pipe to stdout, # nonblocking @@ -95,7 +95,7 @@ read_pipe () # OUTPUT: Contents of named pipe on fd3 # PARAMETERS: $1: (Optional) If 1, read data out but also write it back # into the pipe -#=========================================================================# +#==============================================================================# { # Note: This implementation allows for things like this to work: # tr "a" "b" < <(read_pipe) | write_pipe 1 @@ -114,7 +114,7 @@ while IFS= read -r line <&3; do done return 0 } -#=========================================================================# +#==============================================================================# write_pipe () # DESCRIPTION: Write information to the named pipe, nonblocking unless it # is told to look for input on stdin and nothing is sent there. Works in @@ -125,14 +125,14 @@ write_pipe () # PARAMETERS: $1: '0' if passing another parameter(s), '1' if writing to # stdin instead. # $2 and on: If $1 is 0, write_pipe will write the remaining parameters -#=========================================================================# +#==============================================================================# { # + is put at the beginning of every line echoed to the pipe, so that # read_pipe can operate in a non-blocking manner local line { [[ "$1" == "0" ]] || [[ "$1" == "1" ]]; } || unexpected_error write_pipe if (($1)); then - while IFS= read -r line; do + while IFS= read -r line; do echo "+$line" >&3 done else @@ -156,34 +156,34 @@ return 0 # closing lv_api_do into something that must be managed manually by the # coder -#=========================================================================# +#==============================================================================# lv_api_do_check () # DESCRIPTION: See if lv_api_do is present in the expected location. If # not, put it there # INPUT: None # OUTPUT: None # PARAMETERS: None -#=========================================================================# +#==============================================================================# { local loc="/run/user/${UID}" [[ -e "${loc}/lv_api_do" ]] && return cd "$loc" || unexpected_error lv_api_do_check echo "$archive" | base64 -d | tar -zx lv_api_do } -#=========================================================================# +#==============================================================================# lv_api_do_close () # DESCRIPTION: Tell lv_api_do to exit and close the extra pipe # INPUT: None # OUTPUT: None # PARAMETERS: None -#=========================================================================# +#==============================================================================# { echo "exit" >&4 exec 4>&- 4<&- rm -rf "${lv_api_temp:?}" return 0 } -#=========================================================================# +#==============================================================================# lv_api_do_comm () # DESCRIPTION: Issue a command to lv_api_do # INPUT: The command @@ -191,7 +191,7 @@ lv_api_do_comm () # with read_pipe. Exit and error message if lv_api_do encounters # a fatal error # PARAMETERS: $@: command string to lv_api_do -#=========================================================================# +#==============================================================================# { # Ensure lv_api_do is open ( : >&4 ; ) &>/dev/null || unexpected_error lv_api_do_comm @@ -216,13 +216,13 @@ done return 0 } -#=========================================================================# +#==============================================================================# lv_api_do_open () # DESCRIPTION: Open lv_api_do in background # INPUT: None # OUTPUT: Return 0 on success, exit on failure # PARAMETERS: None -#=========================================================================# +#==============================================================================# { declare -g lv_api_temp; lv_api_temp="$(mktemp -d )" || temp_error @@ -244,7 +244,7 @@ return 0 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# #-------------------------------------------# -#=========================================================================# +#==============================================================================# check_config () #= DESCRIPTION: Given a name or name/value pair, check if it is a #= valid configuration option @@ -252,7 +252,7 @@ check_config () #= OUTPUT: Return 1 if the name is not a valid config option or if the #= given value does is not valid for the given name. Return 0 else #= PARAMETERS: $1: Name of config option, $2: (optional) value of option -#=========================================================================# +#==============================================================================# { (($#)) || unexpected_error check_config declare -A def_opt @@ -265,7 +265,7 @@ def_opt[S_TIMEOUT]="^[0-9]+$" def_opt[NORUN]="^[01]$" def_opt[STORAGE]="^/.*" -(( $# == 1 )) && +(( $# == 1 )) && { [[ " ${!def_opt[*]} " =~ [[:space:]]${1}[[:space:]] ]]; return $?; } @@ -275,13 +275,13 @@ local patt="${def_opt["${1}"]}" return 0 } -#=========================================================================# +#==============================================================================# check_depends () # DESCRIPTION: Check that required software is present # INPUT: None # OUTPUT: Return 0 on success or exits with descriptive message on failure # PARAMETERS: None -#=========================================================================# +#==============================================================================# { local elem missing=0 @@ -314,13 +314,13 @@ done return 0 } -#=========================================================================# +#==============================================================================# disp_conf_names () # DESCRIPTION: Display the name and value of all configuration options # INPUT: None # OUTPUT: Echoes config name="value" pairs # PARAMETERS: None -#=========================================================================# +#==============================================================================# { local name value while read -r name; do @@ -329,13 +329,13 @@ while read -r name; do done < <(sqlite3 "select name,value from CONFIG") return 0 } -#=========================================================================# +#==============================================================================# disp_conf_desc () # DESCRIPTION: Display the description of a config option to the user # INPUT: The name of the option # OUTPUT: Echoes relevant lines of information # PARAMETERS: $1: The config option name -#=========================================================================# +#==============================================================================# { if [[ "$1" == "TEMPLATE_DIR" ]]; then echo "This is the where template XML files will be kept" @@ -358,7 +358,7 @@ elif [[ "$1" == "SPICY" ]]; then elif [[ "$1" == "USE_SPICE" ]]; then echo "If set to 1, attempt to connect to the spice graphics of a virtual" echo "machine by default when cloning it, if it is configured to use" - echo "spice graphics. qq2clone can do this using the programs spicy and" + echo "spice graphics. qq2clone can do this using the programs spicy and" echo "virt-viewer. If either is installed on your system during the" echo "first run, the default value is '1' (enabled). Otherwise, the" echo "default value is '0'" @@ -377,20 +377,20 @@ elif [[ "$1" == "STORAGE" ]]; then echo "that whatever location you do choose is only used by qq2clone" echo echo "Default value: '${QQ2_DIR}/qq2clone-pool'" -else +else echo "No such configuration option '$1'" return 1 fi return 0 } -#=========================================================================# +#==============================================================================# first_run_setup () # DESCRIPTION: Generate a new database with default config values, # create subdirectories of QQ2_DIR # INPUT: None # OUTPUT: None # PARAMETERS: None -#=========================================================================# +#==============================================================================# { make_dir "${HOME}/.config" @@ -410,7 +410,7 @@ make_dir "$POOL_DIR" check_rw -r "$QQ2_DIR" local use_spice spicy -if command -v virt-viewer &>/dev/null; then +if command -v virt-viewer &>/dev/null; then use_spice=1 spicy=0 elif command -v spicy &>/dev/null; then @@ -452,13 +452,13 @@ echo "Setup complete" return 0 } -#=========================================================================# +#==============================================================================# get_config () # DESCRIPTION: Load configuration from database # INPUT: None # OUTPUT: Silent except on error # PARAMETERS: None -#=========================================================================# +#==============================================================================# { declare -g QQ2_DIR QQ2_DIR="$(<"${HOME}/.config/qq2clone")" @@ -482,14 +482,14 @@ for elem in "${opts[@]}"; do done OPT[COPY_DISKS]=0 # Hardcoded default, overriden with --copy-disks/-C } -#=========================================================================# +#==============================================================================# write_config () # DESCRIPTION: Write an option name and value pair to config table. # Checks that the option name and value are good. # INPUT: Name and value of configuration option # OUTPUT: Return 0 on success, 1 on bad option, 2 on bad value # PARAMETERS: $1: Name of variable, $2: Value of variable -#=========================================================================# +#==============================================================================# { check_config "$1" || return 1 check_config "$1" "$2" || return 2 @@ -505,13 +505,13 @@ return 0 #@@@@@@@@@@@@@@@@@@@@@@@# #-----------------------# -#=========================================================================# +#==============================================================================# usage () # DESCRIPTION: Output basic usage information # INPUT: None # OUTPUT: Echo information to stdout to be read by the user # PARAMETERS: None -#=========================================================================# +#==============================================================================# { (( OPT[QUIET] )) && return 0 echo "qq2clone: quick qcow2 clone" @@ -522,7 +522,7 @@ echo " images with backing files" echo echo "Usage:" echo " qq2clone [OPTION]... [COMMAND] [ARGUMENT]..." -echo +echo echo ' options: --connection/-c (URI) --no-spice/-f --help/-h' echo ' --no-run/-n --quiet/-q --quieter/-Q --run/-r --spicy/-S' echo ' --storage/-s (filepath/pool-name) --template/-t (name)' @@ -545,33 +545,33 @@ return 0 #-----------------------------------# -#=========================================================================# +#==============================================================================# prompt_num () # DESCRIPTION: Prompt user for a number between $1 and $2 # INPUT: Inclusive endpoints of accepted interval, where the right hand # endpoint is less than 10 # OUTPUT: Echo choice when proper input is received # PARAMETERS: $1: LH of interval, $2: RH of interval -#=========================================================================# +#==============================================================================# { { (( $1 > -1 )) && (( $1 < $2 )) && (( $2 < 10 )) ; } || \ unexpected_error prompt_num local n read -rsn 1 n -{ [[ "$n" =~ ^[0-9]$ ]] && (($1 <= n)) && ((n <= $2)); } || +{ [[ "$n" =~ ^[0-9]$ ]] && (($1 <= n)) && ((n <= $2)); } || { echo "Enter a number from $1 to $2" >&2; prompt_num "$1" "$2"; return 0; } echo "$n" return 0 } -#=========================================================================# +#==============================================================================# prompt_yes_abort () # DESCRIPTION: Prompt user to enter y, or any other key to abort # INPUT: A keystroke # OUTPUT: Prompts for input, returns 0 for Y/y and 1 for else # PARAMETERS: $1, $2: (Optional) override disp with $1 and patt with $2 -#=========================================================================# +#==============================================================================# { local disp="Press (y) to accept, anthing else to abort" patt="^[Yy]$" [[ -n "$1" ]] && disp="$1" @@ -583,7 +583,7 @@ echo [[ "$char" =~ $patt ]] && return 0 return 1 } -#=========================================================================# +#==============================================================================# prompt_yes_no () # DESCRIPTION: Prompt user to enter y or n, repeatedly until they do # INPUT: Keystrokes @@ -608,25 +608,25 @@ return 0 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# #-----------------------------# -#=========================================================================# +#==============================================================================# do_virt_xml () # DESCRIPTION: Run a given virt-xml command, reading from and writing to # the named pipe -# INPUT: Parameters to send to virt-xml +# INPUT: Parameters to send to virt-xml # OUTPUT: Nothing except on error # PARAMETERS: $@: Passed to virt-xml -#=========================================================================# +#==============================================================================# { local xml xml="$(virt-xml "$@" <<<"$(read_pipe)" 2>/dev/null)" || - { echo "Attempt to generate xml with virt-xml failed." + { echo "Attempt to generate xml with virt-xml failed." echo "This is probably a bug in qq2clone" exit "$E_unexpected" ; } >&2 write_pipe 1 <<<"$xml" return 0 } -#=========================================================================# +#==============================================================================# find_tag () # DESCRIPTION: Use xmllint to do an xpath search of xml and write_pipe # all matches @@ -634,7 +634,7 @@ find_tag () # OUTPUT: Write one match per line # PARAMETERS: $1: Xpath, $2: XML location, or leave blank to read from # stdin -#=========================================================================# +#==============================================================================# { if [[ -n "$2" ]]; then write_pipe 1 <"$2" @@ -653,14 +653,14 @@ xmllint --noblanks --dropdtd --nowarning --xpath "$1" \ return 0 } -#=========================================================================# +#==============================================================================# get_attr_value () # DESCRIPTION: Given an attribute="value" pair, echo the value # INPUT: Attribute value pair # OUTPUT: Value, or unexpected_error if input doesn't match pattern (do not # rely on this function for checking that a string is a name=value pair # PARAMETERS: $1: attribute="value" -#=========================================================================# +#==============================================================================# { p="$(strip_ws "$1")" [[ "$p" =~ ^[^\=]+\=[[:space:]]*[^[:space:]](.*).$ ]] && @@ -668,13 +668,13 @@ p="$(strip_ws "$1")" unexpected_error get_attr_value return 0 } -#=========================================================================# +#==============================================================================# get_disk_devices () # DESCRIPTION: Find all disk device file locations from an XML file # INPUT: libvirt domain XML file on stdin # OUTPUT: writepipe each file location # PARAMETERS: None -#=========================================================================# +#==============================================================================# { find_tag '//devices/disk[@type="file"][@device="disk"]/source/@file' local line val @@ -684,13 +684,13 @@ while read -r line; do done < <(read_pipe) return 0 } -#=========================================================================# +#==============================================================================# get_disk_devices_db () # DESCRIPTION: Like get_disk_devices, but get info from the database # INPUT: Machine number, or omit to get template info # OUTPUT: writepipe each file location # PARAMETERS: $1: (Optional) machine number -#=========================================================================# +#==============================================================================# { local query disk if (($#)); then @@ -704,13 +704,13 @@ while read -r disk; do done < <(sqlite3 "$query") return 0 } -#=========================================================================# +#==============================================================================# has_spice () # DESCRIPTION: Check whether a machine supports spice graphics # INPUT: A machine number # OUTPUT: Returns 0 if yes and 1 if no # PARAMETERS: $1: A machine number -#=========================================================================# +#==============================================================================# { local uuid uuid="${CL_MAP["$1"]}" @@ -729,13 +729,13 @@ return 1 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# #--------------------------------# -#=========================================================================# +#==============================================================================# copy_file () # DESCRIPTION: Copy file $1 to $2 and give error messages, exit on error # INPUT: File to copy # OUTPUT: Error messages and exit codes as needed # PARAMETERS: $1: File to copy, $2: Location to copy to -#=========================================================================# +#==============================================================================# { (($# == 2)) || unexpected_error copy_file check_rw "$1" "$(dirname "$2")" @@ -743,13 +743,13 @@ check_rw "$1" "$(dirname "$2")" cp -fR "$1" "$2" &>/dev/null || unexpected_error copy_file return 0 } -#=========================================================================# +#==============================================================================# get_md5 () # DESCRIPTION: Get md5sum of a file without the trailing filename # INPUT: A filepath # OUTPUT: The md5sum # PARAMETERS: $1: Filepath -#=========================================================================# +#==============================================================================# { local md5 check_rw "$1" || unexpected_error get_md5 @@ -758,14 +758,14 @@ md5="$(md5sum "$1")" echo "${BASH_REMATCH[1]}" return 0 } -#=========================================================================# +#==============================================================================# make_dir () -# DESCRIPTION: Make a directory at given location or exit with error +# DESCRIPTION: Make a directory at given location or exit with error # message # INPUT: Filepath # OUTPUT: Error messages and exit code as needed # PARAMETERS: $1: Filepath of directory to make -#=========================================================================# +#==============================================================================# { (($# == 1)) || unexpected_error make_dir if [[ -e "$1" ]]; then @@ -781,13 +781,13 @@ mkdir -p "$1" &>/dev/null check_rw "$1" return 0 } -#=========================================================================# +#==============================================================================# move_file () # DESCRIPTION: Move file $1 to $2 or give error messages, exit on error # INPUT: File to move, new location # OUTPUT: Error messages and exit codes as needed # PARAMETERS: $1: File to move, $2: Location to move to -#=========================================================================# +#==============================================================================# { (($# == 2)) || unexpected_error move_file check_rw "$1" "$(dirname "$2")" @@ -798,13 +798,13 @@ fi mv -f "$1" "$2" &>/dev/null || unexpected_error move_file return 0 } -#=========================================================================# +#==============================================================================# write_file () # DESCRIPTION: Write contents of named pipe to file or error and exit # INPUT: Filepath as parameter, content via write_pipe # OUTPUT: Error messages and exit codes as needed # PARAMETERS: $1: Filepath -#=========================================================================# +#==============================================================================# { (($# == 1)) || unexpected_error write_file touch "$1" @@ -813,7 +813,7 @@ check_rw "$1" local temp1 temp2 temp1="$(mktemp)" || temp_error temp2="$(mktemp)" || { rm -f "$temp1" &>/dev/null; temp_error; } -cp "$1" "$temp1" || +cp "$1" "$temp1" || { rm -f "$temp1" "$temp2" &>/dev/null; unexpected_error write_file; } read_pipe > "$temp2" || { rm -f "$temp1" "$temp2" &>/dev/null; unexpected_error write_file; } @@ -822,14 +822,14 @@ mv -f "$temp2" "$1" &> /dev/null || rm -f "$temp1" "$temp2" &>/dev/null; unexpected_error write_file; } return 0 } -#=========================================================================# +#==============================================================================# sqlite3 () # DESCRIPTION: Pass arguments to sqlite3 binary, prepending basic # parameters that are always used # INPUT: Arguments to sqlite3 # OUTPUT: Dependent on sqlite3 # PARAMETERS: Arbitrary -#=========================================================================# +#==============================================================================# { $(unset sqlite3; command -v sqlite3) --batch --separator $'\n' \ "${QQ2_DIR}/qq2clone.db"\ @@ -842,13 +842,13 @@ $(unset sqlite3; command -v sqlite3) --batch --separator $'\n' \ #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# #-----------------------------# -#=========================================================================# +#==============================================================================# commit_image () # DESCRIPTION: Commit changes from staging image(s) to template image(s) # INPUT: Parameters to calling function # OUTPUT: Status updates, prompts, error messages # PARAMETERS: $@ from calling function -#=========================================================================# +#==============================================================================# { if (( ${#CL_MAP[@]} + ${#BAD_CL[@]} > 1)); then echo "Cannot commit image while there are clones. Aborting." >&2 @@ -871,7 +871,7 @@ while read -r disk; do echo " $disk" echo echo "If changes are committed, these clones may become" - echo "corrupted. To avoid this, retrieve any information you" + echo "corrupted. To avoid this, retrieve any information you" echo "need from these clones and then delete them. Aborting." exit "$E_args" fi >&2 @@ -905,15 +905,15 @@ delete_machine 0 0 return 0 } -#=========================================================================# +#==============================================================================# get_template_name () -# DESCRIPTION: Helper for exec_com_import_template. write_pipes the +# DESCRIPTION: Helper for exec_com_import_template. write_pipes the # original name from the xml, then the new one # INPUT: XML is write_piped, and argument to exec_com_import_template # giving the name to import template with is optionally provided # OUTPUT: See description. Error messages if name is bad # PARAMETERS: $1: (optional) Template name, overrides value from XML -#=========================================================================# +#==============================================================================# { local name char name="$(strip_ws "$1")" @@ -942,15 +942,15 @@ fi write_pipe 0 "$name" return 0 } -#=========================================================================# +#==============================================================================# import_get_xml () # DESCRIPTION: Determine if argument to exec_com_import_template is a -# libvirt domain on the current connection or a filepath, check that it +# libvirt domain on the current connection or a filepath, check that it # is valid, and write_pipe the xml # INPUT: argument designating template XML or domain name/uuid # OUTPUT: Error messages as needed, write_pipes XML on success # PARAMETERS: $1: $1 from calling funcion -#=========================================================================# +#==============================================================================# { if [[ "$1" =~ ^/ ]]; then { [[ -e "$1" ]] && [[ -r "$1" ]] ; } || @@ -984,13 +984,13 @@ else fi return 0 } -#=========================================================================# +#==============================================================================# rename_template () # DESCRIPTION: Change template name, and all of its clone names # INPUT: A current template name, and a new one # OUTPUT: Status updates, error messages # PARAMETERS: $@ from calling function exec_com_modify_template -#=========================================================================# +#==============================================================================# { local old_name="$1" new_name="$3" local tdir="${OPT[TEMPLATE_DIR]}" @@ -1045,13 +1045,13 @@ fi (( OPT[QUIET] )) || echo "Template rename complete" exit 0 } -#=========================================================================# +#==============================================================================# user_undefine_domain () # DESCRIPTION: Prompt the user to undefine libvirt domain (or not) # INPUT: Domain name # OUTPUT: Gives info to and prompts user # PARAMETERS: $1: Domain name -#=========================================================================# +#==============================================================================# { ((OPT[QUIET] == 2)) && return 0 echo @@ -1068,7 +1068,7 @@ echo if prompt_yes_no; then virsh domstate "$1" 2>/dev/null | grep -q "shut off" || virsh domstate "$1" 2>/dev/null | grep -q "crashed" || - { echo "This domain is still running, so make sure you turn it off"; + { echo "This domain is still running, so make sure you turn it off"; echo "before making clones from this template. Otherwise, it will" echo "continue modifying the template storage device even" echo "though it is undefined."; } >&2 @@ -1087,16 +1087,16 @@ fi #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# #-------------------------------# -#=========================================================================# +#==============================================================================# arg_error () # DESCRIPTION: If args are too few, too many, or simply wrong, this # function provides a concise way to exit with proper message and code # INPUT: Type of problem, name of command, name of bad arg # OUTPUT: Echo error message, exit with E_args # PARAMETERS: $1: 0) too few args 1) too many args 2) incorrect arg -# $2: The command, $3: in the case of incorrect arguments, the bad +# $2: The command, $3: in the case of incorrect arguments, the bad # argument in question (omit otherwise) -#=========================================================================# +#==============================================================================# { local line if (( $1 == 0 )); then @@ -1108,13 +1108,13 @@ else fi exit "$E_args" } >&2 -#=========================================================================# +#==============================================================================# check_dir () # DESCRIPTION: Checks that a directory can be written to # INPUT: A filepath # OUTPUT: Error messages and exit codes as needed # PARAMETERS: $1: Filepath -#=========================================================================# +#==============================================================================# { [[ "$1" =~ ^/ ]] || { echo "Invalid filepath $1 specified. Use an absolute filepath"; @@ -1126,7 +1126,7 @@ mkdir -p "$1" &>/dev/null exit "$E_permission"; } return 0 } >&2 -#=========================================================================# +#==============================================================================# check_rw () # DESCRIPTION: Provide an error message and exit if specified file cannot # be read and written to. If file is a directory and a preceding @@ -1135,10 +1135,10 @@ check_rw () # be named '-r') # OUTPUT: Error messages and exit codes as needed # PARAMETERS: $@: Filepaths to check -#=========================================================================# +#==============================================================================# { local redir -if [[ "$1" == "-r" ]]; then +if [[ "$1" == "-r" ]]; then redir=1; shift else redir=0 @@ -1167,17 +1167,17 @@ then done return 0 } >&2 -#=========================================================================# +#==============================================================================# check_template () # DESCRIPTION: Check if OPT[TEMPLATE] is defined. If it is, see if its # md5sum is in agreement with the database. If it isn't, update the # database and see if it is valid. Make sure that aspect of the db is -# updated too. Return 1 if template is not defined, or 2 if it is not +# updated too. Return 1 if template is not defined, or 2 if it is not # valid # INPUT: None # OUTPUT: Error message and exit # PARAMETERS: None -#=========================================================================# +#==============================================================================# { local md5 md5_curr valid @@ -1194,7 +1194,7 @@ valid="$(sqlite3 "select valid from TEMPLATES where \ name='${OPT[TEMPLATE]}';")" check_rw "${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml" -local md5_curr +local md5_curr md5_curr="$(get_md5 "${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml")" [[ "$md5" == "$md5_curr" ]] && [[ "$valid" == "1" ]] && return 0 @@ -1218,7 +1218,7 @@ fi ((valid)) || return 2 return 0 } -#=========================================================================# +#==============================================================================# check_template_disks () # DESCRIPTION: Verify that the disks named by a template exist, can be read # and are not locked. This check is not needed for most commands, but @@ -1227,7 +1227,7 @@ check_template_disks () # INPUT: None # OUTPUT: Error messages and exit if needed # PARAMETERS: None -#=========================================================================# +#==============================================================================# { local disk qemu_out while read -r disk; do @@ -1249,7 +1249,7 @@ done < <( sqlite3 "select disks from TEMPLATES where \ return 0 } -#=========================================================================# +#==============================================================================# check_template_exists () # DESCRIPTION: There are a few places where it is necessary to check that # a template exists, but not the rest of check_template, so this is its @@ -1257,7 +1257,7 @@ check_template_exists () # INPUT: None # OUTPUT: Return 0 if OPT[TEMPLATE] exists and 1 if it does not # PARAMETERS: None -#=========================================================================# +#==============================================================================# { local check check="$(sqlite3 "select exists ( select * from TEMPLATES where\ @@ -1265,41 +1265,41 @@ check="$(sqlite3 "select exists ( select * from TEMPLATES where\ ((check)) && return 0 return 1 } -#=========================================================================# +#==============================================================================# fifo_error () # DESCRIPTION: Error to display if fifo creation files # INPUT: None # OUTPUT: Error message and exit code # PARAMETERS: None -#=========================================================================# +#==============================================================================# { echo "Cannot make fifo" exit "$E_extcom" } >&2 -#=========================================================================# +#==============================================================================# lv_api_do_bad_conn () # DESCRIPTION: Error displayed when lv_api_do cannot connect to API # INPUT: None # OUTPUT: Error message and exit code # PARAMETERS: None -#=========================================================================# +#==============================================================================# { echo "Cannot connect to libvirt API" exit "$E_libvirt" } 2>/dev/null -#=========================================================================# +#==============================================================================# set_error () # DESCRIPTION: Used when convert_to_seq fails # INPUT: None # OUTPUT: Error message and exit # PARAMETERS: None -#=========================================================================# +#==============================================================================# { echo "Improper or badly formatted argument specifying machine or set of " echo "machines" exit "$E_args" } >&2 -#=========================================================================# +#==============================================================================# target_error () # DESCRIPTION: Used when intersection of user-specified set and set of # existing machines that are valid targets for current command results in @@ -1307,13 +1307,13 @@ target_error () # INPUT: Name of command # OUTPUT: Error message and exit # PARAMETERS: $1: Name of command invoked with set -#=========================================================================# +#==============================================================================# { echo "Specified set of machines does not contain any valid targets" echo "for $1 to operate on" exit "$E_args" } >&2 -#=========================================================================# +#==============================================================================# stage_error () # DESCRIPTION: When an action is attempted on a 'staging' clone, (one # created by modify-template prepare-image) but that clone is listed in @@ -1321,11 +1321,11 @@ stage_error () # INPUT: None # OUTPUT: Error message and exit # PARAMETERS: None -#=========================================================================# +#==============================================================================# { echo "A clone staging changes to the template iamge was previously made," echo "but it is now missing. Restore it manually, connect to the" -echo "appropriate URI if it is on another connection, or delete it from" +echo "appropriate URI if it is on another connection, or delete it from" echo "qq2clone's database using the command:" echo echo " qq2clone check ${OPT[TEMPLATE]}" @@ -1333,25 +1333,25 @@ echo echo "(This clone will have ID: 0)" exit "$E_permission" } >&2 -#=========================================================================# +#==============================================================================# temp_error () # DESCRIPTION: If mktemp fails, this function should be invoked # INPUT: None # OUTPUT: Error message and exit with E_extcom # PARAMETERS: None -#=========================================================================# +#==============================================================================# { echo "Attempt to create a temp file with mktemp failed" exit "$E_extcom" } >&2 -#=========================================================================# +#==============================================================================# template_error () # DESCRIPTION: Takes a return code from check_template, gives appropriate # error message and exits if it is nonzero # INPUT: Check_template return status # OUTPUT: Error message and exit code or nothing # PARAMETERS: $1: Return code from check_template -#=========================================================================# +#==============================================================================# { (($1 == 1)) && { echo "The template '${OPT[TEMPLATE]}' does not exist"; @@ -1365,14 +1365,14 @@ template_error () exit "$E_template"; } return 0 } >&2 -#=========================================================================# +#==============================================================================# template_name_available () # DESCRIPTION: Check that the template name is available, and give an # appropriate error message if not # INPUT: A name # OUTPUT: An error message if needed # PARAMETERS: $1: Template name to check -#=========================================================================# +#==============================================================================# { local name while IFS= read -r name; do @@ -1385,33 +1385,33 @@ if [[ -e "${OPT[TEMPLATE_DIR]}/${1}.xml" ]]; then echo "Although template name $1 is not currently in use," echo "a file where this template's XML document belongs already" echo "exists. Move or delete this file:" - echo + echo echo " ${OPT[TEMPLATE_DIR]}/${1}.xml" exit "$E_template" fi return 0 } >&2 -#=========================================================================# +#==============================================================================# unexpected_error () # DESCRIPTION: Error on unexpected event, which is likely a bug in qq2clone # INPUT: None # OUTPUT: Error message and exit code # PARAMETERS: $1: function name where error occurred -#=========================================================================# +#==============================================================================# { echo "qq2clone has encountered an unexpected problem." echo "The problem occurred in function: $1" exit "$E_unexpected" } >&2 -#=========================================================================# +#==============================================================================# valid_xml_name_check () -# DESCRIPTION: Check that XML is valid after modifying name and return 0 +# DESCRIPTION: Check that XML is valid after modifying name and return 0 # if so, exit with error message else # INPUT: write_piped XML file and new name as parameter. Leaves XML in # pipe after execution # OUTPUT: Error message and exit code if needed # PARAMETERS: $1: The new name -#=========================================================================# +#==============================================================================# { virt-xml --edit --metadata name="$1"<<<"$(read_pipe 1)" &>/dev/null || { echo "When trying to use name $1 to generate an xml" @@ -1426,13 +1426,13 @@ virt-xml --edit --metadata name="$1"<<<"$(read_pipe 1)" &>/dev/null || #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# #--------------------------------# -#=========================================================================# +#==============================================================================# delete_template_and_clones () # DESCRIPTION: Delete a template and all of its clones # INPUT: None # OUTPUT: Status updates # PARAMETERS: None -#=========================================================================# +#==============================================================================# { echo hr @@ -1472,7 +1472,7 @@ fi echo echo " All clones deleted." echo -rm -f "${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml" &>/dev/null +rm -f "${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml" &>/dev/null if [[ -e "${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml" ]]; then echo "Failed to delete template XML at" >&2 echo "${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml" >&2 @@ -1483,16 +1483,16 @@ sqlite3 "delete from TEMPLATES where name='${OPT[TEMPLATE]}';" echo "TEMPLATE DELETED: Template ${OPT[TEMPLATE]} deleted." return 0 } -#=========================================================================# +#==============================================================================# prompt_delete_bad_clones () -# DESCRIPTION: Iterate through missing clones, prompting user before +# DESCRIPTION: Iterate through missing clones, prompting user before # taking action # INPUT: None # OUTPUT: Prompts and status updates to user # PARAMETERS: None -#=========================================================================# +#==============================================================================# { -local id i=0 total="${#BAD_CL[@]}" disk prompt=1 select +local id i=0 total="${#BAD_CL[@]}" disk prompt=1 select local t="${OPT[TEMPLATE]}" for id in "${!BAD_CL[@]}"; do ((i++)) @@ -1548,14 +1548,14 @@ return 0 #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@# #-----------------------------# -#=========================================================================# +#==============================================================================# clone () # DESCRIPTION: Clone a virtual machine from OPT[TEMPLATE] # INPUT: If desired, designate that clone should have special ID 0 # OUTPUT: Echo message when complete or on error -# PARAMETERS: $1: (Optional) If '0', create clone intended for staging +# PARAMETERS: $1: (Optional) If '0', create clone intended for staging # changes to a base template image -#=========================================================================# +#==============================================================================# { local base_mach_name line check i local txml="${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml" @@ -1639,14 +1639,14 @@ fi return 0 } -#=========================================================================# +#==============================================================================# connect () # DESCRIPTION: Run machine. If it has spice graphics, connect to graphical # console with virt-viewer/spicy # INPUT: Machine number # OUTPUT: None except on error # PARAMETERS: $1: Machine number -#=========================================================================# +#==============================================================================# { if (( OPT[SPICY] )); then command -v spicy &> /dev/null || @@ -1680,14 +1680,14 @@ fi return 0 } -#=========================================================================# +#==============================================================================# copy_disks () # DESCRIPTION: Go through XML file, find all disk images, and copy them. # A base name is provided, and new files placed in OPT[STORAGE] # INPUT: XML file in pipe # OUTPUT: Altered XML in pipe, and new image files created # PARAMETERS: $1: Base name for disks -#=========================================================================# +#==============================================================================# { (($#==1)) || unexpected_error copy_disks local elem i=0 name xml @@ -1714,14 +1714,14 @@ done return 0 } -#=========================================================================# +#==============================================================================# delete_machine () -# DESCRIPTION: Delete a clone +# DESCRIPTION: Delete a clone # INPUT: Machine number # OUTPUT: None # PARAMETERS: $1: machine number, $2: 0 to just delete disks, 1 to wipe, 2 # to shred -#=========================================================================# +#==============================================================================# { local uuid uuid="${CL_MAP["$1"]}" @@ -1736,10 +1736,10 @@ done < <(sqlite3 "select disks from CLONES where id='$1' and\ declare -a undef_args undef_args=( '--managed-save' '--snapshots-metadata' '--nvram' \ '--checkpoints-metadata' ) -if (( $2 < 2 )); then +if (( $2 < 2 )); then undef_args=( "${undef_args[@]}" '--storage' "$disks") fi -if (($2==1)); then +if (($2==1)); then undef_args=( "${undef_args[@]}" "--wipe-storage" ) fi @@ -1752,7 +1752,7 @@ if (( $2 == 2 )); then { cd "$(dirname "$disk")" && { shred -vf "$(basename "$disk")" 2>&1 | sed "s/^./ &/"; } && - rm -f "$disk" &>/dev/null + rm -f "$disk" &>/dev/null } || unexpected_error delete_machine done < <(sqlite3 "select disks from CLONES where id='$1' and\ template='${OPT[TEMPLATE]}';") @@ -1767,39 +1767,39 @@ sqlite3 "delete from CLONES where id='$1' and \ return 0 } -#=========================================================================# +#==============================================================================# destroy_domain () # DESCRIPTION: Invoke virsh destroy on given machine # INPUT: A machine number # OUTPUT: None # PARAMETERS: A machine number -#=========================================================================# +#==============================================================================# { local uuid uuid="${CL_MAP["$1"]}" virsh destroy "$uuid" &>/dev/null return 0 } -#=========================================================================# +#==============================================================================# discard_save () # DESCRIPTION: Delete a saved state file associated with a clone # INPUT: A saved machine number # OUTPUT: Error message, exit code if needed # PARAMETERS: $1: A saved machine number -#=========================================================================# +#==============================================================================# { local virsh_out virsh_out="$(virsh managedsave-remove "${CL_MAP["$1"]}" 2>&1)" || { echo "$virsh_out"; exit "${E_libvirt}"; } return 0 } -#=========================================================================# +#==============================================================================# edit_xml () # DESCRIPTION: Edit and verify OPT[TEMPLATE]'s XML file # INPUT: None # OUTPUT: Status updates to user, error messages as needed # PARAMETERS: None -#=========================================================================# +#==============================================================================# { local xml="${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml" [[ -e "$xml" ]] || @@ -1808,7 +1808,7 @@ local xml="${OPT[TEMPLATE_DIR]}/${OPT[TEMPLATE]}.xml" exit "$E_template"; } >&2 check_template_exists || template_error $? - + local temp temp="$(mktemp)" || temp_error trap 'rm -f "$temp" &>/dev/null;' INT @@ -1824,7 +1824,7 @@ copy_file "$xml" "$temp" local loop=1 while ((loop)); do - "$ed_com" "$temp" || + "$ed_com" "$temp" || { echo "Editing XML with $ed_com failed" >&2; rm -f "$temp" &>/dev/null; exit "$E_extcom"; } @@ -1843,10 +1843,10 @@ while ((loop)); do echo "Changes resulted in malformed XML file. " >&2 prompt_yes_abort \ "Press (e) to edit again, anything else to abort and revert" \ - "^[Ee]$" >&2 || { rm "$temp" &>/dev/null; exit "$E_xml"; } + "^[Ee]$" >&2 || { rm "$temp" &>/dev/null; exit "$E_xml"; } fi done -move_file "$temp" "$xml" +move_file "$temp" "$xml" check_template ((OPT[QUIET])) || echo "XML modified" @@ -1854,13 +1854,13 @@ trap 'exit' INT return 0 } -#=========================================================================# +#==============================================================================# get_format () # DESCRIPTION: Find format of given virtual machine image file # INPUT: Absolute filepath to a virtual machine image file # OUTPUT: Echoes the name of the format # PARAMETERS: $1: Filepath -#=========================================================================# +#==============================================================================# { local line level=0 while read -r line; do @@ -1874,25 +1874,25 @@ while read -r line; do done < <(qemu-img info --output=json "$1") return 1 } -#=========================================================================# +#==============================================================================# get_template_list () -# DESCRIPTION: List existing templates, alphabetically ordered, one per +# DESCRIPTION: List existing templates, alphabetically ordered, one per # line # INPUT: None # OUTPUT: List of templates # PARAMETERS: None -#=========================================================================# +#==============================================================================# { sqlite3 "select name from TEMPLATES;" | sort return 0 } -#=========================================================================# +#==============================================================================# get_spice () # DESCRIPTION: Get the spice host and port of a running machine # INPUT: The machine number # OUTPUT: Echoes '$hostname $port' pertaining to spice connection # PARAMETERS: $1: The machine number -#=========================================================================# +#==============================================================================# { local uuid time line spice state time="$(date +%s)" @@ -1900,29 +1900,29 @@ uuid="${CL_MAP["$1"]}" until (( OPT[S_TIMEOUT] <= $(date +%s)-time )) ; do spice="$(virsh domdisplay --type spice "$uuid" 2>&1)" [[ "$spice" =~ ^'spice://'(.+):(.+)$ ]] || continue - echo "${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"; + echo "${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"; return 0; done return 1 } -#=========================================================================# +#==============================================================================# get_state () # DESCRIPTION: Get the state of a machine by number # INPUT: The number of a clone managed by qq2clone # OUTPUT: Echoes the state of the machine # PARAMETERS: $1: Machine number -#=========================================================================# +#==============================================================================# { echo "${CL_STATE["$1"]}" return 0 } -#=========================================================================# -get_state_set () +#==============================================================================# +get_state_set () # DESCRIPTION: List all machines in a given state # INPUT: Name of a state # OUTPUT: Echoes a space delimited list of machine numbers (can be empty) # PARAMETERS: $1: state name -#=========================================================================# +#==============================================================================# { local id before=0 state while read -r id; do @@ -1936,14 +1936,14 @@ done < <(echo "${!CL_MAP[@]}" | tr " " "\n") echo return 0 } -#=========================================================================# +#==============================================================================# get_target_set () # DESCRIPTION: Get the set of all machines that a command may legally # operate on # INPUT: A command name # OUTPUT: Echoes a space delimited set of machine numbers (can be empty) # PARAMETERS: $1: The command name -#=========================================================================# +#==============================================================================# { local id before=0 statelist state statelist="$(list_states "$1")" @@ -1958,14 +1958,14 @@ done < <(echo "${!CL_MAP[@]}" | tr " " "\n") echo return 0 } -#=========================================================================# +#==============================================================================# list_display () # DESCRIPTION: Display list of clones and their state to user # INPUT: Optionally, pass argument "1" to print detailed xml list # OUTPUT: Clone list -# PARAMETERS: $1: Set to one to print xml summary instead of a regular +# PARAMETERS: $1: Set to one to print xml summary instead of a regular # list -#=========================================================================# +#==============================================================================# { declare -A statelist local line state @@ -1992,7 +1992,7 @@ if (($1)); then #shellcheck disable=2119 # This is only a (functioning) mock implementation meant as a proof of # concept for what XML describing qq2clone's current state may be like -# For this feature to be complete, it would: use a defined format, be +# For this feature to be complete, it would: use a defined format, be # implemented with proper, modular code, and contain all information to # fully define qq2clone's state except for machine images and domain xml. echo "