Class: ZFS

Inherits:
Object
  • Object
show all
Defined in:
lib/zfs.rb

Overview

Pathname-inspired class to handle ZFS filesystems/snapshots/volumes

Direct Known Subclasses

Filesystem, Snapshot

Defined Under Namespace

Classes: AlreadyExists, Filesystem, InvalidName, NotFound, Snapshot

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ ZFS

Create a new ZFS object (not filesystem).



36
37
38
# File 'lib/zfs.rb', line 36

def initialize(name)
	@name, @pool, @path = name, *name.split('/', 2)
end

Class Attribute Details

.zfs_pathObject

Returns the value of attribute zfs_path.



151
152
153
# File 'lib/zfs.rb', line 151

def zfs_path
  @zfs_path
end

.zpool_pathObject

Returns the value of attribute zpool_path.



152
153
154
# File 'lib/zfs.rb', line 152

def zpool_path
  @zpool_path
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



27
28
29
# File 'lib/zfs.rb', line 27

def name
  @name
end

#pathObject (readonly)

Returns the value of attribute path.



29
30
31
# File 'lib/zfs.rb', line 29

def path
  @path
end

#poolObject (readonly)

Returns the value of attribute pool.



28
29
30
# File 'lib/zfs.rb', line 28

def pool
  @pool
end

Class Method Details

.mountsObject

Get a Hash of all mountpoints and their filesystems



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/zfs.rb', line 170

def mounts
	cmd = [ZFS.zfs_path].flatten + %w(get -rHp -oname,value mountpoint)

	stdout, stderr, status = Open3.capture3(*cmd)

	if status.success? and stderr.empty?
		mounts = stdout.lines.collect do |line|
			fs, path = line.chomp.split(/\t/, 2)
			[path, ZFS(fs)]
		end
		Hash[mounts]
	else
		raise Exception, "something went wrong"
	end
end

.poolsObject

Get an Array of all pools



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

def pools
	cmd = [ZFS.zpool_path].flatten + %w(list -Honame)

	stdout, stderr, status = Open3.capture3(*cmd)

	if status.success? and stderr.empty?
		stdout.lines.collect do |pool|
			ZFS(pool.chomp)
		end
	else
		raise Exception, "something went wrong"
	end
end

.property(name, opts = {}) ⇒ Object

Define an attribute



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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/zfs.rb', line 187

def property(name, opts={})

	case opts[:type]
	when :size, :integer
		# FIXME: also takes :values. if :values is all-Integers, these are the only options. if there are non-ints, then :values is a supplement

		define_method name do
			Integer(self[name])
		end
		define_method "#{name}=" do |value|
			self[name] = value.to_s
		end if opts[:edit]

	when :boolean
		# FIXME: booleans can take extra values, so there are on/true, off/false, plus what amounts to an enum
		# FIXME: if options[:values] is defined, also create a 'name' method, since 'name?' might not ring true
		# FIXME: replace '_' by '-' in opts[:values]
		define_method "#{name}?" do
			self[name] == 'on'
		end
		define_method "#{name}=" do |value|
			self[name] = value ? 'on' : 'off'
		end if opts[:edit]

	when :enum
		define_method name do
			sym = (self[name] || "").gsub('-', '_').to_sym
			if opts[:values].grep(sym)
				return sym
			else
				raise "#{name} has value #{sym}, which is not in enum-list"
			end
		end
		define_method "#{name}=" do |value|
			self[name] = value.to_s.gsub('_', '-')
		end if opts[:edit]

	when :snapshot
		define_method name do
			val = self[name]
			if val.nil? or val == '-'
				nil
			else
				ZFS(val)
			end
		end

	when :float
		define_method name do
			Float(self[name])
		end
		define_method "#{name}=" do |value|
			self[name] = value
		end if opts[:edit]

	when :string
		define_method name do
			self[name]
		end
		define_method "#{name}=" do |value|
			self[name] = value
		end if opts[:edit]

	when :date
		define_method name do
			DateTime.strptime(self[name], '%s')
		end

	when :pathname
		define_method name do
			Pathname(self[name])
		end
		define_method "#{name}=" do |value|
			self[name] = value.to_s
		end if opts[:edit]

	else
		puts "Unknown type '#{opts[:type]}'"
	end
end

Instance Method Details

#==(other) ⇒ Object

ZFS’s are considered equal if they are the same class and name



122
123
124
# File 'lib/zfs.rb', line 122

def ==(other)
	other.class == self.class && other.name == self.name
end

#[](key) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
# File 'lib/zfs.rb', line 126

def [](key)
	cmd = [ZFS.zfs_path].flatten + %w(get -ovalue -Hp) + [key.to_s, name]

	stdout, stderr, status = Open3.capture3(*cmd)

	if status.success? and stderr.empty? and stdout.lines.count == 1
		return stdout.chomp
	else
		raise Exception, "something went wrong"
	end
end

#[]=(key, value) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/zfs.rb', line 138

def []=(key, value)
	cmd = [ZFS.zfs_path].flatten + ['set', "#{key.to_s}=#{value}", name]

	out, status = Open3.capture2e(*cmd)

	if status.success? and out.empty?
		return value
	else
		raise Exception, "something went wrong"
	end
end

#children(opts = {}) ⇒ Object

Returns the children of this filesystem

Raises:



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/zfs.rb', line 51

def children(opts={})
	raise NotFound if !exist?

	cmd = [ZFS.zfs_path].flatten + %w(list -H -r -oname -tfilesystem)
	cmd << '-d1' unless opts[:recursive]
	cmd << name

	stdout, stderr, status = Open3.capture3(*cmd)
	if status.success? and stderr == ""
		stdout.lines.drop(1).collect do |filesystem|
			ZFS(filesystem.chomp)
		end
	else
		raise Exception, "something went wrong"
	end
end

#create(opts = {}) ⇒ Object

Create filesystem



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/zfs.rb', line 81

def create(opts={})
	return nil if exist?

	cmd = [ZFS.zfs_path].flatten + ['create']
	cmd << '-p' if opts[:parents]
	cmd += ['-V', opts[:volume]] if opts[:volume]
	cmd << name

	out, status = Open3.capture2e(*cmd)
	if status.success? and out.empty?
		return self
	elsif out.match(/dataset already exists\n$/)
		nil
	else
		raise Exception, "something went wrong: #{out}, #{status}"
	end
end

#destroy!(opts = {}) ⇒ Object

Destroy filesystem

Raises:



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/zfs.rb', line 100

def destroy!(opts={})
	raise NotFound if !exist?

	cmd = [ZFS.zfs_path].flatten + ['destroy']
	cmd << '-r' if opts[:children]
	cmd << name

	out, status = Open3.capture2e(*cmd)

	if status.success? and out.empty?
		return true
	else
		raise Exception, "something went wrong"
	end
end

#exist?Boolean

Does the filesystem exist?

Returns:

  • (Boolean)


69
70
71
72
73
74
75
76
77
78
# File 'lib/zfs.rb', line 69

def exist?
	cmd = [ZFS.zfs_path].flatten + %w(list -H -oname) + [name]

	out, status = Open3.capture2e(*cmd)
	if status.success? and out == "#{name}\n"
		true
	else
		false
	end
end

#parentObject

Return the parent of the current filesystem, or nil if there is none.



41
42
43
44
45
46
47
48
# File 'lib/zfs.rb', line 41

def parent
	p = Pathname(name).parent.to_s
	if p == '.'
		nil
	else
		ZFS(p)
	end
end

#to_sObject

Stringify



117
118
119
# File 'lib/zfs.rb', line 117

def to_s
	"#<ZFS:#{name}>"
end