diff options
author | Steve Kemp <steve@steve.org.uk> | 2015-06-03 15:55:28 +0100 |
---|---|---|
committer | Steve Kemp <steve@steve.org.uk> | 2015-06-03 15:55:28 +0100 |
commit | a29380762b93737ae6949121010cd9bceb8196b2 (patch) | |
tree | 4dfe2931401d0cc73c38b9a90377e31210d6706e /bin/byteback-prune | |
parent | c4da983bd2a1e35450dcb21bdc7110f5fc0d166a (diff) |
Relocated the binaries to bin/
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}") |