#!/bin/bash # ====================================================================== # # BACKUP SCRIPTS - CREATE LOCAL DUMPS # # ---------------------------------------------------------------------- # ah - Axel Hahn <axel.hahn@iml.unibe.ch> # ds - Daniel Schueler <daniel.schueler@iml.unibe.ch> # # 2016-11-04 ah,ds created # 2016-11-10 ..... v1.0 # 2017-03-27 ..... added first param for mode backup|restore # 2018-02-09 ..... fix: restore-selection of target uses default on return only # 2021-05-18 ..... move supported backup types to plugins/localdump/[service].sh # 2021-07-13 ..... remove leading ./ in localdump.sh restore # 2022-02-18 ..... WIP: use class like functions # 2022-03-17 ..... WIP: add lines with prefix __DB__ # ====================================================================== # --- variables: # BACKUP_BASEDIR {string} base directory for db dumps # BACKUP_DATE {string} string with current timestamp; will be part of filename for backups # BACKUP_KEEP_DAYS {int} count of days how long to keep db dumps below $BACKUP_BASEDIR # BACKUP_PLUGINDIR {string} scripts for supported databases; [APP]/plugins/localdump # BACKUP_SCRIPT {string} script name of db service # BACKUP_TARGETDIR {string} target directory db dumps of current service # SERVICENAME {string} name of db service (one of mysql|pgsql|...) # ---------------------------------------------------------------------- # CONFIG VARS # ---------------------------------------------------------------------- . `dirname $0`/jobhelper.sh . `dirname $0`/inc_bash.sh # if [ -r ~/.backup.conf ]; then # . ~/.backup.conf # fi if [ ! -r "${JOBFILE}" ]; then color error echo ERROR: missing config file ${JOBFILE}. color reset exit 1 fi LOCALDUMP_LOADED=1 BACKUP_BASEDIR= BACKUP_PLUGINDIR= # Cleanup local dumps older N days typeset -i BACKUP_KEEP_DAYS=0 BACKUP_DATE= # ---------------------------------------------------------------------- # FUNCTIONS 4 DB-WRAPPER # ---------------------------------------------------------------------- function db.init(){ BACKUP_BASEDIR=`_j_getvar ${JOBFILE} "dir-localdumps"` # check if [ -z "$BACKUP_BASEDIR" ]; then color error echo ERROR: missing config for backup target. echo There must be an entry dir-localdumps in ${JOBFILE} color reset exit 1 fi BACKUP_PLUGINDIR=`dirname $0`/plugins/localdump BACKUP_KEEP_DAYS=`_j_getvar ${JOBFILE} "keep-days"` if [ $BACKUP_KEEP_DAYS -eq 0 ]; then BACKUP_KEEP_DAYS=7 fi BACKUP_DATE=$(/bin/date +%Y%m%d-%H%M) } # helpfer function for SERVICENAME.backup # it is called after the service specific dump was done. # param {string} filename of created dump file function db._compressDumpfile(){ local _outfile=$1 # $myrc is last returncode - set in fetchrc if [ $myrc -eq 0 ]; then echo -n "gzip $1 ... " gzip -9 -f "${1}" fetchrc else cecho error "ERROR occured while dumping - no gzip of $_outfile" fi # echo -n "__DB__$SERVICENAME INFO: backup to " # ls -l "$_outfile"* 2>&1 # echo } # ---------------------------------------------------------------------- # FUNCTIONS 4 BACKUP # ---------------------------------------------------------------------- # ------------------------------------------------------------ # cleanup a backup dir: remove old files and delete empty dirs function cleanup_backup_target(){ if [ -d "${BACKUP_TARGETDIR}" ]; then h3 "CLEANUP ${BACKUP_TARGETDIR} older $BACKUP_KEEP_DAYS days ..." echo find "${BACKUP_TARGETDIR}" -mtime +$BACKUP_KEEP_DAYS -delete -print color cmd find "${BACKUP_TARGETDIR}" -mtime +$BACKUP_KEEP_DAYS -delete -print color reset if [ `find "${BACKUP_TARGETDIR}" -type f | wc -l` -eq 0 ]; then echo "INFO: the directory is empty - deleting it" rm -rf "${BACKUP_TARGETDIR}" fi fi } # ------------------------------------------------------------ # compress a file # shared function in localdump_* # param string filename of uncompressed output file function compress_file(){ echo compressing $1 ... gzip -9 -f "${1}" fetchrc } # ------------------------------------------------------------ # create a backup directory with name of service # shared function in localdump_* function create_targetdir(){ mkdir -p "${BACKUP_TARGETDIR}" 2>/dev/null if [ ! -d "${BACKUP_TARGETDIR}" ]; then color error echo FATAL ERROR: directory ${BACKUP_TARGETDIR} was not created color reset exit 1 fi } # ------------------------------------------------------------ # generate a base filename for backup dump based on on db name # ... and added timestamp # param string name of database schema # --> see listBackupedDBs() and guessDB() - these function must be able to split this function get_outfile(){ echo $*__${BACKUP_DATE} } # ------------------------------------------------------------ # get name of a service script # param string name of a service function get_service_script(){ local _service=$1 ls -1 ${BACKUP_PLUGINDIR}/${_service}.sh 2>/dev/null } # ------------------------------------------------------------ # get a list of existing dumper scripts function get_services(){ #ls -1 `dirname $0`/localdump_* | sed "s#`dirname $0`/localdump_##" | sed "s#\.sh##" ls -1 ${BACKUP_PLUGINDIR}/*.sh | sed "s#${BACKUP_PLUGINDIR}/##" | sed "s#\.sh##" | sort } # ------------------------------------------------------------ # show directory infos with count of files and used space # show used space and count of files and dirs function show_info_backup_target(){ if [ -d "${BACKUP_TARGETDIR}" ]; then h3 "INFO about backup target ${BACKUP_TARGETDIR}" echo -n "used space: " du -hs "${BACKUP_TARGETDIR}" echo -n "subdirs : " find "${BACKUP_TARGETDIR}" -type d | wc -l echo -n "files : " find "${BACKUP_TARGETDIR}" -type f | wc -l echo -n "free space: " df -h "${BACKUP_TARGETDIR}" | tail -1 | awk '{ print $4 }' echo fi } # ---------------------------------------------------------------------- # FUNCTIONS 4 RESTORE # ---------------------------------------------------------------------- # ------------------------------------------------------------ # restore: show databases that can be restored # param string file filter optional; function listBackupedDBs(){ if [ -d "${BACKUP_TARGETDIR}" ]; then cd "${BACKUP_TARGETDIR}" if [ -z $1 ]; then find -type f | sed "s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g" | grep -v "\.meta" | sort -ud| sed "s#^\./##g" else ls -ltr "$*__"* | sed "s#^\./##g" fi cd - >/dev/null else color error echo ERROR: ${BACKUP_TARGETDIR} does not exist - here are no backups to restore. color reset echo echo You can try to restore dumps: echo "1) Restore dump files from a backup set" echo " `dirname $0`/restore.sh $BACKUP_BASEDIR" echo "2) Copy restored dumps into $BACKUP_TARGETDIR" echo "3) Start database restore again" echo " `dirname $0`/localdump.sh restore [service]" echo exit 1 fi } # ------------------------------------------------------------ # guess name of the database file # param string filename of db dump; can be full path or not function guessDB(){ dumpfile=$1 # the metafile is written in sqlite backup to store full path metafile=${BACKUP_TARGETDIR}/${dumpfile}.meta if [ -f $metafile ]; then cat $metafile else sBasename=`basename $1` sDb=`echo ${sBasename} | sed "s#__[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9].*##g"` if [ -z $sDb ]; then color error echo ERROR: db name was not detected from file $1 color reset exit 1 fi echo $sDb fi } # ------------------------------------------------------------ # show help # ------------------------------------------------------------ function showhelp(){ local _self _self=$( basename "$0" ) echo "SYNTAX: " echo "$_self [[operation]] [Name_of_ervice] [[more services]]" echo echo "$_self backup [Name_of_service]" echo "$_self restore [Name_of_service] [[file-to-restore]]" echo echo " operation - one of check|backup|restore; optional parameter; default is backup" echo " check WIP: show info only if the service is available" echo " backup dump all databases/ schemes of a given service" echo " restore import a dump into same or new database" echo " Without a filename it starts an interactive mode" echo " Name_of_service - name of database service" echo " You get a list of all available services without parameter" echo " Use ALL for bulk command" echo " file - filename of db dump to restore to origin database scheme" } # ---------------------------------------------------------------------- # INIT # ---------------------------------------------------------------------- db.init # ----- checks # . /usr/local/bin/inc_cronfunctions.sh j_requireUser "root" h1 `date` IML BACKUP :: LOCALDUMP :: $* if [ $# -eq 0 ]; then color error echo "ERROR: missing parameter." color reset echo showhelp echo echo "Known services (see ${BACKUP_PLUGINDIR}):" get_services exit 1 fi mode="backup" case "$1" in backup|check|restore|shell) mode=$1 shift 1 ;; esac export SERVICENAME=$1 BACKUP_TARGETDIR=${BACKUP_BASEDIR}/${SERVICENAME} BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} ) case "$mode" in # ------------------------------------------------------------ check) . $BACKUP_SCRIPT $mode ;; # ------------------------------------------------------------ backup) if [ "$SERVICENAME" = "ALL" ]; then services=$(get_services) echo AUTO: calling local backup scripts for all known services echo $services echo else services=$* fi # ----- check all params for SERVICENAME in $services do BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} ) if [ ! -f $BACKUP_SCRIPT ]; then color error echo ERROR: parameter $SERVICENAME seems to be wrong. echo The backup script does not exist: $BACKUP_SCRIPT color reset echo echo services in this folder are: get_services exit 2 fi done # ----- GO for SERVICENAME in $services do BACKUP_TARGETDIR=${BACKUP_BASEDIR}/${SERVICENAME} BACKUP_SCRIPT=$( get_service_script ${SERVICENAME} ) # ----- start service specific script h2 "START SCRIPT FOR ${SERVICENAME} - $BACKUP_SCRIPT" _j_runHooks "12-before-db-service" . $BACKUP_SCRIPT $mode _j_runHooks "18-after-db-service" "$rc" # ----- post jobs: cleanup cleanup_backup_target show_info_backup_target done ;; # ------------------------------------------------------------ restore) h1 "RESTORE DATABASE" if [ -z $2 ]; then # ----- file selection h2 "select database" listBackupedDBs color input echo -n "name of db to restore >" color reset read fileprefix echo h2 "select a specific dump for that database" listBackupedDBs $fileprefix color input echo -n "backupset to import >" color reset read dbfile echo color input sTargetDb=`guessDB ${dbfile}` echo -n "new database name [$sTargetDb] >" color reset read sTargetDb if [ -z $sTargetDb ]; then sTargetDb=`guessDB ${dbfile}` fi echo sDumpfile="${BACKUP_TARGETDIR}/${dbfile}" else sDumpfile=$2 sTargetDb=$3 fi shift 2 # ----- start restore if [ ! -f "${sDumpfile}" ]; then color error echo ERROR: ${sDumpfile} is not a file color reset rc=$rc+1 else . $BACKUP_SCRIPT $mode "${sDumpfile}" "${sTargetDb}" if [ $? -ne 0 -o $rc -ne 0 ]; then color error echo ERROR: $mode failed. See ouput above. :-/ color reset else color ok echo OK, $mode was successful. color reset fi fi ;; # ------------------------------------------------------------ shell) export BACKUP_TARGETDIR . $BACKUP_SCRIPT ( mycmd= echo echo "Starting interactive shell..." echo echo "STATUS: STILL ALPHA as long existing db plugins are not rewritten." echo echo "INFO: Try ${SERVICENAME}.help to see database specific commands." echo "INFO: Type exit and return to leave the shell." echo while [ ! "$mycmd" = "exit" ]; do echo -n "[${SERVICENAME}]" color input echo -n " $( pwd )" color reset echo -n " % " read -r mycmd if [ ! "$mycmd" = "exit" ];then color cmd eval $mycmd color reset fi done ) ;; esac echo _______________________________________________________________________________ echo STATUS $0 exit with final returncode rc=$rc exit $rc # ----------------------------------------------------------------------