Class: Snapsync::Snapshot
- Inherits:
-
Object
- Object
- Snapsync::Snapshot
- Defined in:
- lib/snapsync/snapshot.rb
Overview
Representation of a single Snapper snapshot
Defined Under Namespace
Classes: SnapshotCompareError
Constant Summary collapse
- PARTIAL_MARKER =
"snapsync-partial"
Instance Attribute Summary collapse
- #btrfs ⇒ Btrfs readonly
-
#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) {|snapshot| ... } ⇒ Object
Enumerate the snapshots from the given directory.
- .each_snapshot_raw(snapshot_dir) {|path, snapshot, err| ... } ⇒ Object
- .partial_marker_path(snapshot_dir) ⇒ Object
Instance Method Summary collapse
- #info ⇒ SubvolumeInfo
-
#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.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/snapsync/snapshot.rb', line 77 def initialize(snapshot_dir) @snapshot_dir = snapshot_dir @btrfs = Btrfs.get(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
30 31 32 |
# File 'lib/snapsync/snapshot.rb', line 30 def date @date end |
#num ⇒ Object (readonly)
The snapshot number
33 34 35 |
# File 'lib/snapsync/snapshot.rb', line 33 def num @num end |
#snapshot_dir ⇒ Pathname (readonly)
The path to the snapshot directory
10 11 12 |
# File 'lib/snapsync/snapshot.rb', line 10 def snapshot_dir @snapshot_dir end |
#user_data ⇒ Hash<String,String> (readonly)
The snapshot’s user data
38 39 40 |
# File 'lib/snapsync/snapshot.rb', line 38 def user_data @user_data end |
Class Method Details
.each(snapshot_dir, with_partial: false) {|snapshot| ... } ⇒ 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
162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/snapsync/snapshot.rb', line 162 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, err| ... } ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/snapsync/snapshot.rb', line 142 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
42 43 44 |
# File 'lib/snapsync/snapshot.rb', line 42 def self.partial_marker_path(snapshot_dir) snapshot_dir + PARTIAL_MARKER end |
Instance Method Details
#info ⇒ SubvolumeInfo
16 17 18 19 20 |
# File 'lib/snapsync/snapshot.rb', line 16 def info # Load btrfs subvolume info @info = SubvolumeInfo.new(subvolume_dir) unless @info @info end |
#load_info ⇒ Object
Loads snapper’s info.xml, validates it and assigns the information to the relevant attributes
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/snapsync/snapshot.rb', line 179 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
55 56 57 |
# File 'lib/snapsync/snapshot.rb', line 55 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
50 51 52 |
# File 'lib/snapsync/snapshot.rb', line 50 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
118 119 120 |
# File 'lib/snapsync/snapshot.rb', line 118 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
101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/snapsync/snapshot.rb', line 101 def size_diff_from(snapshot) if btrfs.mountpoint != snapshot.btrfs.mountpoint recv_uuid = snapshot.info.received_uuid local_snapshot = btrfs.subvolume_table.find do |s| s.uuid == recv_uuid end raise "Cannot find snapshot with uuid #{recv_uuid} locally." if local_snapshot.nil? snapshot_gen = local_snapshot.cgen else snapshot_gen = snapshot.info.gen_at_creation end 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
130 131 132 133 134 135 136 137 |
# File 'lib/snapsync/snapshot.rb', line 130 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
25 |
# File 'lib/snapsync/snapshot.rb', line 25 def subvolume_dir; snapshot_dir + "snapshot" end |
#synchronization_point? ⇒ Boolean
Whether this snapshot is one of snapsync’s synchronization points
65 66 67 |
# File 'lib/snapsync/snapshot.rb', line 65 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
71 72 73 |
# File 'lib/snapsync/snapshot.rb', line 71 def synchronization_point_for?(target) user_data['snapsync'] == target.uuid end |
#to_time ⇒ Object
This snapshot’s reference time
60 61 62 |
# File 'lib/snapsync/snapshot.rb', line 60 def to_time date.to_time end |