xref: /netbsd-src/external/cddl/osnet/dist/lib/pyzfs/common/dataset.py (revision 3227e6cf668bd374971740bd6660f43cee4417ac)
1*3227e6cfSchs#! /usr/bin/python2.6
2*3227e6cfSchs#
3*3227e6cfSchs# CDDL HEADER START
4*3227e6cfSchs#
5*3227e6cfSchs# The contents of this file are subject to the terms of the
6*3227e6cfSchs# Common Development and Distribution License (the "License").
7*3227e6cfSchs# You may not use this file except in compliance with the License.
8*3227e6cfSchs#
9*3227e6cfSchs# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*3227e6cfSchs# or http://www.opensolaris.org/os/licensing.
11*3227e6cfSchs# See the License for the specific language governing permissions
12*3227e6cfSchs# and limitations under the License.
13*3227e6cfSchs#
14*3227e6cfSchs# When distributing Covered Code, include this CDDL HEADER in each
15*3227e6cfSchs# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*3227e6cfSchs# If applicable, add the following below this CDDL HEADER, with the
17*3227e6cfSchs# fields enclosed by brackets "[]" replaced with your own identifying
18*3227e6cfSchs# information: Portions Copyright [yyyy] [name of copyright owner]
19*3227e6cfSchs#
20*3227e6cfSchs# CDDL HEADER END
21*3227e6cfSchs#
22*3227e6cfSchs# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23*3227e6cfSchs#
24*3227e6cfSchs
25*3227e6cfSchs"""Implements the Dataset class, providing methods for manipulating ZFS
26*3227e6cfSchsdatasets.  Also implements the Property class, which describes ZFS
27*3227e6cfSchsproperties."""
28*3227e6cfSchs
29*3227e6cfSchsimport zfs.ioctl
30*3227e6cfSchsimport zfs.util
31*3227e6cfSchsimport errno
32*3227e6cfSchs
33*3227e6cfSchs_ = zfs.util._
34*3227e6cfSchs
35*3227e6cfSchsclass Property(object):
36*3227e6cfSchs	"""This class represents a ZFS property.  It contains
37*3227e6cfSchs	information about the property -- if it's readonly, a number vs
38*3227e6cfSchs	string vs index, etc.  Only native properties are represented by
39*3227e6cfSchs	this class -- not user properties (eg "user:prop") or userspace
40*3227e6cfSchs	properties (eg "userquota@joe")."""
41*3227e6cfSchs
42*3227e6cfSchs	__slots__ = "name", "number", "type", "default", "attr", "validtypes", \
43*3227e6cfSchs	    "values", "colname", "rightalign", "visible", "indextable"
44*3227e6cfSchs	__repr__ = zfs.util.default_repr
45*3227e6cfSchs
46*3227e6cfSchs	def __init__(self, t):
47*3227e6cfSchs		"""t is the tuple of information about this property
48*3227e6cfSchs		from zfs.ioctl.get_proptable, which should match the
49*3227e6cfSchs		members of zprop_desc_t (see zfs_prop.h)."""
50*3227e6cfSchs
51*3227e6cfSchs		self.name = t[0]
52*3227e6cfSchs		self.number = t[1]
53*3227e6cfSchs		self.type = t[2]
54*3227e6cfSchs		if self.type == "string":
55*3227e6cfSchs			self.default = t[3]
56*3227e6cfSchs		else:
57*3227e6cfSchs			self.default = t[4]
58*3227e6cfSchs		self.attr = t[5]
59*3227e6cfSchs		self.validtypes = t[6]
60*3227e6cfSchs		self.values = t[7]
61*3227e6cfSchs		self.colname = t[8]
62*3227e6cfSchs		self.rightalign = t[9]
63*3227e6cfSchs		self.visible = t[10]
64*3227e6cfSchs		self.indextable = t[11]
65*3227e6cfSchs
66*3227e6cfSchs	def delegatable(self):
67*3227e6cfSchs		"""Return True if this property can be delegated with
68*3227e6cfSchs		"zfs allow"."""
69*3227e6cfSchs		return self.attr != "readonly"
70*3227e6cfSchs
71*3227e6cfSchsproptable = dict()
72*3227e6cfSchsfor name, t in zfs.ioctl.get_proptable().iteritems():
73*3227e6cfSchs	proptable[name] = Property(t)
74*3227e6cfSchsdel name, t
75*3227e6cfSchs
76*3227e6cfSchsdef getpropobj(name):
77*3227e6cfSchs	"""Return the Property object that is identified by the given
78*3227e6cfSchs	name string.  It can be the full name, or the column name."""
79*3227e6cfSchs	try:
80*3227e6cfSchs		return proptable[name]
81*3227e6cfSchs	except KeyError:
82*3227e6cfSchs		for p in proptable.itervalues():
83*3227e6cfSchs			if p.colname and p.colname.lower() == name:
84*3227e6cfSchs				return p
85*3227e6cfSchs		raise
86*3227e6cfSchs
87*3227e6cfSchsclass Dataset(object):
88*3227e6cfSchs	"""Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc).
89*3227e6cfSchs
90*3227e6cfSchs	Generally, this class provides interfaces to the C functions in
91*3227e6cfSchs	zfs.ioctl which actually interface with the kernel to manipulate
92*3227e6cfSchs	datasets.
93*3227e6cfSchs
94*3227e6cfSchs	Unless otherwise noted, any method can raise a ZFSError to
95*3227e6cfSchs	indicate failure."""
96*3227e6cfSchs
97*3227e6cfSchs	__slots__ = "name", "__props"
98*3227e6cfSchs	__repr__ = zfs.util.default_repr
99*3227e6cfSchs
100*3227e6cfSchs	def __init__(self, name, props=None,
101*3227e6cfSchs	    types=("filesystem", "volume"), snaps=True):
102*3227e6cfSchs		"""Open the named dataset, checking that it exists and
103*3227e6cfSchs		is of the specified type.
104*3227e6cfSchs
105*3227e6cfSchs		name is the string name of this dataset.
106*3227e6cfSchs
107*3227e6cfSchs		props is the property settings dict from zfs.ioctl.next_dataset.
108*3227e6cfSchs
109*3227e6cfSchs		types is an iterable of strings specifying which types
110*3227e6cfSchs		of datasets are permitted.  Accepted strings are
111*3227e6cfSchs		"filesystem" and "volume".  Defaults to accepting all
112*3227e6cfSchs		types.
113*3227e6cfSchs
114*3227e6cfSchs		snaps is a boolean specifying if snapshots are acceptable.
115*3227e6cfSchs
116*3227e6cfSchs		Raises a ZFSError if the dataset can't be accessed (eg
117*3227e6cfSchs		doesn't exist) or is not of the specified type.
118*3227e6cfSchs		"""
119*3227e6cfSchs
120*3227e6cfSchs		self.name = name
121*3227e6cfSchs
122*3227e6cfSchs		e = zfs.util.ZFSError(errno.EINVAL,
123*3227e6cfSchs		    _("cannot open %s") % name,
124*3227e6cfSchs		    _("operation not applicable to datasets of this type"))
125*3227e6cfSchs		if "@" in name and not snaps:
126*3227e6cfSchs			raise e
127*3227e6cfSchs		if not props:
128*3227e6cfSchs			props = zfs.ioctl.dataset_props(name)
129*3227e6cfSchs		self.__props = props
130*3227e6cfSchs		if "volume" not in types and self.getprop("type") == 3:
131*3227e6cfSchs			raise e
132*3227e6cfSchs		if "filesystem" not in types and self.getprop("type") == 2:
133*3227e6cfSchs			raise e
134*3227e6cfSchs
135*3227e6cfSchs	def getprop(self, propname):
136*3227e6cfSchs		"""Return the value of the given property for this dataset.
137*3227e6cfSchs
138*3227e6cfSchs		Currently only works for native properties (those with a
139*3227e6cfSchs		Property object.)
140*3227e6cfSchs
141*3227e6cfSchs		Raises KeyError if propname does not specify a native property.
142*3227e6cfSchs		Does not raise ZFSError.
143*3227e6cfSchs		"""
144*3227e6cfSchs
145*3227e6cfSchs		p = getpropobj(propname)
146*3227e6cfSchs		try:
147*3227e6cfSchs			return self.__props[p.name]["value"]
148*3227e6cfSchs		except KeyError:
149*3227e6cfSchs			return p.default
150*3227e6cfSchs
151*3227e6cfSchs	def parent(self):
152*3227e6cfSchs		"""Return a Dataset representing the parent of this one."""
153*3227e6cfSchs		return Dataset(self.name[:self.name.rindex("/")])
154*3227e6cfSchs
155*3227e6cfSchs	def descendents(self):
156*3227e6cfSchs		"""A generator function which iterates over all
157*3227e6cfSchs		descendent Datasets (not including snapshots."""
158*3227e6cfSchs
159*3227e6cfSchs		cookie = 0
160*3227e6cfSchs		while True:
161*3227e6cfSchs			# next_dataset raises StopIteration when done
162*3227e6cfSchs			(name, cookie, props) = \
163*3227e6cfSchs			    zfs.ioctl.next_dataset(self.name, False, cookie)
164*3227e6cfSchs			ds = Dataset(name, props)
165*3227e6cfSchs			yield ds
166*3227e6cfSchs			for child in ds.descendents():
167*3227e6cfSchs				yield child
168*3227e6cfSchs
169*3227e6cfSchs	def userspace(self, prop):
170*3227e6cfSchs		"""A generator function which iterates over a
171*3227e6cfSchs		userspace-type property.
172*3227e6cfSchs
173*3227e6cfSchs		prop specifies which property ("userused@",
174*3227e6cfSchs		"userquota@", "groupused@", or "groupquota@").
175*3227e6cfSchs
176*3227e6cfSchs		returns 3-tuple of domain (string), rid (int), and space (int).
177*3227e6cfSchs		"""
178*3227e6cfSchs
179*3227e6cfSchs		d = zfs.ioctl.userspace_many(self.name, prop)
180*3227e6cfSchs		for ((domain, rid), space) in d.iteritems():
181*3227e6cfSchs			yield (domain, rid, space)
182*3227e6cfSchs
183*3227e6cfSchs	def userspace_upgrade(self):
184*3227e6cfSchs		"""Initialize the accounting information for
185*3227e6cfSchs		userused@... and groupused@... properties."""
186*3227e6cfSchs		return zfs.ioctl.userspace_upgrade(self.name)
187*3227e6cfSchs
188*3227e6cfSchs	def set_fsacl(self, un, d):
189*3227e6cfSchs		"""Add to the "zfs allow"-ed permissions on this Dataset.
190*3227e6cfSchs
191*3227e6cfSchs		un is True if the specified permissions should be removed.
192*3227e6cfSchs
193*3227e6cfSchs		d is a dict specifying which permissions to add/remove:
194*3227e6cfSchs		{ "whostr" -> None # remove all perms for this entity
195*3227e6cfSchs		  "whostr" -> { "perm" -> None} # add/remove these perms
196*3227e6cfSchs		} """
197*3227e6cfSchs		return zfs.ioctl.set_fsacl(self.name, un, d)
198*3227e6cfSchs
199*3227e6cfSchs	def get_fsacl(self):
200*3227e6cfSchs		"""Get the "zfs allow"-ed permissions on the Dataset.
201*3227e6cfSchs
202*3227e6cfSchs		Return a dict("whostr": { "perm" -> None })."""
203*3227e6cfSchs
204*3227e6cfSchs		return zfs.ioctl.get_fsacl(self.name)
205*3227e6cfSchs
206*3227e6cfSchs	def get_holds(self):
207*3227e6cfSchs		"""Get the user holds on this Dataset.
208*3227e6cfSchs
209*3227e6cfSchs		Return a dict("tag": timestamp)."""
210*3227e6cfSchs
211*3227e6cfSchs		return zfs.ioctl.get_holds(self.name)
212*3227e6cfSchs
213*3227e6cfSchsdef snapshots_fromcmdline(dsnames, recursive):
214*3227e6cfSchs	for dsname in dsnames:
215*3227e6cfSchs		if not "@" in dsname:
216*3227e6cfSchs			raise zfs.util.ZFSError(errno.EINVAL,
217*3227e6cfSchs			    _("cannot open %s") % dsname,
218*3227e6cfSchs			    _("operation only applies to snapshots"))
219*3227e6cfSchs		try:
220*3227e6cfSchs			ds = Dataset(dsname)
221*3227e6cfSchs			yield ds
222*3227e6cfSchs		except zfs.util.ZFSError, e:
223*3227e6cfSchs			if not recursive or e.errno != errno.ENOENT:
224*3227e6cfSchs				raise
225*3227e6cfSchs		if recursive:
226*3227e6cfSchs			(base, snapname) = dsname.split('@')
227*3227e6cfSchs			parent = Dataset(base)
228*3227e6cfSchs			for child in parent.descendents():
229*3227e6cfSchs				try:
230*3227e6cfSchs					yield Dataset(child.name + "@" +
231*3227e6cfSchs					    snapname)
232*3227e6cfSchs				except zfs.util.ZFSError, e:
233*3227e6cfSchs					if e.errno != errno.ENOENT:
234*3227e6cfSchs						raise
235