Class: OsCtl::Lib::Exporter::Zfs

Inherits:
Base
  • Object
show all
Includes:
Utils::Log, Utils::System
Defined in:
lib/libosctl/exporter/zfs.rb

Overview

Handles dumping containers as ZFS streams into tar archives

Usage:

exporter.dump_rootfs do
  # Create a snapshot a dump it
  exporter.dump_base

  # Create another snapshot and dump it as an incremental stream
  # from the base snapshot
  exporter.dump_incremental
end

Constant Summary

Constants inherited from Base

Base::BLOCK_SIZE, Base::DIR_MODE, Base::FILE_MODE

Instance Attribute Summary collapse

Attributes inherited from Base

#ct, #datasets, #opts, #tar

Instance Method Summary collapse

Methods included from Utils::System

#repeat_on_failure, #syscmd, #zfs

Methods included from Utils::Log

included

Methods inherited from Base

#add_file_from_disk, #close, #dump_configs, #dump_metadata, #dump_user_hook_scripts

Constructor Details

#initialize(*_) ⇒ Zfs

Returns a new instance of Zfs.



20
21
22
23
24
25
# File 'lib/libosctl/exporter/zfs.rb', line 20

def initialize(*_)
  super

  @datasets = ct.datasets[1..] # skip the root dataset
  @snapshots = []
end

Instance Attribute Details

#base_snapObject (readonly, protected)

Returns the value of attribute base_snap.



80
81
82
# File 'lib/libosctl/exporter/zfs.rb', line 80

def base_snap
  @base_snap
end

#snapshotsObject (readonly, protected)

Returns the value of attribute snapshots.



80
81
82
# File 'lib/libosctl/exporter/zfs.rb', line 80

def snapshots
  @snapshots
end

Instance Method Details

#dump_baseObject

Dump initial data stream

Should be called from within the block given to #dump_rootfs.



55
56
57
58
59
60
61
# File 'lib/libosctl/exporter/zfs.rb', line 55

def dump_base
  @base_snap = snapshot(ct.dataset, 'base')

  each_dataset_file('base') do |ds, file|
    dump_stream(file, ds, base_snap)
  end
end

#dump_file_name(compression, name) ⇒ Object (protected)



168
169
170
171
172
173
174
175
176
177
178
# File 'lib/libosctl/exporter/zfs.rb', line 168

def dump_file_name(compression, name)
  base = File.join('rootfs', "#{name}.dat")

  case compression
  when :gzip
    "#{base}.gz"

  else
    base
  end
end

#dump_incremental(from_snap: nil) ⇒ Object

Dump incremental data stream from the base stream

Should be called from within the block given to #dump_rootfs.



66
67
68
69
70
71
72
# File 'lib/libosctl/exporter/zfs.rb', line 66

def dump_incremental(from_snap: nil)
  snap = snapshot(ct.dataset, 'incr')

  each_dataset_file('incremental') do |ds, file|
    dump_stream(file, ds, snap, from_snap || base_snap)
  end
end

#dump_rootfs { ... } ⇒ Object

Method used to wrap dumping of base and incremental data streams of rootfs

Yields:



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/libosctl/exporter/zfs.rb', line 30

def dump_rootfs
  tar.mkdir('rootfs', DIR_MODE)

  each_dataset_dir do |_ds, dir|
    tar.mkdir(File.join('rootfs', dir), DIR_MODE)
  end

  yield

  snapshots.reverse!

  each_dataset do |ds|
    snapshots.each do |snap|
      zfs(:destroy, '', "#{ds}@#{snap}")
    end
  end

  tar.add_file('snapshots.yml', FILE_MODE) do |tf|
    tf.write(ConfigFile.dump_yaml(snapshots))
  end
end

#dump_stream(name, dataset, snap, from_snap = nil) ⇒ Object (protected)



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/libosctl/exporter/zfs.rb', line 123

def dump_stream(name, dataset, snap, from_snap = nil)
  compression = get_compression(dataset)

  cmd = if from_snap
          "#{zfs_send} -I @#{from_snap} #{dataset}@#{snap}"
        else
          "#{zfs_send} #{dataset}@#{snap}"
        end

  tar.add_file(dump_file_name(compression, name), FILE_MODE) do |tf|
    IO.popen("exec #{cmd}") do |io|
      process_stream(compression, io, tf)
    end
  end
end

#each_dataset {|ds| ... } ⇒ Object (protected)

Iterate over all datasets

Yield Parameters:



84
85
86
87
# File 'lib/libosctl/exporter/zfs.rb', line 84

def each_dataset(&block)
  block.call(ct.dataset)
  datasets.each(&block)
end

#each_dataset_dir {|ds, dir_name| ... } ⇒ Object (protected)

Iterate over all datasets and yield the dataset along with directory name for the tar archive, where its streams will be stored.

Yield Parameters:

  • ds (Zfs::Dataset)
  • dir_name (String)

    directory name within the archive



94
95
96
97
98
# File 'lib/libosctl/exporter/zfs.rb', line 94

def each_dataset_dir
  each_dataset do |ds|
    yield(ds, ds.relative_name)
  end
end

#each_dataset_file(name) {|ds, fname| ... } ⇒ Object (protected)

Iterate over all datasets and yield the dataset along with file name for the archive.

Parameters:

  • name (String)

    base/incremental

Yield Parameters:

  • ds (Zfs::Dataset)
  • fname (String)

    file name within the archive



106
107
108
109
110
# File 'lib/libosctl/exporter/zfs.rb', line 106

def each_dataset_file(name)
  each_dataset_dir do |ds, dir|
    yield(ds, File.join(dir, name))
  end
end

#formatObject



74
75
76
# File 'lib/libosctl/exporter/zfs.rb', line 74

def format
  :zfs
end

#get_compression(dataset) ⇒ Object (protected)



154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/libosctl/exporter/zfs.rb', line 154

def get_compression(dataset)
  case opts[:compression]
  when :auto
    if !opts[:compressed_send] || zfs(:get, '-H -o value compression', dataset).output.strip == 'off'
      :gzip
    else
      :off
    end

  else
    opts[:compression].to_sym
  end
end

#process_stream(compression, stream, tf) ⇒ Object (protected)



139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/libosctl/exporter/zfs.rb', line 139

def process_stream(compression, stream, tf)
  case compression
  when :gzip
    gz = Zlib::GzipWriter.new(tf)
    gz.write(stream.read(BLOCK_SIZE)) until stream.eof?
    gz.close

  when :off
    tf.write(stream.read(BLOCK_SIZE)) until stream.eof?

  else
    raise "unexpected compression type '#{compression}'"
  end
end

#snapshot(dataset, type) ⇒ Object (protected)



116
117
118
119
120
121
# File 'lib/libosctl/exporter/zfs.rb', line 116

def snapshot(dataset, type)
  snap = snapshot_name(type)
  zfs(:snapshot, '-r', "#{dataset}@#{snap}")
  snapshots << snap
  snap
end

#snapshot_name(type) ⇒ Object (protected)



112
113
114
# File 'lib/libosctl/exporter/zfs.rb', line 112

def snapshot_name(type)
  "osctl-#{type}-#{Time.now.to_i}"
end

#zfs_sendObject (protected)



180
181
182
183
184
185
186
187
# File 'lib/libosctl/exporter/zfs.rb', line 180

def zfs_send
  if opts[:compressed_send]
    'zfs send -c'

  else
    'zfs send'
  end
end