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 26datasets. Also implements the Property class, which describes ZFS 27properties.""" 28 29import zfs.ioctl 30import zfs.util 31import errno 32 33_ = zfs.util._ 34 35class 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 71proptable = dict() 72for name, t in zfs.ioctl.get_proptable().iteritems(): 73 proptable[name] = Property(t) 74del name, t 75 76def 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 87class 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 213def 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