1219089Spjd#! /usr/bin/python2.6 2209962Smm# 3209962Smm# CDDL HEADER START 4209962Smm# 5209962Smm# The contents of this file are subject to the terms of the 6209962Smm# Common Development and Distribution License (the "License"). 7209962Smm# You may not use this file except in compliance with the License. 8209962Smm# 9209962Smm# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10209962Smm# or http://www.opensolaris.org/os/licensing. 11209962Smm# See the License for the specific language governing permissions 12209962Smm# and limitations under the License. 13209962Smm# 14209962Smm# When distributing Covered Code, include this CDDL HEADER in each 15209962Smm# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16209962Smm# If applicable, add the following below this CDDL HEADER, with the 17209962Smm# fields enclosed by brackets "[]" replaced with your own identifying 18209962Smm# information: Portions Copyright [yyyy] [name of copyright owner] 19209962Smm# 20209962Smm# CDDL HEADER END 21209962Smm# 22219089Spjd# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23209962Smm# 24209962Smm 25209962Smm"""Implements the Dataset class, providing methods for manipulating ZFS 26209962Smmdatasets. Also implements the Property class, which describes ZFS 27209962Smmproperties.""" 28209962Smm 29209962Smmimport zfs.ioctl 30209962Smmimport zfs.util 31209962Smmimport errno 32209962Smm 33209962Smm_ = zfs.util._ 34209962Smm 35209962Smmclass Property(object): 36209962Smm """This class represents a ZFS property. It contains 37209962Smm information about the property -- if it's readonly, a number vs 38209962Smm string vs index, etc. Only native properties are represented by 39209962Smm this class -- not user properties (eg "user:prop") or userspace 40209962Smm properties (eg "userquota@joe").""" 41209962Smm 42209962Smm __slots__ = "name", "number", "type", "default", "attr", "validtypes", \ 43209962Smm "values", "colname", "rightalign", "visible", "indextable" 44209962Smm __repr__ = zfs.util.default_repr 45209962Smm 46209962Smm def __init__(self, t): 47209962Smm """t is the tuple of information about this property 48209962Smm from zfs.ioctl.get_proptable, which should match the 49209962Smm members of zprop_desc_t (see zfs_prop.h).""" 50209962Smm 51209962Smm self.name = t[0] 52209962Smm self.number = t[1] 53209962Smm self.type = t[2] 54209962Smm if self.type == "string": 55209962Smm self.default = t[3] 56209962Smm else: 57209962Smm self.default = t[4] 58209962Smm self.attr = t[5] 59209962Smm self.validtypes = t[6] 60209962Smm self.values = t[7] 61209962Smm self.colname = t[8] 62209962Smm self.rightalign = t[9] 63209962Smm self.visible = t[10] 64209962Smm self.indextable = t[11] 65209962Smm 66209962Smm def delegatable(self): 67209962Smm """Return True if this property can be delegated with 68209962Smm "zfs allow".""" 69209962Smm return self.attr != "readonly" 70209962Smm 71209962Smmproptable = dict() 72209962Smmfor name, t in zfs.ioctl.get_proptable().iteritems(): 73209962Smm proptable[name] = Property(t) 74209962Smmdel name, t 75209962Smm 76209962Smmdef getpropobj(name): 77209962Smm """Return the Property object that is identified by the given 78209962Smm name string. It can be the full name, or the column name.""" 79209962Smm try: 80209962Smm return proptable[name] 81209962Smm except KeyError: 82209962Smm for p in proptable.itervalues(): 83209962Smm if p.colname and p.colname.lower() == name: 84209962Smm return p 85209962Smm raise 86209962Smm 87209962Smmclass Dataset(object): 88209962Smm """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc). 89209962Smm 90209962Smm Generally, this class provides interfaces to the C functions in 91209962Smm zfs.ioctl which actually interface with the kernel to manipulate 92209962Smm datasets. 93209962Smm 94209962Smm Unless otherwise noted, any method can raise a ZFSError to 95209962Smm indicate failure.""" 96209962Smm 97209962Smm __slots__ = "name", "__props" 98209962Smm __repr__ = zfs.util.default_repr 99209962Smm 100209962Smm def __init__(self, name, props=None, 101209962Smm types=("filesystem", "volume"), snaps=True): 102209962Smm """Open the named dataset, checking that it exists and 103209962Smm is of the specified type. 104209962Smm 105209962Smm name is the string name of this dataset. 106209962Smm 107209962Smm props is the property settings dict from zfs.ioctl.next_dataset. 108209962Smm 109209962Smm types is an iterable of strings specifying which types 110209962Smm of datasets are permitted. Accepted strings are 111219089Spjd "filesystem" and "volume". Defaults to accepting all 112209962Smm types. 113209962Smm 114209962Smm snaps is a boolean specifying if snapshots are acceptable. 115209962Smm 116209962Smm Raises a ZFSError if the dataset can't be accessed (eg 117209962Smm doesn't exist) or is not of the specified type. 118209962Smm """ 119209962Smm 120209962Smm self.name = name 121209962Smm 122209962Smm e = zfs.util.ZFSError(errno.EINVAL, 123209962Smm _("cannot open %s") % name, 124209962Smm _("operation not applicable to datasets of this type")) 125209962Smm if "@" in name and not snaps: 126209962Smm raise e 127209962Smm if not props: 128209962Smm props = zfs.ioctl.dataset_props(name) 129209962Smm self.__props = props 130209962Smm if "volume" not in types and self.getprop("type") == 3: 131209962Smm raise e 132209962Smm if "filesystem" not in types and self.getprop("type") == 2: 133209962Smm raise e 134209962Smm 135209962Smm def getprop(self, propname): 136209962Smm """Return the value of the given property for this dataset. 137209962Smm 138209962Smm Currently only works for native properties (those with a 139209962Smm Property object.) 140209962Smm 141209962Smm Raises KeyError if propname does not specify a native property. 142209962Smm Does not raise ZFSError. 143209962Smm """ 144209962Smm 145209962Smm p = getpropobj(propname) 146209962Smm try: 147209962Smm return self.__props[p.name]["value"] 148209962Smm except KeyError: 149209962Smm return p.default 150209962Smm 151209962Smm def parent(self): 152209962Smm """Return a Dataset representing the parent of this one.""" 153209962Smm return Dataset(self.name[:self.name.rindex("/")]) 154209962Smm 155209962Smm def descendents(self): 156209962Smm """A generator function which iterates over all 157209962Smm descendent Datasets (not including snapshots.""" 158209962Smm 159209962Smm cookie = 0 160209962Smm while True: 161209962Smm # next_dataset raises StopIteration when done 162209962Smm (name, cookie, props) = \ 163209962Smm zfs.ioctl.next_dataset(self.name, False, cookie) 164209962Smm ds = Dataset(name, props) 165209962Smm yield ds 166209962Smm for child in ds.descendents(): 167209962Smm yield child 168209962Smm 169209962Smm def userspace(self, prop): 170209962Smm """A generator function which iterates over a 171209962Smm userspace-type property. 172209962Smm 173209962Smm prop specifies which property ("userused@", 174209962Smm "userquota@", "groupused@", or "groupquota@"). 175209962Smm 176209962Smm returns 3-tuple of domain (string), rid (int), and space (int). 177209962Smm """ 178209962Smm 179209962Smm d = zfs.ioctl.userspace_many(self.name, prop) 180209962Smm for ((domain, rid), space) in d.iteritems(): 181209962Smm yield (domain, rid, space) 182209962Smm 183209962Smm def userspace_upgrade(self): 184209962Smm """Initialize the accounting information for 185209962Smm userused@... and groupused@... properties.""" 186209962Smm return zfs.ioctl.userspace_upgrade(self.name) 187209962Smm 188209962Smm def set_fsacl(self, un, d): 189209962Smm """Add to the "zfs allow"-ed permissions on this Dataset. 190209962Smm 191209962Smm un is True if the specified permissions should be removed. 192209962Smm 193209962Smm d is a dict specifying which permissions to add/remove: 194209962Smm { "whostr" -> None # remove all perms for this entity 195209962Smm "whostr" -> { "perm" -> None} # add/remove these perms 196209962Smm } """ 197209962Smm return zfs.ioctl.set_fsacl(self.name, un, d) 198209962Smm 199209962Smm def get_fsacl(self): 200209962Smm """Get the "zfs allow"-ed permissions on the Dataset. 201209962Smm 202209962Smm Return a dict("whostr": { "perm" -> None }).""" 203209962Smm 204209962Smm return zfs.ioctl.get_fsacl(self.name) 205219089Spjd 206219089Spjd def get_holds(self): 207219089Spjd """Get the user holds on this Dataset. 208219089Spjd 209219089Spjd Return a dict("tag": timestamp).""" 210219089Spjd 211219089Spjd return zfs.ioctl.get_holds(self.name) 212219089Spjd 213219089Spjddef snapshots_fromcmdline(dsnames, recursive): 214219089Spjd for dsname in dsnames: 215219089Spjd if not "@" in dsname: 216219089Spjd raise zfs.util.ZFSError(errno.EINVAL, 217219089Spjd _("cannot open %s") % dsname, 218219089Spjd _("operation only applies to snapshots")) 219219089Spjd try: 220219089Spjd ds = Dataset(dsname) 221219089Spjd yield ds 222219089Spjd except zfs.util.ZFSError, e: 223219089Spjd if not recursive or e.errno != errno.ENOENT: 224219089Spjd raise 225219089Spjd if recursive: 226219089Spjd (base, snapname) = dsname.split('@') 227219089Spjd parent = Dataset(base) 228219089Spjd for child in parent.descendents(): 229219089Spjd try: 230219089Spjd yield Dataset(child.name + "@" + 231219089Spjd snapname) 232219089Spjd except zfs.util.ZFSError, e: 233219089Spjd if e.errno != errno.ENOENT: 234219089Spjd raise 235