Added restore mode

Added colours
This commit is contained in:
Rob Pearce 2020-11-03 15:04:57 +11:00
parent b8843a5d22
commit 5c007da2b0
1 changed files with 471 additions and 64 deletions

503
bare.sh
View File

@ -1,11 +1,12 @@
#!/bin/bash #!/bin/bash
VALIDCOMMANDS="go ls init repos stats diff forget" VALIDCOMMANDS="go ls init repos stats diff forget get"
RCFILE=${HOME}/.backup/config RCFILE=${HOME}/.backup/config
DEF_AUTHFILE=${HOME}/.backup/auth DEF_AUTHFILE=${HOME}/.backup/auth
REPOFILE=${HOME}/.backup/repos REPOFILE=${HOME}/.backup/repos
STATFILE=${HOME}/.backup/stats STATFILE=${HOME}/.backup/stats
LOGFILE=/var/log/backup.log LOGFILE=/var/log/backup.log
FULLLOGFILE_BASE=/tmp/full_backup_log
RESTIC=/usr/local/bin/restic RESTIC=/usr/local/bin/restic
RCLONE=/usr/local/bin/rclone RCLONE=/usr/local/bin/rclone
@ -16,19 +17,51 @@ USMB=/usr/local/bin/usmb
RCLONEOPTS="--progress --buffer-size 10M --cache-chunk-no-memory" RCLONEOPTS="--progress --buffer-size 10M --cache-chunk-no-memory"
SPEED="" SPEED=""
RESTOREDIRBASE="/mnt/restore"
CRONMODE=0 CRONMODE=0
CONNECTIONS="" CONNECTIONS=""
LOG=/dev/stdout LOG=/dev/stdout
DATE=/bin/date DATE=/bin/date
DOALL=0 DOALL=0
REPOSTOBACKUP="" REPO_LIST=""
TESTMODE=0 TESTMODE=0
VERBOSE=0 VERBOSE=0
function cecho() {
local col
col="$1"
shift 1
echo -e "${col}$*${PLAIN}"
}
function error() {
cecho "$RED" "Error: $*"
}
function action() {
cecho "$CYAN" "$*"
}
function log() { function log() {
local now local now col lowrer
now=`${DATE} +%Y/%m/%d-%H:%M:%S` now=`${DATE} +%Y/%m/%d-%H:%M:%S`
echo "${now} <${mode}> $*" >>${LOG} lower=$(echo "$*" | tr '[:upper:]' '[:lower:]')
if [[ $lower == *error* ]]; then
col="$RED"
elif [[ $lower == *fail* ]]; then
col="$RED"
elif [[ $lower == *success* ]]; then
col="$GREEN"
elif [[ $lower == *starting* ]]; then
col="$BOLD"
elif [[ $lower == *created* ]]; then
col="$BOLD"
elif [[ $lower == *creating* ]]; then
col="$BOLD"
elif [[ $lower == *finished* ]]; then
col="$BOLD"
else
col="$CYAN"
fi
echo -e "$CYAN${now} <${mode}> $col$*$PLAIN" >>${LOG}
} }
function updatestats() { function updatestats() {
@ -54,7 +87,8 @@ function usage-repo() {
echo "Backup method tags:" echo "Backup method tags:"
echo " restic Backup using restic (default repo is Backblaze B2 bucket \$B2_BUCKET_PREFIX-<reponame>)" echo " restic Backup using restic (default repo is Backblaze B2 bucket \$B2_BUCKET_PREFIX-<reponame>)"
echo " rclone Backup using rclone (default repo is Backblaze B2 bucket \$B2_BUCKET_PREFIX-<reponame>)" echo " rclone Backup using rclone (default repo is Backblaze B2 bucket \$B2_BUCKET_PREFIX-<reponame>)"
echo " rsync Backup using rsync (defualt repo is \$DEF_RSYNC_USER@\$DEF_RSYNC_SERVER:\$DEF_RSYNC_DIR/<reponame>)" echo " rsync Backup using rsync (default repo is \$DEF_RSYNC_USER@\$DEF_RSYNC_SERVER:\$DEF_RSYNC_DIR/<reponame>)"
echo " postgres Dump all postgres databases. Instead of local file path, specify database name to backup. If empty, all dbs are backed up."
echo "" echo ""
echo "Pre-backup commands to access local files:" echo "Pre-backup commands to access local files:"
echo " nfs Mount \$DEF_NFS_SERVER:\$DEF_NFS_SERVER_BASE/<sharename> to <repopath>" echo " nfs Mount \$DEF_NFS_SERVER:\$DEF_NFS_SERVER_BASE/<sharename> to <repopath>"
@ -78,6 +112,8 @@ function usage-rc() {
echo " export RESTIC_KEEPDAYS=31 # Days to keep restic backups for" echo " export RESTIC_KEEPDAYS=31 # Days to keep restic backups for"
echo " export RESTICOPTS=\"--one-file-system\" # Extra args to pass to restic" echo " export RESTICOPTS=\"--one-file-system\" # Extra args to pass to restic"
echo "" echo ""
echo " export PGUSER=postgres # Postgresql username"
echo ""
echo " export DEF_RSYNC_SERVER=xxx # Server used for repos with 'rsync' tag" echo " export DEF_RSYNC_SERVER=xxx # Server used for repos with 'rsync' tag"
echo " export DEF_RSYNC_USER=backups # Username for rsync server" echo " export DEF_RSYNC_USER=backups # Username for rsync server"
echo " export DEF_RSYNC_DIR=/home/backups/backups # Remote directory on rsync server" echo " export DEF_RSYNC_DIR=/home/backups/backups # Remote directory on rsync server"
@ -94,6 +130,7 @@ function usage() {
echo "usage: $0 command reponame" echo "usage: $0 command reponame"
echo "" echo ""
echo " -a Run on all repos defined as 'auto'" echo " -a Run on all repos defined as 'auto'"
echo " -d dir Restore mode: specify where to put restored files (default: $RESTOREDIRBASE)"
echo " -h Show this usage text" echo " -h Show this usage text"
echo " -s num Limit speed to 'num' Mbps (default: unlimited)" echo " -s num Limit speed to 'num' Mbps (default: unlimited)"
echo " -x num Use 'num' simultaneous connections (default: 20)" echo " -x num Use 'num' simultaneous connections (default: 20)"
@ -320,6 +357,17 @@ function do_mount() {
fi fi
fi fi
checktag "$f" postgres # datapath is used for sql filename in pgsql backups
if [[ $? -eq 0 ]]; then
# Make sure the db exists
if ! [[ -z ${DATAPATH} ]]; then
psql -lqt | cut -d \| -f 1 | grep -qw ${DATAPATH}
if [[ $? -ne 0 ]]; then
log "Error: PostgreSQL database '${DATAPATH}' doesn't exist"
return 1
fi
fi
else
if ! [[ -e ${DATAPATH} ]]; then if ! [[ -e ${DATAPATH} ]]; then
log "Error: ${DATAPATH} doesn't exist" log "Error: ${DATAPATH} doesn't exist"
return 1 return 1
@ -330,6 +378,7 @@ function do_mount() {
log "Error: ${DATAPATH} exists but appears to be empty" log "Error: ${DATAPATH} exists but appears to be empty"
return 1 return 1
fi fi
fi
return 0 return 0
} }
@ -346,14 +395,60 @@ function do_umount() {
fi fi
} }
function updatedir() {
local temp r tempwd
tempwd=$(echo "$wd" | sed -e 's-/\{1,\}-/-g')
temp=$( ${RESTIC} $AUTHOPTS $CONNECTIONSOPTS -q ls -l $RESTORESNAPID $tempwd 2>/dev/null )
r=$?
if [[ $r -ne 0 || -z $temp ]]; then
return 1
fi
wd="$tempwd"
contents=$( echo "$temp" | awk -v wd=$wd '{ sub(wd,""); sub(wd "/", ""); print $0; }')
subdirs=$( echo "${contents}" | grep "^d" | awk '{ print $NF}' | sed -e 's,^/,,')
subfiles=$( echo "${contents}" | awk '{ print $NF}' | sed -e 's,^/,,')
return 0
}
function showrestoreinfo() {
cecho "$YELLOW$BOLD" "Repo to restore from:"
cecho "$YELLOW" " $f (snapshot $RESTORESNAPID)"
echo
cecho "$YELLOW$BOLD" "Files to restore:"
if [[ -z $RESTORELIST ]]; then
cecho "$YELLOW" " (none)"
else
cecho "$YELLOW" "$RESTORELIST" | sed -e 's/^ //' | tr ' ' '\n' | sed -e 's/^/ /g'
fi
echo
}
function restorecmd_help() {
echo
echo "x Abort"
echo "q Abort"
echo "lcd dir Change local restore dir"
echo "ls Show files in current repo dir"
echo "cd Change current repo dir"
echo "dirs Start subdirectories of current repo dir"
echo "go Start restore of marked file"
echo "w List files marked for restore"
echo "a file Mark file to be restored"
echo "d file Un-mark file from restore list"
echo
}
# Handle args # Handle args
ARGS="acdhs:tvx:" ARGS="acd:hs:tvx:"
while getopts "$ARGS" i; do while getopts "$ARGS" i; do
case "$i" in case "$i" in
a) a)
DOALL=1 DOALL=1
;; ;;
d)
RESTOREDIRBASE="$OPTARG";
;;
h) h)
usage; usage;
exit 1 exit 1
@ -382,21 +477,33 @@ while getopts "$ARGS" i; do
done done
shift $((OPTIND - 1)) shift $((OPTIND - 1))
if [[ $CRONMODE -eq 0 ]]; then
BOLD="\033[1m"
PLAIN="\033[0m"
UNDERLINE="\033[4m"
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[33m"
BLUE="\033[34m"
CYAN="\033[36m"
LINK="$BLUE$UNDERLINE"
fi
if ! [ -e ${RCFILE} ]; then if ! [ -e ${RCFILE} ]; then
echo "Error - can't find ${RCFILE}" error "can't find ${RCFILE}"
echo "" echo ""
usage-rc usage-rc
exit 1 exit 1
fi fi
if ! [ -e ${REPOFILE} ]; then if ! [ -e ${REPOFILE} ]; then
echo "Error - can't find ${REPOFILE}" error "can't find ${REPOFILE}"
echo "" echo ""
usage-repo usage-repo
exit 1 exit 1
fi fi
which bc >/dev/null 2>&1 which bc >/dev/null 2>&1
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Error - can't find bc in path" error "can't find bc in path"
echo "" echo ""
exit 1 exit 1
fi fi
@ -404,7 +511,7 @@ fi
. ${RCFILE} . ${RCFILE}
if [[ -z $RESTIC_KEEPDAYS ]]; then if [[ -z $RESTIC_KEEPDAYS ]]; then
echo "Error - \$RESTIC_KEEPDAYS not set." error "\$RESTIC_KEEPDAYS not set."
exit 1 exit 1
fi fi
@ -415,6 +522,25 @@ else
KSPEED="" KSPEED=""
fi fi
CMD="$1"
shift 1
# command synonyms
if [[ $CMD == "repolist" ]]; then
CMD="repos"
elif [[ $CMD == "restore" ]]; then
CMD="get"
elif [[ $CMD == "snapshots" ]]; then
CMD="ls"
fi
if (echo "$VALIDCOMMANDS" | grep -wq $CMD) ; then
true
else
error "invalid command $CMD. Should one of: $VALIDCOMMANDS"
exit 1
fi
# Get list of defined repos # Get list of defined repos
idx=0 idx=0
@ -426,11 +552,11 @@ for f in `cat $REPOFILE | grep ";" | grep -v "^#"`; do
thisrpath=`getrepopath $f` thisrpath=`getrepopath $f`
if [[ ${thisrpath,,} == *b2* ]]; then if [[ ${thisrpath,,} == *b2* ]]; then
if [[ -z $B2_APP_ID ]]; then if [[ -z $B2_APP_ID ]]; then
echo "Error - \$B2_APP_ID not set." error "\$B2_APP_ID not set."
exit 1 exit 1
fi fi
if [[ -z $B2_APP_KEY ]]; then if [[ -z $B2_APP_KEY ]]; then
echo "Error - \$B2_APP_KEY not set." error "\$B2_APP_KEY not set."
exit 1 exit 1
fi fi
fi fi
@ -439,31 +565,50 @@ for f in `cat $REPOFILE | grep ";" | grep -v "^#"`; do
if [[ $DOALL -eq 1 ]]; then if [[ $DOALL -eq 1 ]]; then
checktag "$f" auto checktag "$f" auto
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
REPOSTOBACKUP="$REPOSTOBACKUP `echo $f | sed -e 's/\;.*//'`" REPO_LIST="$REPO_LIST `echo $f | sed -e 's/\;.*//'`"
fi fi
fi fi
idx=$((idx + 1)) idx=$((idx + 1))
done done
if ! [[ $VALIDCOMMANDS == *"$CMD"* ]]; then
echo "Error - invalid command $CMD. Should one of: $VALIDCOMMANDS" if [[ $CMD == "get" ]]; then
# one repo + snapshotid/date
err=0
if [[ -z $REPO_LIST ]]; then
if [[ $# -ge 1 ]]; then
REPO_LIST=$1 ;shift 1
else
error "no reponame provided."
exit 1 exit 1
fi fi
if [[ $# -ge 1 ]]; then
CMD="$1" RESTORESNAPID=$1 ; shift 1
shift 1 fi
RESTORELIST="$*"
if [[ -z $REPOSTOBACKUP ]]; then else
REPOSTOBACKUP="$*" echo "restore usage: $0 get repo_name (snapshotid|latest)"
echo " or"
echo " $0 get repo_name (snapshotid|latest) file1 file2 etc"
echo
echo "Files will be restored to $RESTOREDIRBASE/repo_name/"
echo
exit 1
fi
else
# one or more repos
if [[ -z $REPO_LIST ]]; then
REPO_LIST="$*"
fi
fi fi
if [[ $CMD == "repos" ]]; then if [[ $CMD == "repos" ]]; then
format="%-10s%-20s%-20s%s\n" format="%-20s%-20s%-30s%s\n"
echo "Repos defined in ${RCFILE}:" cecho "${CYAN}" "Repos defined in $BOLD${RCFILE}$PLAIN$CYAN:"
echo "" echo ""
printf "$format" "Repo" "Path" "Tags" "Repo path" head=$(printf "$format" "Repo" "Path/DB" "Repo path" "Tags")
cecho "$BOLD$YELLOW" "$head"
for x in ${REPODEFS[@]}; do for x in ${REPODEFS[@]}; do
IFS=';' read -ra tok <<< "$x" IFS=';' read -ra tok <<< "$x"
thisrepo=${tok[0]} thisrepo=${tok[0]}
thispath=${tok[1]} thispath=${tok[1]}
@ -475,19 +620,24 @@ if [[ $CMD == "repos" ]]; then
thisrpath=`getrepopath $thisrepo` thisrpath=`getrepopath $thisrepo`
[[ -z $thisrpath ]] && thisrpath="(default)" [[ -z $thisrpath ]] && thisrpath="(default)"
printf "$format" "$thisrepo" "$thispath" "$tags" "$thisrpath" if [[ $tags == *postgres* && -z $thispath ]]; then
thispath="(all dbs)"
fi
line=$(printf "$format" "$thisrepo" "$thispath" "$thisrpath" "$tags")
cecho "$YELLOW" "$line"
done done
exit 0 exit 0
fi fi
if [[ -z $REPOSTOBACKUP ]]; then if [[ -z $REPO_LIST ]]; then
usage usage
exit 1 exit 1
fi fi
# Validate repos # Validate repos
GOODREPOS="" GOODREPOS=""
for f in $REPOSTOBACKUP; do for f in $REPO_LIST; do
REPO=${f} REPO=${f}
DATAPATH=$(getdatapath $f) DATAPATH=$(getdatapath $f)
REPOPATH=$(getrepopath $f) REPOPATH=$(getrepopath $f)
@ -559,12 +709,17 @@ for f in $REPOSTOBACKUP; do
fi fi
if [[ -z $DATAPATH ]]; then if [[ -z $DATAPATH ]]; then
log "can't find matching repo for $f - make sure it is listed in ${REPOFILE}" checktag "$f" postgres
if [[ $? -ne 0 ]]; then
log "error - can't find matching repo for $f - make sure it is listed in ${REPOFILE}"
continue continue
fi fi
fi
if [[ $CMD == "go" ]]; then if [[ $CMD == "go" ]]; then
NEEDMOUNT=1 NEEDMOUNT=1
elif [[ $CMD == "get" ]]; then
NEEDMOUNT=1
elif [[ $CMD == "diff" ]]; then elif [[ $CMD == "diff" ]]; then
NEEDMOUNT=1 NEEDMOUNT=1
else else
@ -585,15 +740,15 @@ for f in $REPOSTOBACKUP; do
fi fi
done done
REPOSTOBACKUP="$GOODREPOS" REPO_LIST="$GOODREPOS"
if [[ -z $REPOSTOBACKUP ]]; then if [[ -z $REPO_LIST ]]; then
log "Error: errors found with all repos. Aborting." log "Error: errors found with all repos. Aborting."
exit 1 exit 1
fi fi
if [[ $TESTMODE -eq 1 ]]; then if [[ $TESTMODE -eq 1 ]]; then
echo "Would have run '$CMD' on these repos:" action "Would have run '$CMD' on these repos:"
for f in $REPOSTOBACKUP; do for f in $REPO_LIST; do
DATAPATH=$(getdatapath $f) DATAPATH=$(getdatapath $f)
echo " $f -> $DATAPATH" echo " $f -> $DATAPATH"
if [[ $NEEDMOUNT -eq 1 ]]; then if [[ $NEEDMOUNT -eq 1 ]]; then
@ -606,9 +761,10 @@ fi
errcount=0 # used for 'check' mode errcount=0 # used for 'check' mode
warncount=0 # used for 'check' mode warncount=0 # used for 'check' mode
errtext="" errtext=""
for f in $REPOSTOBACKUP; do for f in $REPO_LIST; do
REPO=${f} REPO=${f}
DATAPATH=$(getdatapath $REPO) DATAPATH=$(getdatapath $REPO)
FULLLOGFILE="${FULLLOGFILE_BASE}_${REPO}.txt"
checktag "$f" usmb checktag "$f" usmb
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
if [[ -z $USMB_PREFIX ]]; then if [[ -z $USMB_PREFIX ]]; then
@ -661,6 +817,8 @@ for f in $REPOSTOBACKUP; do
if [[ $CMD == "go" ]]; then if [[ $CMD == "go" ]]; then
NEEDMOUNT=1 NEEDMOUNT=1
elif [[ $CMD == "get" ]]; then
NEEDMOUNT=1
elif [[ $CMD == "diff" ]]; then elif [[ $CMD == "diff" ]]; then
NEEDMOUNT=1 NEEDMOUNT=1
else else
@ -695,10 +853,32 @@ for f in $REPOSTOBACKUP; do
else else
CONNECTIONSOPTS="-o b2.connections=${CONNECTIONS}" CONNECTIONSOPTS="-o b2.connections=${CONNECTIONS}"
fi fi
checktag "$f" postgres
if [[ $? -eq 0 ]]; then
CONNECTIONSOPTS="$OTHEROPTS --tag postgres"
fi
if [[ $VERBOSE -eq 1 ]]; then if [[ $VERBOSE -eq 1 ]]; then
OTHEROPTS="$OTHEROPTS -v" OTHEROPTS="$OTHEROPTS -v"
fi fi
DBOPTS=""
DBDUMPCMD=""
checktag "$f" postgres
if [[ $? -eq 0 ]]; then
DBOPTS="$DBOPTS --clean"
OTHEROPTS="$OTHEROPTS --stdin"
if [[ -z ${DATAPATH} ]]; then
DBDUMPCMD="pg_dumpall"
DBFILENAME="alldbs.sql"
else
DBDUMPCMD="pg_dump"
DBFILENAME="${DATAPATH}.sql"
DBOPTS="$DBOPTS ${DATAPATH}" # Last dbdump option is database name
fi
fi
export RESTIC_EXCLUDEFILE=`getrepoexcludefile "$f"` export RESTIC_EXCLUDEFILE=`getrepoexcludefile "$f"`
if [[ ! -z $RESTIC_EXCLUDEFILE ]]; then if [[ ! -z $RESTIC_EXCLUDEFILE ]]; then
OTHEROPTS="$OTHEROPTS --exclude-file=${RESTIC_EXCLUDEFILE}" OTHEROPTS="$OTHEROPTS --exclude-file=${RESTIC_EXCLUDEFILE}"
@ -718,7 +898,7 @@ for f in $REPOSTOBACKUP; do
for xx in $list; do for xx in $list; do
eval val='$'$xx eval val='$'$xx
if [[ -z $val ]]; then if [[ -z $val ]]; then
echo "Error - \$$xx not set. Please update ${RCFILE}." error "\$$xx not set. Please update ${RCFILE}."
echo "" echo ""
usage-rc usage-rc
exit 1 exit 1
@ -770,34 +950,257 @@ for f in $REPOSTOBACKUP; do
oneday=$( echo "24 * 60 * 60" | bc) oneday=$( echo "24 * 60 * 60" | bc)
if [[ $code -eq 0 ]]; then if [[ $code -eq 0 ]]; then
if [[ $age -le $oneday ]]; then if [[ $age -le $oneday ]]; then
echo "OK: Last backup for '$REPO' succeeded on $humanstamp." cecho "$GREEN" "OK: Last backup for '$REPO' succeeded on $humanstamp."
rv=0 rv=0
else else
echo "CRITICAL: Last backup for '$REPO' succeeded on $humanstamp (age $age not in last 24 hours)." cecho "$RED" "CRITICAL: Last backup for '$REPO' succeeded on $humanstamp (age $age not in last 24 hours)."
errcount=$(( $errcount + 1)) errcount=$(( $errcount + 1))
rv=2 rv=2
fi fi
else else
echo "CRITICAL: Last backup for '$REPO' failed on $humanstamp." cecho "$RED" "CRITICAL: Last backup for '$REPO' failed on $humanstamp."
rv=2 rv=2
errcount=$(( $errcount + 1)) errcount=$(( $errcount + 1))
fi fi
else else
echo "WARNING: No history found for repo '$REPO'" cecho "$YELLOW" "WARNING: No history found for repo '$REPO'"
rv=1 rv=1
warncount=$(( $warncount + 1)) warncount=$(( $warncount + 1))
fi fi
elif [[ $mode == "restic" ]]; then elif [[ $mode == "restic" ]]; then
if [[ $CMD == "go" ]]; then if [[ $CMD == "go" ]]; then
if [[ $CRONMODE -eq 1 ]]; then if [[ $CRONMODE -eq 1 ]]; then
if [[ $VERBOSE -eq 1 ]]; then
echo "debug: running: [${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH" >>${LOG}
echo "debug: full log is in ${FULLLOGFILE}" >>${LOG}
checktag "$f" postgres
if [[ $? -eq 0 ]] ; then
$DBDUMPCMD $DBOPTS | ${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS --stdin-filename $DBFILENAME 2>&1 >> ${FULLLOGFILE}
rv=$?
else
${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH 2>&1 >> ${FULLLOGFILE}
rv=$?
fi
egrep "^(Added|processed|snapshot)" ${FULLLOGFILE} >>${LOG}
else
checktag "$f" postgres
if [[ $? -eq 0 ]] ; then
${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH 2>&1 | egrep "^(Added|processed|snapshot)" >> ${LOG} ${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH 2>&1 | egrep "^(Added|processed|snapshot)" >> ${LOG}
rv=${PIPESTATUS[0]} rv=${PIPESTATUS[0]}
else
$DBDUMPCMD $DBOPTS | ${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS --stdin-filename $DBFILENAME 2>&1 | egrep "^(Added|processed|snapshot)" >> ${LOG}
rv=${PIPESTATUS[0]}
fi
fi
else
checktag "$f" postgres
if [[ $? -eq 0 ]] ; then
[[ $VERBOSE -eq 1 ]] && log "debug: running: [$DBDUMPCMD $DBOPTS | ${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS --stdin-filename $DBFILENAME"
$DBDUMPCMD $DBOPTS | ${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS --stdin-filename $DBFILENAME 2>&1 >> ${LOG}
rv=$?
else else
[[ $VERBOSE -eq 1 ]] && log "debug: running: [${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH" [[ $VERBOSE -eq 1 ]] && log "debug: running: [${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH"
${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH 2>&1 >> ${LOG} ${RESTIC} backup $AUTHOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH 2>&1 >> ${LOG}
rv=$? rv=$?
fi fi
fi
elif [[ $CMD == "get" ]]; then
RESTOREDIR="$RESTOREDIRBASE/$f/"
snaps=$(${RESTIC} snapshots $AUTHOPTS $CONNECTIONSOPTS 2>&1 | grep -v ID | awk '(NF >= 4) { print }')
checktag "$f" postgres
if [[ $? -ne 0 ]]; then
snaps=$(echo "$snaps" | grep -v postgres)
fi
snapids=$(echo "$snaps" | awk '{ print $1 }')
defsid=$(echo "$snapids" | tail -1)
if [[ -z $RESTORESNAPID ]]; then
cecho "$BOLD$CYAN" "Found these snapshots:"
echo "$snaps"
RESTORESNAPID="xxxxxx"
while [[ ! $snaps == *$RESTORESNAPID* ]]; do
read -p "snapshot [$defsid]: " RESTORESNAPID
if [[ $RESTORESNAPID == "latest" || -z $RESTORESNAPID ]]; then
RESTORESNAPID=$defsid
fi
done
elif [[ $RESTORESNAPID == "latest" ]]; then
RESTORESNAPID=$defsid
elif [[ ! $snaps == *$RESTORESNAPID* ]]; then
error "snapshot '$RESTORESNAPID' not found."
echo
cecho "$RED" "Valid snapshots for repo $f:"
cecho "$RED" "$snaps"
exit 1
fi
if [[ -z $RESTORELIST ]]; then
action "Opening repo for snapshot $BOLD$RESTORESNAPID$PLAIN$CYAN..."
wd=/
updatedir
c="ls"
doit=0
while [[ ! $c == "x" && ! c == "q" ]]; do
if [[ $c == "ls" ]]; then
echo "$contents"
elif [[ $c == "go" ]]; then
if [[ -z $RESTORELIST ]]; then
cecho "$RED" "Nothing to restore!"
else
doit=1
fi
break
elif [[ $c == "x" || $c == "q" ]]; then
doit=0
break
elif [[ $c == "?" || $c == "h" ]]; then
restorecmd_help
elif [[ -z $c ]]; then
true
elif [[ $c == "dirs" ]]; then
echo "subdirs:"
echo "$subdirs" | sed -e 's/^/ /'
elif [[ $c == w* ]]; then
showrestoreinfo
elif [[ $c == a* || $c == mark* ]]; then
file=$(echo "$c" | awk '{ print $2 }')
# remove leading /
file=$(echo $file | sed -e 's,^/,,')
if [[ -z $file ]]; then
error "no file provided"
else
echo "$RESTORELIST" | tr ' ' '\n' | egrep -q "^$wd$file$"
thisrv=$?
if [[ $thisrv -eq 0 ]]; then
error "'$file' already in restore list"
else
if [[ $file == */* ]]; then
# add without checking
if [[ -z $RESTORELIST ]]; then
RESTORELIST="$wd$file"
else
RESTORELIST="$RESTORELIST $wd$file"
fi
action "Added to restore list: $BOLD$file$PLAIN$CYAN (no verify)"
else
echo "$subfiles" | grep -qw "$file"
rv=$?
if [[ $rv -eq 0 ]]; then
RESTORELIST="$RESTORELIST $wd$file"
action "Added to restore list: $BOLD$file"
else
error "'$file' doesn't exist"
fi
fi
fi
fi
elif [[ $c == d* || $c == unmark* ]]; then
file=$(echo "$c" | awk '{ print $2 }')
file="$wd$file"
echo "$RESTORELIST" | tr ' ' '\n' | egrep -q "^$file$"
if [[ $? -eq 0 ]]; then
#RESTORELIST=$(echo "$RESTORELIST" | sed -e 's/^ //' | tr ' ' '\n' | grep -wv $file | tr '\n' ' ' | egrep -v "^$" | sed -e 's/^/ /')
RESTORELIST=$(echo "$RESTORELIST" | sed -e "s, $file,,")
action "Removed from restore list: $BOLD$file"
else
error "Restore list doesn't contain '$file'"
fi
elif [[ $c == cd* ]]; then
newdir=$(echo "$c" | awk '{ print $2 }')
olddir=$wd
needupdate=1
if [[ $newdir == "/" ]]; then
wd=$newdir
rv=0
elif [[ $newdir == /* ]]; then
wd=$newdir/
updatedir
rv=$?
needupdate=0
elif [[ $newdir == */* ]]; then
wd=$wd$newdir/
updatedir
rv=$?
needupdate=0
elif [[ $newdir == ".." ]]; then
wd=$(dirname $wd)
rv=0
else
echo "$subdirs" | grep -qw "$newdir"
rv=$?
if [[ $rv -eq 0 ]]; then
wd=$wd$newdir/
fi
fi
if [[ $rv -eq 0 ]]; then
[[ $needupdate -eq 1 ]] && updatedir
else
error "no such directory '$newdir'"
wd=$olddir
fi
elif [[ $c == lcd* ]]; then
newlocaldir=$(echo "$c" | awk '{ print $2 }')
if [[ -d $newlocaldir ]]; then
RESTOREDIR="$newlocaldir"
elif [[ -e $newlocaldir ]]; then
error "'$newlocaldir' is not a directory"
else
error "'$newlocaldir' does not exist"
fi
else
error "bad command '$c' (? for help)"
fi
echo
action "Restore path: $RESTOREDIR"
echo -en "$CYAN$wd> $PLAIN"
read c
done
else
# restorelist not empty
doit=1
fi
if [[ $doit -eq 1 ]]; then
showrestoreinfo
action "Performing restore..."
echo
RESTOREOPTS=""
for r in $RESTORELIST; do
RESTOREOPTS="$RESTOREOPTS -i $r"
done
[[ $VERBOSE -eq 1 ]] && log "debug: running: ${RESTIC} restore $AUTHOPTS $CONNECTIONSOPTS -t "$RESTOREDIR" $RESTOREOPTS $RESTORESNAPID"
${RESTIC} restore $AUTHOPTS $CONNECTIONSOPTS -t "$RESTOREDIR" $RESTOREOPTS $RESTORESNAPID 2>&1 >> ${LOG}
rv=$?
if [[ $rv -eq 0 ]]; then
# check that we got them all
tried=0
success=0
failed=0
for r in $RESTORELIST; do
tried=$((tried + 1))
if [[ -e $RESTOREDIR/$r ]]; then
success=$((success + 1))
else
failed=$((failed + 1))
fi
done
echo
echo -en "${CYAN}${BOLD}Restored $success / $tried items$PLAIN"
[[ $failed -gt 0 ]] && cecho "$RED" " ($failed failed)" || echo
echo
if [[ $success -eq $tried ]]; then
rv=0
else
rv=1
fi
fi
else
action "${BOLD}Aborting..."
rv=1
fi
elif [[ $CMD == "ls" ]]; then elif [[ $CMD == "ls" ]]; then
[[ $VERBOSE -eq 1 ]] && log "debug: running: ${RESTIC} snapshots $AUTHOPTS $CONNECTIONSOPTS $OTHEROPTS "
${RESTIC} snapshots $AUTHOPTS $CONNECTIONSOPTS 2>&1 >> ${LOG} ${RESTIC} snapshots $AUTHOPTS $CONNECTIONSOPTS 2>&1 >> ${LOG}
rv=$? rv=$?
elif [[ $CMD == "stats" ]]; then elif [[ $CMD == "stats" ]]; then
@ -815,9 +1218,6 @@ for f in $REPOSTOBACKUP; do
else else
cmdtorun="${RESTIC} init $AUTHOPTS $CONNECTIONSOPTS" cmdtorun="${RESTIC} init $AUTHOPTS $CONNECTIONSOPTS"
if [[ $VERBOSE -eq 1 ]]; then if [[ $VERBOSE -eq 1 ]]; then
log "export RESTIC_REPOSITORY=\"$REPO_PATH\"" log "export RESTIC_REPOSITORY=\"$REPO_PATH\""
log "export RESTIC_PASSWORD_FILE=`getrepopassfile \"$f\"`" log "export RESTIC_PASSWORD_FILE=`getrepopassfile \"$f\"`"
log "will run: [${cmdtorun}]" log "will run: [${cmdtorun}]"
@ -837,16 +1237,18 @@ for f in $REPOSTOBACKUP; do
fi fi
elif [[ $mode == "rsync" ]]; then elif [[ $mode == "rsync" ]]; then
REMOTE="" REMOTE=""
if [[ $CMD == "go" ]]; then checktag "$f" postgres
#echo run ${RSYNC} ${RSYNC_OPTIONS} $DATAPATH/ ${RSYNC_REPOSITORY} 2>&1 >> ${LOG} if [[ $? -eq 0 ]] ; then
#exit 1 log "Error: postgres backups not supported for rsync mode."
rv=1
elif [[ $CMD == "go" ]]; then
${RSYNC} ${RSYNC_OPTIONS} $DATAPATH/ ${RSYNC_REPOSITORY} 2>&1 >> ${LOG} ${RSYNC} ${RSYNC_OPTIONS} $DATAPATH/ ${RSYNC_REPOSITORY} 2>&1 >> ${LOG}
rv=$? rv=$?
elif [[ $CMD == "ls" ]]; then elif [[ $CMD == "ls" ]]; then
ssh ${DEF_RSYNC_USER}@${DEF_RSYNC_SERVER} ls ${RSYNC_FULLDIR} 2>&1 >> ${LOG} ssh ${DEF_RSYNC_USER}@${DEF_RSYNC_SERVER} ls ${RSYNC_FULLDIR} 2>&1 >> ${LOG}
rv=$? rv=$?
elif [[ $CMD == "stats" ]]; then elif [[ $CMD == "stats" ]]; then
echo "Size of remote data for ${REPO}:" action "Size of remote data for ${REPO}:"
ssh ${DEF_RSYNC_USER}@${DEF_RSYNC_SERVER} du -hs ${RSYNC_FULLDIR} 2>&1 >> ${LOG} ssh ${DEF_RSYNC_USER}@${DEF_RSYNC_SERVER} du -hs ${RSYNC_FULLDIR} 2>&1 >> ${LOG}
rv=$? rv=$?
elif [[ $CMD == "diff" ]]; then elif [[ $CMD == "diff" ]]; then
@ -866,7 +1268,7 @@ for f in $REPOSTOBACKUP; do
fi fi
fi fi
elif [[ $CMD == "forget" ]]; then elif [[ $CMD == "forget" ]]; then
log "Error: forget not supported for rsync mode." log "Error: forget not supported for $mode mode."
rv=1 rv=1
else else
log "Error: invalid command $CMD" log "Error: invalid command $CMD"
@ -875,7 +1277,11 @@ for f in $REPOSTOBACKUP; do
else else
# rclone # rclone
REMOTE="" REMOTE=""
if [[ $CMD == "go" ]]; then checktag "$f" postgres
if [[ $? -eq 0 ]] ; then
log "Error: postgres backups not supported for $mode mode."
rv=1
elif [[ $CMD == "go" ]]; then
${RCLONE} sync $RCLONEOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH $RCLONE_REPOSITORY 2>&1 >> ${LOG} ${RCLONE} sync $RCLONEOPTS $CONNECTIONSOPTS $SPEEDOPTS $OTHEROPTS $DATAPATH $RCLONE_REPOSITORY 2>&1 >> ${LOG}
rv=$? rv=$?
elif [[ $CMD == "ls" ]]; then elif [[ $CMD == "ls" ]]; then
@ -965,3 +1371,4 @@ if [[ $CMD == "check" ]]; then
fi fi
exit $rv exit $rv
fi fi