Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • liberate/backupninja
  • Benzhaomin/backupninja
  • ergonlogic/backupninja
  • louis/backupninja
  • guido/backupninja
  • ibauer/backupninja
  • romain/backupninja
  • gsubiron/backupninja
  • davidkg/backupninja
  • fkrauthan/backupninja
  • Glandos/backupninja
  • lyz/backupninja
  • nosmo/backupninja
  • orel/backupninja
  • raabf/backupninja
  • wu-lee/backupninja
  • huthamcau/backupninja
  • julien/backupninja
  • sensespidey/backupninja
  • LeLutin/backupninja
  • raT/backupninja
  • petrklima/backupninja
  • fancsali/backupninja
  • ko7ashiV/backupninja
  • yova/backupninja
  • jipem/backupninja
  • debian-janitor/backupninja
  • phlummox/backupninja
  • e1k/backupninja
  • jonhattan_/backupninja
  • illuusio/backupninja
  • maethor/backupninja
32 results
Select Git revision
Show changes
Commits on Source (36)
...@@ -68,3 +68,5 @@ Glandos <bugs-0xacab@antipoul.fr> -- sys excludes zram devices ...@@ -68,3 +68,5 @@ Glandos <bugs-0xacab@antipoul.fr> -- sys excludes zram devices
Nicolas Karolak <nicolas@karolak.fr> -- Add restic support Nicolas Karolak <nicolas@karolak.fr> -- Add restic support
Derek Laventure -- Add restic helper Derek Laventure -- Add restic helper
Colan Schwartz -- Fix restic options handler Colan Schwartz -- Fix restic options handler
... and other contributors, thank you!
...@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file. ...@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.2.2] - 2023-02-12
### Added
- [core] prometheus metrics for backup status
### Changed
- [borg] don't hardcode ssh port default (DEBBUG-993951)
- [core] allow for disabling reports by email
### Fixed
- [borg] fix borg example: set cache_directory to source section
- [dup] actually run duplicity `remove-older-than` (#11345)
- [dup] remove default duplicity option `--extra-clean` (#11335)
- [dup.helper] re-add a version of the do_dup_src function (#11332)
- [restic] restic handler doesn't initialize test variable (#11328)
- [sys] remove old LUKS header backup file prior to regenerating (#11333)
- [sys] don't cancel partition backup if hwinfo absent (#11333)
- [tests] fixes and improvements
## [1.2.1] - 2021-01-25 ## [1.2.1] - 2021-01-25
### Added ### Added
......
...@@ -17,6 +17,7 @@ Vagrant.configure("2") do |config| ...@@ -17,6 +17,7 @@ Vagrant.configure("2") do |config|
remote.vm.hostname = "bntest1" remote.vm.hostname = "bntest1"
remote.vm.network "private_network", ip: "192.168.181.5" remote.vm.network "private_network", ip: "192.168.181.5"
remote.vm.provision "shell", inline: <<-SHELL remote.vm.provision "shell", inline: <<-SHELL
export DEBIAN_FRONTEND=noninteractive
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen locale-gen
apt-get update apt-get update
...@@ -36,6 +37,7 @@ Vagrant.configure("2") do |config| ...@@ -36,6 +37,7 @@ Vagrant.configure("2") do |config|
local.vm.hostname = "bntest0" local.vm.hostname = "bntest0"
local.vm.network "private_network", ip: "192.168.181.4" local.vm.network "private_network", ip: "192.168.181.4"
local.vm.provision "shell", inline: <<-SHELL local.vm.provision "shell", inline: <<-SHELL
export DEBIAN_FRONTEND=noninteractive
echo "root: vagrant" >> /etc/aliases echo "root: vagrant" >> /etc/aliases
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen locale-gen
......
...@@ -11,7 +11,7 @@ URL: https://0xacab.org/liberate/backupninja ...@@ -11,7 +11,7 @@ URL: https://0xacab.org/liberate/backupninja
Source: %{name}-%{version}.tar.gz Source: %{name}-%{version}.tar.gz
Requires: bash, gawk, rdiff-backup, gzip Requires: bash, gawk, rdiff-backup, gzip
Provides: %{name} Provides: %{name}
Packager: Petr Klima <Petr.Klima@madeta-group.cz> Packager: Petr Klima <Petr.Klima@madeta.cz>
BuildRoot: %{_tmppath}/%{name}-%{version} BuildRoot: %{_tmppath}/%{name}-%{version}
Prefix: %{_prefix} Prefix: %{_prefix}
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# The maintainer mode is causing me grief with newest versions of autotools # The maintainer mode is causing me grief with newest versions of autotools
#AM_MAINTAINER_MODE #AM_MAINTAINER_MODE
AC_INIT([backupninja],[1.2.1],[backupninja@lists.riseup.net]) AC_INIT([backupninja],[1.2.2],[backupninja@lists.riseup.net])
AC_CONFIG_SRCDIR([src/backupninja.in]) AC_CONFIG_SRCDIR([src/backupninja.in])
AM_INIT_AUTOMAKE([foreign]) AM_INIT_AUTOMAKE([foreign])
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
# 1 -- Fatal errors (only) # 1 -- Fatal errors (only)
loglevel = 4 loglevel = 4
# Produce prometheus metrics of backup status (default = no).
# Requires `prometheus-node-exporter` to be installed
reportprom = false
# send a summary of the backup status to # send a summary of the backup status to
# this email address: # this email address:
reportemail = root reportemail = root
......
...@@ -11,6 +11,13 @@ ...@@ -11,6 +11,13 @@
## destination host and user. ## destination host and user.
## ##
## passed directly to borg commands, e.g. to use a particular remote
## borg executable (here: 'borg12'), set this to:
## options = --remote-path=borg12
##
## Default:
# options =
## default is 0, but set to 19 if you want to lower the priority. ## default is 0, but set to 19 if you want to lower the priority.
## an example setting would be: ## an example setting would be:
## nicelevel = 19 ## nicelevel = 19
...@@ -39,6 +46,22 @@ ...@@ -39,6 +46,22 @@
## Default: ## Default:
# bwlimit = 0 # bwlimit = 0
## export "borg info last_archive" to a given file
## this is usefull for monitoring without using borg
##
## Example:
# borginfo = /var/backups/borginfo.json
## Default:
# borginfo =
## export "borg list repository" to a given file
## this is usefull for monitoring without using borg
##
## Example
# borglist = /var/backups/borglist.json
## Default:
# borglist =
###################################################### ######################################################
## source section ## source section
## (where the files to be backed up are coming from) ## (where the files to be backed up are coming from)
...@@ -107,6 +130,16 @@ exclude = /var/lib/mysql ...@@ -107,6 +130,16 @@ exclude = /var/lib/mysql
## Default: ## Default:
# keep = 30d # keep = 30d
## define hourly, daily, weekly and monthly retention for the "borg prune" operation.
##
## theses options will be ignored if set to 0
##
## Default:
## keephourly = 0
## keepdaily = 0
## keepweekly = 0
## keepmonthly = 0
## define extra command-line options for the "borg prune" operation. ## define extra command-line options for the "borg prune" operation.
## ##
## Example: ## Example:
...@@ -117,15 +150,32 @@ exclude = /var/lib/mysql ...@@ -117,15 +150,32 @@ exclude = /var/lib/mysql
## Default: ## Default:
# prune_options = # prune_options =
## by default borg emits a warning when a source file or directory ## Path to the directory that will hold borg's cache files. By default this is
## vanishes during the backup operations ## empty, which will let borg use its default path of "~/.cache/borg".
## set to yes to ignore such warnings ##
## Default:
# cache_directory =
## by default borg emits various warnings that are impossible to check on large
## infrastructures.
## - when some files/repositories included in borg create does not exists
## - when some files have changed during the backup (happens a lot on log files)
## This option allows to disable these warning.
## ##
## Example: ## Default:
## ignore_missing = yes # filter_warnings = yes
## when filter_warning == yes, allows to choose to disable warning if
## file changed during backup
##
## Default:
# warning_if_file_changed_during_backup = yes
## when warning_if_file_changed_during_backup == yes, allows to ignore some
## paths or filenames.
## ##
## Default: ## Default:
# ignore_missing = # file_changes_to_ignore = /
###################################################### ######################################################
## destination section ## destination section
...@@ -201,12 +251,6 @@ exclude = /var/lib/mysql ...@@ -201,12 +251,6 @@ exclude = /var/lib/mysql
## Default: ## Default:
# passphrase = # passphrase =
## Path to the directory that will hold borg's cache files. By default this is
## empty, which will let borg use its default path of "~/.cache/borg".
##
## Default:
# cache_directory =
## command-line options to use with ssh ## command-line options to use with ssh
## ##
## an example setting would be: ## an example setting would be:
......
...@@ -294,6 +294,13 @@ do_borg_finish() { ...@@ -294,6 +294,13 @@ do_borg_finish() {
## - example.borg ## - example.borg
## - $borg_docs ## - $borg_docs
## passed directly to borg commands, e.g. to use a particular remote
## borg executable (here: 'borg12'), set this to:
## options = --remote-path=borg12
##
## Default:
# options =
[source] [source]
EOF EOF
## includes ## ## includes ##
......
#!/bin/bash
# shellcheck shell=bash
# shellcheck disable=SC2154
# -*- mode: sh; sh-basic-offset: 3; indent-tabs-mode: nil; -*- # -*- mode: sh; sh-basic-offset: 3; indent-tabs-mode: nil; -*-
# vim: set filetype=sh sw=3 sts=3 expandtab autoindent: # vim: set filetype=sh sw=3 sts=3 expandtab autoindent:
# #
...@@ -22,11 +25,15 @@ export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes ...@@ -22,11 +25,15 @@ export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
### GET CONFIG ### ### GET CONFIG ###
getconf options
getconf testconnect yes getconf testconnect yes
getconf nicelevel 0 getconf nicelevel 0
getconf ionicelevel getconf ionicelevel
getconf bwlimit getconf bwlimit
getconf borginfo
getconf borglist
setsection source setsection source
getconf init yes getconf init yes
getconf include getconf include
...@@ -34,18 +41,24 @@ getconf exclude ...@@ -34,18 +41,24 @@ getconf exclude
getconf create_options getconf create_options
getconf prune yes getconf prune yes
getconf keep 30d getconf keep 30d
getconf keephourly 0
getconf keepdaily 0
getconf keepweekly 0
getconf keepmonthly 0
getconf prune_options getconf prune_options
getconf cache_directory getconf cache_directory
getconf ignore_missing getconf filter_warnings yes
getconf warning_if_file_changed_during_backup yes
getconf file_changes_to_ignore /
setsection dest setsection dest
getconf user getconf user
getconf host getconf host
getconf port 22 getconf port
getconf directory getconf directory
# strip trailing / # strip trailing /
directory=${directory%/} directory=${directory%/}
getconf archive {now:%Y-%m-%dT%H:%M:%S} getconf archive "{now:%Y-%m-%dT%H:%M:%S}"
getconf compression lz4 getconf compression lz4
getconf encryption none getconf encryption none
getconf passphrase getconf passphrase
...@@ -69,7 +82,7 @@ fi ...@@ -69,7 +82,7 @@ fi
if [ "$host" != "localhost" ]; then if [ "$host" != "localhost" ]; then
[ -n "$user" ] || fatal "Destination user not set" [ -n "$user" ] || fatal "Destination user not set"
[ -n "$host" ] || fatal "Destination host not set" [ -n "$host" ] || fatal "Destination host not set"
execstr_repository="ssh://${user}@${host}:${port}${directory}" execstr_repository="ssh://${user}@${host}${port:+:${port}}${directory}"
else else
execstr_repository="$directory" execstr_repository="$directory"
fi fi
...@@ -103,21 +116,23 @@ fi ...@@ -103,21 +116,23 @@ fi
# check the connection at the source and destination # check the connection at the source and destination
[ -n "$test" ] || test=0 [ -n "$test" ] || test=0
# shellcheck disable=SC2235
if [ "$host" != "localhost" ] && ([ "$testconnect" = "yes" ] || [ "${test}" -eq 1 ]); then if [ "$host" != "localhost" ] && ([ "$testconnect" = "yes" ] || [ "${test}" -eq 1 ]); then
debug "ssh $sshoptions -o PasswordAuthentication=no $host -p $port -l $user 'echo -n 1'" teststr="ssh $sshoptions -o PasswordAuthentication=no ${host}${port:+ -p ${port}} -l $user 'echo -n 1'"
local ret=`ssh $sshoptions -o PasswordAuthentication=no $host -p $port -l $user 'echo -n 1'` debug "$teststr"
ret=$(su -c "$teststr")
if [ "$ret" = 1 ]; then if [ "$ret" = 1 ]; then
debug "Connected to $host as $user successfully" debug "Connected to $host as $user successfully"
else else
teststr="borg list --show-rc -v $execstr_repository" teststr="borg list $options --show-rc -v $execstr_repository"
debug "$teststr" debug "$teststr"
output=`su -c "$teststr" 2>&1` output=$(su -c "$teststr" 2>&1)
if echo "$output" | grep "terminating with success status" \ if echo "$output" | grep "terminating with success status" \
|| echo "$output" | grep "^\S\+ is not a valid repository." \ || echo "$output" | grep "^\S\+ is not a valid repository." \
|| echo "$output" | grep "^Repository \S\+ does not exist."; then || echo "$output" | grep "^Repository \S\+ does not exist."; then
debug "Connected to $host as $user successfully (forced command)" debug "Connected to $host as $user successfully (forced command)"
else else
error $output error "$output"
fatal "Can't connect to $host as $user." fatal "Can't connect to $host as $user."
fi fi
fi fi
...@@ -126,16 +141,16 @@ fi ...@@ -126,16 +141,16 @@ fi
### INIT IF NEEDED ### ### INIT IF NEEDED ###
if [ "$init" == "yes" ]; then if [ "$init" == "yes" ]; then
initstr="borg init --encryption=$encryption $execstr_repository" initstr="borg init $options --encryption=$encryption $execstr_repository"
debug "executing borg init" debug "executing borg init"
debug "$initstr" debug "$initstr"
if [ $test = 0 ]; then if [ "$test" = 0 ]; then
output="`su -c "$initstr" 2>&1`" output="$(su -c "$initstr" 2>&1)"
if [ $? = 2 ]; then if [ $? = 2 ]; then
debug $output debug "$output"
info "Repository was already initialized" info "Repository was already initialized"
else else
warning $output warning "$output"
warning "Repository has been initialized" warning "Repository has been initialized"
fi fi
fi fi
...@@ -143,7 +158,7 @@ fi ...@@ -143,7 +158,7 @@ fi
### EXECUTE ### ### EXECUTE ###
execstr="borg create --stats --compression $compression" execstr="borg create $options --stats --compression $compression"
set -o noglob set -o noglob
...@@ -165,11 +180,11 @@ IFS=$SAVEIFS ...@@ -165,11 +180,11 @@ IFS=$SAVEIFS
set +o noglob set +o noglob
if [ ! -z $bwlimit ]; then if [ -n "$bwlimit" ]; then
execstr="${execstr} --remote-ratelimit=${bwlimit}" execstr="${execstr} --remote-ratelimit=${bwlimit}"
fi fi
if [ ! -z "$create_options" ]; then if [ -n "$create_options" ]; then
execstr="${execstr} ${create_options}" execstr="${execstr} ${create_options}"
fi fi
...@@ -179,24 +194,50 @@ execstr="${execstr} ${excludes} $execstr_repository::$execstr_archive ${includes ...@@ -179,24 +194,50 @@ execstr="${execstr} ${excludes} $execstr_repository::$execstr_archive ${includes
debug "executing borg create" debug "executing borg create"
debug "$nice $execstr" debug "$nice $execstr"
if [ $test = 0 ]; then if [ "$test" = 0 ]; then
output=`$nice su -c "$execstr" 2>&1` output=$($nice su -c "$execstr" 2>&1)
ret=$? ret=$?
if [ $ret = 0 ]; then if [ $ret = 0 ]; then # borg ok
debug $output debug "$output"
info "Successfully finished backing up source." info "Successfully finished backing up source"
elif [ $ret = 1 ]; then elif [ $ret = 1 ]; then # borg warning
warnmsg=$(echo "$output" | @SED@ -n '1,/^-\+$/{x;p;d;}; x' | @SED@ '/^$/d') # Borg can return 1 for warnings that are impossible to manually check on large infrastructures :
if [ "$ignore_missing" = "yes" ] && ! echo "$warnmsg" | grep -qv '\[Errno 2\] No such file or directory:'; then # - when some files/repositories included in borg create does not exist
debug $output # - when some files have changed during the backup (happens a lot on log files)
info "Backing up source finished with missing file warnings." # So we need to filter the output line by line to print these warnings as debug.
# Warnings are always printed by borg before a long "---…" line.
# So we break the output in two along this line.
warning_output=$(echo "$output" | sed '/----------------------------/Q') # Before -----…
debug_output=$(echo "$output" | sed -n '/----------------------------/,$p') # After -----…
if [ -n "$warning_output" ] && [ "$filter_warnings" == "yes" ]; then
if [ "$warning_if_file_changed_during_backup" == "yes" ]; then
file_changes_to_ignore="($file_changes_to_ignore"'|/var/lib/apt/.*|/var/backups/atop/.*|/var/lib/postfix/.*|.*prometheus.*/wal/.*|.*/pg_wal/.*|.*/upload(s)?/.*|.*/(page_)?cache/.*|/var/mail/.*|.*/log(s)?/.*|.*/var/log/.*|.*(log\.json|\.log|\.err|\.wal|\.rrd(4j)?|\.wsp|\.part|\.seq|\.out|\.aof|\.rdb))'
else else
warning $output file_changes_to_ignore=""
warning "Backing up source finished with warnings."
fi fi
non_warning_regex="(Attempting to access a previously unknown unencrypted repository|The repository at location.*was previously located at|Do you want to continue?|No such file or directory|$file_changes_to_ignore: file changed while we backed it up|Using a pure-python msgpack! This will result in lower performance.|/var/backups/drbd/.*scandir.)"
echo "$warning_output" | while IFS= read -r line; do
if echo "$line" | grep -Eq "$non_warning_regex"; then
debug "$line"
else else
error $output warning "$line"
fatal "Failed backing up source." fi
done
debug "$debug_output"
info "Successfully finished backing up source"
else
warning "$output"
warning "Backing up source finished with warnings."
fi
elif [ "$ret" -gt 128 ]; then # borg killed by linux signal
signal=$((ret-128))
warning "$output"
fatal "Failed backing up source. Borg killed by signal ${signal}."
else # borg error
error "$output"
fatal "Failed backing up source. Borg returned exit code ${ret}."
fi fi
fi fi
...@@ -207,21 +248,88 @@ if [ "$prune" == "yes" ]; then ...@@ -207,21 +248,88 @@ if [ "$prune" == "yes" ]; then
if [ ! "$keep" == "0" ]; then if [ ! "$keep" == "0" ]; then
prune_options="${prune_options} --keep-within=${keep}" prune_options="${prune_options} --keep-within=${keep}"
fi fi
prunestr="borg prune $prune_options $execstr_repository" if [ ! "$keephourly" == "0" ]; then
prune_options="${prune_options} --keep-hourly=${keephourly}"
fi
if [ ! "$keepdaily" == "0" ]; then
prune_options="${prune_options} --keep-daily=${keepdaily}"
fi
if [ ! "$keepweekly" == "0" ]; then
prune_options="${prune_options} --keep-weekly=${keepweekly}"
fi
if [ ! "$keepmonthly" == "0" ]; then
prune_options="${prune_options} --keep-monthly=${keepmonthly}"
fi
prunestr="borg prune $options $prune_options $execstr_repository"
debug "executing borg prune" debug "executing borg prune"
debug "$prunestr" debug "$prunestr"
if [ $test = 0 ]; then if [ "$test" = 0 ]; then
output="`su -c "$prunestr" 2>&1`" output="$(su -c "$prunestr" 2>&1)"
ret=$? ret=$?
if [ $ret = 0 ]; then if [ $ret = 0 ]; then
debug $output debug "$output"
info "Removing old backups succeeded." info "Removing old backups succeeded."
if [[ "$(borg --version)" > "borg 1.2" ]] ; then
compactstr="borg compact $execstr_repository"
debug "$compactstr"
output="$(su -c "$compactstr" 2>&1)"
ret=$?
if [ $ret = 0 ]; then
debug "$output"
info "Compacting borg repository succeeded."
else
info "$output"
warning "Compacting borg repository failed. Borg returned exit code ${ret}."
fi
fi
elif [ $ret = 1 ]; then elif [ $ret = 1 ]; then
warning $output warning "$output"
warning "Removing old backups finished with warnings." warning "Removing old backups finished with warnings."
else else
error $output info "$output"
fatal "Failed removing old backups." warning "Failed removing old backups. Borg returned exit code ${ret}."
fi
fi
fi
### WRITE STATS FILES ###
if [ -n "$borginfo" ]; then
mkdir -p "$(dirname "$borginfo")"
infostr="borg info $execstr_repository --last 1 --json > $borginfo"
debug "$infostr"
if [ "$test" = 0 ]; then
output=$(su -c "$infostr" 2>&1)
ret=$?
if [ $ret = 0 ]; then
debug "$output"
info "Successfully writing borg info to $borginfo"
else
info "$output"
error "Failed to write borg info to $borginfo"
fi
fi
fi
if [ -n "$borglist" ]; then
mkdir -p "$(dirname "$borglist")"
infostr="borg list $execstr_repository --json > $borglist"
debug "$infostr"
if [ "$test" = 0 ]; then
output=$(su -c "$infostr" 2>&1)
ret=$?
if [ $ret = 0 ]; then
debug "$output"
info "Successfully writing borg list to $borglist"
else
info "$output"
error "Failed to write borg list to $borglist"
fi fi
fi fi
fi fi
......
...@@ -313,7 +313,7 @@ fi ...@@ -313,7 +313,7 @@ fi
if [ "$keep" != "yes" ]; then if [ "$keep" != "yes" ]; then
debug "executing duplicity remove-older-than" debug "executing duplicity remove-older-than"
debug "$nice $execstr_precmd duplicity remove-older-than $keep --force $execstr_options $execstr_serverpart" debug "$nice $execstr_precmd duplicity remove-older-than $keep --force $execstr_options $execstr_serverpart"
if [ ! $test ]; then if [ $test = 0 ]; then
export PASSPHRASE=$password export PASSPHRASE=$password
export SIGN_PASSPHRASE=$signpassword export SIGN_PASSPHRASE=$signpassword
export FTP_PASSWORD=$ftp_password export FTP_PASSWORD=$ftp_password
......
...@@ -230,6 +230,8 @@ else ...@@ -230,6 +230,8 @@ else
fi fi
fi fi
[ -n "$test" ] || test=0
### INIT ####################################################################### ### INIT #######################################################################
if [ "$need_init" = "yes" ]; then if [ "$need_init" = "yes" ]; then
......
...@@ -566,7 +566,7 @@ if [ "$partitions" == "yes" ] || [ "$luksheaders" == "yes" ] || [ "$mbr" == "yes ...@@ -566,7 +566,7 @@ if [ "$partitions" == "yes" ] || [ "$luksheaders" == "yes" ] || [ "$mbr" == "yes
if [ "$devparts" == "" ]; then if [ "$devparts" == "" ]; then
info "No partitions found on this system." info "No partitions found on this system."
else else
info "$(echo "Partitions found: $partitions" | tr "\n" " ")" info "$(echo "Partitions found: $devparts" | tr "\n" " ")"
fi fi
fi fi
......
...@@ -81,6 +81,7 @@ For example: ...@@ -81,6 +81,7 @@ For example:
when = manual when = manual
These values for "when" are invalid: These values for "when" are invalid:
when = everyday at 5:00
when = tuesday at 2am when = tuesday at 2am
when = tuesday at 2 when = tuesday at 2
when = tues at 02 when = tues at 02
......
...@@ -42,6 +42,11 @@ How verbose to make the logs. ...@@ -42,6 +42,11 @@ How verbose to make the logs.
.br .br
1 = Fatal errors 1 = Fatal errors
.TP
.B reportprom
Produce prometheus metrics of backup status. Requires `prometheus-node-exporter`
to be installed.
.TP .TP
.B reportemail .B reportemail
Send a summary of the backup status to this email address. Send a summary of the backup status to this email address.
......
...@@ -513,6 +513,8 @@ setfile $conffile ...@@ -513,6 +513,8 @@ setfile $conffile
getconf configdirectory @CFGDIR@/backup.d getconf configdirectory @CFGDIR@/backup.d
getconf scriptdirectory @datadir@ getconf scriptdirectory @datadir@
getconf reportdirectory getconf reportdirectory
getconf reportprom
getconf prom_textfile_dir /var/lib/prometheus/node-exporter
getconf reportemail getconf reportemail
getconf reporthost getconf reporthost
getconf reportspace getconf reportspace
...@@ -601,10 +603,10 @@ for file in $files; do ...@@ -601,10 +603,10 @@ for file in $files; do
fi fi
done done
## mail the messages to the report address ## reporting
if [ $actions_run == 0 ]; then doit=0 if [ $actions_run == 0 ]; then doit=0
elif [ "$reportemail" == "" ]; then doit=0 elif [ "$reportemail" == "" -a "$reportprom" == "" ]; then doit=0
elif [ $fatals != 0 ]; then doit=1 elif [ $fatals != 0 ]; then doit=1
elif [ $errors != 0 ]; then doit=1 elif [ $errors != 0 ]; then doit=1
elif [ $halts != 0 ]; then doit=1 elif [ $halts != 0 ]; then doit=1
...@@ -614,6 +616,52 @@ else doit=0 ...@@ -614,6 +616,52 @@ else doit=0
fi fi
if [ $doit == 1 ]; then if [ $doit == 1 ]; then
if [ ! -z "$reportprom" ]; then
if [ -d "$prom_textfile_dir" ]; then
debug "reporting to prometheus"
hostname=`hostname`
# set some defaults, so there aren't empty strings
[ ! -z $warnings ] || warnings=0
[ ! -z $errors ] || errors=0
[ ! -z $fatals ] || fatals=0
[ ! -z $halts ] || halts=0
[ ! -z $actions_run ] || actions_run=0
output_file="${prom_textfile_dir}/backupninja.prom"
tmp_file="${output_file}.$$"
trap "rm -f $tmp_file 2>/dev/null" EXIT INT TERM
cat <<EOF > "$tmp_file"
# HELP backupninja_warnings Number of warnings reported by Backupninja
# TYPE backupninja_warnings gauge
# HELP backupninja_errors Number of errors reported by Backupninja
# TYPE backupninja_errors gauge
# HELP backupninja_fatals Number of fatals reported by Backupninja
# TYPE backupninja_fatals gauge
# HELP backupninja_halts Number of halts reported by Backupninja
# TYPE backupninja_halts gauge
# HELP backupninja_actions Number of actions run by Backupninja
# TYPE backupninja_actions gauge
backupninja_warnings{host="$hostname"} $warnings
backupninja_errors{host="$hostname"} $errors
backupninja_fatals{host="$hostname"} $fatals
backupninja_halts{host="$hostname"} $halts
backupninja_actions{host="$hostname"} $actions_run
EOF
if [ $? -gt 0 ]; then
rm -f "$tmp_file" 2>/dev/null
error "could not write metrics to ${prom_textfile_dir}!"
let "errors +-1"
else
mv -f "$tmp_file" "$output_file"
chmod 0644 "$output_file"
fi
else
error "$prom_textfile_dir does not exist!"
let "errors +-1"
fi
fi
if [ ! -z "$reportemail" ]; then
if [ -x "$(which mail 2>/dev/null)" ]; then if [ -x "$(which mail 2>/dev/null)" ]; then
debug "send report to $reportemail" debug "send report to $reportemail"
hostname=`hostname` hostname=`hostname`
...@@ -643,7 +691,7 @@ if [ $doit == 1 ]; then ...@@ -643,7 +691,7 @@ if [ $doit == 1 ]; then
let "errors += 1" let "errors += 1"
fi fi
fi fi
fi
if [ $actions_run != 0 ]; then if [ $actions_run != 0 ]; then
info "FINISHED: $actions_run actions run. $fatals fatal. $errors error. $warnings warning." info "FINISHED: $actions_run actions run. $fatals fatal. $errors error. $warnings warning."
if [ "$halts" != "0" ]; then if [ "$halts" != "0" ]; then
......
load common load common
begin_backupninja() {
install_pkgs prometheus-node-exporter
}
teardown_backupninja() { teardown_backupninja() {
[ -x /usr/bin/mail.moved ] && mv /usr/bin/mail.moved /usr/bin/mail [ -x /usr/bin/mail.moved ] && mv /usr/bin/mail.moved /usr/bin/mail
[ -x /usr/bin/rsync.moved ] && mv /usr/bin/rsync.moved /usr/bin/rsync [ -x /usr/bin/rsync.moved ] && mv /usr/bin/rsync.moved /usr/bin/rsync
rm -f /var/mail/vagrant
rm -f /var/lib/prometheus/node-exporter/backupninja.prom
} }
create_test_action() { create_test_action() {
...@@ -235,6 +241,24 @@ create_test_action() { ...@@ -235,6 +241,24 @@ create_test_action() {
[ "$status" -eq 1 ] [ "$status" -eq 1 ]
} }
@test "reports: email not sent when reportemail is no" {
create_test_action info test_info
setconfig backupninja.conf reportsuccess yes
setconfig backupninja.conf reportinfo yes
setconfig backupninja.conf reportemail no
run backupninja --now -f "${BATS_TMPDIR}/backupninja.conf" --run "${BATS_TMPDIR}/backup.d/test.sh"
[ "$status" -eq 0 ]
! test -f /var/mail/vagrant
}
@test "reports: writes prometheus metrics when reportprom is yes" {
create_test_action info test_info
setconfig backupninja.conf reportprom yes
run backupninja --now -f "${BATS_TMPDIR}/backupninja.conf" --run "${BATS_TMPDIR}/backup.d/test.sh"
[ "$status" -eq 0 ]
grep -q "^backupninja_actions{host=\"$(hostname)\"} 1$" /var/lib/prometheus/node-exporter/backupninja.prom
}
@test "scheduling: runs when = 'everyday at 01' and time matches" { @test "scheduling: runs when = 'everyday at 01' and time matches" {
create_test_action info test_info create_test_action info test_info
setconfig backupninja.conf when 'everyday at 01' setconfig backupninja.conf when 'everyday at 01'
......
load common load common
begin_borg() { begin_borg() {
apt-get -qq install debootstrap borgbackup install_pkgs debootstrap borgbackup
if [ ! -d "$BN_SRCDIR" ]; then if [ ! -d "$BN_SRCDIR" ]; then
debootstrap --variant=minbase testing "$BN_SRCDIR" debootstrap --variant=minbase testing "$BN_SRCDIR"
fi fi
...@@ -28,7 +28,6 @@ cache_directory = ...@@ -28,7 +28,6 @@ cache_directory =
[dest] [dest]
user = user =
host = localhost host = localhost
port = 22
directory = ${BN_BACKUPDIR}/testborg directory = ${BN_BACKUPDIR}/testborg
archive = archive =
compression = lz4 compression = lz4
...@@ -219,16 +218,16 @@ finish_borg() { ...@@ -219,16 +218,16 @@ finish_borg() {
greplog 'Debug: export BORG_CACHE_DIR="/var/cache/borg"$' greplog 'Debug: export BORG_CACHE_DIR="/var/cache/borg"$'
} }
@test "check config parameter source/ignore_missing" { @test "check config parameter source/filter_warnings" {
setconfig source ignore_missing yes delconfig source include
setconfig_repeat source include "$BN_SRCDIR/boot" "$BN_SRCDIR/etc" "$BN_SRCDIR/lib" "$BN_SRCDIR/var" "$BN_SRCDIR/foo"
setconfig source filter_warnings yes
setconfig dest archive testarchive setconfig dest archive testarchive
setconfig dest encryption none setconfig dest encryption none
setconfig dest compression zstd,16 setconfig dest compression zstd,16
delconfig dest passphrase delconfig dest passphrase
cleanup_backups local cleanup_backups local
( sleep 1; mv /var/cache/bntest/lib /tmp ) &
runaction runaction
mv /tmp/lib /var/cache/bntest
greplog "Info: Backing up source finished with missing file warnings." greplog "Info: Backing up source finished with missing file warnings."
} }
...@@ -243,8 +242,8 @@ finish_borg() { ...@@ -243,8 +242,8 @@ finish_borg() {
setconfig dest user "$BN_REMOTEUSER" setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST" setconfig dest host "$BN_REMOTEHOST"
testaction testaction
greplog "Debug: ssh\s\+-o PasswordAuthentication=no ${BN_REMOTEHOST} -p 22 -l ${BN_REMOTEUSER} 'echo -n 1'" greplog "Debug: ssh\s\+-o PasswordAuthentication=no ${BN_REMOTEHOST} -l ${BN_REMOTEUSER} 'echo -n 1'"
greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::" greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}${BN_BACKUPDIR}/testborg::"
} }
@test "check config parameter dest/host" { @test "check config parameter dest/host" {
...@@ -258,18 +257,18 @@ finish_borg() { ...@@ -258,18 +257,18 @@ finish_borg() {
setconfig dest user "$BN_REMOTEUSER" setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST" setconfig dest host "$BN_REMOTEHOST"
testaction testaction
greplog "Debug: ssh\s\+-o PasswordAuthentication=no ${BN_REMOTEHOST} -p 22 -l ${BN_REMOTEUSER} 'echo -n 1'" greplog "Debug: ssh\s\+-o PasswordAuthentication=no ${BN_REMOTEHOST} -l ${BN_REMOTEUSER} 'echo -n 1'"
greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::" greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}${BN_BACKUPDIR}/testborg::"
} }
@test "check config parameter dest/port" { @test "check config parameter dest/port" {
# absent parameter, defaults to 22 # absent parameter, port not specified
setconfig dest user "$BN_REMOTEUSER" setconfig dest user "$BN_REMOTEUSER"
setconfig dest host "$BN_REMOTEHOST" setconfig dest host "$BN_REMOTEHOST"
delconfig dest port delconfig dest port
testaction testaction
greplog "Debug: ssh\s\+ -o PasswordAuthentication=no ${BN_REMOTEHOST} -p 22 -l ${BN_REMOTEUSER} 'echo -n 1'" greplog "Debug: ssh\s\+-o PasswordAuthentication=no ${BN_REMOTEHOST} -l ${BN_REMOTEUSER} 'echo -n 1'"
greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::" greplog 'Debug: executing borg create$' "\bssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}${BN_BACKUPDIR}/testborg::"
# defined parameter # defined parameter
setconfig dest port 7722 setconfig dest port 7722
...@@ -385,8 +384,8 @@ finish_borg() { ...@@ -385,8 +384,8 @@ finish_borg() {
@test "verify remote backup with encryption" { @test "verify remote backup with encryption" {
export BORG_PASSPHRASE="123foo" export BORG_PASSPHRASE="123foo"
run borg extract --dry-run "ssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::testarchive" run borg extract --dry-run "ssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}${BN_BACKUPDIR}/testborg::testarchive"
[ "$status" -eq 2 ] [ "$status" -eq 2 ]
export BORG_PASSPHRASE="123test" export BORG_PASSPHRASE="123test"
borg extract --dry-run "ssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}:22${BN_BACKUPDIR}/testborg::testarchive" borg extract --dry-run "ssh://${BN_REMOTEUSER}@${BN_REMOTEHOST}${BN_BACKUPDIR}/testborg::testarchive"
} }
...@@ -241,3 +241,7 @@ makegpgkeys() { ...@@ -241,3 +241,7 @@ makegpgkeys() {
BN_SIGNKEY=$(gpg --keyid-format long -k sign@bntest0 | sed -n '2p' | grep -o '\S\+') BN_SIGNKEY=$(gpg --keyid-format long -k sign@bntest0 | sed -n '2p' | grep -o '\S\+')
export BN_SIGNKEY export BN_SIGNKEY
} }
install_pkgs() {
TMPDIR=/tmp DEBIAN_FRONTEND=noninteractive apt-get -qq install $@
}
load common load common
begin_dup() { begin_dup() {
apt-get -qq install debootstrap duplicity trickle install_pkgs debootstrap duplicity trickle
if [ ! -d /var/cache/bntest ]; then if [ ! -d /var/cache/bntest ]; then
debootstrap --variant=minbase testing "$BN_SRCDIR" debootstrap --variant=minbase testing "$BN_SRCDIR"
fi fi
......
load common load common
begin_mysql() { begin_mysql() {
apt-get -qq install default-mysql-server install_pkgs default-mysql-server
systemctl is-active mysql || systemctl start mysql systemctl is-active mysql || systemctl start mysql
zcat "${BATS_TEST_DIRNAME}/samples/bntest_p8Cz8k.sql.gz" | mysql --defaults-file=/etc/mysql/debian.cnf zcat "${BATS_TEST_DIRNAME}/samples/bntest_p8Cz8k.sql.gz" | mysql --defaults-file=/etc/mysql/debian.cnf
zcat "${BATS_TEST_DIRNAME}/samples/bntest_v11vJj.sql.gz" | mysql --defaults-file=/etc/mysql/debian.cnf zcat "${BATS_TEST_DIRNAME}/samples/bntest_v11vJj.sql.gz" | mysql --defaults-file=/etc/mysql/debian.cnf
......