1146040Stjr#! /usr/bin/python2.6 2146040Stjr# 3146040Stjr# CDDL HEADER START 4146040Stjr# 5218Sconklin# The contents of this file are subject to the terms of the 6126209Sache# Common Development and Distribution License (the "License"). 7146040Stjr# You may not use this file except in compliance with the License. 8146040Stjr# 9146040Stjr# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10218Sconklin# or http://www.opensolaris.org/os/licensing. 11126209Sache# See the License for the specific language governing permissions 12218Sconklin# and limitations under the License. 13126209Sache# 14146040Stjr# When distributing Covered Code, include this CDDL HEADER in each 15218Sconklin# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16146040Stjr# If applicable, add the following below this CDDL HEADER, with the 17146040Stjr# fields enclosed by brackets "[]" replaced with your own identifying 18146040Stjr# information: Portions Copyright [yyyy] [name of copyright owner] 19146040Stjr# 20218Sconklin# CDDL HEADER END 21218Sconklin# 22146040Stjr# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23218Sconklin# 24218Sconklin 25146040Stjr"""Implements the Dataset class, providing methods for manipulating ZFS 26146040Stjrdatasets. Also implements the Property class, which describes ZFS 27218Sconklinproperties.""" 28146040Stjr 29146040Stjrimport zfs.ioctl 30146040Stjrimport zfs.util 31146040Stjrimport errno 32146040Stjr 33146040Stjr_ = zfs.util._ 34146040Stjr 35146040Stjrclass Property(object): 36146040Stjr """This class represents a ZFS property. It contains 37146040Stjr information about the property -- if it's readonly, a number vs 38146040Stjr string vs index, etc. Only native properties are represented by 39146040Stjr this class -- not user properties (eg "user:prop") or userspace 40146040Stjr properties (eg "userquota@joe").""" 41146040Stjr 42146040Stjr __slots__ = "name", "number", "type", "default", "attr", "validtypes", \ 43146040Stjr "values", "colname", "rightalign", "visible", "indextable" 44146040Stjr __repr__ = zfs.util.default_repr 45146040Stjr 46146040Stjr def __init__(self, t): 47218Sconklin """t is the tuple of information about this property 48218Sconklin from zfs.ioctl.get_proptable, which should match the 49126209Sache members of zprop_desc_t (see zfs_prop.h).""" 50126209Sache 51126209Sache self.name = t[0] 52126209Sache self.number = t[1] 53126209Sache self.type = t[2] 54126209Sache if self.type == "string": 55126209Sache self.default = t[3] 56126209Sache else: 57126209Sache self.default = t[4] 58126209Sache self.attr = t[5] 59126209Sache self.validtypes = t[6] 60126209Sache self.values = t[7] 61126209Sache self.colname = t[8] 62126209Sache self.rightalign = t[9] 63126209Sache self.visible = t[10] 64126209Sache self.indextable = t[11] 65126209Sache 66126209Sache def delegatable(self): 67126209Sache """Return True if this property can be delegated with 68126209Sache "zfs allow".""" 69126209Sache return self.attr != "readonly" 70218Sconklin 71146040Stjrproptable = dict() 72218Sconklinfor name, t in zfs.ioctl.get_proptable().iteritems(): 73218Sconklin proptable[name] = Property(t) 74146040Stjrdel name, t 75146040Stjr 76146040Stjrdef getpropobj(name): 77218Sconklin """Return the Property object that is identified by the given 78146040Stjr name string. It can be the full name, or the column name.""" 79146040Stjr try: 80146040Stjr return proptable[name] 81146040Stjr except KeyError: 82131543Stjr for p in proptable.itervalues(): 83126209Sache if p.colname and p.colname.lower() == name: 84146040Stjr return p 85218Sconklin raise 86146040Stjr 87146040Stjrclass Dataset(object): 88146040Stjr """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc). 89218Sconklin 90146040Stjr Generally, this class provides interfaces to the C functions in 91146040Stjr zfs.ioctl which actually interface with the kernel to manipulate 92146040Stjr datasets. 93146040Stjr 94146040Stjr Unless otherwise noted, any method can raise a ZFSError to 95126209Sache indicate failure.""" 96126209Sache 97126209Sache __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