zfs-snapshotter/zfs-snapshot.rb
2024-02-09 14:32:06 +01:00

77 lines
2.0 KiB
Ruby

#!/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