Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/cddl/contrib/opensolaris/lib/pyzfs/common/dataset.py
39563 views
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
235
236