diff options
Diffstat (limited to 'bin/byteback-prune')
| -rwxr-xr-x | bin/byteback-prune | 140 | 
1 files changed, 140 insertions, 0 deletions
| diff --git a/bin/byteback-prune b/bin/byteback-prune new file mode 100755 index 0000000..d005289 --- /dev/null +++ b/bin/byteback-prune @@ -0,0 +1,140 @@ +#!/usr/bin/ruby +# +# Program to free up space on the backup-storage volume, by removing +# backups (whether by age, or importance). +# + +$LOAD_PATH.unshift('/usr/lib/byteback') + +require 'trollop' +require 'byteback' +require 'sys/filesystem' +include Byteback +include Byteback::Log +include Byteback::Util + +opts = Trollop.options do +  banner "Prune old backup directories to ensure there's enough space" + +  opt :minpercent, 'Start prune when disk has less than this %age free', +      type: :integer, +      default: 5 + +  opt :maxpercent, 'Stop prune when disk has more than this %age free', +      type: :integer, +      default: 10 + +  opt :list, 'List backups in pruning order, no other action' + +  opt :prune, 'Prune the next backup if necessary' + +  opt :prune_force, 'Prune the next backup regardless' + +  opt :order, "Order backups by 'age' or 'importance'", +      type: :string, +      default: 'importance' + +  opt :verbose, 'Show debugging messages' +end + +@order = opts[:order] +@verbose = opts[:verbose] +@do_list = opts[:list] +@do_prune = opts[:prune] +@do_prune_force = opts[:prune_force] +@minpercent = opts[:minpercent] +@maxpercent = opts[:maxpercent] + +@do_prune = true if @do_prune_force + +fatal('Must specify one of --prune or --list') unless +  (@do_prune || @do_list) && +  !(@do_prune && @do_list) + +fatal("Must specify --order as 'age' or 'importance'") unless +  @order == 'age' || @order == 'importance' + +if BackupDirectory.all.empty? +  fatal('No backup directories found, need to run byteback-snapshot') +end + +lock_out_other_processes('byteback-prune') + +@df_history = DiskFreeHistory.new(ENV['HOME']) +begin +  @df_history.new_reading! +rescue Errno::ENOSPC +  if @do_list +    warn("Couldn't write disk history file due to lack of space, ignoring") +  else +    warn("Couldn't write disk history file due to lack of space, going to --prune-force") +    @do_prune = @do_prune_force = true +  end +rescue => anything_else +  error("Couldn't record disk history of #{@df_history.mountpoint} in #{@df_history.history_file}, installation problem?") +  raise +end + +gradient_30m = @df_history.gradient(1800) + +# Check whether we should still be pruning +# +@free = @df_history.list.last.percent_free +PRUNING_FLAG = "#{ENV['HOME']}/.byteback.pruning" + +if @do_prune_force +  info('Forcing prune') +elsif @free <= @minpercent && !File.exist?(PRUNING_FLAG) +  info("Starting prune #{@free}% -> #{@maxpercent} free") +  File.write(PRUNING_FLAG, '') +elsif @free >= @maxpercent && File.exist?(PRUNING_FLAG) +  info("Stopping prune, reached #{@free}% free") +  File.unlink(PRUNING_FLAG) +elsif File.exist?(PRUNING_FLAG) +  info("Continuing prune #{@free}% -> #{@maxpercent}, gradient = #{gradient_30m}") +end + +debug("Disc free #{@free}%, 30m gradient = #{gradient_30m}") + +def snapshots_in_order +  list = BackupDirectory.all_snapshots +  if @order == 'importance' +    Snapshot.sort_by_importance(list) +  elsif @order == 'age' +    list.sort.reverse +  else +    fail ArgumentError.new("Unknown snapshot sort method #{method}") +  end +end + +snapshots = snapshots_in_order + +if @do_list +  print "Backups by #{@order}:\n" +  snapshots.each_with_index do |snapshot, index| +    print "#{sprintf('% 3d', index)}: #{snapshot.path}\n" +  end +end + +# Don't do anything if we've not got two hours of readings +# +unless @do_prune_force +  if @df_history.list.last.time - @df_history.list.first.time < 1800 +    warn('Not enough disc space history to make a decision') +    exit 0 +  end +end + +exit 0 unless +  (@do_prune && File.exist?(PRUNING_FLAG)) || +  @do_prune_force + +exit 0 unless @do_prune_force || gradient_30m == 0 + +if snapshots.empty? +  error('No snapshots to delete, is there enough disc space?') +  exit 1 +end + +info("Deleting #{snapshots.last.path}") +log_system("/sbin/btrfs subvolume delete #{snapshots.last.path}") | 
