Class: Snapsync::Snapshot
- Inherits:
-
Object
- Object
- Snapsync::Snapshot
- Defined in:
- lib/snapsync/snapshot.rb
Overview
Representation of a single Snapper snapshot
Constant Summary collapse
- PARTIAL_MARKER =
"snapsync-partial"
Instance Attribute Summary collapse
-
#date ⇒ DateTime
readonly
The snapshot’s date.
-
#num ⇒ Object
readonly
The snapshot number.
-
#snapshot_dir ⇒ Pathname
readonly
The path to the snapshot directory.
-
#user_data ⇒ Hash<String,String>
readonly
The snapshot’s user data.
Class Method Summary collapse
-
.each(snapshot_dir, with_partial: false) ⇒ Object
Enumerate the snapshots from the given directory.
- .each_snapshot_raw(snapshot_dir) ⇒ Object
- .partial_marker_path(snapshot_dir) ⇒ Object
Instance Method Summary collapse
-
#initialize(snapshot_dir) ⇒ Snapshot
constructor
A new instance of Snapshot.
-
#load_info ⇒ Object
Loads snapper’s info.xml, validates it and assigns the information to the relevant attributes.
-
#partial? ⇒ Boolean
Whether this snapshot has only been partially synchronized.
-
#partial_marker_path ⇒ Pathname
A file that is used to mark the snapshot has having only been partially synchronized.
-
#size ⇒ Integer
Compute an estimate of the size of sending the whole subvolume.
-
#size_diff_from(snapshot) ⇒ Integer
Compute the size difference between the given snapshot and self.
-
#size_diff_from_gen(gen) ⇒ Integer
private
Compute the size of the ‘diff’ between a generation and the subvolume’s current state.
-
#subvolume_dir ⇒ Pathname
The path to the snapshot’s subvolume.
-
#synchronization_point? ⇒ Boolean
Whether this snapshot is one of snapsync’s synchronization points.
-
#synchronization_point_for?(target) ⇒ Boolean
Whether this snapshot is one of snapsync’s synchronization points for the given target.
-
#to_time ⇒ Object
This snapshot’s reference time.
Constructor Details
#initialize(snapshot_dir) ⇒ Snapshot
Returns a new instance of Snapshot.
62 63 64 65 66 67 68 69 70 71 72 73 74 |
# 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 |
Instance Attribute Details
#date ⇒ DateTime (readonly)
The snapshot’s date
17 18 19 |
# File 'lib/snapsync/snapshot.rb', line 17 def date @date end |
#num ⇒ Object (readonly)
The snapshot number
20 21 22 |
# File 'lib/snapsync/snapshot.rb', line 20 def num @num end |
#snapshot_dir ⇒ Pathname (readonly)
The path to the snapshot directory
7 8 9 |
# File 'lib/snapsync/snapshot.rb', line 7 def snapshot_dir @snapshot_dir end |
#user_data ⇒ Hash<String,String> (readonly)
The snapshot’s user data
25 26 27 |
# File 'lib/snapsync/snapshot.rb', line 25 def user_data @user_data end |
Class Method Details
.each(snapshot_dir, with_partial: false) ⇒ Object
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
133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# 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) ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 125 126 |
# 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 |
.partial_marker_path(snapshot_dir) ⇒ Object
29 30 31 |
# File 'lib/snapsync/snapshot.rb', line 29 def self.partial_marker_path(snapshot_dir) snapshot_dir + PARTIAL_MARKER end |
Instance Method Details
#load_info ⇒ Object
Loads snapper’s info.xml, validates it and assigns the information to the relevant attributes
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# 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? ⇒ Boolean
Whether this snapshot has only been partially synchronized
42 43 44 |
# File 'lib/snapsync/snapshot.rb', line 42 def partial? partial_marker_path.exist? end |
#partial_marker_path ⇒ Pathname
A file that is used to mark the snapshot has having only been partially synchronized
37 38 39 |
# File 'lib/snapsync/snapshot.rb', line 37 def partial_marker_path self.class.partial_marker_path(snapshot_dir) end |
#size ⇒ Integer
Compute an estimate of the size of sending the whole subvolume
93 94 95 |
# File 'lib/snapsync/snapshot.rb', line 93 def size size_diff_from_gen(0) end |
#size_diff_from(snapshot) ⇒ Integer
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
85 86 87 88 |
# 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) ⇒ Integer
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Compute the size of the ‘diff’ between a generation and the subvolume’s current state
105 106 107 108 109 110 111 112 |
# 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 ⇒ Pathname
The path to the snapshot’s subvolume
12 |
# File 'lib/snapsync/snapshot.rb', line 12 def subvolume_dir; snapshot_dir + "snapshot" end |
#synchronization_point? ⇒ Boolean
Whether this snapshot is one of snapsync’s synchronization points
52 53 54 |
# File 'lib/snapsync/snapshot.rb', line 52 def synchronization_point? user_data['snapsync'] end |
#synchronization_point_for?(target) ⇒ Boolean
Whether this snapshot is one of snapsync’s synchronization points for the given target
58 59 60 |
# File 'lib/snapsync/snapshot.rb', line 58 def synchronization_point_for?(target) user_data['snapsync'] == target.uuid end |
#to_time ⇒ Object
This snapshot’s reference time
47 48 49 |
# File 'lib/snapsync/snapshot.rb', line 47 def to_time date.to_time end |