Import zfs snapshots service as Arch package into git

This commit is contained in:
Daniel de Cloet 2024-02-09 14:16:43 +01:00
commit dfe963e461
6 changed files with 123 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/*.src.tar.gz
/*.pkg.tar.zst

20
PKGBUILD Normal file
View File

@ -0,0 +1,20 @@
# Maintainer: Daniel <daniel.de.clooet@yoursurprise.com>
pkgname=zfs-snapshotter
pkgver=0.0.1
pkgrel=1
pkgdesc='Systemd snapshotter tool'
source=("zfs-daily-snapshot@.timer" "zfs-daily-snapshot@.service" "zfs-snapshot.rb")
arch=('x86_64' 'aarch64' 'armv7h')
depends=('systemd' 'zfs' 'ruby')
sha256sums=('SKIP'
'SKIP'
'SKIP')
package() {
install -Dm755 "${srcdir}/zfs-snapshot.rb" "${pkgdir}/usr/bin/zfs-snapshot.rb"
install -Dm644 "${srcdir}/zfs-daily-snapshot@.timer" "${pkgdir}/etc/systemd/system/zfs-daily-snapshot@.timer"
install -Dm644 "${srcdir}/zfs-daily-snapshot@.service" "${pkgdir}/etc/systemd/system/zfs-daily-snapshot@.service"
}

9
README.md Normal file
View File

@ -0,0 +1,9 @@
# ZFS Snapshotter
This is my personal opiniated package for automatically creating and cleaning up ZFS snapshots.
## Usage:
- Clone this repository.
- Build and install: `$ makepkg -Si`
- Enable the systemd timer for your datasets:
- Use `-` instead of `/`, due to systemd encoding: `# systemctl enable --now zfs-daily-snapshot@pool-my-dataset.timer`

View File

@ -0,0 +1,7 @@
[Unit]
Description=Create a ZFS snapshot for %I
DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/usr/bin/zfs-snapshot.rb "%I"

View File

@ -0,0 +1,9 @@
[Unit]
Description=Create a daily ZFS snapshot for %I
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target

76
zfs-snapshot.rb Normal file
View File

@ -0,0 +1,76 @@
#!/usr/bin/env ruby
require 'date'
SNAPSHOTS_TO_RETAIN = 7
SNAPSHOT_FORMAT = "daily-%Y-%m-%d"
input = ARGV
def usage
puts "Usage: "
puts " #{$PROGRAM_NAME} <pool>"
exit -1
end
def run_command(*cmd)
pipe = IO.popen(cmd)
stdout = pipe.read
puts stdout
pipe.close
return stdout, $?.success?
end
class Array
def drop_tail(n)
slice(0...(length - n))
end
end
class Dataset
def initialize(name)
_, dataset_exists = run_command("zfs", "list", "-H", name)
raise "No such pool: #{name}" unless dataset_exists
@name = name
end
def has_snapshot(name)
_, snapshot_exists = run_command("zfs", "list", "-H", "#{@name}@#{name}")
return snapshot_exists
end
def create_snapshot_today
current_snapshot_name = Date.today.strftime(SNAPSHOT_FORMAT)
raise "Snapshot already created: #{@name}@#{current_snapshot_name}" if has_snapshot(current_snapshot_name)
_, made_snapshot = run_command("zfs", "snapshot", "#{@name}@#{current_snapshot_name}")
raise "Could not create new snapshot #{@name}@#{current_snapshot_name}" unless made_snapshot
end
def get_all_snapshots
all_snapshots, could_list_all_snapshot = run_command("zfs", "list", "-t", "snapshot", "-H", @name)
raise "Could not list snapshots for #{@name}" unless could_list_all_snapshot
return all_snapshots.lines.map { |line| line.split("\t").first }.map { |snapshot_name| snapshot_name.split("@").last }
end
def clean_up_old_snapshots
snapshots_to_delete = get_all_snapshots.drop_tail(SNAPSHOTS_TO_RETAIN)
p snapshots_to_delete
return if snapshots_to_delete.length == 0
output, could_delete_old_snapshots = run_command("zfs", "destroy", "#{@name}@#{snapshots_to_delete.first}%#{snapshots_to_delete.last}")
raise "Could not delete old snapshots for #{@name}" unless could_delete_old_snapshots
end
end
usage unless input.length == 1
usage if input[0] == "-h" or input[0] == "--help"
dataset = Dataset.new input[0]
dataset.create_snapshot_today
dataset.clean_up_old_snapshots