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"> <p align="center">
<span>English</span> | <span>English</span> |
<!-- a href="lang/spanish#dsnap-sync">Spanish</a> | --> <!-- a href="lang/spanish">Spanish</a> | -->
<a href="lang/german#dsnap-sync">Deutsch</a> <a href="lang/german">Deutsch</a>
</p> </p>
## About ## About
@@ -140,17 +140,17 @@ Following tools are used:
`dsnap-sync` is a shell script. Thus no compilation is required. `dsnap-sync` is a shell script. Thus no compilation is required.
To simplify correct target locations, this project uses a Makefile. 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 If your system uses a non-default location for the snapper
configuration defaults, specify the location with an environment variable configuration defaults, specify the location with an environment variable
(`SNAPPER_CONFIG`). (`SNAPPER_CONFIG`).
Arch Linux/Fedora/Gentoo: Arch Linux/Fedora/Gentoo:
# make SNAPPER_CONFIG=/etc/conf.d/snapper install # make SNAPPER_CONFIG=/etc/conf.d/snapper install
Debian/Ubuntu: Debian/Ubuntu:
# make SNAPPER_CONFIG=/etc/default/snapper install # make SNAPPER_CONFIG=/etc/default/snapper install
The local `snapper` configuration will be extended to make use The local `snapper` configuration will be extended to make use
of a new template 'dsnap-sync'. of a new template 'dsnap-sync'.
@@ -172,41 +172,41 @@ Please use your host software package manager.
## Options ## Options
Usage: dsnap-sync [options] Usage: dsnap-sync [options]
Options: Options:
-a, --automount <path> start automount for given path to get a valid target mountpoint. -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 -b, --backupdir <prefix> backupdir is a relative path that will be appended to target backup-root
--backuptype <type> Specify backup type <archive | child | parent> --backuptype <type> Specify backup type <archive | child | parent>
--batch no user interaction --batch no user interaction
-d, --description <desc> Change the snapper description. Default: "latest incremental backup" -d, --description <desc> Change the snapper description. Default: "latest incremental backup"
--label-finished <desc> snapper description tagging successful jobs. Default: "dsnap-sync 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-running <desc> snapper description tagging active jobs. Default: "dsnap-sync in progress"
--label-synced <desc> snapper description tagging last synced jobs. --label-synced <desc> snapper description tagging last synced jobs.
Default: "dsnap-sync last incremental" Default: "dsnap-sync last incremental"
--color Enable colored output messages --color Enable colored output messages
-c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper -c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper
configuration. You can select multiple configurations configuration. You can select multiple configurations
(e.g. -c "root" -c "home"; --config root --config home) (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. --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). --dry-run perform a trial run (no changes are written).
--mediapool Specify the name of the tape MediaPool --mediapool Specify the name of the tape MediaPool
-n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup -n, --noconfirm Do not ask for confirmation for each configuration. Will still prompt for backup
--nonotify Disable graphical notification (via dbus) --nonotify Disable graphical notification (via dbus)
--nopv Disable graphical progress output (disable pv) --nopv Disable graphical progress output (disable pv)
--noionice Disable setting of I/O class and priority options on target --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 -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 You should specify the remote machine's hostname or ip address. The 'root' user
must be permitted to login on the remote machine must be permitted to login on the remote machine
-p, --port <port> The remote port -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. -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 --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 -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 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 -t, --target <target> Specify the mountpoint of the backup device
--volumename Specify the name of the tape volume --volumename Specify the name of the tape volume
-v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3) -v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3)
--version show program version --version show program version
## First run ## First run
@@ -259,7 +259,7 @@ backup types are differenciated:
* create a config specific subdirectory (`archive-<config-name>`) * create a config specific subdirectory (`archive-<config-name>`)
* create a snapshot-id subdirectory (`<snapper-id>`) * create a snapshot-id subdirectory (`<snapper-id>`)
* create the btrfs stream file inside the subdirectory * 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` * the proccess metadata are saved to a file called `info.xml`
If you enabled the `ltfs` package, support for backups to tape is possible. 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 An open-source implementation can be found at
[LinearTapeFileSystem](https://github.com/LinearTapeFileSystem/ltfs). [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 ## Contributing
Help is very welcome! Feel free to fork and issue a pull request to add 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] ![Creative Common Logo][Logo-CC_BY]
© 2016, 2017 James W. Barnett; © 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. # difference for the next incremental snapshot.
progname="${0##*/}" progname="${0##*/}"
version="0.6.5" version="0.6.5.1"
# The following lines are modified by the Makefile or # The following lines are modified by the Makefile or
# find_snapper_config script # find_snapper_config script
@@ -79,6 +79,7 @@ snapper_backup_type='none'
#snapper_config_postfix="."`hostname` #snapper_config_postfix="."`hostname`
snapper_config_postfix= snapper_config_postfix=
snap_cleanup_algorithm="timeline" snap_cleanup_algorithm="timeline"
transfer_size=0
verbose=0 verbose=0
volume_name= volume_name=
@@ -133,7 +134,7 @@ check_snapper_failed_ids () {
| awk '/'"$snap_description_running"'/ {cnt++} END {print cnt}') | awk '/'"$snap_description_running"'/ {cnt++} END {print cnt}')
#snapper_failed_ids="snapper --config $selected_config list --type single \ #snapper_failed_ids="snapper --config $selected_config list --type single \
# | awk '/'"$snap_description_running"'/' \ # | 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 [ ${#snapper_failed_ids} -gt 0 ]; then
if [ "$batch" ]; then if [ "$batch" ]; then
@@ -161,6 +162,103 @@ check_snapper_failed_ids () {
fi 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 () { create_snapshot () {
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
printf "${MAGENTA}create_snapshot()...${NO_COLOR}\n" $snapper_config printf "${MAGENTA}create_snapshot()...${NO_COLOR}\n" $snapper_config
@@ -278,12 +376,12 @@ get_archive_last_sync_id () {
case ${archive_type} in case ${archive_type} in
#incremental) #incremental)
# cmd="find ${selected_target}/${backupdir}/${snapper_target_config} -name *_${archive_type}.btrfs \ # 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} ' " # | awk ' \$1 == $snapper_source_sync_id {print \$1} ' "
# ;; # ;;
*) *)
cmd="find ${selected_target}/${backupdir}/${snapper_target_config} -name *_${archive_type}.btrfs \ cmd="find ${selected_target}/${backupdir}/${snapper_target_config} -name *_${archive_type}.btrfs \
| awk -F '.*/' '{ gsub(/_${archive_type}.btrfs\$/,"_"); cnt++} END {print cnt}'" | awk -F '.*/' '{ gsub(/_${archive_type}.btrfs\$/,"_"); cnt++} END {print cnt}'"
ret=$(eval $run_ssh "$cmd" 2>/dev/null) ret=$(eval $run_ssh "$cmd" 2>/dev/null)
if [ ${#ret} -ge 1 ]; then if [ ${#ret} -ge 1 ]; then
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
@@ -292,7 +390,7 @@ get_archive_last_sync_id () {
fi fi
# get last sync-id # get last sync-id
cmd="find ${selected_target}/${backupdir}/${snapper_target_config} -name *_${archive_type}.btrfs \ 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 else
return 1 return 1
fi fi
@@ -565,7 +663,7 @@ get_disk_infos () {
# local root filesystem will be excluded as a valid target location # local root filesystem will be excluded as a valid target location
exclude_uuid=$(findmnt --noheadings --nofsroot --mountpoint / --output UUID) exclude_uuid=$(findmnt --noheadings --nofsroot --mountpoint / --output UUID)
disk_uuids=$(findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \ disk_uuids=$(findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \
| grep -v $exclude_uuid \ | grep -v $exclude_uuid \
| awk '{print $1}') | awk '{print $1}')
disk_targets=$(findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \ disk_targets=$(findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \
| grep -v $exclude_uuid \ | grep -v $exclude_uuid \
@@ -579,7 +677,7 @@ get_disk_infos () {
exclude_uuid=$($ssh findmnt --noheadings --nofsroot --mountpoint / --output UUID) exclude_uuid=$($ssh findmnt --noheadings --nofsroot --mountpoint / --output UUID)
sleep 0.2 sleep 0.2
disk_uuids=$($ssh "findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \ disk_uuids=$($ssh "findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \
| grep -v \"$exclude_uuid\" \ | grep -v \"$exclude_uuid\" \
| awk '{print \$1}'") | awk '{print \$1}'")
sleep 0.2 sleep 0.2
disk_targets=$($ssh "findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list \ 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 \ fs_options=$($ssh "findmnt --noheadings --nofsroot --types btrfs --output UUID,OPTIONS --list \
| grep -v \"$exclude_uuid\" \ | grep -v \"$exclude_uuid\" \
| awk '{print \$2}'") | awk '{print \$2}'")
fi fi
else else
# target location is not mounted as root subvolume # target location is not mounted as root subvolume
sleep 0.2 sleep 0.2
@@ -781,13 +879,13 @@ get_snapper_last_sync_id () {
fi fi
fi fi
if [ ${#snapper_subvolid} -ge 1 -a ${#snapper_uuid} -ge 1 ]; then 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 '/$snapper_description/' \
| awk '/subvolid="$snapper_subvolid", uuid="$snapper_uuid"/' \ | awk '/subvolid="$snapper_subvolid", uuid="$snapper_uuid"/' \
| awk 'END {print \$1}'" | awk 'END {print \$1}'"
elif [ ${#snapper_tapeid} -ge 1 ]; then elif [ ${#snapper_tapeid} -ge 1 ]; then
# | awk '/"$snapper_description"' '/"$snap_description_finished/"' \ # | 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 '/$snapper_description/' \
| awk '/tapeid="$snapper_tapeid"/' \ | awk '/tapeid="$snapper_tapeid"/' \
| awk 'END {print \$1}'" | awk 'END {print \$1}'"
@@ -810,17 +908,17 @@ get_snapper_last_sync_id () {
# no snapshot found, grap latest successfull sync # no snapshot found, grap latest successfull sync
if [ ${#snapper_subvolid} -ge 1 -a ${#snapper_uuid} -ge 1 ]; then 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 '/$snap_description_finished/' \ | awk '/$snap_description_finished/' \
| awk '/subvolid="$selected_subvol", uuid="$selected_uuid"/' \ | awk '/subvolid="$selected_subvol", uuid="$selected_uuid"/' \
| awk 'END {print \$1}'" | awk 'END {print \$1}'"
elif [ ${#snapper_tapeid} -ge 1 ]; then 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 '/$snap_description_finished/' \
| awk '/tapeid="$snapper_tapeid"/' \ | awk '/tapeid="$snapper_tapeid"/' \
| awk 'END {print \$1}'" | awk 'END {print \$1}'"
else else
cmd="snapper --config $snapper_config list --type single \ cmd="snapper --config $snapper_config list --type single \
| awk '/$snap_description_finished/' \ | awk '/$snap_description_finished/' \
| awk 'END {print \$1}'" | awk 'END {print \$1}'"
fi fi
snapper_sync_id=$(eval $run_ssh "$cmd") snapper_sync_id=$(eval $run_ssh "$cmd")
@@ -857,7 +955,7 @@ get_snapper_sync_id () {
[ ${#remote} -gt 0 ] && run_ssh=$ssh [ ${#remote} -gt 0 ] && run_ssh=$ssh
cmd="snapper --config "$snapper_config" list --type single \ 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") ret=$(eval $run_ssh "$cmd")
if [ ${#ret} -ge 1 ]; then if [ ${#ret} -ge 1 ]; then
@@ -1193,7 +1291,7 @@ parse_params () {
else else
$ssh which sh >/dev/null 2>&1 || \ $ssh which sh >/dev/null 2>&1 || \
{ printf "'remote shell' is not working!\n \ { 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
fi fi
@@ -1406,12 +1504,12 @@ run_config_preparation () {
# get backupdir from snapper target # get backupdir from snapper target
get_snapper_target_backupdir $backupdir get_snapper_target_backupdir $backupdir
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
if [ $remote ]; then if [ $remote ]; then
printf "${MAGENTA}backupdir on remote '%s': ${GREEN}'%s'${NO_COLOR}\n" \ printf "${MAGENTA}backupdir on remote '%s': ${GREEN}'%s'${NO_COLOR}\n" \
"$remote" "$backupdir" "$remote" "$backupdir"
else else
printf "${MAGENTA}backupdir: ${GREEN}'%s'${NO_COLOR}\n" \ printf "${MAGENTA}backupdir: ${GREEN}'%s'${NO_COLOR}\n" \
"$backupdir" "$backupdir"
fi fi
fi fi
# set target sync_snapshot path # 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 snapper_target_sync_snapshot=$backup_root/.snapshots/$snapper_target_sync_id/$snapper_snapshot_name
fi fi
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
if [ $remote ]; then if [ $remote ]; then
printf "${MAGENTA}backup_root on remote '%s': ${GREEN}'%s'${NO_COLOR}\n" \ printf "${MAGENTA}backup_root on remote '%s': ${GREEN}'%s'${NO_COLOR}\n" \
"$remote" "$backup_root" "$remote" "$backup_root"
else else
printf "${MAGENTA}backup_root: ${GREEN}'%s'${NO_COLOR}\n" \ printf "${MAGENTA}backup_root: ${GREEN}'%s'${NO_COLOR}\n" \
"$backup_root" "$backup_root"
fi fi
fi fi
;; ;;
*) *)
@@ -1539,15 +1637,15 @@ run_backup () {
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
printf "${MAGENTA}snapper_source_config: ${GREEN}'%s'${NO_COLOR}\n" \ 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" \ 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" \ printf "${MAGENTA}snapper_backup_type: ${GREEN}'%s'${NO_COLOR}\n" \
"$snapper_backup_type" "$snapper_backup_type"
printf "${MAGENTA}snapper_source_sync_id: ${GREEN}'%s'${NO_COLOR}\n" \ 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" \ 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_dir: ${GREEN}'%s'${NO_COLOR}\n" "$backup_dir"
printf "${MAGENTA}backup_root: ${GREEN}'%s'${NO_COLOR}\n" "$backup_root" printf "${MAGENTA}backup_root: ${GREEN}'%s'${NO_COLOR}\n" "$backup_root"
fi fi
@@ -1616,64 +1714,6 @@ run_backup () {
continue continue
fi 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 # setting process I/O scheduling options
if [ $do_ionice_cmd -eq 1 ]; then if [ $do_ionice_cmd -eq 1 ]; then
# class: best-efford, priority: medium # class: best-efford, priority: medium
@@ -1684,59 +1724,58 @@ run_backup () {
cmd_ionice='' cmd_ionice=''
fi fi
# settings for interactive progress status # prepare pipe command
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
if [ "$dryrun" -eq 0 ]; then if [ "$dryrun" -eq 0 ]; then
if [ "$snapper_source_sync_id" -eq 0 ] \ if [ "$snapper_source_sync_id" -eq 0 ] \
|| [ "$snapper_target_sync_id" -eq 0 ] \ || [ "$snapper_target_sync_id" -eq 0 ] \
|| [ "$backup_mode" = "full" ] ; then || [ "$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 # send full snapshot to target
if [ $verbose -ge 2 ]; then 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" \ 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 else
printf "${MAGENTA}Sending ${GREEN}snapshot${NO_COLOR} for snapper config ${GREEN}'%s' ${MAGENTA}(id='%s')${NO_COLOR} ...\n" \ printf "${MAGENTA}Sending ${GREEN}snapshot${NO_COLOR} for snapper config ${GREEN}'%s' ${MAGENTA}(id='%s')${NO_COLOR} ...\n" \
"$selected_config" "$selected_config"
fi fi
fi fi
# the actual data sync to the target # the actual data sync to the target
# this may take a while, depending on datasize and line-speed # this may take a while, depending on datasize and line-speed
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
@@ -1751,8 +1790,8 @@ run_backup () {
continue continue
fi fi
else else
# source holds synced snapshots # source holds synced snapshots
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
printf "New ${GREEN}source${NO_COLOR} snapshot id: ${GREEN}'%s'${NO_COLOR} (path: ${GREEN}'%s'${NO_COLOR})\n" \ printf "New ${GREEN}source${NO_COLOR} snapshot id: ${GREEN}'%s'${NO_COLOR} (path: ${GREEN}'%s'${NO_COLOR})\n" \
"$snapper_source_id" "$snapper_source_snapshot" "$snapper_source_id" "$snapper_source_snapshot"
printf "New ${GREEN}target${NO_COLOR} snapshot id: ${GREEN}'%s'${NO_COLOR} (path: ${GREEN}'%s'${NO_COLOR})\n" \ 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" "$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" \ 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" "$snapper_target_sync_id" "$snapper_target_sync_snapshot"
fi fi
if [ $verbose -ge 2 ]; then 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" \ 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" "$snapper_target_id" "$selected_config"
fi fi
# verify that we have a matching source and target snapshot-id
if [ $snapper_common_sync_id -eq 0 ]; then # 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 if [ ${snapper_source_id} -eq ${snapper_target_sync_id} ]; then
# nothing to do, snapshot already in sync # nothing to do, snapshot already in sync
if [ $verbose -ge 3 ]; then 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" "$snapper_target_sync_id"
fi fi
# go for next configuration # go for next configuration
@@ -1782,17 +1822,17 @@ run_backup () {
elif [ ${snapper_source_sync_id} != ${snapper_target_sync_id} ]; then elif [ ${snapper_source_sync_id} != ${snapper_target_sync_id} ]; then
if [ $snapper_target_sync_id -lt $snapper_target_id ]; then if [ $snapper_target_sync_id -lt $snapper_target_id ]; then
# try to find last target_sync_id in source_config # try to find last target_sync_id in source_config
get_snapper_sync_id "snapper_config=${snapper_source_config}" "remote=" get_snapper_sync_id "snapper_config=${snapper_source_config}" "remote="
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
snapper_source_sync_id=$snapper_sync_id snapper_source_sync_id=$snapper_sync_id
snapper_source_sync_snapshot=$SUBVOLUME/.snapshots/$snapper_sync_id/$snapper_snapshot_name 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" printf "${RED}Error: ${MAGENTA}No common sync id found. Aborting backup for config ${GREEN}'%s'${NO_COLOR}\n"
error_count=$(($error_count+1)) error_count=$(($error_count+1))
# go for next configuration # go for next configuration
i=$(($i+1)) i=$(($i+1))
continue continue
fi fi
fi fi
elif [ ${snapper_source_id} != ${snapper_source_sync_id} ]; then elif [ ${snapper_source_id} != ${snapper_source_sync_id} ]; then
snapper_common_sync_id=$snapper_source_sync_id snapper_common_sync_id=$snapper_source_sync_id
@@ -1811,18 +1851,22 @@ run_backup () {
fi fi
fi fi
cmd="$ssh stat --format %i $snapper_target_snapshot 2>/dev/null" cmd="$ssh stat --format %i $snapper_target_snapshot 2>/dev/null"
ret=$(eval $cmd) ret=$(eval $cmd)
if [ $? -eq 0 ]; then 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 case $selected_fstype in
btrfs) 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 # 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). # 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 # 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) # 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="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" $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 snapper_target_stream=${snapper_target_id}_incremental.btrfs
if [ -z $remote ]; then if [ -z $remote ]; then
cmd="btrfs send $btrfs_verbose_flag -p $snapper_common_sync_snapshot $snapper_source_snapshot 2>$BTRFS_PIPE \ cmd="btrfs send $btrfs_verbose_flag -p $snapper_common_sync_snapshot $snapper_source_snapshot 2>$BTRFS_PIPE \
| $cmd_pv \ | $cmd_pv \
$cmd_ionice cat > $snapper_target_snapshot/$snapper_target_stream 2>$BTRFS_PIPE" $cmd_ionice cat > $snapper_target_snapshot/$snapper_target_stream 2>$BTRFS_PIPE"
else else
cmd="btrfs send $btrfs_verbose_flag -p $snapper_common_sync_snapshot $snapper_source_snapshot 2>$BTRFS_PIPE \ cmd="btrfs send $btrfs_verbose_flag -p $snapper_common_sync_snapshot $snapper_source_snapshot 2>$BTRFS_PIPE \
| $cmd_pv \ | $cmd_pv \
$cmd_ionice $ssh 'cat > $snapper_target_snapshot/$snapper_target_stream' 2>$BTRFS_PIPE" $cmd_ionice $ssh 'cat > $snapper_target_snapshot/$snapper_target_stream' 2>$BTRFS_PIPE"
fi fi
;; ;;
esac esac
if [ $verbose -ge 2 ]; then 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" \ " 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" \ " snapshot id: ${GREEN}'$snapper_source_id'${NO_COLOR} (path: ${GREEN}'$snapper_source_snapshot'${NO_COLOR})\n" \
" to construct an incremental data-stream ...\n" " to construct an incremental data-stream ...\n"
fi fi
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
printf "${GREEN}btrfs command:${NO_COLOR} '%s'\n" "$cmd" printf "${GREEN}btrfs command:${NO_COLOR} '%s'\n" "$cmd"
fi fi
$(eval $cmd) $(eval $cmd)
ret=$? ret=$?
@@ -1874,10 +1918,10 @@ run_backup () {
continue continue
;; ;;
esac esac
else else
# is this clause possible? # is this clause possible?
if [ $verbose -ge 3 ]; then 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" "$snapper_common_sync_snapshot"
error_count=$(($error_count+1)) error_count=$(($error_count+1))
# go for next configuration # go for next configuration
@@ -1885,14 +1929,14 @@ run_backup () {
continue continue
fi fi
fi fi
fi fi
else else
printf "${MAGENTA}dryrun${NO_COLOR}: Would run btrfs send / btrfs receive pipe\n" printf "${MAGENTA}dryrun${NO_COLOR}: Would run btrfs send / btrfs receive pipe\n"
fi fi
# send the snapper info metadata # send the snapper info metadata
if [ $dryrun -eq 0 ]; then if [ $dryrun -eq 0 ]; then
if [ -z "$remote" ]; then if [ -z "$remote" ]; then
cp "$snapper_source_info" "$snapper_target_snapshot/info.xml" cp "$snapper_source_info" "$snapper_target_snapshot/info.xml"
else else
@@ -1957,9 +2001,9 @@ run_finalize () {
SNAP_SYNC_EXCLUDE=no SNAP_SYNC_EXCLUDE=no
if [ -f "/etc/snapper/configs/$selected_config" ]; then if [ -f "/etc/snapper/configs/$selected_config" ]; then
. /etc/snapper/configs/$selected_config . /etc/snapper/configs/$selected_config
else 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 return 1
fi fi
@@ -1967,7 +2011,7 @@ run_finalize () {
if [ "$cont_backup" = "no" ] || [ "$SNAP_SYNC_EXCLUDE" = "yes" ]; then if [ "$cont_backup" = "no" ] || [ "$SNAP_SYNC_EXCLUDE" = "yes" ]; then
if [ $donotify -gt 0 ]; then if [ $donotify -gt 0 ]; then
notify_info "Finalize backup" "NOTE: Skipping '$selected_config' configuration." notify_info "Finalize backup" "NOTE: Skipping '$selected_config' configuration."
fi fi
continue continue
fi fi
@@ -2038,8 +2082,8 @@ run_finalize () {
# save target-id # save target-id
if [ $snapper_source_id -gt 0 ]; then if [ $snapper_source_id -gt 0 ]; then
cmd="snapper --config $selected_config modify \ cmd="snapper --config $selected_config modify \
--cleanup-algorithm \"dsnap-sync\" \ --cleanup-algorithm \"dsnap-sync\" \
--userdata \"tapeid=$volume_name\" \ --userdata \"tapeid=$volume_name\" \
$snapper_source_id" $snapper_source_id"
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
@@ -2068,8 +2112,8 @@ run_finalize () {
;; ;;
btrfs-snapshot) btrfs-snapshot)
# create btrfs-snapshot on target # create btrfs-snapshot on target
cmd="$ssh snapper --config \"$snapper_target_config\" list --type single \ cmd="$ssh snapper --config \"$snapper_target_config\" list --type single \
| awk ' /'\"$snap_description_running\"'/ ' \ | awk ' /'\"$snap_description_running\"'/ ' \
| awk -F '|' ' \$1 == $snapper_target_id {print \$1} ' " | awk -F '|' ' \$1 == $snapper_target_id {print \$1} ' "
;; ;;
esac 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" \ 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" "$snapper_target_id" "$snapper_target_config"
fi 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" \ 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" "$snapper_target_id" "$snapper_target_config"
#printf "calling: '%s'\n" "$($cmd)" #printf "calling: '%s'\n" "$($cmd)"
@@ -2095,16 +2139,16 @@ run_finalize () {
# call command (respect needed quotes) # call command (respect needed quotes)
if [ $remote ]; then if [ $remote ]; then
ret=$(eval $ssh snapper --config \\\'$snapper_target_config\\\' modify \ ret=$(eval $ssh snapper --config \\\'$snapper_target_config\\\' modify \
--description \\\'$snap_description_finished\\\' \ --description \\\'$snap_description_finished\\\' \
--userdata \\\'host=$src_host, subvolid=$src_subvolid, uuid=$src_uuid\\\' \ --userdata \\\'host=$src_host, subvolid=$src_subvolid, uuid=$src_uuid\\\' \
--cleanup-algorithm \'timeline\' \ --cleanup-algorithm \'timeline\' \
\'$snapper_target_id\') \'$snapper_target_id\')
else else
ret=$(eval snapper --config $snapper_target_config modify \ 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" \ --userdata "host=$src_host, subvolid=$src_subvolid, uuid=$src_uuid" \
--cleanup-algorithm "timeline" \ --cleanup-algorithm "timeline" \
$snapper_target_id) $snapper_target_id)
fi fi
if [ $verbose -ge 3 ]; then if [ $verbose -ge 3 ]; then
printf "return: '%s'\n" "$ret" printf "return: '%s'\n" "$ret"
@@ -2127,7 +2171,7 @@ run_finalize () {
fi fi
if [ $snapper_source_id -gt 0 ]; then if [ $snapper_source_id -gt 0 ]; then
cmd="snapper --config $selected_config modify \ 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\" \ --userdata \"backupdir=$backupdir, important=yes, host=$remote, subvolid=$selected_subvol, uuid=$selected_uuid\" \
--cleanup-algorithm \"timeline\" \ --cleanup-algorithm \"timeline\" \
$snapper_source_id" $snapper_source_id"
@@ -2152,7 +2196,7 @@ run_finalize () {
if [ ${#snapper_source_sync_id} -gt 0 ]; then if [ ${#snapper_source_sync_id} -gt 0 ]; then
# TODO: no snapper method to remove userdata pair, use awk # TODO: no snapper method to remove userdata pair, use awk
cmd="snapper --config $selected_config modify \ cmd="snapper --config $selected_config modify \
--description \"$snap_description_finished\" \ --description \"$snap_description_finished\" \
--userdata \"important=no\" \ --userdata \"important=no\" \
$snapper_source_sync_id" $snapper_source_sync_id"
@@ -2208,9 +2252,9 @@ select_target () {
# print selection table # print selection table
if [ $verbose -ge 2 ]; then if [ $verbose -ge 2 ]; then
if [ -z "$remote" ]; 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 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
fi fi
while [ "$target_id" -eq 0 ] || [ "$target_id" -le $target_count ]; do while [ "$target_id" -eq 0 ] || [ "$target_id" -le $target_count ]; do
@@ -2310,7 +2354,7 @@ select_target () {
esac esac
done done
if [ "$target_selected" = x ]; then if [ "$target_selected" = x ]; then
exit 0 exit 0
fi fi
selected_target=$(eval echo \$target_$target_selected) selected_target=$(eval echo \$target_$target_selected)
@@ -2376,9 +2420,9 @@ select_target_disk () {
# print selection table # print selection table
if [ $verbose -ge 2 ]; then if [ $verbose -ge 2 ]; then
if [ -z "$remote" ]; 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 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
fi fi
while [ "$disk_id" -eq -1 ] || [ "$disk_id" -le $disk_count ]; do while [ "$disk_id" -eq -1 ] || [ "$disk_id" -le $disk_count ]; do
@@ -2450,7 +2494,7 @@ select_target_disk () {
esac esac
done done
if [ "$disk_selected" = x ]; then if [ "$disk_selected" = x ]; then
exit 0 exit 0
fi fi
selected_uuid=$(eval echo \$disk_uuid_$disk_selected) selected_uuid=$(eval echo \$disk_uuid_$disk_selected)
@@ -2507,9 +2551,9 @@ select_target_tape () {
# print selection table # print selection table
if [ $verbose -ge 2 ]; then if [ $verbose -ge 2 ]; then
if [ -z "$remote" ]; 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 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
fi fi
while [ "$tape_id" -eq -1 ] || [ "$tape_id" -le $tape_count ]; do while [ "$tape_id" -eq -1 ] || [ "$tape_id" -le $tape_count ]; do
@@ -2582,7 +2626,7 @@ select_target_tape () {
esac esac
done done
if [ "$tape_selected" = x ]; then if [ "$tape_selected" = x ]; then
exit 0 exit 0
fi fi
selected_tape_id=$(eval echo \$tape_id_$tape_selected) 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-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-running <desc> snapper description tagging active jobs. Default: "dsnap-sync in progress"
--label-synced <desc> snapper description tagging last synced jobs --label-synced <desc> snapper description tagging last synced jobs
Default: "dsnap-sync last incremental" Default: "dsnap-sync last incremental"
--color Enable colored output messages --color Enable colored output messages
-c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper -c, --config <config> Specify the snapper configuration to use. Otherwise will perform for each snapper
configuration. You can select multiple configurations configuration. You can select multiple configurations
(e.g. -c "root" -c "home"; --config root --config home) (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 --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) --dry-run perform a trial run (no changes are written)
--mediapool Specify the name of the tape MediaPool --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 --noionice Disable setting of I/O class and priority options on target
-p, --port <port> The remote port -p, --port <port> The remote port
-r, --remote <address> Send the snapshot backup to a remote machine. The snapshot will be sent via ssh -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 You should specify the remote machine's hostname or ip address. The 'root' user
must be permitted to login on the remote machine 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 -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 --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 -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 -t, --target <target> Specify the mountpoint of the backup device
--volumename Specify the name of the tape volume --volumename Specify the name of the tape volume
-v, --verbose Be verbose on what's going on (min: --verbose=1, max: --verbose=3) -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 # no inode for given backup_root
if [ $dryrun -eq 0 ]; then if [ $dryrun -eq 0 ]; then
if [ $verbose -ge 2 ]; then if [ $verbose -ge 2 ]; then
if [ -z "$remote" ]; then if [ -z "$remote" ]; then
printf "${MAGENTA}Create${NO_COLOR} new snapper capable BTRFS ${MAGENTA}subvolume ${GREEN}'%s'${NO_COLOR} ...\n" \ printf "${MAGENTA}Create${NO_COLOR} new snapper capable BTRFS ${MAGENTA}subvolume ${GREEN}'%s'${NO_COLOR} ...\n" \
"$backup_root" "$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" \ 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" "$backup_root" "$remote_host"
fi fi
fi fi
# verify that we can use the correct snapper template # verify that we can use the correct snapper template
cmd="$ssh stat --format %i $SNAPPER_TEMPLATE_DIR/$snapper_subvolume_template 2>/dev/null" 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" \ #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" # "$snapper_subvolume_template" "$snapper_config" "$SNAPPER_TEMPLATE_DIR" "$remote_host"
fi 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" cmd="$ssh btrfs subvolume create $backup_root 1>/dev/null"
ret=$(eval $cmd) ret=$(eval $cmd)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@@ -2992,14 +3036,14 @@ verify_snapper_structure () {
if [ $? -eq 1 ]; then if [ $? -eq 1 ]; then
# path does not exist, let snapper create the structure # path does not exist, let snapper create the structure
# and path $backup_root/.snapshots # and path $backup_root/.snapshots
cmd="$ssh snapper --config $snapper_config create-config \ cmd="$ssh snapper --config $snapper_config create-config \
--template $snapper_subvolume_template \ --template $snapper_subvolume_template \
--fstype btrfs $backup_root" --fstype btrfs $backup_root"
if [ $verbose -ge 2 ]; then if [ $verbose -ge 2 ]; then
printf "${MAGENTA}Create new BTRFS subvolume ${GREEN}'%s'${NO_COLOR} using template ${GREEN}'%s'${NO_COLOR}\n" \ printf "${MAGENTA}Create new BTRFS subvolume ${GREEN}'%s'${NO_COLOR} using template ${GREEN}'%s'${NO_COLOR}\n" \
$snapper_config $snapper_subvolume_template $snapper_config $snapper_subvolume_template
fi fi
$(eval $cmd) $(eval $cmd)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
if [ $remote ]; then if [ $remote ]; then
printf "${RED}Error: ${MAGENTA}Creation of snapper capable config ${GREEN}%s${MAGENTA} on ${GREEN}%s${MAGENTA} failed${NO_COLOR}\n" \ 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 fi
return 1 return 1
fi fi
else else
# WIP: # WIP:
# snapper_config exist, now verify if SUBVOLUME needs to be updated # 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 -F '|' '/'\"^$snapper_config\"'/ {print \$1}'"
#cmd="$ssh snapper list-configs | awk '/'\"^$snapper_config\"'/' | awk -F '|' ' /'\$1 == "$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 $backup_root/$snapper_snapshots
fi fi
cmd="$ssh btrfs subvolume create $backup_root/$snapper_snapshots 2>/dev/null" cmd="$ssh btrfs subvolume create $backup_root/$snapper_snapshots 2>/dev/null"
ret=$(eval $cmd) ret=$(eval $cmd)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
printf "${RED}Error: ${MAGENTA}Creation of snapper subvolume ${GREEN}%s${MAGENTA} failed${NO_COLOR}\n" \ 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 return 1
fi fi
else 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" cmd="$ssh stat --format %i $backup_root/$snapper_snapshots/$snapper_id/$snapper_snapshot_name 2>/dev/null"
ret=$(eval $cmd) ret=$(eval $cmd)
if [ $? -eq 0 ] && [ $ret -ne 256 ]; then 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 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" \ 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" "$snapper_id" "$backup_root/$snapper_snapshots"

View File

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

View File

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