class Snapsync::Snapshot

Representation of a single Snapper snapshot

Constants

PARTIAL_MARKER

Attributes

date[R]

The snapshot's date

@return [DateTime]

num[R]

The snapshot number

snapshot_dir[R]

The path to the snapshot directory

@return [Pathname]

user_data[R]

The snapshot's user data

@return [Hash<String,String>]

Public Class Methods

each(snapshot_dir, with_partial: false) { |snapshot| ... } click to toggle source

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
each_snapshot_raw(snapshot_dir) { |path, snapshot, nil| ... } click to toggle source
# 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
new(snapshot_dir) click to toggle source
# 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
partial_marker_path(snapshot_dir) click to toggle source
# File lib/snapsync/snapshot.rb, line 29
def self.partial_marker_path(snapshot_dir)
    snapshot_dir + PARTIAL_MARKER
end

Public Instance Methods

load_info() click to toggle source

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
partial?() click to toggle source

Whether this snapshot has only been partially synchronized

# File lib/snapsync/snapshot.rb, line 42
def partial?
    partial_marker_path.exist?
end
partial_marker_path() click to toggle source

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
size() click to toggle source

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
size_diff_from(snapshot) click to toggle source

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
size_diff_from_gen(gen) click to toggle source

@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
subvolume_dir() click to toggle source

The path to the snapshot's subvolume

@return [Pathname]

# File lib/snapsync/snapshot.rb, line 12
def subvolume_dir; snapshot_dir + "snapshot" end
synchronization_point?() click to toggle source

Whether this snapshot is one of snapsync's synchronization points

# File lib/snapsync/snapshot.rb, line 52
def synchronization_point?
    user_data['snapsync']
end
synchronization_point_for?(target) click to toggle source

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
to_time() click to toggle source

This snapshot's reference time

# File lib/snapsync/snapshot.rb, line 47
def to_time
    date.to_time
end