#!/bin/bash
# sys/backup - System Backup/Restore Support
set -e
lib_load 'net/tool/rsync'
lib_load 'sys/tool/tar'
######
# Library Configuration
sys_backup_config_init() {
# $backup_modes[] - List of all supported modes of operation.
# Default: ``backup restore``
lib_setting_arrays -ro backup_modes
backup_modes=( backup restore )
# $backup_targets_supported[] - List of targets supported by all
# operation modes.
# Default: ``full config data``
lib_setting_arrays -ro backup_targets_supported
backup_targets_supported=( full config data )
# $backup_targets[] - List of backup/restore targets to execute.
lib_setting_arrays backup_targets
# $backup_datadir - Location of all package backup data
# Default: ``$backup_datadir/backups``
lib_setting_vars -ro backup_datadir
backup_datadir="$datadir/backups"
# $backup_stagedir - Path to staged backup data.
# Default: ``$backup_datadir/stage``
lib_setting_vars -ro backup_stagedir
backup_stagedir="$backup_datadir/stage"
# backup_script_stagedir - Path to staged backup data for the
# current script.
# Default: ``$backup_stagedir/$script_name``
lib_setting_vars -ro backup_script_stagedir
backup_script_stagedir="$backup_stagedir/$script_name"
# $backup_storedir - Path to stored backup archives.
# Default: ``$backup_datadir/store``
lib_setting_vars -ro backup_storedir
backup_storedir="$backup_datadir/store"
# $backup_rsync_opts[] - Extra options to pass to ``rsync``.
# Default: ``--archive``
lib_setting_arrays -ro backup_rsync_opts
backup_rsync_opts=( --archive )
}
######
# Backup Targets
# backup_target_add() - Adds a backup target to ``$backup_targets[]``.
backup_target_add() {
local target=$1
if in_list backup_targets $target; then
warn "$target: ignoring duplicate target"
else
backup_targets+=( "$target" )
fi
}
######
# File Backups
# backup_file() - Uses ``rsync`` to back up a single file or directory ($1)
# to the staging directory.
backup_file() {
has_args 1 "$@"
local src=$1
[ -f "$src" ] || error "$src: does not exist"
local dst="${backup_script_stagedir}${src}"
file_mkdir "$dst"
rsync_run "${backup_rsync_opts[@]}" "$src" "$dst"
}
# restore_file() - Uses ``rsync`` to restore a single file or directory
# ($1) from the staging directory.
restore_file() {
local dst=$1
local src="${backup_script_stagedir}${dst}"
[ ! -f "$dst" ] || warn "$dst: exists, overwriting..."
file_mkdir "$src"
rsync_run "${backup_rsync_opts[@]}" "$src" "$dst"
}
# backup_exec() - Executes a command for all ``$backup_targets[]`` for
# the given backup mode ($1). Additional arguments ($@) are passed
# to the backup function.
backup_exec() {
min_args 1 "$@"
local mode=$1
shift
debug "$mode: starting..."
for target in "${backup_targets[@]}"; do
local cmd="${mode}_${target}"
if script_cmd_exists $cmd; then
debug "$mode $target: starting ..."
script_cmd_exec $cmd "$@"
debug "$mode $target: ... done"
elif lib_cmd_exists backup $cmd; then
debug "$mode $target: built-in: starting ..."
lib_cmd_exec backup $cmd "$@"
debug "$mode $target: built-in: ... done"
else
debug "$mode $target: not supported"
fi
done
debug "$mode: ... done!"
}
######
# Backup archives
backup_pack() {
min_args 2 "$@"
local archive=$1
[ ! -f "$archive" ] || error "$archive: archive exists"
shift
file_mkdir "$archive"
local tardir="$backup_stagedir"
tar_create "$archive" "$@"
}
backup_unpack() {
has_args 1 "$@"
local file=$1
[ -f "$file" ] || error "$file: archive does not exist"
file_mkdir "$archive"
local tardir="$backup_stagedir"
tar_extract "$file"
}
######
# Built-in backup methods
backup_config_file() {
local src=$1
if [ -f "$src" ]; then
${backup_mode}_file "$src"
else
# local/user configuration files may not exist
debug "$src: does not exist; skipping..."
fi
}
backup_config() {
for_each backup_config_file "${sys_configs[@]}"
}
backup_backup_config() { local backup_mode=backup; backup_config "$@"; }
backup_restore_config() { local backup_mode=restore; backup_config "$@"; }
######
# Tool backup/restore
backup_tool_tarfile() { echo "$backup_stagedir/$1.tar"; }
backup_tool() {
local tool=$1
shift
run $tool backup "$@"
local archive
archive=$(backup_tool_tarfile "$tool")
run rm -f "$archive"
backup_pack "$archive" $tool
}
restore_tool() {
local tool=$1
shift
local archive
archive=$(backup_tool_tarfile "$tool")
backup_unpack "$archive"
run "$tool" restore "$@"
}
######
# Host backup/restore interface
# backup_host_storedir - Path to stored backup data for host ($1).
backup_host_storedir() { echo "$backup_storedir/$1"; }
backup_host() {
has_args 2 "$@"
local name=$1
local target=$2
info "host backup: starting ..."
# secure archive name before starting
local aname
aname="$target-$(date +%y%m%d-%H%M%S).tar.xz"
for_each_tool _backup_host_tool $target
local files=( "${package_apps[@]}" )
list_map tar_filename files
local archive
archive="$(backup_host_storedir "$name")/$aname"
backup_pack "$archive" "${files[@]}"
info "host backup: ... done."
}
_backup_host_tool() {
local target=$1
local tool=$2
debug "host $name: $tool: backing up $archive"
backup_tool "$tool" "$target"
}
restore_host() {
has_args 2 "$@"
local name=$1
local archive=$2
debug "host $name: unpacking $archive"
backup_unpack "$archive"
for_each_tool _restore_host_tool
}
_restore_host_tool() {
local tool=$1
local archive
archive=$(backup_tool_tarfile "$tool")
debug "host $name: $tool: restoring $archive"
restore_tool "$tool" "$archive"
}
######
# Remote Backup and Restore
backup_remote() {
local host=$1
local target=$2
warn "$FUNCNAME: FIXME: $target backup unimplemented ($host)"
}
restore_remote() {
local host=$1
local target=$2
warn "$FUNCNAME: FIXME: $target restore unimplemented ($host)"
}
######
# Cloud Backup/Restore Interface
backup_cloud() {
has_args 2 "$@"
local cloud=$1
local target=$2
for_each_cloud_host "$cloud" backup_cloud_host "$cloud" "$target"
}
backup_cloud_host() {
local cloud=$1
local target=$2
local name=$3
info "$cloud: $name: cloud host $target backup: starting..."
if [ "$name" = "$host" ]; then
backup_host "$name" "$target"
else
backup_remote "$name" "$target"
fi
info "$cloud: $name: cloud host $target backup: ... done!"
}
restore_cloud() {
has_args 2 "$@"
local cloud=$1
local target=$2
for_each_cloud_host "$cloud" restore_cloud_host "$cloud" "$target"
}
restore_cloud_host() {
local cloud=$1
local target=$2
local name=$3
info "$cloud: $name: cloud host $target restore: starting..."
if [ "$name" = "$host" ]; then
restore_host "$name" "$target"
else
restore_remote "$name" "$target"
fi
info "$cloud: $name: cloud host $target restore: ... done!"
}
######
# Backup CLI support
backup_dispatch() {
min_args 2 "$@"
local mode=$1
shift
local -a backup_targets
local got_opt=true
while $got_opt; do
local target=$1
case "$target" in
(data|config)
backup_target_add "$target"
shift
;;
(full)
backup_target_add config
backup_target_add data
shift
;;
('')
got_opt=false
;;
(*)
error_usage "$target: unknown target"
;;
esac
done
[ "${backup_targets[*]}" ] || error "$mode: no targets specified"
backup_exec "$mode" "$@"
}
backup_usage() {
cat <<USAGE
<cmd>
Backup Commands:
full Saves all script-related files
config Saves script configuration files
data Saves script data files
Archive Commands
pack <file> Pack staging directory into archive
USAGE
}
restore_usage() {
cat <<USAGE
<cmd> <file>
Restore Commands:
full <file> Perform a full restore
config <file> Restore script configuration files
data <file> Restore script data files
Archive Commands
unpack <file> Unpack archive to staging directory
USAGE
}
Generated on Fri Jul 28 14:35:57 PDT 2017 by mcsh d14 v0.23.0.