summaryrefslogtreecommitdiffstats
path: root/cddl/contrib/opensolaris/lib/pyzfs/common/dataset.py
blob: 26192e4075d2473962f410ba50f092a21922c8ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#! /usr/bin/python2.6
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
#

"""Implements the Dataset class, providing methods for manipulating ZFS
datasets.  Also implements the Property class, which describes ZFS
properties."""

import zfs.ioctl
import zfs.util
import errno

_ = zfs.util._

class Property(object):
	"""This class represents a ZFS property.  It contains
	information about the property -- if it's readonly, a number vs
	string vs index, etc.  Only native properties are represented by
	this class -- not user properties (eg "user:prop") or userspace
	properties (eg "userquota@joe")."""

	__slots__ = "name", "number", "type", "default", "attr", "validtypes", \
	    "values", "colname", "rightalign", "visible", "indextable"
	__repr__ = zfs.util.default_repr

	def __init__(self, t):
		"""t is the tuple of information about this property
		from zfs.ioctl.get_proptable, which should match the
		members of zprop_desc_t (see zfs_prop.h)."""

		self.name = t[0]
		self.number = t[1]
		self.type = t[2]
		if self.type == "string":
			self.default = t[3]
		else:
			self.default = t[4]
		self.attr = t[5]
		self.validtypes = t[6]
		self.values = t[7]
		self.colname = t[8]
		self.rightalign = t[9]
		self.visible = t[10]
		self.indextable = t[11]

	def delegatable(self):
		"""Return True if this property can be delegated with
		"zfs allow"."""
		return self.attr != "readonly"

proptable = dict()
for name, t in zfs.ioctl.get_proptable().iteritems():
	proptable[name] = Property(t)
del name, t

def getpropobj(name):
	"""Return the Property object that is identified by the given
	name string.  It can be the full name, or the column name."""
	try:
		return proptable[name]
	except KeyError:
		for p in proptable.itervalues():
			if p.colname and p.colname.lower() == name:
				return p
		raise

class Dataset(object):
	"""Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc).

	Generally, this class provides interfaces to the C functions in
	zfs.ioctl which actually interface with the kernel to manipulate
	datasets.
	
	Unless otherwise noted, any method can raise a ZFSError to
	indicate failure."""

	__slots__ = "name", "__props"
	__repr__ = zfs.util.default_repr

	def __init__(self, name, props=None,
	    types=("filesystem", "volume"), snaps=True):
		"""Open the named dataset, checking that it exists and
		is of the specified type.
		
		name is the string name of this dataset.

		props is the property settings dict from zfs.ioctl.next_dataset.

		types is an iterable of strings specifying which types
		of datasets are permitted.  Accepted strings are
		"filesystem" and "volume".  Defaults to accepting all
		types.

		snaps is a boolean specifying if snapshots are acceptable.

		Raises a ZFSError if the dataset can't be accessed (eg
		doesn't exist) or is not of the specified type.
		"""

		self.name = name

		e = zfs.util.ZFSError(errno.EINVAL,
		    _("cannot open %s") % name,
		    _("operation not applicable to datasets of this type"))
		if "@" in name and not snaps:
			raise e
		if not props:
			props = zfs.ioctl.dataset_props(name)
		self.__props = props
		if "volume" not in types and self.getprop("type") == 3:
			raise e
		if "filesystem" not in types and self.getprop("type") == 2:
			raise e

	def getprop(self, propname):
		"""Return the value of the given property for this dataset.

		Currently only works for native properties (those with a
		Property object.)
		
		Raises KeyError if propname does not specify a native property.
		Does not raise ZFSError.
		"""

		p = getpropobj(propname)
		try:
			return self.__props[p.name]["value"]
		except KeyError:
			return p.default

	def parent(self):
		"""Return a Dataset representing the parent of this one."""
		return Dataset(self.name[:self.name.rindex("/")])

	def descendents(self):
		"""A generator function which iterates over all
		descendent Datasets (not including snapshots."""

		cookie = 0
		while True:
			# next_dataset raises StopIteration when done
			(name, cookie, props) = \
			    zfs.ioctl.next_dataset(self.name, False, cookie)
			ds = Dataset(name, props)
			yield ds
			for child in ds.descendents():
				yield child
	
	def userspace(self, prop):
		"""A generator function which iterates over a
		userspace-type property.

		prop specifies which property ("userused@",
		"userquota@", "groupused@", or "groupquota@").

		returns 3-tuple of domain (string), rid (int), and space (int).
		"""

		d = zfs.ioctl.userspace_many(self.name, prop)
		for ((domain, rid), space) in d.iteritems():
			yield (domain, rid, space)

	def userspace_upgrade(self):
		"""Initialize the accounting information for
		userused@... and groupused@... properties."""
		return zfs.ioctl.userspace_upgrade(self.name)
	
	def set_fsacl(self, un, d):
		"""Add to the "zfs allow"-ed permissions on this Dataset.

		un is True if the specified permissions should be removed.

		d is a dict specifying which permissions to add/remove:
		{ "whostr" -> None # remove all perms for this entity
		  "whostr" -> { "perm" -> None} # add/remove these perms
		} """
		return zfs.ioctl.set_fsacl(self.name, un, d)

	def get_fsacl(self):
		"""Get the "zfs allow"-ed permissions on the Dataset.

		Return a dict("whostr": { "perm" -> None })."""

		return zfs.ioctl.get_fsacl(self.name)

	def get_holds(self):
		"""Get the user holds on this Dataset.

		Return a dict("tag": timestamp)."""

		return zfs.ioctl.get_holds(self.name)

def snapshots_fromcmdline(dsnames, recursive):
	for dsname in dsnames:
		if not "@" in dsname:
			raise zfs.util.ZFSError(errno.EINVAL,
			    _("cannot open %s") % dsname,
			    _("operation only applies to snapshots"))
		try:
			ds = Dataset(dsname)
			yield ds
		except zfs.util.ZFSError, e:
			if not recursive or e.errno != errno.ENOENT:
				raise
		if recursive:
			(base, snapname) = dsname.split('@')
			parent = Dataset(base)
			for child in parent.descendents():
				try:
					yield Dataset(child.name + "@" +
					    snapname)
				except zfs.util.ZFSError, e:
					if e.errno != errno.ENOENT:
						raise
OpenPOWER on IntegriCloud