summaryrefslogtreecommitdiff
path: root/lib/byteback/disk_free_history.rb
diff options
context:
space:
mode:
authorMatthew Bloch <matthew@bytemark.co.uk>2014-11-05 14:00:45 +0000
committerMatthew Bloch <matthew@bytemark.co.uk>2014-11-05 14:00:45 +0000
commitc88333e19b459685f57ba3f246c3fdd684681542 (patch)
tree3be762cf0a9ebb57c8a8528bcf98433434d371ad /lib/byteback/disk_free_history.rb
parentf6c155ff0b261f23b6fc9465b7f2e6d85bab573a (diff)
Split out -prune from -snapshot, tested the latter on various live setups.
Diffstat (limited to 'lib/byteback/disk_free_history.rb')
-rwxr-xr-xlib/byteback/disk_free_history.rb110
1 files changed, 110 insertions, 0 deletions
diff --git a/lib/byteback/disk_free_history.rb b/lib/byteback/disk_free_history.rb
new file mode 100755
index 0000000..ba66863
--- /dev/null
+++ b/lib/byteback/disk_free_history.rb
@@ -0,0 +1,110 @@
+#!/usr/bin/ruby
+
+require 'sys/filesystem'
+
+module Byteback
+ class DiskFreeReading < Struct.new(:fsstat, :time)
+ def initialize(fsstat,time=Time.now)
+ self.fsstat = fsstat
+ self.time = time
+ end
+
+ # helper method to return %age of disc space free
+ #
+ def percent_free
+ fsstat.blocks_available * 100 / fsstat.blocks
+ end
+ end
+
+ # A simple round-robin list to store a short history of a given mount
+ # point's disk space history.
+ #
+ class DiskFreeHistory
+ MINIMUM_INTERVAL = 5*60 # don't take readings more than 5 mins apart
+ MAXIMUM_AGE = 7*24*60*60 # delete readings after a week
+
+ # Initialize a new list storing the disc space history for the given
+ # mount point.
+ #
+ def initialize(mountpoint, history_file=nil)
+ history_file = "#{mountpoint}/.disk_free_history" unless
+ history_file
+ @history_file = history_file
+ @mountpoint = mountpoint
+ load!
+ end
+
+ # Take a new reading
+ #
+ def new_reading!
+ reading = DiskFreeReading.new(Sys::Filesystem.stat(@mountpoint))
+
+ # Don't record a new reading if it's exactly the same as last time,
+ # and less than the minimum interval.
+ #
+ return nil if @list.last &&
+ @list.last.fsstat.blocks_available == reading.fsstat.blocks_available
+ Time.now - @list.last.time < MINIMUM_INTERVAL
+
+ @list << reading
+
+ save!
+ end
+
+ def list
+ load! unless @list
+ @list
+ end
+
+ def gradient(last_n_seconds, &value_from_reading)
+ value_from_reading ||= proc { |r| r.fsstat.blocks_available }
+ earliest = Time.now - last_n_seconds
+
+ total = 0
+ readings = 0
+ later_reading = nil
+
+ list.reverse.each do |reading|
+ if later_reading
+ difference =
+ value_from_reading.call(reading) -
+ value_from_reading.call(later_reading)
+ total += difference
+ p difference
+ end
+ break if reading.time < earliest
+ readings += 1
+ later_reading = reading
+ end
+
+ total / readings
+ end
+
+ protected
+
+ def load!
+ begin
+ File.open(@history_file) do |fh|
+ @list = Marshal.restore(fh.read(1000000))
+ end
+ rescue Errno::ENOENT, TypeError => err
+ @list = []
+ new_reading!
+ end
+ end
+
+ def save!
+ list.shift while Time.now - list.first.time > MAXIMUM_AGE
+
+ tmp = "@history_file.#{$$}.#{rand(9999999999)}"
+ begin
+ File.open(tmp, "w") do |fh|
+ fh.write(Marshal.dump(list))
+ File.rename(tmp, @history_file)
+ end
+ ensure
+ File.unlink(tmp) if File.exists?(tmp)
+ end
+ end
+ end
+end