-
Hahn Axel (hahn) authoredHahn Axel (hahn) authored
restic.sh 11.70 KiB
#!/bin/bash
# ================================================================================
#
# TRANSFER :: PLUGIN - TEMPLATE
#
# this script will be included in ../../transfer.sh
#
# --------------------------------------------------------------------------------
# ah - Axel Hahn <axel.hahn@iml.unibe.ch>
# 2021-05-19 ah v0.0 INIT ... WIP
# 2022-01-06 ah v0.0 added support for Repository with REST and authentication
# ================================================================================
# --------------------------------------------------------------------------------
# INIT
# --------------------------------------------------------------------------------
function t_checkRequirements(){
j_requireBinary "restic"
j_requireUser "root"
}
function t_setVars(){
export RESTIC_PASSWORD=$PASSPHRASE
# if we set RESTIC_REPOSITORY then "-r TARGET" is not
# needed in restic commands
# TODO: for restic with https and auth - remove the host in the path
local _target=$( j_getFullTarget )
echo ${_target} | grep "https.*@" >/dev/null
if [ $? -eq 0 ]; then
local _host=$( hostname -f )
_target=$( echo $_target | sed "s#${_host}/##" )
fi
export RESTIC_REPOSITORY=$_target
# WORKAROUND for bug while writing on a SMB target
export GODEBUG="asyncpreemptoff=1"
RESTORE_ITEM=latest
RESTIC_MOUNTPOINT=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}mountpoint" )
}
# --------------------------------------------------------------------------------
# GENERATE PARAMS :: ALL DIRS
# --------------------------------------------------------------------------------
# return a string with default params
# param string param1 of transfer.sh; one of full|inc|auto
# param string param2 of transfer.sh; for auto: date i.e. 3M for 3 monthes
function t_getParamDefault(){
# verbose to see more details
echo -n --verbose=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}verbose" )
}
# return a string with backup parameters that will be added to defaults
function t_getParamBackup(){
local _tag
local _nocacheFlag
# tagging
_tag=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}tag" )
if [ "$_tag" != "" ]; then
echo -n "--tag $_tag "
fi
# no cache ... to create smaller local cache dirs, but backup 3 times slower
_nocacheFlag=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}nocache" )
if [ "$_nocacheFlag" != "" ] && [ "$_nocacheFlag" != "0" ] && [ "$_nocacheFlag" != "false" ]; then
echo -n "--no-cache "
fi
}
# return a cli parameter for a single exlude directory
# param string cache directory for local index files
function t_getParamCacheDir(){
if [ ! -z "$1" ]; then
local sCacheDir="$1"
if [ ! -d $sCacheDir ]; then
mkdir -p $sCacheDir
chmod 750 $sCacheDir
fi
echo --cache-dir $sCacheDir
fi
}
# return a cli parameter for a single exlude directory
# param string exlude pattern
function t_getParamExlude(){
test -z "$1" || echo --exclude "'"$*"'"
}
# return a cli parameter for a single exlude directory
# param string exlude pattern
function t_getParamInlude(){
test -z "$1" || echo --include "'"$*"'"
}
# return a cli parameter to use an ssh keyfile
# param string filename if ssh private key file
function t_getParamSshKey(){
if [ ! -z "$1" ]; then
echo $STORAGE_BASEDIR | grep -E "^(scp|sftp|rsync):" >/dev/null
if [ $? -eq 0 ]; then
# scp://backup@storage.example.com//netshare/backup/one
# ^^^^^^^^^^^^^^^^^^^^^^^^^^
# sshtarget fetches user@host ... if protocol matches
local sshtarget=$( echo $STORAGE_BASEDIR | cut -f 3 -d '/' )
echo -o sftp.command="'"ssh -i ${1} ${sshtarget} -s sftp"'"
fi
fi
}
# --------------------------------------------------------------------------------
# BACKUP ACTIONS :: TRANSFER
# --------------------------------------------------------------------------------
# pre backup actions
# uses global vars from ../../transfer.sh
# function t_backupDoPreTasks(){
function t_backupDoPreTasks(){
local _mycmd="restic init ${ARGS_DEFAULT}"
echo $_mycmd
color cmd
eval $_mycmd
local _myrc=$?
color reset
# detect return code ... do not abort on any error.
t_rcCheckInit $_myrc
}
# post backup actions
# uses global vars from ../../transfer.sh
function t_backupDoPostTasks(){
echo "--- CLEANUP local data:"
echo restic cache --cleanup
color cmd
restic cache --cleanup
local _myrc=$?
color reset
case $_myrc in
0) color ok; echo "OK" ;;
*) color error; echo "Cleanup error - returncode was $_myrc" ;;
esac
color reset
echo
echo "--- UNLOCK ... just in case :-)"
echo restic unlock ${ARGS_DEFAULT}
color cmd
eval restic unlock ${ARGS_DEFAULT}
color reset
echo
echo "--- PRUNE"
local _tag=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}tag")
local _keep_h=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}keep-hourly")
local _keep_d=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}keep-daily")
local _keep_w=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}keep-weekly")
local _keep_m=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}keep-monthly")
local _keep_y=$( _j_getvar ${STORAGEFILE} "${CFGPREFIX}keep-yearly")
local _mycmd="restic forget \
${ARGS_DEFAULT} \
--tag $_tag \
--group-by "paths,tags" \
--keep-hourly $_keep_h \
--keep-daily $_keep_d \
--keep-weekly $_keep_w \
--keep-monthly $_keep_m \
--keep-yearly $_keep_y"
echo $_mycmd
color cmd
eval $_mycmd
local _myrc=$?
color reset
t_rcCheckCleanup $_myrc
echo
}
# --------------------------------------------------------------------------------
# BACKUP ACTIONS :: SINGLE DIR
# --------------------------------------------------------------------------------
# get target url/ directory
# param string directory to backup
function t_backupDirGetTarget(){
# directory based target
# j_getFullTarget "$1"
# for host based backup target - remove param:
j_getFullTarget ""
}
function t_backupDirGetCmdBackup(){
echo eval restic backup ${ARGS_DEFAULT} ${ARGS_BACKUP} ${mydir}
}
# pre backup actions
# uses global vars from ../../transfer.sh
function t_backupDirDoPreTasks(){
echo "Nothing to do."
}
# post backup actions
# uses global vars from ../../transfer.sh
function t_backupDirDoPostTasks(){
echo "Nothing to do."
}
# --------------------------------------------------------------------------------
# RESTORE
# --------------------------------------------------------------------------------
# show stored volumes on backup repository
# used in restore; directory param is checked before
# param string name of backup dir, i.e. /etc
function t_restoreDoShowVolumes(){
eval restic snapshots ${ARGS_DEFAULT} --path ${BACKUP_DIR}
}
# select a snapshot to restore from
function t_restoreDoSelect(){
local _selid=
echo "--- Existing snapshots:"
color cmd
t_restoreDoShowVolumes
color reset
showPrompt "ID of the snapshot or \"latest\" to restore from [${RESTORE_ITEM}] >"
read _selid
test -z "$_selid" && _selid=${RESTORE_ITEM}
RESTORE_ITEM=${_selid}
test "$RESTORE_ITEM" = "latest" && RESTORE_ITEMINFO="automatic value"
test "$RESTORE_ITEM" = "latest" || RESTORE_ITEMINFO=$( t_restoreDoShowVolumes | grep "^${RESTORE_ITEM} " | awk '{ print $2 " " $3} ' )
if [ -z "${RESTORE_ITEMINFO}" ]; then
color error
echo ERROR: A napshot ID \"${_selid}\" does not exist.
RESTORE_ITEM=latest
color reset
fi
echo using \"${RESTORE_ITEM}\"
echo
}
# set a filter to reduce count of files to restore
function t_restoreDoSetIncludeFilter(){
local _inc=
echo "You can enter ..."
echo " - a single directory name anywhere in the folderstructure"
echo " - a filename without path"
echo " - a filemask"
showPrompt "Include >"
read _inc
RESTORE_FILTER="$( t_getParamInlude "${_inc}" )"
echo using parameter \"${RESTORE_FILTER}\"
echo
}
# show stored volumes on backup repository
# used in restore; directory param is checked before
# param string name of backup dir, i.e. /etc
function t_restoreDoRestore(){
echo "eval restic restore ${ARGS_DEFAULT} --path ${BACKUP_DIR} --target ${RESTORE_TARGETPATH} ${RESTORE_FILTER} ${RESTORE_ITEM}"
}
# Mount backup data
# see "restic help mount"
function t_restoreDoMountBackupdata(){
local _cmd=
echo "HINT: This feature requires fuse. It works with root on UNIX/ LINUX platforms - not on MS Windows."
echo " It can mount a single directory and shows all snapshots (not only [$RESTORE_ITEM])."
echo
if [ -z "$RESTIC_MOUNTPOINT" ]; then
color error
echo "ERROR: no mountpoint was set in ${STORAGEFILE}; example: restic_mountpoint = /mnt/restore"
color reset
else
j_requireUser "root"
test -d "$RESTIC_MOUNTPOINT" || mkdir -p $RESTIC_MOUNTPOINT
_cmd="restic mount ${ARGS_DEFAULT} $RESTIC_MOUNTPOINT"
test -z "${BACKUP_DIR}" || _cmd="restic mount ${ARGS_DEFAULT} --path ${BACKUP_DIR} $RESTIC_MOUNTPOINT"
echo $_cmd
color cmd
eval $_cmd
color reset
fi
}
# search a file in the given snapshot and backup dir
# param string regex to filter
function t_restoreDoSearchFile(){
eval restic ls ${ARGS_DEFAULT} --path "${BACKUP_DIR}" ${RESTORE_ITEM} | grep -E "$1"
}
# --------------------------------------------------------------------------------
# VERIFY RETURNCODES
# --------------------------------------------------------------------------------
# init repository
function t_rcCheckInit(){
case $1 in
0) color ok; echo "OK - the repository was created." ;;
1) color warning; echo "You can ignore the error if the repository was initialized already." ;;
*) color error; echo "Verify output above - returncode of restic init was $1" ;;
esac
color reset
}
# backup files
function t_rcCheckBackup(){
case $1 in
0) color ok; echo "OK" ;;
1) color error; echo "Unable to connect with restic repository." ;;
*) color error; echo "Backup error - returncode was $1" ;;
esac
color reset
}
# repository cleanup
function t_rcCheckCleanup(){
case $1 in
0) color ok; echo "OK" ;;
*) color error; echo "Cleanup error - returncode was $1" ;;
esac
color reset
}
# restore files
function t_rcCheckRestore(){
case $1 in
0) color ok; echo "OK" ;;
*) color error; echo "Restore error - returncode was $1" ;;
esac
color reset
}
# --------------------------------------------------------------------------------