7 Commits
stable ... next

Author SHA1 Message Date
e406209d35 Documentation: update README in line with v6.5.0
* README.md (english)
* README.md (german)
* correction the relative path selection

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2020-01-16 12:07:02 +01:00
ef0784262c README.md: explain restore method for saved files on LTFS tape
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2019-12-19 01:36:39 +01:00
0e43525a97 dsnap-sync: correction for tranfer_size calculation
* adapt btrfs output to be compatible with pv requested format
  convert MiB or GiB to M or G

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2019-12-19 01:34:45 +01:00
62a9698427 dsnap-sync: handle transfer size
* new function check_transfer_size()
  will calculate the transfer size send via btrfs-send
* update run_backup()
  - handle dryrun
  - handle crate-pv_cmd()
  - send incremantal size

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2019-12-18 02:46:24 +01:00
b8e9d4bbf1 tape-admin: 0.0.15 version bump
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2019-12-13 19:24:56 +01:00
8bafb04735 tape-admin: indentation update
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2019-12-13 19:21:14 +01:00
5949555747 tapt-admin: basic function feedback
Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
2019-12-13 19:21:14 +01:00
4 changed files with 564 additions and 489 deletions

114
README.md
View File

@@ -5,8 +5,8 @@
<p align="center">
<span>English</span> |
<!-- a href="lang/spanish#dsnap-sync">Spanish</a> | -->
<a href="lang/german#dsnap-sync">Deutsch</a>
<!-- a href="lang/spanish">Spanish</a> | -->
<a href="lang/german">Deutsch</a>
</p>
## About
@@ -140,17 +140,17 @@ Following tools are used:
`dsnap-sync` is a shell script. Thus no compilation is required.
To simplify correct target locations, this project uses a Makefile.
# make install
# make install
If your system uses a non-default location for the snapper
configuration defaults, specify the location with an environment variable
(`SNAPPER_CONFIG`).
Arch Linux/Fedora/Gentoo:
# make SNAPPER_CONFIG=/etc/conf.d/snapper install
Arch Linux/Fedora/Gentoo:
# make SNAPPER_CONFIG=/etc/conf.d/snapper install
Debian/Ubuntu:
# make SNAPPER_CONFIG=/etc/default/snapper install
Debian/Ubuntu:
# make SNAPPER_CONFIG=/etc/default/snapper install
The local `snapper` configuration will be extended to make use
of a new template 'dsnap-sync'.
@@ -172,41 +172,41 @@ Please use your host software package manager.
## Options
Usage: dsnap-sync [options]
Usage: dsnap-sync [options]
Options:
-a, --automount <path> start automount for given path to get a valid target mountpoint.
-b, --backupdir <prefix> backupdir is a relative path that will be appended to target backup-root
--backuptype <type> Specify backup type <archive | child | parent>
--batch no user interaction
-d, --description <desc> Change the snapper description. Default: "latest incremental backup"
--label-finished <desc> snapper description tagging successful jobs. Default: "dsnap-sync backup"
--label-running <desc> snapper description tagging active jobs. Default: "dsnap-sync in progress"
--label-synced <desc> snapper description tagging last synced jobs.
Default: "dsnap-sync last incremental"
--color Enable colored output messages
-c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper
configuration. You can select multiple configurations
(e.g. -c "root" -c "home"; --config root --config home)
--config-postfix <name> Specify a postfix that will be appended to the destination snapper config name.
--dry-run perform a trial run (no changes are written).
--mediapool Specify the name of the tape MediaPool
-n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup
--nonotify Disable graphical notification (via dbus)
--nopv Disable graphical progress output (disable pv)
--noionice Disable setting of I/O class and priority options on target
-r, --remote <address> Send the snapshot backup to a remote machine. The snapshot will be sent via ssh
You should specify the remote machine's hostname or ip address. The 'root' user
must be permitted to login on the remote machine
-p, --port <port> The remote port
-s, --subvolid <subvlid> Specify the subvolume id of the mounted BTRFS subvolume to back up to. Defaults to 5.
--use-btrfs-quota use btrfs-quota to calculate snapshot size
-u, --uuid <UUID> Specify the UUID of the mounted BTRFS subvolume to back up to. Otherwise will prompt
If multiple mount points are found with the same UUID, will prompt for user selection
-t, --target <target> Specify the mountpoint of the backup device
--volumename Specify the name of the tape volume
-v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3)
--version show program version
Options:
-a, --automount <path> start automount for given path to get a valid target mountpoint.
-b, --backupdir <prefix> backupdir is a relative path that will be appended to target backup-root
--backuptype <type> Specify backup type <archive | child | parent>
--batch no user interaction
-d, --description <desc> Change the snapper description. Default: "latest incremental backup"
--label-finished <desc> snapper description tagging successful jobs. Default: "dsnap-sync backup"
--label-running <desc> snapper description tagging active jobs. Default: "dsnap-sync in progress"
--label-synced <desc> snapper description tagging last synced jobs.
Default: "dsnap-sync last incremental"
--color Enable colored output messages
-c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper
configuration. You can select multiple configurations
(e.g. -c "root" -c "home"; --config root --config home)
--config-postfix <name> Specify a postfix that will be appended to the destination snapper config name.
--dry-run perform a trial run (no changes are written).
--mediapool Specify the name of the tape MediaPool
-n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup
--nonotify Disable graphical notification (via dbus)
--nopv Disable graphical progress output (disable pv)
--noionice Disable setting of I/O class and priority options on target
-r, --remote <address> Send the snapshot backup to a remote machine. The snapshot will be sent via ssh
You should specify the remote machine's hostname or ip address. The 'root' user
must be permitted to login on the remote machine
-p, --port <port> The remote port
-s, --subvolid <subvlid> Specify the subvolume id of the mounted BTRFS subvolume to back up to. Defaults to 5.
--use-btrfs-quota use btrfs-quota to calculate snapshot size
-u, --uuid <UUID> Specify the UUID of the mounted BTRFS subvolume to back up to. Otherwise will prompt
If multiple mount points are found with the same UUID, will prompt for user selection
-t, --target <target> Specify the mountpoint of the backup device
--volumename Specify the name of the tape volume
-v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3)
--version show program version
## First run
@@ -259,7 +259,7 @@ backup types are differenciated:
* create a config specific subdirectory (`archive-<config-name>`)
* create a snapshot-id subdirectory (`<snapper-id>`)
* create the btrfs stream file inside the subdirectory
(`<snapper-id>_[full | incremental].btrfs`)
(`<snapper-id>_[full | incremental].btrfs`)
* the proccess metadata are saved to a file called `info.xml`
If you enabled the `ltfs` package, support for backups to tape is possible.
@@ -304,6 +304,34 @@ FUSE). Read and write access can be managed using common OS tools.
An open-source implementation can be found at
[LinearTapeFileSystem](https://github.com/LinearTapeFileSystem/ltfs).
## Restore
### From Tape
When `dsnap-sync` did save the data with method `btrfs-archive`, you will find
the corresponding data in a snapper compatible directory structure on the tape.
The structure may look like:
└── backups
└── @<server-name>
├── archive-<subvol-name>
│   └── <subvol-id>
│   ├── <subvol-id>_full.btrfs
│   └── info.xml
The file `info.xml` provide the metadata corresponding to the snapshot.
The data of the snapshot is stored in the file `<subvol-id>_full.btrfs`.
This file has to be decrypted with btrfs tool `btrfs-send` to a btrfs
restore directory:
cd /target_btrfs_path
cp /path_to_tape_root/backups/@<server-name>/archive-<subvol-name>/<subvol-id>_full.btrfs .
cat <subvol-id>_full.btrfs | btrfs receive -v .
rm <subvol-id>_full.btrfs
Please consult btrfs-send man-page for further info.
## Contributing
Help is very welcome! Feel free to fork and issue a pull request to add
@@ -331,4 +359,4 @@ This work is licensed under a [Creative Common License 4.0][License-CC_BY]
![Creative Common Logo][Logo-CC_BY]
© 2016, 2017 James W. Barnett;
© 2017 - 2018 Ralf Zerres
© 2017 - 2019 Ralf Zerres

View File

@@ -27,7 +27,7 @@
# difference for the next incremental snapshot.
progname="${0##*/}"
version="0.6.5"
version="0.6.5.1"
# The following lines are modified by the Makefile or
# find_snapper_config script
@@ -79,6 +79,7 @@ snapper_backup_type='none'
#snapper_config_postfix="."`hostname`
snapper_config_postfix=
snap_cleanup_algorithm="timeline"
transfer_size=0
verbose=0
volume_name=
@@ -133,7 +134,7 @@ check_snapper_failed_ids () {
| awk '/'"$snap_description_running"'/ {cnt++} END {print cnt}')
#snapper_failed_ids="snapper --config $selected_config list --type single \
# | awk '/'"$snap_description_running"'/' \
# | awk ' /'host='"$remote"'/ {cnt++} END {print cnt}'"
# | awk ' /'host='"$remote"'/ {cnt++} END {print cnt}'"
if [ ${#snapper_failed_ids} -gt 0 ]; then
if [ "$batch" ]; then
@@ -161,6 +162,103 @@ check_snapper_failed_ids () {
fi
}
check_transfer_size () {
local source_snapshot=${1##source_snapshot=}
local clone_snapshot=${2##clone_snapshot=}
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}check_transfer_size()...${NO_COLOR}\n"
fi
if [ $dryrun -eq 0 ]; then
transfer_size=0
if [ "$interactive" -eq 1 ]; then
if [ $verbose -ge 2 ]; then
if [ ${#clone_snapshot} -gt 0 ]; then
printf "${MAGENTA}Calculate transfer size for incremental snapshot (clone=${GREEN}'%s'${MAGENTA}, source=${GREEN}'%s'${MAGENTA})${NO_COLOR} ...\n" \
"$clone_snapshot" "$source_snapshot"
else
printf "${MAGENTA}Calculate transfer size for snapshot (source=${GREEN}'%s'${MAGENTA})${NO_COLOR} ...\n" \
"$ssource_snapshot"
fi
fi
if [ $btrfs_quota -eq 1 ]; then
# qgroup for given path, exclude ancestrals
# qgroup identifiers conform to level/id where level 0 is reserved to the qgroups associated with subvolumes
transfer_size=$(btrfs qgroup show -f --raw $source_snapshot 2>/dev/null \
| awk 'FNR>2 {print $2}')
if [ $? -eq 1 ]; then
# subvolume is not configured for quota, (temporary?, expensive?) enable that
if [ $btrfs_quota_tmp -eq 1 ]; then
btrfs quota enable $source_snapshot 2>/dev/null
btrfs quota rescan -w $source_snapshot 2>/dev/null
transfer_size=$(btrfs qgroup show -f --raw $source_snapshot 2>/dev/null \
| awk 'FNR>2 {print $2}')
fi
fi
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}BTRFS qgroup show result: ${GREEN}'%s'\b${NO_COLOR}'%s'\n" \
"$?" "$transfer_size"
fi
# need to substitue btrfs 'x.yyGiB' suffix, since pv will need 'xG'
if [ $transfer_size -ge 1048576 ]; then
transfer_size=$(btrfs qgroup show -f --gbytes $source_snapshot 2>/dev/null \
| awk 'FNR>2 { gsub(/.[0-9][0-9]GiB/,"G"); print $2}')
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}BTRFS quota size for ${GREEN}source snapshot${MAGENTA}: size=${GREEN}'%s'${NO_COLOR} ...\n" \
"$snapper_source_id" "$transfer_size"
fi
# should we disable quota usage again?
if [ $btrfs_quota_tmp -eq 1 ]; then btrfs quota disable $source_snapshot; fi
else
if [ ${#clone_snapshot} -gt 0 ]; then
# WIP: dry run with btrfs send
# need to substitue btrfs 'x.yyGiB' suffix, since pv will need 'xG'
transfer_size=$(btrfs send -v -p $clone_snapshot $source_snapshot 2>$BTRFS_PIPE \
| pv -f 2>&1 >/dev/null \
| awk -F ' ' '{ gsub(/.[0-9][0-9]MiB/,"M"); gsub(/.[0-9][0-9]GiB/,"G"); print $1 }' )
else
# filesystem size
transfer_size=$(du --one-file-system --summarize $snapper_source_snapshot 2>/dev/null \
| awk -F ' ' '{print $1}')
if [ $transfer_size -ge 1048576 ]; then
transfer_size=$(($transfer_size / 1024 / 1024))G
fi
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}BTRFS transfer size for ${GREEN}source snapshot${MAGENTA}: size=${GREEN}'%s'${NO_COLOR} ...\n" \
"$transfer_size"
fi
fi
fi
else
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}dryrun: Would calculate transfer size for BTRFS ${GREEN}source snapshot${NO_COLOR} ...\n" \
"$snapper_source_id"
fi
fi
}
create_pv_cmd () {
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}create_pv_cmd()...${NO_COLOR}\n"
fi
# prepare cmdline output settings for interactive progress status
if [ $do_pv_cmd -eq 1 ]; then
pv_options="--delay-start 2 --interval 5 --format \"time elapsed [%t] | avg rate %a | rate %r | transmitted [%b] | %p | time remaining [%e]\" "
cmd_pv="pv --size $transfer_size $pv_options | "
#cmd_pv="pv $pv_options --size ${transfer_size} | dialog --gauge \"$progname: Progress for config '$selected_config'\" 6 85 |"
else
cmd_pv=''
fi
}
create_snapshot () {
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}create_snapshot()...${NO_COLOR}\n" $snapper_config
@@ -278,12 +376,12 @@ get_archive_last_sync_id () {
case ${archive_type} in
#incremental)
# cmd="find ${selected_target}/${backupdir}/${snapper_target_config} -name *_${archive_type}.btrfs \
# | awk -F '.*/' '{ gsub(/_${archive_type}.btrfs\$/,"_"); print \$2}' \
# | awk -F '.*/' '{ gsub(/_${archive_type}.btrfs\$/,"_"); print \$2}' \
# | awk ' \$1 == $snapper_source_sync_id {print \$1} ' "
# ;;
*)
cmd="find ${selected_target}/${backupdir}/${snapper_target_config} -name *_${archive_type}.btrfs \
| awk -F '.*/' '{ gsub(/_${archive_type}.btrfs\$/,"_"); cnt++} END {print cnt}'"
cmd="find ${selected_target}/${backupdir}/${snapper_target_config} -name *_${archive_type}.btrfs \
| awk -F '.*/' '{ gsub(/_${archive_type}.btrfs\$/,"_"); cnt++} END {print cnt}'"
ret=$(eval $run_ssh "$cmd" 2>/dev/null)
if [ ${#ret} -ge 1 ]; then
if [ $verbose -ge 3 ]; then
@@ -292,7 +390,7 @@ get_archive_last_sync_id () {
fi
# get last sync-id
cmd="find ${selected_target}/${backupdir}/${snapper_target_config} -name *_${archive_type}.btrfs \
| awk -F '.*/' '{ gsub(/_${archive_type}.btrfs\$/,"_") } END {print \$2}'"
| awk -F '.*/' '{ gsub(/_${archive_type}.btrfs\$/,"_") } END {print \$2}'"
else
return 1
fi
@@ -565,7 +663,7 @@ get_disk_infos () {
# local root filesystem will be excluded as a valid target location
exclude_uuid=$(findmnt --noheadings --nofsroot --mountpoint / --output UUID)
disk_uuids=$(findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \
| grep -v $exclude_uuid \
| grep -v $exclude_uuid \
| awk '{print $1}')
disk_targets=$(findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \
| grep -v $exclude_uuid \
@@ -579,7 +677,7 @@ get_disk_infos () {
exclude_uuid=$($ssh findmnt --noheadings --nofsroot --mountpoint / --output UUID)
sleep 0.2
disk_uuids=$($ssh "findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \
| grep -v \"$exclude_uuid\" \
| grep -v \"$exclude_uuid\" \
| awk '{print \$1}'")
sleep 0.2
disk_targets=$($ssh "findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \
@@ -589,7 +687,7 @@ get_disk_infos () {
fs_options=$($ssh "findmnt --noheadings --nofsroot --types btrfs --output UUID,OPTIONS --list \
| grep -v \"$exclude_uuid\" \
| awk '{print \$2}'")
fi
fi
else
# target location is not mounted as root subvolume
sleep 0.2
@@ -781,13 +879,13 @@ get_snapper_last_sync_id () {
fi
fi
if [ ${#snapper_subvolid} -ge 1 -a ${#snapper_uuid} -ge 1 ]; then
cmd="snapper --config $snapper_config list --type single \
cmd="snapper --config $snapper_config list --type single \
| awk '/$snapper_description/' \
| awk '/subvolid="$snapper_subvolid", uuid="$snapper_uuid"/' \
| awk 'END {print \$1}'"
elif [ ${#snapper_tapeid} -ge 1 ]; then
# | awk '/"$snapper_description"' '/"$snap_description_finished/"' \
cmd="snapper --config $snapper_config list --type single \
cmd="snapper --config $snapper_config list --type single \
| awk '/$snapper_description/' \
| awk '/tapeid="$snapper_tapeid"/' \
| awk 'END {print \$1}'"
@@ -810,17 +908,17 @@ get_snapper_last_sync_id () {
# no snapshot found, grap latest successfull sync
if [ ${#snapper_subvolid} -ge 1 -a ${#snapper_uuid} -ge 1 ]; then
cmd="snapper --config $snapper_config list --type single \
| awk '/$snap_description_finished/' \
| awk '/subvolid="$selected_subvol", uuid="$selected_uuid"/' \
| awk '/$snap_description_finished/' \
| awk '/subvolid="$selected_subvol", uuid="$selected_uuid"/' \
| awk 'END {print \$1}'"
elif [ ${#snapper_tapeid} -ge 1 ]; then
cmd="snapper --config $snapper_config list --type single \
cmd="snapper --config $snapper_config list --type single \
| awk '/$snap_description_finished/' \
| awk '/tapeid="$snapper_tapeid"/' \
| awk 'END {print \$1}'"
else
cmd="snapper --config $snapper_config list --type single \
| awk '/$snap_description_finished/' \
| awk '/$snap_description_finished/' \
| awk 'END {print \$1}'"
fi
snapper_sync_id=$(eval $run_ssh "$cmd")
@@ -857,7 +955,7 @@ get_snapper_sync_id () {
[ ${#remote} -gt 0 ] && run_ssh=$ssh
cmd="snapper --config "$snapper_config" list --type single \
| awk -F '|' ' \$1 == $snapper_sync_id { gsub(/ /,_); print \$1} '"
| awk -F '|' ' \$1 == $snapper_sync_id { gsub(/ /,_); print \$1} '"
ret=$(eval $run_ssh "$cmd")
if [ ${#ret} -ge 1 ]; then
@@ -1193,7 +1291,7 @@ parse_params () {
else
$ssh which sh >/dev/null 2>&1 || \
{ printf "'remote shell' is not working!\n \
Please correct your public authentication and try again.\n" && exit 1; }
Please correct your public authentication and try again.\n" && exit 1; }
fi
fi
@@ -1406,12 +1504,12 @@ run_config_preparation () {
# get backupdir from snapper target
get_snapper_target_backupdir $backupdir
if [ $verbose -ge 3 ]; then
if [ $remote ]; then
if [ $remote ]; then
printf "${MAGENTA}backupdir on remote '%s': ${GREEN}'%s'${NO_COLOR}\n" \
"$remote" "$backupdir"
"$remote" "$backupdir"
else
printf "${MAGENTA}backupdir: ${GREEN}'%s'${NO_COLOR}\n" \
"$backupdir"
"$backupdir"
fi
fi
# set target sync_snapshot path
@@ -1428,13 +1526,13 @@ run_config_preparation () {
snapper_target_sync_snapshot=$backup_root/.snapshots/$snapper_target_sync_id/$snapper_snapshot_name
fi
if [ $verbose -ge 3 ]; then
if [ $remote ]; then
if [ $remote ]; then
printf "${MAGENTA}backup_root on remote '%s': ${GREEN}'%s'${NO_COLOR}\n" \
"$remote" "$backup_root"
else
"$remote" "$backup_root"
else
printf "${MAGENTA}backup_root: ${GREEN}'%s'${NO_COLOR}\n" \
"$backup_root"
fi
"$backup_root"
fi
fi
;;
*)
@@ -1539,15 +1637,15 @@ run_backup () {
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}snapper_source_config: ${GREEN}'%s'${NO_COLOR}\n" \
"$snapper_source_config"
"$snapper_source_config"
printf "${MAGENTA}snapper_target_config: ${GREEN}'%s'${NO_COLOR}\n" \
"$snapper_target_config"
"$snapper_target_config"
printf "${MAGENTA}snapper_backup_type: ${GREEN}'%s'${NO_COLOR}\n" \
"$snapper_backup_type"
printf "${MAGENTA}snapper_source_sync_id: ${GREEN}'%s'${NO_COLOR}\n" \
"$snapper_source_sync_id"
"$snapper_source_sync_id"
printf "${MAGENTA}snapper_common_sync_id: ${GREEN}'%s'${NO_COLOR}\n" \
"$snapper_common_sync_id"
"$snapper_common_sync_id"
printf "${MAGENTA}backup_dir: ${GREEN}'%s'${NO_COLOR}\n" "$backup_dir"
printf "${MAGENTA}backup_root: ${GREEN}'%s'${NO_COLOR}\n" "$backup_root"
fi
@@ -1616,64 +1714,6 @@ run_backup () {
continue
fi
if [ $dryrun -eq 0 ]; then
snapper_source_snapshot_size=0
if [ "$interactive" -eq 1 ]; then
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Get size for given source snapshot (id=${GREEN}'%s'${MAGENTA}, path=${GREEN}'%s'${MAGENTA})${NO_COLOR} ...\n" \
"$snapper_source_id" "$snapper_source_snapshot"
fi
if [ $btrfs_quota -eq 1 ]; then
# qgroup for given path, exclude ancestrals
# qgroup identifiers conform to level/id where level 0 is reserved to the qgroups associated with subvolumes
snapper_source_snapshot_size=$(btrfs qgroup show -f --raw $snapper_source_snapshot 2>/dev/null \
| awk 'FNR>2 {print $2}')
if [ $? -eq 1 ]; then
# subvolume is not configured for quota, (temporary?) enable that
if [ $btrfs_quota_tmp -eq 1 ]; then
btrfs quota enable $snapper_source_snapshot 2>/dev/null
btrfs quota rescan -w $snapper_source_snapshot 2>/dev/null
snapper_source_snapshot_size=$(btrfs qgroup show -f --raw $snapper_source_snapshot 2>/dev/null \
| awk 'FNR>2 {print $2}')
fi
fi
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}BTRFS qgroup show result: ${GREEN}'%s'\b${NO_COLOR}'%s'\n" \
"$?" "$snapper_source_snapshot_size"
fi
# need to substitue btrfs 'x.yyGiB' suffix, since pv will need 'xG'
if [ $snapper_source_snapshot_size -ge 1048576 ]; then
snapper_source_snapshot_size=$(btrfs qgroup show -f --gbytes $snapper_source_snapshot 2>/dev/null \
| awk 'FNR>2 { gsub(/.[0-9][0-9]GiB/,"G"); print $2}')
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}BTRFS quota size for ${GREEN}source snapshot${MAGENTA}: id=${GREEN}'%s'${MAGENTA}, size=${GREEN}'%s'${NO_COLOR} ...\n" \
"$snapper_source_id" "$snapper_source_snapshot_size"
fi
# should we disable quota usage again?
if [ $btrfs_quota_tmp -eq 1 ]; then btrfs quota disable $snapper_source_snapshot; fi
else
snapper_source_snapshot_size=$(du --one-file-system --summarize $snapper_source_snapshot 2>/dev/null \
| awk -F ' ' '{print $1}')
if [ $snapper_source_snapshot_size -ge 1048576 ]; then
snapper_source_snapshot_size=$(($snapper_source_snapshot_size / 1024 / 1024))G
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}BTRFS subvolume size for ${GREEN}source snapshot${MAGENTA}: id=${GREEN}'%s'${MAGENTA}, size=${GREEN}'%s'${NO_COLOR} ...\n" \
"$snapper_source_id" "$snapper_source_snapshot_size"
fi
fi
fi
else
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}dryrun: Would calculate BTRFS subvolume size for ${GREEN}source snapshot${NO_COLOR} ...\n" \
"$snapper_source_id"
fi
fi
# setting process I/O scheduling options
if [ $do_ionice_cmd -eq 1 ]; then
# class: best-efford, priority: medium
@@ -1684,59 +1724,58 @@ run_backup () {
cmd_ionice=''
fi
# settings for interactive progress status
if [ $do_pv_cmd -eq 1 ]; then
pv_options="--delay-start 2 --interval 5 --format \"time elapsed [%t] | avg rate %a | rate %r | transmitted [%b] | %p | time remaining [%e]\" "
cmd_pv="pv --size $snapper_source_snapshot_size $pv_options | "
#cmd_pv="pv $pv_options --size ${snapper_source_snapshot_size} | dialog --gauge \"$progname: Progress for config '$selected_config'\" 6 85 |"
else
cmd_pv=''
fi
case $selected_fstype in
btrfs)
cmd="btrfs send $btrfs_verbose_flag $snapper_source_snapshot 2>$BTRFS_PIPE \
| $cmd_pv \
$cmd_ionice $ssh btrfs receive $btrfs_verbose_flag $snapper_target_snapshot/ 1>$BTRFS_PIPE 2>&1"
;;
*)
# Can't use btrfs receive, since target filesystem can't support btrfs snapshot feature
snapper_target_stream=${snapper_target_id}_${archive_type}.btrfs
if [ ! -f $snapper_target_snapshot/$snapper_target_stream ]; then
if [ -z $remote ]; then
cmd="btrfs send $btrfs_verbose_flag $snapper_source_snapshot 2>/dev/null \
| $cmd_pv \
$cmd_ionice cat > $snapper_target_snapshot/$snapper_target_stream"
else
cmd="btrfs send $btrfs_verbose_flag $snapper_source_snapshot 2>/dev/null \
| $cmd_pv \
$cmd_ionice $ssh 'cat > $snapper_target_snapshot/$snapper_target_stream' 2>$BTRFS_PIPE"
fi
else
if [ $verbose -ge 2 ]; then
printf "${RED}BTRFS_Stream: %s${NO_COLOR} already saved.\n" \
"$snapper_target_snapshot/$snapper_target_stream"
fi
# go for next configuration
i=$(($i+1))
continue
fi
;;
esac
# prepare pipe command
if [ "$dryrun" -eq 0 ]; then
if [ "$snapper_source_sync_id" -eq 0 ] \
|| [ "$snapper_target_sync_id" -eq 0 ] \
|| [ "$backup_mode" = "full" ] ; then
# get size of stream that needs to be transfered
check_transfer_size "source_snapshot=$snapper_source_snapshot"
# prepare send pipe command
create_pv_cmd
case $selected_fstype in
btrfs)
cmd="btrfs send $btrfs_verbose_flag $snapper_source_snapshot 2>$BTRFS_PIPE \
| $cmd_pv \
$cmd_ionice $ssh btrfs receive $btrfs_verbose_flag $snapper_target_snapshot/ 1>$BTRFS_PIPE 2>&1"
;;
*)
# Can't use btrfs receive, since target filesystem can't support btrfs snapshot feature
snapper_target_stream=${snapper_target_id}_${archive_type}.btrfs
if [ ! -f $snapper_target_snapshot/$snapper_target_stream ]; then
if [ -z $remote ]; then
cmd="btrfs send $btrfs_verbose_flag $snapper_source_snapshot 2>/dev/null \
| $cmd_pv \
$cmd_ionice cat > $snapper_target_snapshot/$snapper_target_stream"
else
cmd="btrfs send $btrfs_verbose_flag $snapper_source_snapshot 2>/dev/null \
| $cmd_pv \
$cmd_ionice $ssh 'cat > $snapper_target_snapshot/$snapper_target_stream' 2>$BTRFS_PIPE"
fi
else
if [ $verbose -ge 2 ]; then
printf "${RED}BTRFS_Stream: %s${NO_COLOR} already saved.\n" \
"$snapper_target_snapshot/$snapper_target_stream"
fi
# go for next configuration
i=$(($i+1))
continue
fi
;;
esac
# send full snapshot to target
if [ $verbose -ge 2 ]; then
if [ ${#snapper_source_snapshot_size} -gt 0 ]; then
if [ ${#transfer_size} -gt 0 ]; then
printf "${MAGENTA}Sending ${GREEN}snapshot${NO_COLOR} for snapper config ${GREEN}'%s' ${MAGENTA}(id='%s', size='%s')${NO_COLOR} ...\n" \
"$selected_config" "$snapper_source_id" "$snapper_source_snapshot_size"
"$selected_config" "$snapper_source_id" "$transfer_size"
else
printf "${MAGENTA}Sending ${GREEN}snapshot${NO_COLOR} for snapper config ${GREEN}'%s' ${MAGENTA}(id='%s')${NO_COLOR} ...\n" \
"$selected_config"
fi
fi
fi
# the actual data sync to the target
# this may take a while, depending on datasize and line-speed
if [ $verbose -ge 3 ]; then
@@ -1751,8 +1790,8 @@ run_backup () {
continue
fi
else
# source holds synced snapshots
if [ $verbose -ge 3 ]; then
# source holds synced snapshots
if [ $verbose -ge 3 ]; then
printf "New ${GREEN}source${NO_COLOR} snapshot id: ${GREEN}'%s'${NO_COLOR} (path: ${GREEN}'%s'${NO_COLOR})\n" \
"$snapper_source_id" "$snapper_source_snapshot"
printf "New ${GREEN}target${NO_COLOR} snapshot id: ${GREEN}'%s'${NO_COLOR} (path: ${GREEN}'%s'${NO_COLOR})\n" \
@@ -1763,17 +1802,18 @@ run_backup () {
"$snapper_source_sync_id" "$snapper_source_sync_snapshot"
printf "Last synced ${GREEN}target${NO_COLOR} snapshot id: ${GREEN}'%s'${NO_COLOR} (path: ${GREEN}'%s'${NO_COLOR})\n" \
"$snapper_target_sync_id" "$snapper_target_sync_snapshot"
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Prepare ${GREEN}incremental snapshot${NO_COLOR} (id: ${GREEN}'%s'${NO_COLOR}) for snapper config ${GREEN}'%s'${NO_COLOR} ...\n" \
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Prepare ${GREEN}incremental snapshot${NO_COLOR} (id: ${GREEN}'%s'${NO_COLOR}) for snapper config ${GREEN}'%s'${NO_COLOR} ...\n" \
"$snapper_target_id" "$selected_config"
fi
# verify that we have a matching source and target snapshot-id
if [ $snapper_common_sync_id -eq 0 ]; then
fi
# verify that we have a matching source and target snapshot-id
if [ $snapper_common_sync_id -eq 0 ]; then
if [ ${snapper_source_id} -eq ${snapper_target_sync_id} ]; then
# nothing to do, snapshot already in sync
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Nothing to do! Source and target snapshot (id: ${GREEN}'%s'${MAGENTA}) are in sync.${NO_COLOR}\n" \
printf "${MAGENTA}Nothing to do! Source and target snapshot (id: ${GREEN}'%s'${MAGENTA}) are in sync.${NO_COLOR}\n" \
"$snapper_target_sync_id"
fi
# go for next configuration
@@ -1782,17 +1822,17 @@ run_backup () {
elif [ ${snapper_source_sync_id} != ${snapper_target_sync_id} ]; then
if [ $snapper_target_sync_id -lt $snapper_target_id ]; then
# try to find last target_sync_id in source_config
get_snapper_sync_id "snapper_config=${snapper_source_config}" "remote="
if [ $? -eq 0 ]; then
get_snapper_sync_id "snapper_config=${snapper_source_config}" "remote="
if [ $? -eq 0 ]; then
snapper_source_sync_id=$snapper_sync_id
snapper_source_sync_snapshot=$SUBVOLUME/.snapshots/$snapper_sync_id/$snapper_snapshot_name
else
else
printf "${RED}Error: ${MAGENTA}No common sync id found. Aborting backup for config ${GREEN}'%s'${NO_COLOR}\n"
error_count=$(($error_count+1))
# go for next configuration
i=$(($i+1))
continue
fi
fi
fi
elif [ ${snapper_source_id} != ${snapper_source_sync_id} ]; then
snapper_common_sync_id=$snapper_source_sync_id
@@ -1811,18 +1851,22 @@ run_backup () {
fi
fi
cmd="$ssh stat --format %i $snapper_target_snapshot 2>/dev/null"
ret=$(eval $cmd)
if [ $? -eq 0 ]; then
cmd="$ssh stat --format %i $snapper_target_snapshot 2>/dev/null"
ret=$(eval $cmd)
if [ $? -eq 0 ]; then
# get size of stream that needs to be transfered
check_transfer_size "source_snapshot=$snapper_source_snapshot" "clone_snapshot=$snapper_common_sync_snapshot"
create_pv_cmd
case $selected_fstype in
btrfs)
# Sends the difference between the new snapshot and old synced snapshot.
# Sends the difference between the new snapshot and old synced snapshot.
# Using the flag -c (clone-src) will require the availibility of an identical readonly
# subvolume on the source and the receiving location (the parent-id).
# using "btrfs send -p" instead of "btrfs send -c", then no parent search would be
# needed (Andreij explained in: https://www.spinics.net/lists/linux-btrfs/msg69369.html)
cmd="btrfs send $btrfs_verbose_flag -p $snapper_common_sync_snapshot $snapper_source_snapshot 2>$BTRFS_PIPE \
| $cmd_pv \
| $cmd_pv \
$cmd_ionice $ssh btrfs receive $btrfs_verbose_flag $snapper_target_snapshot/ 1>$BTRFS_PIPE 2>&1"
;;
*)
@@ -1830,23 +1874,23 @@ run_backup () {
snapper_target_stream=${snapper_target_id}_incremental.btrfs
if [ -z $remote ]; then
cmd="btrfs send $btrfs_verbose_flag -p $snapper_common_sync_snapshot $snapper_source_snapshot 2>$BTRFS_PIPE \
| $cmd_pv \
$cmd_ionice cat > $snapper_target_snapshot/$snapper_target_stream 2>$BTRFS_PIPE"
| $cmd_pv \
$cmd_ionice cat > $snapper_target_snapshot/$snapper_target_stream 2>$BTRFS_PIPE"
else
cmd="btrfs send $btrfs_verbose_flag -p $snapper_common_sync_snapshot $snapper_source_snapshot 2>$BTRFS_PIPE \
| $cmd_pv \
$cmd_ionice $ssh 'cat > $snapper_target_snapshot/$snapper_target_stream' 2>$BTRFS_PIPE"
| $cmd_pv \
$cmd_ionice $ssh 'cat > $snapper_target_snapshot/$snapper_target_stream' 2>$BTRFS_PIPE"
fi
;;
esac
if [ $verbose -ge 2 ]; then
printf "%b" "${GREEN}btrfs send${NO_COLOR} is using\n" \
printf "%b" "${GREEN}btrfs send${NO_COLOR} is using\n" \
" parent snapshot id: ${GREEN}'$snapper_common_sync_id'${NO_COLOR} (path: ${GREEN}'$snapper_common_sync_snapshot'${NO_COLOR}) and\n" \
" snapshot id: ${GREEN}'$snapper_source_id'${NO_COLOR} (path: ${GREEN}'$snapper_source_snapshot'${NO_COLOR})\n" \
" to construct an incremental data-stream ...\n"
fi
if [ $verbose -ge 3 ]; then
printf "${GREEN}btrfs command:${NO_COLOR} '%s'\n" "$cmd"
printf "${GREEN}btrfs command:${NO_COLOR} '%s'\n" "$cmd"
fi
$(eval $cmd)
ret=$?
@@ -1874,10 +1918,10 @@ run_backup () {
continue
;;
esac
else
else
# is this clause possible?
if [ $verbose -ge 3 ]; then
printf "${RED}Error: ${MAGENTA}No commen sync snapshot ${GREEN}'%s'${MAGENTA} on ${GREEN}source${MAGENTA} to sync metadata ...${NO_COLOR} \n" \
printf "${RED}Error: ${MAGENTA}No commen sync snapshot ${GREEN}'%s'${MAGENTA} on ${GREEN}source${MAGENTA} to sync metadata ...${NO_COLOR} \n" \
"$snapper_common_sync_snapshot"
error_count=$(($error_count+1))
# go for next configuration
@@ -1885,14 +1929,14 @@ run_backup () {
continue
fi
fi
fi
fi
else
printf "${MAGENTA}dryrun${NO_COLOR}: Would run btrfs send / btrfs receive pipe\n"
fi
# send the snapper info metadata
if [ $dryrun -eq 0 ]; then
if [ $dryrun -eq 0 ]; then
if [ -z "$remote" ]; then
cp "$snapper_source_info" "$snapper_target_snapshot/info.xml"
else
@@ -1957,9 +2001,9 @@ run_finalize () {
SNAP_SYNC_EXCLUDE=no
if [ -f "/etc/snapper/configs/$selected_config" ]; then
. /etc/snapper/configs/$selected_config
. /etc/snapper/configs/$selected_config
else
printf "${RED}Error: ${MAGENTA}Selected snapper configuration ${GREEN}'$selected_config'${MAGENTA} does not exist${NO_COLOR}"
printf "${RED}Error: ${MAGENTA}Selected snapper configuration ${GREEN}'$selected_config'${MAGENTA} does not exist${NO_COLOR}"
return 1
fi
@@ -1967,7 +2011,7 @@ run_finalize () {
if [ "$cont_backup" = "no" ] || [ "$SNAP_SYNC_EXCLUDE" = "yes" ]; then
if [ $donotify -gt 0 ]; then
notify_info "Finalize backup" "NOTE: Skipping '$selected_config' configuration."
fi
fi
continue
fi
@@ -2038,8 +2082,8 @@ run_finalize () {
# save target-id
if [ $snapper_source_id -gt 0 ]; then
cmd="snapper --config $selected_config modify \
--cleanup-algorithm \"dsnap-sync\" \
--userdata \"tapeid=$volume_name\" \
--cleanup-algorithm \"dsnap-sync\" \
--userdata \"tapeid=$volume_name\" \
$snapper_source_id"
if [ $verbose -ge 3 ]; then
@@ -2068,8 +2112,8 @@ run_finalize () {
;;
btrfs-snapshot)
# create btrfs-snapshot on target
cmd="$ssh snapper --config \"$snapper_target_config\" list --type single \
| awk ' /'\"$snap_description_running\"'/ ' \
cmd="$ssh snapper --config \"$snapper_target_config\" list --type single \
| awk ' /'\"$snap_description_running\"'/ ' \
| awk -F '|' ' \$1 == $snapper_target_id {print \$1} ' "
;;
esac
@@ -2087,7 +2131,7 @@ run_finalize () {
printf "${MAGENTA}Found${NO_COLOR} snapper id ${GREEN}'%s'${NO_COLOR} on target for configuration ${GREEN}'%s'${NO_COLOR}\n" \
"$snapper_target_id" "$snapper_target_config"
fi
if [ $verbose -ge 3 ]; then
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Tagging metadata${NO_COLOR} for snapper id ${GREEN}'%s'${NO_COLOR} on target for configuration ${GREEN}'%s'${NO_COLOR} ...\n" \
"$snapper_target_id" "$snapper_target_config"
#printf "calling: '%s'\n" "$($cmd)"
@@ -2095,16 +2139,16 @@ run_finalize () {
# call command (respect needed quotes)
if [ $remote ]; then
ret=$(eval $ssh snapper --config \\\'$snapper_target_config\\\' modify \
--description \\\'$snap_description_finished\\\' \
--userdata \\\'host=$src_host, subvolid=$src_subvolid, uuid=$src_uuid\\\' \
--cleanup-algorithm \'timeline\' \
\'$snapper_target_id\')
--description \\\'$snap_description_finished\\\' \
--userdata \\\'host=$src_host, subvolid=$src_subvolid, uuid=$src_uuid\\\' \
--cleanup-algorithm \'timeline\' \
\'$snapper_target_id\')
else
ret=$(eval snapper --config $snapper_target_config modify \
--description "$snap_description_finished" \
--description "$snap_description_finished" \
--userdata "host=$src_host, subvolid=$src_subvolid, uuid=$src_uuid" \
--cleanup-algorithm "timeline" \
$snapper_target_id)
$snapper_target_id)
fi
if [ $verbose -ge 3 ]; then
printf "return: '%s'\n" "$ret"
@@ -2127,7 +2171,7 @@ run_finalize () {
fi
if [ $snapper_source_id -gt 0 ]; then
cmd="snapper --config $selected_config modify \
--description \"$snap_description_synced\" \
--description \"$snap_description_synced\" \
--userdata \"backupdir=$backupdir, important=yes, host=$remote, subvolid=$selected_subvol, uuid=$selected_uuid\" \
--cleanup-algorithm \"timeline\" \
$snapper_source_id"
@@ -2152,7 +2196,7 @@ run_finalize () {
if [ ${#snapper_source_sync_id} -gt 0 ]; then
# TODO: no snapper method to remove userdata pair, use awk
cmd="snapper --config $selected_config modify \
--description \"$snap_description_finished\" \
--description \"$snap_description_finished\" \
--userdata \"important=no\" \
$snapper_source_sync_id"
@@ -2208,9 +2252,9 @@ select_target () {
# print selection table
if [ $verbose -ge 2 ]; then
if [ -z "$remote" ]; then
printf "Selecting a mounted device for backups on your local machine.\n"
printf "Selecting a mounted device for backups on your local machine.\n"
else
printf "Selecting a mounted device for backups on %s.\n" "$remote"
printf "Selecting a mounted device for backups on %s.\n" "$remote"
fi
fi
while [ "$target_id" -eq 0 ] || [ "$target_id" -le $target_count ]; do
@@ -2310,7 +2354,7 @@ select_target () {
esac
done
if [ "$target_selected" = x ]; then
exit 0
exit 0
fi
selected_target=$(eval echo \$target_$target_selected)
@@ -2376,9 +2420,9 @@ select_target_disk () {
# print selection table
if [ $verbose -ge 2 ]; then
if [ -z "$remote" ]; then
printf "Selecting a mounted BTRFS device for backups on your local machine.\n"
printf "Selecting a mounted BTRFS device for backups on your local machine.\n"
else
printf "Selecting a mounted BTRFS device for backups on %s.\n" "$remote"
printf "Selecting a mounted BTRFS device for backups on %s.\n" "$remote"
fi
fi
while [ "$disk_id" -eq -1 ] || [ "$disk_id" -le $disk_count ]; do
@@ -2450,7 +2494,7 @@ select_target_disk () {
esac
done
if [ "$disk_selected" = x ]; then
exit 0
exit 0
fi
selected_uuid=$(eval echo \$disk_uuid_$disk_selected)
@@ -2507,9 +2551,9 @@ select_target_tape () {
# print selection table
if [ $verbose -ge 2 ]; then
if [ -z "$remote" ]; then
printf "Selecting a mounted LTFS tape for backups on your local machine.\n"
printf "Selecting a mounted LTFS tape for backups on your local machine.\n"
else
printf "Selecting a mounted LTFS tape for backups on %s.\n" "$remote"
printf "Selecting a mounted LTFS tape for backups on %s.\n" "$remote"
fi
fi
while [ "$tape_id" -eq -1 ] || [ "$tape_id" -le $tape_count ]; do
@@ -2582,7 +2626,7 @@ select_target_tape () {
esac
done
if [ "$tape_selected" = x ]; then
exit 0
exit 0
fi
selected_tape_id=$(eval echo \$tape_id_$tape_selected)
@@ -2665,11 +2709,11 @@ Options:
--label-finished <desc> snapper description tagging successful jobs. Default: "dsnap-sync backup"
--label-running <desc> snapper description tagging active jobs. Default: "dsnap-sync in progress"
--label-synced <desc> snapper description tagging last synced jobs
Default: "dsnap-sync last incremental"
Default: "dsnap-sync last incremental"
--color Enable colored output messages
-c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper
configuration. You can select multiple configurations
(e.g. -c "root" -c "home"; --config root --config home)
configuration. You can select multiple configurations
(e.g. -c "root" -c "home"; --config root --config home)
--config-postfix <name> Specify a postfix that will be appended to the destination snapper config name
--dry-run perform a trial run (no changes are written)
--mediapool Specify the name of the tape MediaPool
@@ -2680,12 +2724,12 @@ Options:
--noionice Disable setting of I/O class and priority options on target
-p, --port <port> The remote port
-r, --remote <address> Send the snapshot backup to a remote machine. The snapshot will be sent via ssh
You should specify the remote machine's hostname or ip address. The 'root' user
must be permitted to login on the remote machine
You should specify the remote machine's hostname or ip address. The 'root' user
must be permitted to login on the remote machine
-s, --subvolid <subvlid> Specify the subvolume id of the mounted BTRFS subvolume to back up to. Defaults to 5
--use-btrfs-quota use btrfs-quota to calculate snapshot size
-u, --uuid <UUID> Specify the UUID of the mounted BTRFS subvolume to back up to. Otherwise will prompt
If multiple mount points are found with the same UUID, will prompt user selection
If multiple mount points are found with the same UUID, will prompt user selection
-t, --target <target> Specify the mountpoint of the backup device
--volumename Specify the name of the tape volume
-v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3)
@@ -2918,13 +2962,13 @@ verify_snapper_structure () {
# no inode for given backup_root
if [ $dryrun -eq 0 ]; then
if [ $verbose -ge 2 ]; then
if [ -z "$remote" ]; then
printf "${MAGENTA}Create${NO_COLOR} new snapper capable BTRFS ${MAGENTA}subvolume ${GREEN}'%s'${NO_COLOR} ...\n" \
if [ -z "$remote" ]; then
printf "${MAGENTA}Create${NO_COLOR} new snapper capable BTRFS ${MAGENTA}subvolume ${GREEN}'%s'${NO_COLOR} ...\n" \
"$backup_root"
else
else
printf "${MAGENTA}Create${NO_COLOR} new snapper capable BTRFS ${MAGENTA}subvolume ${GREEN}'%s'${NO_COLOR} on ${MAGENTA}remote host ${GREEN}'%s'${NO_COLOR} ...\n" \
"$backup_root" "$remote_host"
fi
fi
fi
# verify that we can use the correct snapper template
cmd="$ssh stat --format %i $SNAPPER_TEMPLATE_DIR/$snapper_subvolume_template 2>/dev/null"
@@ -2937,7 +2981,7 @@ verify_snapper_structure () {
#die "snapper template %s to configure the snapper subvolume %s is missing in %s on %s.\n" \
# "$snapper_subvolume_template" "$snapper_config" "$SNAPPER_TEMPLATE_DIR" "$remote_host"
fi
# create the non existing remote BTRFS subvolume for given config
# create the non existing remote BTRFS subvolume for given config
cmd="$ssh btrfs subvolume create $backup_root 1>/dev/null"
ret=$(eval $cmd)
if [ $? -ne 0 ]; then
@@ -2992,14 +3036,14 @@ verify_snapper_structure () {
if [ $? -eq 1 ]; then
# path does not exist, let snapper create the structure
# and path $backup_root/.snapshots
cmd="$ssh snapper --config $snapper_config create-config \
--template $snapper_subvolume_template \
cmd="$ssh snapper --config $snapper_config create-config \
--template $snapper_subvolume_template \
--fstype btrfs $backup_root"
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Create new BTRFS subvolume ${GREEN}'%s'${NO_COLOR} using template ${GREEN}'%s'${NO_COLOR}\n" \
$snapper_config $snapper_subvolume_template
fi
$(eval $cmd)
$(eval $cmd)
if [ $? -ne 0 ]; then
if [ $remote ]; then
printf "${RED}Error: ${MAGENTA}Creation of snapper capable config ${GREEN}%s${MAGENTA} on ${GREEN}%s${MAGENTA} failed${NO_COLOR}\n" \
@@ -3010,8 +3054,8 @@ verify_snapper_structure () {
fi
return 1
fi
else
# WIP:
else
# WIP:
# snapper_config exist, now verify if SUBVOLUME needs to be updated
cmd="$ssh snapper list-configs | awk -F '|' '/'\"^$snapper_config\"'/ {print \$1}'"
#cmd="$ssh snapper list-configs | awk '/'\"^$snapper_config\"'/' | awk -F '|' ' /'\$1 == "$snapper_config"'/ {print \$1}'"
@@ -3039,10 +3083,10 @@ verify_snapper_structure () {
$backup_root/$snapper_snapshots
fi
cmd="$ssh btrfs subvolume create $backup_root/$snapper_snapshots 2>/dev/null"
ret=$(eval $cmd)
ret=$(eval $cmd)
if [ $? -ne 0 ]; then
printf "${RED}Error: ${MAGENTA}Creation of snapper subvolume ${GREEN}%s${MAGENTA} failed${NO_COLOR}\n" \
"$backup_root/$snapper_snapshots"
"$backup_root/$snapper_snapshots"
return 1
fi
else
@@ -3083,7 +3127,7 @@ verify_snapper_structure () {
cmd="$ssh stat --format %i $backup_root/$snapper_snapshots/$snapper_id/$snapper_snapshot_name 2>/dev/null"
ret=$(eval $cmd)
if [ $? -eq 0 ] && [ $ret -ne 256 ]; then
# a snapshot path exists, but is not a btrfs snapshot
# a snapshot path exists, but is not a btrfs snapshot
if [ -z "$remote" ]; then
printf "${RED}Cancel snapshot creation${NO_COLOR}: Directory with id ${GREEN}'%s'${NO_COLOR} already exist in ${BLUE}'%s'${NO_COLOR}, but isn't a btrfs snapshot\n" \
"$snapper_id" "$backup_root/$snapper_snapshots"

View File

@@ -22,7 +22,7 @@
# Helper routines for tape handling
progname="${0##*/}"
version="0.0.14"
version="0.0.15"
# global variables
color=0
@@ -140,7 +140,7 @@ add_retensiondays_to_retensiondate () {
if [ $? -eq 0 ]; then
cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \
| select(.Name == \"$mediapool_name\") \
| .Member[$tape_index] \
| .Member[$tape_index] \
| .RetensionDate) \
|= \"$volume_retensiondate\" ' \
${mediapools_json} > $XDG_RUNTIME_DIR/$json_file"
@@ -148,10 +148,10 @@ add_retensiondays_to_retensiondate () {
if [ $? -eq 0 ]; then
cp "$XDG_RUNTIME_DIR/$json_file" "$mediapools_json"
else
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} Can't update RetensionDate ${GREEN}'%s'${MAGENTA} for Tape ${GREEN}'%s'${NO_COLOR}\n" \
"$volume_retensiondate" "$volume_name"
fi
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} Can't update RetensionDate ${GREEN}'%s'${MAGENTA} for Tape ${GREEN}'%s'${NO_COLOR}\n" \
"$volume_retensiondate" "$volume_name"
fi
return 1
fi
else
@@ -294,15 +294,15 @@ get_lastwrite () {
# select last write date for given volume
cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| select(.Name == \"${mediapool_name}\") \
| .Member[] \
| select(.VolumeName == \"${volume_name}\") \
| select(.VolumeName == \"${volume_name}\") \
| .LastWrite ' \
${mediapools_json}"
else
# select volume with latest write date
cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| select(.Name == \"${mediapool_name}\") \
| .Member \
| map({ \"VolumeName\" : .VolumeName, \"LastWrite\" : ( .LastWrite | scan(\"[0-9]{14}\")) }) \
| sort_by(.LastWrite) \
@@ -312,7 +312,7 @@ get_lastwrite () {
volume_name=$(eval $cmd)
volume_name=$(echo $volume_name | sed -e 's/"//g')
cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| select(.Name == \"${mediapool_name}\") \
| .Member \
| map({ \"VolumeName\" : .VolumeName, \"LastWrite\" : ( .LastWrite | scan(\"[0-9]{14}\")) }) \
| sort_by(.LastWrite) \
@@ -351,9 +351,9 @@ get_mediapolicy () {
fi
cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| select(.Name == \"${mediapool_name}\") \
| .Member[] \
| select(.VolumeName == \"${volume_name}\") \
| select(.VolumeName == \"${volume_name}\") \
| .MediaPolicy ' \
${mediapools_json}"
volume_mediapolicy=$(eval $cmd)
@@ -444,8 +444,8 @@ get_mediapool_retensiondays () {
if [ ${#mediapool_name} -ge 1 ]; then
# select default retension days for given pool
cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| .DefaultRetensionDays ' \
| select(.Name == \"${mediapool_name}\") \
| .DefaultRetensionDays ' \
${mediapools_json}"
fi
mediapool_defaultretensiondays=$(eval $cmd)
@@ -483,8 +483,8 @@ get_poolmember () {
fi
cmd="jq --monochrome-output --ascii-output '.MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| .Member[].VolumeName' \
| select(.Name == \"${mediapool_name}\") \
| .Member[].VolumeName' \
${mediapools_json}"
poolmember=$(eval $cmd)
poolmember=$(echo $poolmember | sed -e 's/"//g')
@@ -496,16 +496,16 @@ get_poolmember () {
i=0
for i in $poolmember ; do
if [ "$volume_name" = "any" ]; then
volume_name=$i
if [ "$volume_name" = "any" ]; then
volume_name=$i
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}selecting first volume_name ${GREEN}'%s'${MAGENTA} from media-pool ${GREEN}'%s'${NO_COLOR}\n" \
"$i" "$mediapool_name"
fi
return 0
break
fi
if test "$i" = "${volume_name}"; then
fi
if test "$i" = "${volume_name}"; then
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}volume_name ${GREEN}'%s'${MAGENTA} is member of media-pool ${GREEN}'%s'${NO_COLOR}\n" \
"$i" "$mediapool_name"
@@ -541,7 +541,7 @@ get_poolmember_next () {
return 1
cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| select(.Name == \"${mediapool_name}\") \
| .Member \
| map({ \"VolumeName\" : .VolumeName, \"LastWrite\" : ( .LastWrite | scan(\"[0-9]{14}\")) }) \
| sort_by(.LastWrite) \
@@ -601,9 +601,9 @@ get_slot () {
return 1
cmd="jq --monochrome-output --ascii-output ' .MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| select(.Name == \"${mediapool_name}\") \
| .Member[] \
| select(.VolumeName == \"${volume_name}\") \
| select(.VolumeName == \"${volume_name}\") \
| .Slot ' \
${mediapools_json}"
volume_slot=$(eval $cmd)
@@ -639,9 +639,9 @@ get_retensiondate () {
return 1
cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \
| select(.Name == \"${mediapool_name}\") \
| select(.Name == \"${mediapool_name}\") \
| .Member[] \
| select(.VolumeName == \"${volume_name}\")) \
| select(.VolumeName == \"${volume_name}\")) \
| .RetensionDate ' \
${mediapools_json}"
volume_retensiondate=$(eval $cmd)
@@ -724,8 +724,8 @@ ltfs_format () {
fi
if [ ${#volume_serial} -eq 0 ]; then
tape_id=$(echo $volume_name | sed -e 's/\([[:alpha:]]*-\)//g')
# tape_id needs to be exactly 6 character long
tape_id=$(echo $volume_name | sed -e 's/\([[:alpha:]]*-\)//g')
# tape_id needs to be exactly 6 character long
volume_serial=$(printf "%0.s0" $(seq 1 $((6 - ${#tape_id}))))
volume_serial="${volume_serial}${tape_id}"
fi
@@ -778,7 +778,7 @@ ltfs_mount () {
if [ ! -d $ltfs_mountpoint ]; then
mkdir -p $ltfs_mountpoint
fi
make_err_file
make_err_file
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}LTFS mounting tape ${GREEN}'%s'${MAGENTA} to ${GREEN}'%s'${NO_COLOR}\n" \
"$ltfs_devname" "$ltfs_mountpoint"
@@ -791,16 +791,16 @@ ltfs_mount () {
return 0
else
# format tape if needed
if [ -z $volume_name_active ]; then
slot_source="0"
mtx_getlabel $slot_source
fi
need_format=$(grep "medium is not partitioned" ${ERRFILE})
rm -f ${ERRFILE}
if [ ${#need_format} -ge 1 ]; then
if [ -z $volume_name_active ]; then
slot_source="0"
mtx_getlabel $slot_source
fi
need_format=$(grep "medium is not partitioned" ${ERRFILE})
rm -f ${ERRFILE}
if [ ${#need_format} -ge 1 ]; then
ltfs_format ${volume_name_active}
ltfs_mount
fi
ltfs_mount
fi
return $?
fi
else
@@ -901,8 +901,8 @@ ltfs_reformat () {
fi
# wiping: put tape in an unformatted state
ltfs_wipe
# RET = 8 -> error?
if [ $? -eq 0 ]; then
# format: create the ltfs partitions
ltfs_format $volume_name_active $volume_serial
if [ $? -eq 0 ]; then
ltfs_mount
@@ -1091,7 +1091,7 @@ mount_tape () {
ltfs_is_mounted
if test $? -eq 0; then
# get date from last written tape in given mediapool
# if no explicit tape should be mounted, get date from last written tape in given mediapool
if [ "${#volume_name}" -eq 0 ]; then
get_lastwrite ${mediapool_name}
if [ $? != 0 ]; then
@@ -1106,7 +1106,7 @@ mount_tape () {
printf "${MAGENTA}Active tape in drive: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name_active}"
fi
if [ "${#volume_name_active}" -ge 1 ] && [ "${volume_name_active}" != "${volume_name}" ]; then
if [ "${#volume_name_active}" -ge 1 ] && [ "${volume_name_active}" != "${volume_name}" ]; then
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Re-Mounting requested Tape: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}"
@@ -1128,92 +1128,94 @@ mount_tape () {
# check if given tape is poolmember of selected pool
get_poolmember ${mediapool_name} ${volume_name}
if [ $? -eq 0 ]; then
# volume_name is member of given mediapool
# check the mediapolicy for given volume_name
get_mediapolicy ${mediapool_name} ${volume_name_active}
if [ ${#volume_mediapolicy} -gt 0 ]; then
if [ ${volume_mediapolicy} = "append" ] ; then
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Valid tape mediapolicy: ${GREEN}%s${NO_COLOR}\n" \
"${volume_mediapolicy}"
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Valid tape mediapolicy: ${GREEN}%s${NO_COLOR}\n" \
"${volume_mediapolicy}"
fi
volume_retensiondate="20180101000000"
get_retensiondate ${mediapool_name} ${volume_name}
if [ $? -eq 0 ]; then
if [ $? -eq 0 ]; then
date_now=$($date_cmd)
compare_date $date_now $volume_retensiondate
if [ $? -eq 2 ]; then
compare_date $date_now $volume_retensiondate
if [ $? -eq 2 ]; then
# retensiondate has exposed: use it
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Valid tape, retensiondate has exposed: ${GREEN}%s${NO_COLOR}\n" \
"${volume_retensiondate}"
fi
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Valid tape, retensiondate has exposed: ${GREEN}%s${NO_COLOR}\n" \
"${volume_retensiondate}"
fi
#ltfs_format ${volume_name_active}
ltfs_reformat ${volume_name_active}
return 0
fi
if [ $? -eq 1 ]; then
fi
if [ $? -eq 1 ]; then
# respect active retensiondate: unload given tape
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Invalid tape ${GREEN}%s${MAGANTA}, respect retensiondate: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}" "${volume_retensiondate}"
fi
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Invalid tape ${GREEN}%s${MAGANTA}, respect retensiondate: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}" "${volume_retensiondate}"
fi
ltfs_umount
mtx_unload
# use next volume_name from pool
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Get next tape from MediaPool ${GREEN}%s${MAGANTA}\n" \
"${mediapool_name}"
fi
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Get next tape from MediaPool ${GREEN}%s${MAGANTA}\n" \
"${mediapool_name}"
fi
get_poolmember_next ${mediapool_name} ${volume_name}
get_slot ${mediapool_name} ${volume_name_next}
mtx_load ${volume_slot}
ltfs_mount
if [ $? -eq 0 ]; then
return 0
return 0
fi
fi
fi
return 0
fi
if [ ${volume_mediapolicy} = "overwrite" ] ; then
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Valid tape mediapolicy: ${GREEN}%s${NO_COLOR}\n" \
"${volume_mediapolicy}"
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Valid tape mediapolicy: ${GREEN}%s${NO_COLOR}\n" \
"${volume_mediapolicy}"
fi
volume_retensiondate="20180101000000"
get_retensiondate ${mediapool_name} ${volume_name}
date_now=$($date_cmd)
compare_date $date_now $volume_retensiondate
if [ $? -eq 2 ]; then
date_now=$($date_cmd)
compare_date $date_now $volume_retensiondate
if [ $? -eq 2 ]; then
# retensiondate has exposed: wipe given tape
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Valid tape, wipe and reformat: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}"
fi
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Valid tape, wipe and reformat: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}"
fi
ltfs_wipe
#ltfs_format ${volume_name} ${volume_serial}
if [ $? -eq 0 ]; then
return 0
fi
fi
if [ $? -eq 1 ]; then
#ltfs_reformat ${volume_name_active}
if [ $? -eq 0 ]; then
return 0
fi
fi
if [ $? -eq 1 ]; then
# respect active retensiondate: unload given tape
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Invalid tape ${GREEN}%s${MAGANTA}, respect retensiondate: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}" "${volume_retensiondate}"
fi
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Invalid tape ${GREEN}%s${MAGANTA}, respect retensiondate: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}" "${volume_retensiondate}"
fi
ltfs_umount
mtx_unload
# use next volume_name from pool
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Get next tape from MediaPool ${GREEN}%s${MAGANTA}\n" \
"${mediapool_name}"
fi
if [ $verbose -ge 3 ]; then
printf "${MAGENTA}Get next tape from MediaPool ${GREEN}%s${MAGANTA}\n" \
"${mediapool_name}"
fi
get_poolmember_next ${mediapool_name} ${volume_name}
get_slot ${mediapool_name} ${volume_name_next}
mtx_load ${volume_slot}
ltfs_mount
if [ $? -eq 0 ]; then
if [ $? -eq 0 ]; then
return 0
fi
fi
fi
fi
fi
fi
fi
@@ -1258,14 +1260,14 @@ mount_tape () {
printf "${MAGENTA}Tape in drive: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name_active}"
fi
if [ "${#volume_name_active}" -gt 1 ] && [ "${volume_name_active}" != "${volume_name}" ]; then
if [ "${#volume_name_active}" -gt 1 ] && [ "${volume_name_active}" != "${volume_name}" ]; then
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Active tape ${GREEN}%s${MAGENTA} needs to be exchanged with ${GREEN}%s${NO_COLOR}\n" \
"${volume_name_active}" "${volume_name}"
fi
mtx_unload
if [ $? -eq 0 ]; then
mount_tape "${mediapool_name}" "${volume_name}"
mount_tape "${mediapool_name}" "${volume_name}"
if [ $? -eq 0 ]; then
return 0
else
@@ -1287,25 +1289,25 @@ mount_tape () {
get_mediapolicy ${mediapool_name} ${volume_name}
if [ ${#volume_mediapolicy} -gt 0 ]; then
if [ ${volume_mediapolicy} = "append" ] ; then
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Valid tape mediapolicy: ${GREEN}%s${NO_COLOR}\n" \
"${volume_mediapolicy}"
fi
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Valid tape mediapolicy: ${GREEN}%s${NO_COLOR}\n" \
"${volume_mediapolicy}"
fi
#volume_retensiondate="20180101000000"
get_retensiondate ${mediapool_name} ${volume_name}
date_now=$($date_cmd)
compare_date $date_now $volume_retensiondate
RET=$?
date_now=$($date_cmd)
compare_date $date_now $volume_retensiondate
RET=$?
if [ $RET -eq 2 ]; then
if [ $verbose -ge 1 ]; then
printf "${MAGENTA}RetensionDate has exposed, mount the tape\n"
fi
printf "${MAGENTA}RetensionDate has exposed, mount the tape${NO_COLOR}\n"
fi
ltfs_mount
return 0
elif [ $RET -eq 1 ]; then
if [ $verbose -ge 1 ]; then
printf "${MAGENTA}Tape is bocked via RetensionDate.\n"
fi
fi
get_poolmember_next "${mediapool_name}"
if test $? -eq 0; then
if [ $verbose -ge 2 ]; then
@@ -1316,22 +1318,22 @@ mount_tape () {
mount_tape "${mediapool_name}" "${volume_name_next}"
return 0
fi
fi
fi
fi
if [ ${volume_mediapolicy} = "overwrite" ] ; then
volume_retensiondate="20180101000000"
get_retensiondate ${mediapool_name} ${volume_name}
date_now=$($date_cmd)
compare_date $date_now $volume_retensiondate
if [ $? -eq 2 ]; then
date_now=$($date_cmd)
compare_date $date_now $volume_retensiondate
if [ $? -eq 2 ]; then
# retensiondate has exposed: wipe given tape
ltfs_wipe
#ltfs_format ${volume_name} ${volume_serial}
ltfs_format ${volume_name} ${volume_serial}
if [ $? -eq 0 ]; then
return 0
return 0
fi
fi
if [ $? -eq 1 ]; then
fi
if [ $? -eq 1 ]; then
# respect active retensiondate: unload given tape
mtx_unload
if [ $? -eq 0 ]; then
@@ -1384,9 +1386,9 @@ mtx_getlabel () {
"${MTX}" "${changer_device}" "${slot_source}"
fi
volume_name_active=$(perl -ne '
/Data Transfer Element (\d+):Full \(Storage Element (\d+) Loaded\)(:VolumeTag =\s*(.+))?/ && print "$4\n";' ${TMPFILE})
volume_name_active=$(echo $volume_name_active | sed -e 's/ *$//g')
if [ $verbose -ge 2 ]; then
/Data Transfer Element (\d+):Full \(Storage Element (\d+) Loaded\)(:VolumeTag =\s*(.+))?/ && print "$4\n";' ${TMPFILE})
volume_name_active=$(echo $volume_name_active | sed -e 's/ *$//g')
if [ $verbose -ge 2 ] && [ ${#volume_name_active} -gt 0 ]; then
printf "${MAGENTA}Tape in slot ${GREEN}%s${MAGENTA} has Label: ${GREEN}%s${NO_COLOR}\n" \
"${slot_source}" "${volume_name_active}"
fi
@@ -1397,8 +1399,8 @@ mtx_getlabel () {
"${MTX}" "${changer_device}" "${slot_source}"
fi
volume_name=$(perl -ne '
/Storage Element ($ENV{"slot_source"}):Full( :VolumeTag=(.+))?/ && print "$3\n";' ${TMPFILE})
volume_name=$(echo $volume_name | sed -e 's/ *$//g')
/Storage Element ($ENV{"slot_source"}):Full( :VolumeTag=(.+))?/ && print "$3\n";' ${TMPFILE})
volume_name=$(echo $volume_name | sed -e 's/ *$//g')
if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Tape in slot ${GREEN}%s${MAGENTA} has Label: ${GREEN}%s${NO_COLOR}\n" \
"${slot_source}" "${volume_name}"
@@ -2030,7 +2032,7 @@ update_lastwrite () {
if [ $? -eq 0 ]; then
cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \
| select(.Name == \"$mediapool_name\") \
| .Member[$tape_index] \
| .Member[$tape_index] \
| .LastWrite) \
|= \"$volume_lastwrite\" ' \
${mediapools_json} > $XDG_RUNTIME_DIR/$json_file"
@@ -2082,10 +2084,10 @@ update_mediapool_retensiondays () {
if [ $? -eq 0 ]; then
cp "$XDG_RUNTIME_DIR/$json_file" "$mediapools_json"
else
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} Can't update DefaultRetensinDays ${GREEN}'%s'${MAGENTA} in media-pool file ${GREEN}'%s'${NO_COLOR}\n" \
"$mediapool_defaultretensiondays" "$mediapools_json"
fi
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} Can't update DefaultRetensinDays ${GREEN}'%s'${MAGENTA} in media-pool file ${GREEN}'%s'${NO_COLOR}\n" \
"$mediapool_defaultretensiondays" "$mediapools_json"
fi
return 1
fi
else
@@ -2138,7 +2140,7 @@ update_retensiondate () {
if [ $? -eq 0 ]; then
cmd="jq --monochrome-output --ascii-output '(.MediaPool[] \
| select(.Name == \"$mediapool_name\") \
| .Member[$tape_index] \
| .Member[$tape_index] \
| .RetensionDate) \
|= \"$volume_retensiondate\" ' \
${mediapools_json} > $XDG_RUNTIME_DIR/$json_file"
@@ -2146,10 +2148,10 @@ update_retensiondate () {
if [ $? -eq 0 ]; then
cp "$XDG_RUNTIME_DIR/$json_file" "$mediapools_json"
else
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} Can't update RetensionDate ${GREEN}'%s'${MAGENTA} for Tape ${GREEN}'%s'${NO_COLOR}\n" \
"$volume_retensiondate" "$volume_name"
fi
if [ $verbose -ge 1 ]; then
printf "${RED}Error:${MAGENTA} Can't update RetensionDate ${GREEN}'%s'${MAGENTA} for Tape ${GREEN}'%s'${NO_COLOR}\n" \
"$volume_retensiondate" "$volume_name"
fi
return 1
fi
else
@@ -2171,62 +2173,62 @@ Usage: $progname [options]
Options:
--add-retensiondays add RetensionDays to LastWrite attribute for given TapeName in Pool (JSON-File)
(input attribute: <mediapool> <volume_name> <days>)
(input attribute: <mediapool> <volume_name> <days>)
--color Enable colored output messages
--get-lastwrite extract LastWrite attribute from given Pool member (JSON-File)
(input attribute: <mediapool> [<volume_name>])
without given volume_name, extract Pool member that was last witten to
(input attribute: <mediapool> [<volume_name>])
without given volume_name, extract Pool member that was last witten to
--get-mediapool-name extract MediaPool name for given VolumeName from configuration (JSON-File)
(input attribute: <volume_name>)
(input attribute: <volume_name>)
--get-mediapools extract MediaPool names from configuration (JSON-File)
--get-mediapolicy extract MediaPolicy attribute from Pool configuration (JSON-File)
(input attribute: <mediapool> [<volume_name>])
(input attribute: <mediapool> [<volume_name>])
--get-poolmember extract VolumeName attribute from Pool configuration (JSON-File)
(input attribute: <mediapool> [<volume_name>])
(input attribute: <mediapool> [<volume_name>])
--get-poolmember-next extract VolumeName for next usable Pool member (JSON-File)
(input attribute: <mediapool> [volume_name])
(input attribute: <mediapool> [volume_name])
--get-retensiondate extract RetensionDate attribute from Pool configuration (JSON-File)
(input attribute: <mediapool> [<volume_name>])
(input attribute: <mediapool> [<volume_name>])
--get-retensiondays extract DefaultRetensionDays attribute from Pool configuration (JSON-File)
(input attribute: <mediapool>)
(input attribute: <mediapool>)
--get-slot extract Slot attribute from Pool configuration (JSON-File)
(input attribute: <mediapool> [<volume_name>])
(input attribute: <mediapool> [<volume_name>])
--ltfs-getattribute report ltfs extended attribute for mounted tape
(input attribute: <ltfs_attribute>)
(input attribute: <ltfs_attribute>)
--ltfs-format format tape
(input attribute: <volume_name> <tape_id>)
(input attribute: <volume_name> <tape_id>)
--ltfs-is-mounted returns true, if ltfs tape is already mounted
--ltfs-mount mount a ltfs tape (returns true on success)
--ltfs-reformat reformat mounted tape
(input attribute: [<volume_name>])
(input attribute: [<volume_name>])
--ltfs-umount unmount a ltfs tape (returns true on success)
--media-change Update RetensionDate for last written Pool-Tape and und exchange tapes in slots
(input attribute: <mediapool> [<volume_name>])
(input attribute: <mediapool> [<volume_name>])
--mtx-exchange exchange tapes in slots
(input attribute: <slot_source> <slot_target>)
(input attribute: <slot_source> <slot_target>)
--mtx-getlabel report tape label/barcode for tape in drive
(input attribute: <slot_source>)
(input attribute: <slot_source>)
--mtx-inventory run inquriy task for tape-changer
--mtx-load load a tape to target slot
(input attribute: <slot_source>)
(input attribute: <slot_source>)
--mtx-status list changer slot status
--mtx-transfer transfer a tape to target slot
(input attribute: <slot_source> <slot_target>)
(input attribute: <slot_source> <slot_target>)
--mtx-unload unload a tape
(input attribute: slot_source drive)
(input attribute: slot_source drive)
--mount make tape accessible for OS
(input attribute: <mediapool_name> [<volume_name>])
(input attribute: <mediapool_name> [<volume_name>])
-q, --quiet Be quiet
--update-lastwrite update LastWrite attribute for given TapeName in Pool (JSON-File)
if no datestring is specified, use "now"
if no datestring is specified, use "now"
(input attribute: <mediapool> <volume_name> [<YYYYMMDDHHMMSS> )
--update-retensiondate update RetensionDate attribute for given TapeName in Pool (JSON-File)
if no datestring is specified, use "now"
if no datestring is specified, use "now"
(input attribute: <mediapool> <volume_name> [<YYYYMMDDHHMMSS> )
--update-retensiondays update DefaultRetensionDays attribute for given Pool (JSON-File)
(input attribute: <mediapool>)
(input attribute: <mediapool>)
--use-mtx use mtx loader handling. If not specified, all mtx commands will use
default device ($default_changer_device)
default device ($default_changer_device)
-v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3)
--version show program version
EOF
@@ -2273,7 +2275,7 @@ case $cmd in
"${volume_name}" "${volume_date}"
fi
fi
;;
;;
get-lastwrite)
valid_member=0
get_lastwrite "${mediapool_name}" "${volume_name}"
@@ -2289,7 +2291,7 @@ case $cmd in
fi
fi
fi
;;
;;
get-mediapolicy)
get_mediapolicy "${mediapool_name}" "${volume_name}"
if test $? -gt 0; then
@@ -2300,7 +2302,7 @@ case $cmd in
"${volume_name}" "${volume_mediapolicy}"
fi
fi
;;
;;
get-mediapool-name)
get_mediapool_name "${volume_name}"
if test $? -gt 0; then
@@ -2311,7 +2313,7 @@ case $cmd in
"${mediapool_name}"
fi
fi
;;
;;
get-mediapools)
get_mediapool_names
if test $? -gt 0; then
@@ -2322,7 +2324,7 @@ case $cmd in
"${mediapool_names}"
fi
fi
;;
;;
get-poolmember)
valid_member=0
get_poolmember "${mediapool_name}" "${volume_name}"
@@ -2335,7 +2337,7 @@ case $cmd in
fi
valid_member=1
fi
;;
;;
get-poolmember-next)
valid_member=0
get_poolmember_next "${mediapool_name}" "${volume_name}"
@@ -2347,7 +2349,7 @@ case $cmd in
"${mediapool_name}" "${volume_name_next}"
fi
fi
;;
;;
get-mediapool-retensiondays)
get_mediapool_retensiondays "${mediapool_name}"
if test $? -gt 0; then
@@ -2358,7 +2360,7 @@ case $cmd in
"${mediapool_name}" "${mediapool_defaultretensiondays}"
fi
fi
;;
;;
get-retensiondate)
get_retensiondate "${mediapool_name}" "${volume_name}"
if test $? -gt 0; then
@@ -2371,7 +2373,7 @@ case $cmd in
"${volume_name}" "${volume_date}"
fi
fi
;;
;;
get-slot)
get_slot "${mediapool_name}" "${volume_name}"
if test $? -eq 0; then
@@ -2381,7 +2383,7 @@ case $cmd in
fi
fi
exit $?
;;
;;
ltfs-format)
ltfs_format "${volume_name}" "${tape_id}"
if test $? -gt 0; then
@@ -2405,78 +2407,72 @@ case $cmd in
fi
;;
ltfs-is-mounted)
if ! $quiet; then
printf "${MAGENTA}LTFS tape mount checke\n"
fi
ltfs_is_mounted
if test $? -gt 0; then
exit 1
else
if ! $quiet; then
printf "${MAGENTA}LTFS Tape is-mounted: ${GREEN}true${NO_COLOR}\n"
fi
fi
;;
ltfs-mount)
if ! $quiet; then
printf "${MAGENTA}LTFS mount Tape\n"
fi
ltfs_mount
if test $? -gt 0; then
exit 1
else
if ! $quiet; then
printf "${MAGENTA}LTFS Tape mount: ${GREEN}true${NO_COLOR}\n"
fi
fi
;;
ltfs-reformat)
if ! $quiet; then
printf "${MAGENTA}LTFS Tape reformat: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}"
fi
ltfs_reformat "${volume_name}"
if test $? -gt 0; then
return 1
else
if ! $quiet; then
printf "${MAGENTA}LTFS Tape reformat: ${GREEN}%s${NO_COLOR}\n" \
"${volume_name}"
fi
fi
;;
ltfs-umount)
if ! $quiet; then
printf "${MAGENTA}LTFS Tape unmount: ${GREEN}true${NO_COLOR}\n"
fi
ltfs_umount
if test $? -gt 0; then
exit 1
else
if ! $quiet; then
printf "${MAGENTA}LTFS Tape unmount: ${GREEN}true${NO_COLOR}\n"
fi
fi
;;
media-change)
if ! $quiet; then
if [ ${#volume_name} -ge 1 ]; then
printf "${MAGENTA}Media change in pool: ${GREEN}%s${NO_COLOR} (Volume-Name: ${GREEN}%s${NO_COLOR})\n" \
"${mediapool_name}" "${volume_name}"
else
printf "${MAGENTA}Media change in pool: ${GREEN}%s${NO_COLOR}\n" \
"${mediapool_name}"
fi
fi
media_change $mediapool_name $volume_name
if test $? -gt 0; then
return 1
else
if ! $quiet; then
if [ ${#volume_name} -ge 1 ]; then
printf "${MAGENTA}Media changed in pool: ${GREEN}%s${NO_COLOR} (Volume-Name: ${GREEN}%s${NO_COLOR})\n" \
"${mediapool_name}" "${volume_name}"
else
printf "${MAGENTA}Media changed in pool: ${GREEN}%s${NO_COLOR}\n" \
"${mediapool_name}"
fi
fi
fi
;;
mount)
mount_tape "${mediapool_name}" "${volume_name}"
if test $? -gt 0; then
exit 1
else
if ! $quiet; then
if test ${#volume_name} -ge 1; then
printf "${MAGENTA}Tape ${GREEN}'%s'${MAGENTA} for ${GREEN}'%s'${MAGENTA} mounted${NO_COLOR}\n" \
"${volume_name}" "${mediapool_name}"
else
printf "${MAGENTA}Next tape for mediapool ${GREEN}'%s'${MAGENTA} mounted${NO_COLOR}\n" \
"${mediapool_name}"
fi
if ! $quiet; then
if test ${#volume_name} -ge 1; then
printf "${MAGENTA}Mount tape ${GREEN}'%s'${MAGENTA} for ${GREEN}'%s'${NO_COLOR}\n" \
"${volume_name}" "${mediapool_name}"
else
printf "${MAGENTA}Mount next tape for mediapool ${GREEN}'%s'${NO_COLOR}\n" \
"${mediapool_name}"
fi
fi
;;
mount_tape "${mediapool_name}" "${volume_name}"
if test $? -gt 0; then
exit 1
fi
;;
mtx-exchange)
mtx_exchange "${slot_source}" "${slot_target}"
exit $?
@@ -2519,7 +2515,7 @@ case $cmd in
fi
fi
fi
;;
;;
update-mediapool-retensiondays)
update_mediapool_retensiondays "${mediapool_name}" "${mediapool_defaultretensiondays}"
if test $? -gt 0; then
@@ -2530,7 +2526,7 @@ case $cmd in
"${mediapool_name}" "${mediapool_defaultretensiondays}"
fi
fi
;;
;;
update-retensiondate)
update_retensiondate "${mediapool_name}" "${volume_name}" "${date_string}"
if test $? -gt 0; then
@@ -2545,5 +2541,5 @@ case $cmd in
fi
fi
fi
;;
;;
esac

View File

@@ -3,6 +3,13 @@
# dsnap-sync
<p align="center">
<span>English</span> |
<a href="../..">Englisch</a>
<!-- a href="../spanish">Spanisch</a> | -->
<a href="lang/german">Deutsch</a>
</p>
## Über
`dsnap-sync` ist konzipiert, um Backups für btrfs formatierte Dateisysteme
@@ -138,17 +145,17 @@ Folgende Tools werden verwendet:
erforderlich. Über ein Makefile wird die Installation an den
richtigen Ziel-Pfad gesteuert.
# make install
# make install
Sollte Ihr System einen unüblichen Speicherort für die snapper
Konfigurationen verwenden, kann der Pfad in einer Umgebungs-Variable
für die Installation einbezogen werden (`SNAPPER_CONFIG`).
Arch Linux/Fedora/Gentoo:
# make SNAPPER_CONFIG=/etc/conf.d/snapper install
Arch Linux/Fedora/Gentoo:
# make SNAPPER_CONFIG=/etc/conf.d/snapper install
Debian/Ubuntu:
# make SNAPPER_CONFIG=/etc/default/snapper install
Debian/Ubuntu:
# make SNAPPER_CONFIG=/etc/default/snapper install
Die lokalen `snapper` Konfiguration werden um ein neues Template
'dsnap-sync' ergänzt.
@@ -172,41 +179,41 @@ Software Paket Manager.
## Optionen
Usage: dsnap-sync [options]
Usage: dsnap-sync [options]
Options:
-a, --automount <path> start automount for given path to get a valid target mountpoint.
-b, --backupdir <prefix> backupdir is a relative path that will be appended to target backup-root
--backuptype <type> Specify backup type <archive | child | parent>
--batch no user interaction
-d, --description <desc> Change the snapper description. Default: "latest incremental backup"
--label-finished <desc> snapper description tagging successful jobs. Default: "dsnap-sync backup"
--label-running <desc> snapper description tagging active jobs. Default: "dsnap-sync in progress"
--label-synced <desc> snapper description tagging last synced jobs.
Default: "dsnap-sync last incremental"
--color Enable colored output messages
-c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper
configuration. You can select multiple configurations
(e.g. -c "root" -c "home"; --config root --config home)
--config-postfix <name> Specify a postfix that will be appended to the destination snapper config name.
--dry-run perform a trial run (no changes are written).
--mediapool Specify the name of the tape MediaPool
-n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup
--nonotify Disable graphical notification (via dbus)
--nopv Disable graphical progress output (disable pv)
--noionice Disable setting of I/O class and priority options on target
-r, --remote <address> Send the snapshot backup to a remote machine. The snapshot will be sent via ssh
You should specify the remote machine's hostname or ip address. The 'root' user
must be permitted to login on the remote machine
-p, --port <port> The remote port
-s, --subvolid <subvlid> Specify the subvolume id of the mounted BTRFS subvolume to back up to. Defaults to 5.
--use-btrfs-quota use btrfs-quota to calculate snapshot size
-u, --uuid <UUID> Specify the UUID of the mounted BTRFS subvolume to back up to. Otherwise will prompt
If multiple mount points are found with the same UUID, will prompt for user selection
-t, --target <target> Specify the mountpoint of the backup device
--volumename Specify the name of the tape volume
-v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3)
--version show program version
Options:
-a, --automount <path> start automount for given path to get a valid target mountpoint.
-b, --backupdir <prefix> backupdir is a relative path that will be appended to target backup-root
--backuptype <type> Specify backup type <archive | child | parent>
--batch no user interaction
-d, --description <desc> Change the snapper description. Default: "latest incremental backup"
--label-finished <desc> snapper description tagging successful jobs. Default: "dsnap-sync backup"
--label-running <desc> snapper description tagging active jobs. Default: "dsnap-sync in progress"
--label-synced <desc> snapper description tagging last synced jobs.
Default: "dsnap-sync last incremental"
--color Enable colored output messages
-c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper
configuration. You can select multiple configurations
(e.g. -c "root" -c "home"; --config root --config home)
--config-postfix <name> Specify a postfix that will be appended to the destination snapper config name.
--dry-run perform a trial run (no changes are written).
--mediapool Specify the name of the tape MediaPool
-n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup
--nonotify Disable graphical notification (via dbus)
--nopv Disable graphical progress output (disable pv)
--noionice Disable setting of I/O class and priority options on target
-r, --remote <address> Send the snapshot backup to a remote machine. The snapshot will be sent via ssh
You should specify the remote machine's hostname or ip address. The 'root' user
must be permitted to login on the remote machine
-p, --port <port> The remote port
-s, --subvolid <subvlid> Specify the subvolume id of the mounted BTRFS subvolume to back up to. Defaults to 5.
--use-btrfs-quota use btrfs-quota to calculate snapshot size
-u, --uuid <UUID> Specify the UUID of the mounted BTRFS subvolume to back up to. Otherwise will prompt
If multiple mount points are found with the same UUID, will prompt for user selection
-t, --target <target> Specify the mountpoint of the backup device
--volumename Specify the name of the tape volume
-v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3)
--version show program version
## Erster Sicherungslauf
@@ -264,7 +271,7 @@ sorgen. Folgende Sicherungstypen werden unterschieden:
* ein Unterverzeichnis des Konfigurations-Namens (`archive-<config-name>`)
* ein Unterverzeichnis der Snapshot-ID (`<snapper-id>`)
* der aktuelle btrfs Stream wird im Unterverzeichnis abgelegt
(`<snapper-id>_[full | incremental].btrfs`)
(`<snapper-id>_[full | incremental].btrfs`)
* die Metadaten des Prozesses werden in der Datei `info.xml` abgelegt
Steht `ltfs` zur Verfügung, ist ein Backup auf Bänder möglich.
@@ -357,4 +364,4 @@ Diese Arbeit ist unter der [Creative Common License 4.0][License-CC_BY] lizensie
![Creative Common Logo][Logo-CC_BY]
© 2016, 2017 James W. Barnett;
© 2017 - 2018 Ralf Zerres
© 2017 - 2019 Ralf Zerres