5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
22 # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
25 """Implements the Dataset class, providing methods for manipulating ZFS
26 datasets. Also implements the Property class, which describes ZFS
35 class Property(object):
36 """This class represents a ZFS property. It contains
37 information about the property -- if it's readonly, a number vs
38 string vs index, etc. Only native properties are represented by
39 this class -- not user properties (eg "user:prop") or userspace
40 properties (eg "userquota@joe")."""
42 __slots__ = "name", "number", "type", "default", "attr", "validtypes", \
43 "values", "colname", "rightalign", "visible", "indextable"
44 __repr__ = zfs.util.default_repr
46 def __init__(self, t):
47 """t is the tuple of information about this property
48 from zfs.ioctl.get_proptable, which should match the
49 members of zprop_desc_t (see zfs_prop.h)."""
54 if self.type == "string":
59 self.validtypes = t[6]
62 self.rightalign = t[9]
64 self.indextable = t[11]
66 def delegatable(self):
67 """Return True if this property can be delegated with
69 return self.attr != "readonly"
72 for name, t in zfs.ioctl.get_proptable().iteritems():
73 proptable[name] = Property(t)
77 """Return the Property object that is identified by the given
78 name string. It can be the full name, or the column name."""
80 return proptable[name]
82 for p in proptable.itervalues():
83 if p.colname and p.colname.lower() == name:
87 class Dataset(object):
88 """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc).
90 Generally, this class provides interfaces to the C functions in
91 zfs.ioctl which actually interface with the kernel to manipulate
94 Unless otherwise noted, any method can raise a ZFSError to
97 __slots__ = "name", "__props"
98 __repr__ = zfs.util.default_repr
100 def __init__(self, name, props=None,
101 types=("filesystem", "volume"), snaps=True):
102 """Open the named dataset, checking that it exists and
103 is of the specified type.
105 name is the string name of this dataset.
107 props is the property settings dict from zfs.ioctl.next_dataset.
109 types is an iterable of strings specifying which types
110 of datasets are permitted. Accepted strings are
111 "filesystem" and "volume". Defaults to accepting all
114 snaps is a boolean specifying if snapshots are acceptable.
116 Raises a ZFSError if the dataset can't be accessed (eg
117 doesn't exist) or is not of the specified type.
122 e = zfs.util.ZFSError(errno.EINVAL,
123 _("cannot open %s") % name,
124 _("operation not applicable to datasets of this type"))
125 if "@" in name and not snaps:
128 props = zfs.ioctl.dataset_props(name)
130 if "volume" not in types and self.getprop("type") == 3:
132 if "filesystem" not in types and self.getprop("type") == 2:
135 def getprop(self, propname):
136 """Return the value of the given property for this dataset.
138 Currently only works for native properties (those with a
141 Raises KeyError if propname does not specify a native property.
142 Does not raise ZFSError.
145 p = getpropobj(propname)
147 return self.__props[p.name]["value"]
152 """Return a Dataset representing the parent of this one."""
153 return Dataset(self.name[:self.name.rindex("/")])
155 def descendents(self):
156 """A generator function which iterates over all
157 descendent Datasets (not including snapshots."""
161 # next_dataset raises StopIteration when done
162 (name, cookie, props) = \
163 zfs.ioctl.next_dataset(self.name, False, cookie)
164 ds = Dataset(name, props)
166 for child in ds.descendents():
169 def userspace(self, prop):
170 """A generator function which iterates over a
171 userspace-type property.
173 prop specifies which property ("userused@",
174 "userquota@", "groupused@", or "groupquota@").
176 returns 3-tuple of domain (string), rid (int), and space (int).
179 d = zfs.ioctl.userspace_many(self.name, prop)
180 for ((domain, rid), space) in d.iteritems():
181 yield (domain, rid, space)
183 def userspace_upgrade(self):
184 """Initialize the accounting information for
185 userused@... and groupused@... properties."""
186 return zfs.ioctl.userspace_upgrade(self.name)
188 def set_fsacl(self, un, d):
189 """Add to the "zfs allow"-ed permissions on this Dataset.
191 un is True if the specified permissions should be removed.
193 d is a dict specifying which permissions to add/remove:
194 { "whostr" -> None # remove all perms for this entity
195 "whostr" -> { "perm" -> None} # add/remove these perms
197 return zfs.ioctl.set_fsacl(self.name, un, d)
200 """Get the "zfs allow"-ed permissions on the Dataset.
202 Return a dict("whostr": { "perm" -> None })."""
204 return zfs.ioctl.get_fsacl(self.name)
207 """Get the user holds on this Dataset.
209 Return a dict("tag": timestamp)."""
211 return zfs.ioctl.get_holds(self.name)
213 def snapshots_fromcmdline(dsnames, recursive):
214 for dsname in dsnames:
215 if not "@" in dsname:
216 raise zfs.util.ZFSError(errno.EINVAL,
217 _("cannot open %s") % dsname,
218 _("operation only applies to snapshots"))
222 except zfs.util.ZFSError, e:
223 if not recursive or e.errno != errno.ENOENT:
226 (base, snapname) = dsname.split('@')
227 parent = Dataset(base)
228 for child in parent.descendents():
230 yield Dataset(child.name + "@" +
232 except zfs.util.ZFSError, e:
233 if e.errno != errno.ENOENT: