class Snapsync::Snapshot
Representation of a single Snapper snapshot
Constants
- PARTIAL_MARKER
Attributes
The snapshot's date
@return [DateTime]
The snapshot number
The path to the snapshot directory
@return [Pathname]
The snapshot's user data
@return [Hash<String,String>]
Public Class Methods
Enumerate the snapshots from the given directory
The directory is supposed to be maintained in a snapper-compatible foramt, meaning that the snapshot directory name must be the snapshot's number
# File lib/snapsync/snapshot.rb, line 133 def self.each(snapshot_dir, with_partial: false) return enum_for(__method__, snapshot_dir, with_partial: with_partial) if !block_given? each_snapshot_raw(snapshot_dir) do |path, snapshot, error| if error Snapsync.warn "ignored #{path} in #{self}: #{error}" elsif snapshot.num != Integer(path.basename.to_s) Snapsync.warn "ignored #{path} in #{self}: the snapshot reports num=#{snapshot.num} but its directory is called #{path.basename}" elsif !with_partial && snapshot.partial? Snapsync.warn "ignored #{path} in #{self}: this is a partial snapshot" else yield snapshot end end end
# File lib/snapsync/snapshot.rb, line 114 def self.each_snapshot_raw(snapshot_dir) return enum_for(__method__, snapshot_dir) if !block_given? snapshot_dir.each_child do |path| if path.directory? && path.basename.to_s =~ /^\d+$/ begin snapshot = Snapshot.new(path) yield(path, snapshot, nil) rescue InvalidSnapshot => e yield(path, nil, e) end end end end
# File lib/snapsync/snapshot.rb, line 62 def initialize(snapshot_dir) @snapshot_dir = snapshot_dir if !snapshot_dir.directory? raise InvalidSnapshot, "#{snapshot_dir} does not exist" elsif !subvolume_dir.directory? raise InvalidSnapshot, "#{snapshot_dir}'s subvolume directory does not exist (#{subvolume_dir})" end # This loads the information and also validates that snapshot_dir is # indeed a snapper snapshot load_info end
# File lib/snapsync/snapshot.rb, line 29 def self.partial_marker_path(snapshot_dir) snapshot_dir + PARTIAL_MARKER end
Public Instance Methods
Loads snapper's info.xml, validates it and assigns the information to the relevant attributes
# File lib/snapsync/snapshot.rb, line 150 def load_info info_xml = snapshot_dir + "info.xml" if !info_xml.file? raise InvalidSnapshot, "#{snapshot_dir}/info.xml does not exist, is this really a snapper snapshot ?" end xml = REXML::Document.new(info_xml.read) if !xml.root raise InvalidInfoFile, "#{snapshot_dir}/info.xml does not look like a snapper info file (not an XML file ?)" elsif xml.root.name != 'snapshot' raise InvalidInfoFile, "#{snapshot_dir}/info.xml does not look like a snapper info file (root is not 'snapshot')" end date = xml.root.elements.to_a('date') if date.empty? raise InvalidInfoFile, "#{snapshot_dir}/info.xml does not have a date element" else @date = DateTime.parse(date.first.text) end num = xml.root.elements.to_a('num') if num.empty? raise InvalidInfoFile, "#{snapshot_dir}/info.xml does not have a num element" else @num = Integer(num.first.text) end @user_data = Hash.new xml.root.elements.to_a('userdata').each do |node| k = node.elements['key'].text v = node.elements['value'].text user_data[k] = v end end
Whether this snapshot has only been partially synchronized
# File lib/snapsync/snapshot.rb, line 42 def partial? partial_marker_path.exist? end
A file that is used to mark the snapshot has having only been partially synchronized
@return [Pathname]
# File lib/snapsync/snapshot.rb, line 37 def partial_marker_path self.class.partial_marker_path(snapshot_dir) end
Compute an estimate of the size of sending the whole subvolume
@return [Integer] size in bytes
# File lib/snapsync/snapshot.rb, line 93 def size size_diff_from_gen(0) end
Compute the size difference between the given snapshot and self
This is an estimate of the size required to send this snapshot using the given snapshot as parent
@param [Snapshot] a reference snapshot, which would be used as parent
in the 'send' operation
@return [Integer] the size in bytes of the difference between the
given snapshot and the current subvolume's state
# File lib/snapsync/snapshot.rb, line 85 def size_diff_from(snapshot) snapshot_gen = Btrfs.generation_of(snapshot.subvolume_dir) size_diff_from_gen(snapshot_gen) end
@api private
Compute the size of the 'diff' between a generation and the subvolume's current state
@param [Integer] gen the reference generation (the 'from' in the diff) @return [Integer] size in bytes @see size_diff_from
size
# File lib/snapsync/snapshot.rb, line 105 def size_diff_from_gen(gen) Btrfs.find_new(subvolume_dir, gen).inject(0) do |size, line| if line =~ /len (\d+)/ size + Integer($1) else size end end end
The path to the snapshot's subvolume
@return [Pathname]
# File lib/snapsync/snapshot.rb, line 12 def subvolume_dir; snapshot_dir + "snapshot" end
Whether this snapshot is one of snapsync's synchronization points
# File lib/snapsync/snapshot.rb, line 52 def synchronization_point? user_data['snapsync'] end
Whether this snapshot is one of snapsync's synchronization points for the given target
# File lib/snapsync/snapshot.rb, line 58 def synchronization_point_for?(target) user_data['snapsync'] == target.uuid end
This snapshot's reference time
# File lib/snapsync/snapshot.rb, line 47 def to_time date.to_time end