digint / btrbk

Tool for creating snapshots and remote backups of btrfs subvolumes
https://digint.ch/btrbk/
GNU General Public License v3.0
1.67k stars 122 forks source link

Check if a external drive is mounted before snapshot / backup #520

Open msmafra opened 1 year ago

msmafra commented 1 year ago

Hello there!

I had this happen to me before and is happening again. I have mine set up to make local snapshots and also backup and snapshots to a external drive. Some times BTRBK ignores that the drive is not mounted and backups/snapshots anyway. Did I configure something wrong?

I use /btfs_pool for the snapshots /btrfs_backup for the external backup + snapshots drive. The drive is not connected right now, but it also happened many times.

 ls /btrfs_backup/                                                                                                                                                                               ✘ 16 10:19:03
home.20221005T0001/         home.20221026T102727-0300/  home.20221030T000000-0300/  home.20221105T000143-0300/  root.20221008T1439/         root.20221217T000000-0300/  root.20221221T000000-0300/
home.20221009T081430-0300/  home.20221027T081940-0300/  home.20221031T000001-0300/  home.20221106T000000-0300/  root.20221106T000000-0300/  root.20221218T000002-0300/  root.20221222T000000-0300/
home.20221016T000102-0300/  home.20221028T000001-0300/  home.20221101T000000-0300/  home.20221107T092408-0300/  root.20221116T000000-0300/  root.20221219T000000-0300/  root.20221223T000000-0300/
home.20221023T083304-0300/  home.20221029T101632-0300/  home.20221102T000000-0300/  home.20221108T000000-0300/  root.20221216T084715-0300/  root.20221220T000001-0300/  root.20221224T000000-0300/
/etc/fstab
...
# BTRBK backups
UUID=3f835c73-1a8c-4bca-9646-4cbd60bc7c6d  /btrfs_pool  btrfs  subvolid=5,compress=zstd:1,noatime,x-systemd.device-timeout=0,x-systemd.after=/ 0 0
UUID=/dev/mapper/luks-afaa1238-7ece-40e4-a069-c33b3ccf9140  /btrfs_backup  btrfs  subvolid=5,nofail,space_cache=v2,compress=zstd:3,noatime,x-gvfs-show,x-systemd.device-timeout=0,x-systemd.after=/   0 0
...
~ ❯ doas btrbk list config
SOURCE_SUBVOLUME  SNAPSHOT_PATH                SNAPSHOT_NAME  TARGET_PATH
/btrfs_pool/root  /btrfs_pool/btrbk_snapshots  root           /btrfs_backup
/btrfs_pool/home  /btrfs_pool/btrbk_snapshots  home           /btrfs_backup

~ ❯ doas btrbk list source
SOURCE_SUBVOLUME  SNAPSHOT_PATH                SNAPSHOT_NAME
/btrfs_pool/root  /btrfs_pool/btrbk_snapshots  root
/btrfs_pool/home  /btrfs_pool/btrbk_snapshots  home

~ ❯ cat /etc/btrbk/btrbk.conf
transaction_log         /var/log/btrbk.log
lockfile                /var/lock/btrbk.lock
timestamp_format        long-iso
stream_buffer           256m
#incremental             strict

snapshot_dir            btrbk_snapshots
snapshot_preserve_min   latest
snapshot_preserve       3h 5d 0w 0m 0y
target_preserve_min     latest
target_preserve         0h 14d 6w 4m 1y
archive_preserve        0h 1d 1w 1m 1y
archive_preserve_min    latest

volume /btrfs_pool
  snapshot_create  ondemand
  target send-receive /btrfs_backup
  subvolume root
  subvolume home

# Enable stream buffer. Adding a buffer between the sending and
# receiving side is generally a good idea.
# NOTE: If enabled, make sure to install the "mbuffer" package!
#stream_buffer           256m
#btrfs_commit_delete     yes

#snapshot_dir            btrbk_snapshots
#incremental             yes
#snapshot_preserve_min   18h
#snapshot_preserve       48h 7d
#target_preserve_min     6h
#target_preserve         24h 24d 24w

#volume /btrfs_pool
#  snapshot_create ondemand
#  incremental yes
#  target send-receive /btrfs_backup
#  subvolume root
#  subvolume home

~ ❯ cat /usr/lib/systemd/system/btrbk.service
[Unit]
Description=btrbk backup
Documentation=man:btrbk(1)

[Service]
Type=oneshot
ExecStart=/usr/bin/btrbk run

~ ❯ cat /usr/lib/systemd/system/btrbk.timer
[Unit]
Description=btrbk daily backup

[Timer]
OnCalendar=daily
AccuracySec=10min
Persistent=true

[Install]
WantedBy=timers.target
joshuataylor commented 1 year ago

Yeah, it won't check that it's mounted.

I back up to an external USB drive, where it may or not be plugged in. I have this listed in my /etc/fstab with nofail,x-systemd.device-timeout=10, so if it's not plugged in at boot the system will just continue to chug along.

So, what I do is I have a script that looks like this:

#!/bin/bash
# I use `mount -U` here to mount /usb_snapshots, -U is the uuid. You can use whatever you want here, such as label etc. check `man mount`.
sudo mount -U db2f33c2-aaaa-aaaa-aaaa-9bbe19ebe734
# Here I check if /usb_snapshots is now mounted.
if mountpoint -q /usb_snapshots; then
    # I then run btrbk

    btrbk -c /etc/btrbk/btrbk_usb.conf -v run
    #curl https://hc-ping.com/xxx # I use hc-ping as a poorman's alerting system :-)

    # Unmount /usb_snapshots, I prefer this drive usually unmounted.
    sudo umount /usb_snapshots
else
    echo "not mounted" # probably alert here.
    exit 1
fi
msmafra commented 1 year ago

Yeah, it won't check that it's mounted.

I backup to an external USB drive, where it may or not be plugged in. I have this listed in my /etc/fstab with nofail,x-systemd.device-timeout=10, so if it's not plugged in at boot the system will just continue to chug along.

So what I do is I have a script that looks like this:

#!/bin/bash

# I use `mount -a` here to mount /usb_snapshots, -a mounts all file systems defined in fstab
mount -a

# Here I check if /usb_snapshots is now mounted.
if mountpoint -q /usb_snapshots; then
  # I then run btrbk
  btrbk -c /etc/btrbk/btrbk_usb.conf -v run

  #curl https://hc-ping.com/xxx # I use hc-ping as a poormans alerting system :-)

  # Unmount /usb_snapshots, I prefer to have this drive usually umounted.
  sudo umount /usb_snapshots  
else
  echo "not mounted" # probably alert here.
  exit 1
fi

That's a good and simple solution, I'll use it for when I run btrbk manually. To solve the service side I added the Requires parameter to the btrbk.service using the .mount unit that Systemd already creates automatically from fstab's information. So it won't run if it's not mounted. Mounting and umounting after the backup tasks are done is very good thing to do.

[Unit]
Description=Runs btrbk backup and snapshots
Documentation=man:btrbk(1)
Requires=btrbk_backups.mount

[Service]
Type=oneshot
ExecStart=/usr/bin/btrbk run

I'll mess with your script and try testing it for a while

#!/usr/bin/env bash
LC_ALL=C
LANG=C

declare MOUNT_POINT
MOUNT_POINT="/btrbk_backups"

check_space() {
    # check percentage of occupied space
    local available_space
    local space_alert
    available_space="$(df --type=btrfs ${MOUNT_POINT} --output=pcent | sed -n '/%/{ n; p }' | sed 's/\%//g' | awk '{$1=$1};1' | tee /dev/null 2>&1)"
    space_alert="91"

    if [[ "${available_space}" -le ${space_alert} ]]; then

        printf "%s\n" "${available_space} is less 91"

    else

        exit 1

    fi

}

run_btrbk() {
    # Run BTRBK if check_space is true
    local mount_exec
    local mountpoint_exec
    local btrbk_exec
    # local btrbk_conf
    local priv_exec
    local umount_exec

    mount_exec="$(command -v mount | tee /dev/null 2>&1)"
    mountpoint_exec="$(command -v mountpoint | tee /dev/null 2>&1)"
    btrbk_exec="$(command -v btrbk | tee /dev/null 2>&1)"
    # btrbk_conf="/etc/btrbk/btrbk.conf"
    # set the priviledges elevator to use: doas or sudo
    priv_exec="$(command -v doas| tee /dev/null 2>&1)"
    umount_exec="$(command -v umount | tee /dev/null 2>&1)"

    # mounting the backup mounting point
    "${priv_exec}" "${mount_exec}" --verbose "${MOUNT_POINT}"
    # Here I check if mount_point is now mounted.
    if "${mountpoint_exec}" --quiet "${MOUNT_POINT}"; then

        # run btrbk with the default configuration file
        "${btrbk_exec}" --verbose run
        # Unmount
        "${priv_exec}" "${umount_exec}" "${MOUNT_POINT}"

    else

        printf "%s\n" "Not mounted"
        exit 1

    fi
}

main() {

    check_space
    run_btrbk

}
main "${@}"
digint commented 1 year ago

Yeah, it won't check that it's mounted.

Correct. The point is that btrbk cannot know if something is mounted on your "volume": if the rootfs / is a btrfs filesystem, volume /btrfs_pool is already a valid source.

The only thing I could do would be to somehow "pin" a volume to a filesystem device (or UUID), something like:

volume /btrfs_pool
  require_mount_from /dev/sdX

or:

volume /btrfs_pool
  must_be_mount_point

The latter sounds pretty straight forward, it would simply check if some btrfs file system is mounted at /btrfs_pool.

Will put some more thoughts on that, and hope to find some time for it soon (sadly pretty busy with many other things atm...)

msmafra commented 1 year ago

That would be phenomenal.

msmafra commented 1 year ago

Using the systemd units did not work. Probably there is some configuration missing, something I didn't get correctly.

msmafra commented 1 year ago

I did a "wrapper" of sorts and also made some changes to my .conf file. Also learned that I miss understood the commands and how to configure it, probably do not understand it completely yet. At least, I'm now using resume and snapshot instead of always using run: https://gist.github.com/msmafra/ba2d0160ac454973209c00add70eea67

zilexa commented 1 year ago

Would it not make more sense to run btrbk archive command for occasionally connected target drives? And then perhaps support archive targets in conf: only if/when there is a btrfs mountpoint behind that path, archive to it, otherwise skip and only use the regular targets.

digint commented 1 year ago

Would it not make more sense to run btrbk archive command for occasionally connected target drives?

It depends. btrbk archive does not guarantee you that the latest common snapshot/backup. Then again, if you keep the snapshots on the sources long enough anyways, this is the way to go.

And then perhaps support archive targets in conf: only if/when there is a btrfs mountpoint behind that path, archive to it, otherwise skip and only use the regular targets.

-> adding "enhancement" tag, implement something like: https://github.com/digint/btrbk/issues/520#issuecomment-1374960899

jaccc commented 10 months ago

btrbk isn't usable because of this bug

lairez commented 7 months ago

The easy "fix" is to have a target in a directory, not at the root of the external drive. Btrbk never creates the target directory itself, so if the drive is not available, it will fail.