1*3227e6cfSchs#! /usr/bin/python2.6 2*3227e6cfSchs# 3*3227e6cfSchs# CDDL HEADER START 4*3227e6cfSchs# 5*3227e6cfSchs# The contents of this file are subject to the terms of the 6*3227e6cfSchs# Common Development and Distribution License (the "License"). 7*3227e6cfSchs# You may not use this file except in compliance with the License. 8*3227e6cfSchs# 9*3227e6cfSchs# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*3227e6cfSchs# or http://www.opensolaris.org/os/licensing. 11*3227e6cfSchs# See the License for the specific language governing permissions 12*3227e6cfSchs# and limitations under the License. 13*3227e6cfSchs# 14*3227e6cfSchs# When distributing Covered Code, include this CDDL HEADER in each 15*3227e6cfSchs# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*3227e6cfSchs# If applicable, add the following below this CDDL HEADER, with the 17*3227e6cfSchs# fields enclosed by brackets "[]" replaced with your own identifying 18*3227e6cfSchs# information: Portions Copyright [yyyy] [name of copyright owner] 19*3227e6cfSchs# 20*3227e6cfSchs# CDDL HEADER END 21*3227e6cfSchs# 22*3227e6cfSchs# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23*3227e6cfSchs# 24*3227e6cfSchs 25*3227e6cfSchs"""Implements the Dataset class, providing methods for manipulating ZFS 26*3227e6cfSchsdatasets. Also implements the Property class, which describes ZFS 27*3227e6cfSchsproperties.""" 28*3227e6cfSchs 29*3227e6cfSchsimport zfs.ioctl 30*3227e6cfSchsimport zfs.util 31*3227e6cfSchsimport errno 32*3227e6cfSchs 33*3227e6cfSchs_ = zfs.util._ 34*3227e6cfSchs 35*3227e6cfSchsclass Property(object): 36*3227e6cfSchs """This class represents a ZFS property. It contains 37*3227e6cfSchs information about the property -- if it's readonly, a number vs 38*3227e6cfSchs string vs index, etc. Only native properties are represented by 39*3227e6cfSchs this class -- not user properties (eg "user:prop") or userspace 40*3227e6cfSchs properties (eg "userquota@joe").""" 41*3227e6cfSchs 42*3227e6cfSchs __slots__ = "name", "number", "type", "default", "attr", "validtypes", \ 43*3227e6cfSchs "values", "colname", "rightalign", "visible", "indextable" 44*3227e6cfSchs __repr__ = zfs.util.default_repr 45*3227e6cfSchs 46*3227e6cfSchs def __init__(self, t): 47*3227e6cfSchs """t is the tuple of information about this property 48*3227e6cfSchs from zfs.ioctl.get_proptable, which should match the 49*3227e6cfSchs members of zprop_desc_t (see zfs_prop.h).""" 50*3227e6cfSchs 51*3227e6cfSchs self.name = t[0] 52*3227e6cfSchs self.number = t[1] 53*3227e6cfSchs self.type = t[2] 54*3227e6cfSchs if self.type == "string": 55*3227e6cfSchs self.default = t[3] 56*3227e6cfSchs else: 57*3227e6cfSchs self.default = t[4] 58*3227e6cfSchs self.attr = t[5] 59*3227e6cfSchs self.validtypes = t[6] 60*3227e6cfSchs self.values = t[7] 61*3227e6cfSchs self.colname = t[8] 62*3227e6cfSchs self.rightalign = t[9] 63*3227e6cfSchs self.visible = t[10] 64*3227e6cfSchs self.indextable = t[11] 65*3227e6cfSchs 66*3227e6cfSchs def delegatable(self): 67*3227e6cfSchs """Return True if this property can be delegated with 68*3227e6cfSchs "zfs allow".""" 69*3227e6cfSchs return self.attr != "readonly" 70*3227e6cfSchs 71*3227e6cfSchsproptable = dict() 72*3227e6cfSchsfor name, t in zfs.ioctl.get_proptable().iteritems(): 73*3227e6cfSchs proptable[name] = Property(t) 74*3227e6cfSchsdel name, t 75*3227e6cfSchs 76*3227e6cfSchsdef getpropobj(name): 77*3227e6cfSchs """Return the Property object that is identified by the given 78*3227e6cfSchs name string. It can be the full name, or the column name.""" 79*3227e6cfSchs try: 80*3227e6cfSchs return proptable[name] 81*3227e6cfSchs except KeyError: 82*3227e6cfSchs for p in proptable.itervalues(): 83*3227e6cfSchs if p.colname and p.colname.lower() == name: 84*3227e6cfSchs return p 85*3227e6cfSchs raise 86*3227e6cfSchs 87*3227e6cfSchsclass Dataset(object): 88*3227e6cfSchs """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc). 89*3227e6cfSchs 90*3227e6cfSchs Generally, this class provides interfaces to the C functions in 91*3227e6cfSchs zfs.ioctl which actually interface with the kernel to manipulate 92*3227e6cfSchs datasets. 93*3227e6cfSchs 94*3227e6cfSchs Unless otherwise noted, any method can raise a ZFSError to 95*3227e6cfSchs indicate failure.""" 96*3227e6cfSchs 97*3227e6cfSchs __slots__ = "name", "__props" 98*3227e6cfSchs __repr__ = zfs.util.default_repr 99*3227e6cfSchs 100*3227e6cfSchs def __init__(self, name, props=None, 101*3227e6cfSchs types=("filesystem", "volume"), snaps=True): 102*3227e6cfSchs """Open the named dataset, checking that it exists and 103*3227e6cfSchs is of the specified type. 104*3227e6cfSchs 105*3227e6cfSchs name is the string name of this dataset. 106*3227e6cfSchs 107*3227e6cfSchs props is the property settings dict from zfs.ioctl.next_dataset. 108*3227e6cfSchs 109*3227e6cfSchs types is an iterable of strings specifying which types 110*3227e6cfSchs of datasets are permitted. Accepted strings are 111*3227e6cfSchs "filesystem" and "volume". Defaults to accepting all 112*3227e6cfSchs types. 113*3227e6cfSchs 114*3227e6cfSchs snaps is a boolean specifying if snapshots are acceptable. 115*3227e6cfSchs 116*3227e6cfSchs Raises a ZFSError if the dataset can't be accessed (eg 117*3227e6cfSchs doesn't exist) or is not of the specified type. 118*3227e6cfSchs """ 119*3227e6cfSchs 120*3227e6cfSchs self.name = name 121*3227e6cfSchs 122*3227e6cfSchs e = zfs.util.ZFSError(errno.EINVAL, 123*3227e6cfSchs _("cannot open %s") % name, 124*3227e6cfSchs _("operation not applicable to datasets of this type")) 125*3227e6cfSchs if "@" in name and not snaps: 126*3227e6cfSchs raise e 127*3227e6cfSchs if not props: 128*3227e6cfSchs props = zfs.ioctl.dataset_props(name) 129*3227e6cfSchs self.__props = props 130*3227e6cfSchs if "volume" not in types and self.getprop("type") == 3: 131*3227e6cfSchs raise e 132*3227e6cfSchs if "filesystem" not in types and self.getprop("type") == 2: 133*3227e6cfSchs raise e 134*3227e6cfSchs 135*3227e6cfSchs def getprop(self, propname): 136*3227e6cfSchs """Return the value of the given property for this dataset. 137*3227e6cfSchs 138*3227e6cfSchs Currently only works for native properties (those with a 139*3227e6cfSchs Property object.) 140*3227e6cfSchs 141*3227e6cfSchs Raises KeyError if propname does not specify a native property. 142*3227e6cfSchs Does not raise ZFSError. 143*3227e6cfSchs """ 144*3227e6cfSchs 145*3227e6cfSchs p = getpropobj(propname) 146*3227e6cfSchs try: 147*3227e6cfSchs return self.__props[p.name]["value"] 148*3227e6cfSchs except KeyError: 149*3227e6cfSchs return p.default 150*3227e6cfSchs 151*3227e6cfSchs def parent(self): 152*3227e6cfSchs """Return a Dataset representing the parent of this one.""" 153*3227e6cfSchs return Dataset(self.name[:self.name.rindex("/")]) 154*3227e6cfSchs 155*3227e6cfSchs def descendents(self): 156*3227e6cfSchs """A generator function which iterates over all 157*3227e6cfSchs descendent Datasets (not including snapshots.""" 158*3227e6cfSchs 159*3227e6cfSchs cookie = 0 160*3227e6cfSchs while True: 161*3227e6cfSchs # next_dataset raises StopIteration when done 162*3227e6cfSchs (name, cookie, props) = \ 163*3227e6cfSchs zfs.ioctl.next_dataset(self.name, False, cookie) 164*3227e6cfSchs ds = Dataset(name, props) 165*3227e6cfSchs yield ds 166*3227e6cfSchs for child in ds.descendents(): 167*3227e6cfSchs yield child 168*3227e6cfSchs 169*3227e6cfSchs def userspace(self, prop): 170*3227e6cfSchs """A generator function which iterates over a 171*3227e6cfSchs userspace-type property. 172*3227e6cfSchs 173*3227e6cfSchs prop specifies which property ("userused@", 174*3227e6cfSchs "userquota@", "groupused@", or "groupquota@"). 175*3227e6cfSchs 176*3227e6cfSchs returns 3-tuple of domain (string), rid (int), and space (int). 177*3227e6cfSchs """ 178*3227e6cfSchs 179*3227e6cfSchs d = zfs.ioctl.userspace_many(self.name, prop) 180*3227e6cfSchs for ((domain, rid), space) in d.iteritems(): 181*3227e6cfSchs yield (domain, rid, space) 182*3227e6cfSchs 183*3227e6cfSchs def userspace_upgrade(self): 184*3227e6cfSchs """Initialize the accounting information for 185*3227e6cfSchs userused@... and groupused@... properties.""" 186*3227e6cfSchs return zfs.ioctl.userspace_upgrade(self.name) 187*3227e6cfSchs 188*3227e6cfSchs def set_fsacl(self, un, d): 189*3227e6cfSchs """Add to the "zfs allow"-ed permissions on this Dataset. 190*3227e6cfSchs 191*3227e6cfSchs un is True if the specified permissions should be removed. 192*3227e6cfSchs 193*3227e6cfSchs d is a dict specifying which permissions to add/remove: 194*3227e6cfSchs { "whostr" -> None # remove all perms for this entity 195*3227e6cfSchs "whostr" -> { "perm" -> None} # add/remove these perms 196*3227e6cfSchs } """ 197*3227e6cfSchs return zfs.ioctl.set_fsacl(self.name, un, d) 198*3227e6cfSchs 199*3227e6cfSchs def get_fsacl(self): 200*3227e6cfSchs """Get the "zfs allow"-ed permissions on the Dataset. 201*3227e6cfSchs 202*3227e6cfSchs Return a dict("whostr": { "perm" -> None }).""" 203*3227e6cfSchs 204*3227e6cfSchs return zfs.ioctl.get_fsacl(self.name) 205*3227e6cfSchs 206*3227e6cfSchs def get_holds(self): 207*3227e6cfSchs """Get the user holds on this Dataset. 208*3227e6cfSchs 209*3227e6cfSchs Return a dict("tag": timestamp).""" 210*3227e6cfSchs 211*3227e6cfSchs return zfs.ioctl.get_holds(self.name) 212*3227e6cfSchs 213*3227e6cfSchsdef snapshots_fromcmdline(dsnames, recursive): 214*3227e6cfSchs for dsname in dsnames: 215*3227e6cfSchs if not "@" in dsname: 216*3227e6cfSchs raise zfs.util.ZFSError(errno.EINVAL, 217*3227e6cfSchs _("cannot open %s") % dsname, 218*3227e6cfSchs _("operation only applies to snapshots")) 219*3227e6cfSchs try: 220*3227e6cfSchs ds = Dataset(dsname) 221*3227e6cfSchs yield ds 222*3227e6cfSchs except zfs.util.ZFSError, e: 223*3227e6cfSchs if not recursive or e.errno != errno.ENOENT: 224*3227e6cfSchs raise 225*3227e6cfSchs if recursive: 226*3227e6cfSchs (base, snapname) = dsname.split('@') 227*3227e6cfSchs parent = Dataset(base) 228*3227e6cfSchs for child in parent.descendents(): 229*3227e6cfSchs try: 230*3227e6cfSchs yield Dataset(child.name + "@" + 231*3227e6cfSchs snapname) 232*3227e6cfSchs except zfs.util.ZFSError, e: 233*3227e6cfSchs if e.errno != errno.ENOENT: 234*3227e6cfSchs raise 235