]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - cddl/contrib/opensolaris/lib/pyzfs/common/dataset.py
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / cddl / contrib / opensolaris / lib / pyzfs / common / dataset.py
1 #! /usr/bin/python2.6
2 #
3 # CDDL HEADER START
4 #
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.
8 #
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.
13 #
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]
19 #
20 # CDDL HEADER END
21 #
22 # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23 #
24
25 """Implements the Dataset class, providing methods for manipulating ZFS
26 datasets.  Also implements the Property class, which describes ZFS
27 properties."""
28
29 import zfs.ioctl
30 import zfs.util
31 import errno
32
33 _ = zfs.util._
34
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")."""
41
42         __slots__ = "name", "number", "type", "default", "attr", "validtypes", \
43             "values", "colname", "rightalign", "visible", "indextable"
44         __repr__ = zfs.util.default_repr
45
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)."""
50
51                 self.name = t[0]
52                 self.number = t[1]
53                 self.type = t[2]
54                 if self.type == "string":
55                         self.default = t[3]
56                 else:
57                         self.default = t[4]
58                 self.attr = t[5]
59                 self.validtypes = t[6]
60                 self.values = t[7]
61                 self.colname = t[8]
62                 self.rightalign = t[9]
63                 self.visible = t[10]
64                 self.indextable = t[11]
65
66         def delegatable(self):
67                 """Return True if this property can be delegated with
68                 "zfs allow"."""
69                 return self.attr != "readonly"
70
71 proptable = dict()
72 for name, t in zfs.ioctl.get_proptable().iteritems():
73         proptable[name] = Property(t)
74 del name, t
75
76 def getpropobj(name):
77         """Return the Property object that is identified by the given
78         name string.  It can be the full name, or the column name."""
79         try:
80                 return proptable[name]
81         except KeyError:
82                 for p in proptable.itervalues():
83                         if p.colname and p.colname.lower() == name:
84                                 return p
85                 raise
86
87 class Dataset(object):
88         """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc).
89
90         Generally, this class provides interfaces to the C functions in
91         zfs.ioctl which actually interface with the kernel to manipulate
92         datasets.
93         
94         Unless otherwise noted, any method can raise a ZFSError to
95         indicate failure."""
96
97         __slots__ = "name", "__props"
98         __repr__ = zfs.util.default_repr
99
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.
104                 
105                 name is the string name of this dataset.
106
107                 props is the property settings dict from zfs.ioctl.next_dataset.
108
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
112                 types.
113
114                 snaps is a boolean specifying if snapshots are acceptable.
115
116                 Raises a ZFSError if the dataset can't be accessed (eg
117                 doesn't exist) or is not of the specified type.
118                 """
119
120                 self.name = name
121
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:
126                         raise e
127                 if not props:
128                         props = zfs.ioctl.dataset_props(name)
129                 self.__props = props
130                 if "volume" not in types and self.getprop("type") == 3:
131                         raise e
132                 if "filesystem" not in types and self.getprop("type") == 2:
133                         raise e
134
135         def getprop(self, propname):
136                 """Return the value of the given property for this dataset.
137
138                 Currently only works for native properties (those with a
139                 Property object.)
140                 
141                 Raises KeyError if propname does not specify a native property.
142                 Does not raise ZFSError.
143                 """
144
145                 p = getpropobj(propname)
146                 try:
147                         return self.__props[p.name]["value"]
148                 except KeyError:
149                         return p.default
150
151         def parent(self):
152                 """Return a Dataset representing the parent of this one."""
153                 return Dataset(self.name[:self.name.rindex("/")])
154
155         def descendents(self):
156                 """A generator function which iterates over all
157                 descendent Datasets (not including snapshots."""
158
159                 cookie = 0
160                 while True:
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)
165                         yield ds
166                         for child in ds.descendents():
167                                 yield child
168         
169         def userspace(self, prop):
170                 """A generator function which iterates over a
171                 userspace-type property.
172
173                 prop specifies which property ("userused@",
174                 "userquota@", "groupused@", or "groupquota@").
175
176                 returns 3-tuple of domain (string), rid (int), and space (int).
177                 """
178
179                 d = zfs.ioctl.userspace_many(self.name, prop)
180                 for ((domain, rid), space) in d.iteritems():
181                         yield (domain, rid, space)
182
183         def userspace_upgrade(self):
184                 """Initialize the accounting information for
185                 userused@... and groupused@... properties."""
186                 return zfs.ioctl.userspace_upgrade(self.name)
187         
188         def set_fsacl(self, un, d):
189                 """Add to the "zfs allow"-ed permissions on this Dataset.
190
191                 un is True if the specified permissions should be removed.
192
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
196                 } """
197                 return zfs.ioctl.set_fsacl(self.name, un, d)
198
199         def get_fsacl(self):
200                 """Get the "zfs allow"-ed permissions on the Dataset.
201
202                 Return a dict("whostr": { "perm" -> None })."""
203
204                 return zfs.ioctl.get_fsacl(self.name)
205
206         def get_holds(self):
207                 """Get the user holds on this Dataset.
208
209                 Return a dict("tag": timestamp)."""
210
211                 return zfs.ioctl.get_holds(self.name)
212
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"))
219                 try:
220                         ds = Dataset(dsname)
221                         yield ds
222                 except zfs.util.ZFSError, e:
223                         if not recursive or e.errno != errno.ENOENT:
224                                 raise
225                 if recursive:
226                         (base, snapname) = dsname.split('@')
227                         parent = Dataset(base)
228                         for child in parent.descendents():
229                                 try:
230                                         yield Dataset(child.name + "@" +
231                                             snapname)
232                                 except zfs.util.ZFSError, e:
233                                         if e.errno != errno.ENOENT:
234                                                 raise