1*10b9d77bSPawel Jakub Dawidek#! /usr/bin/python2.6 28fc25799SMartin Matuska# 38fc25799SMartin Matuska# CDDL HEADER START 48fc25799SMartin Matuska# 58fc25799SMartin Matuska# The contents of this file are subject to the terms of the 68fc25799SMartin Matuska# Common Development and Distribution License (the "License"). 78fc25799SMartin Matuska# You may not use this file except in compliance with the License. 88fc25799SMartin Matuska# 98fc25799SMartin Matuska# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 108fc25799SMartin Matuska# or http://www.opensolaris.org/os/licensing. 118fc25799SMartin Matuska# See the License for the specific language governing permissions 128fc25799SMartin Matuska# and limitations under the License. 138fc25799SMartin Matuska# 148fc25799SMartin Matuska# When distributing Covered Code, include this CDDL HEADER in each 158fc25799SMartin Matuska# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 168fc25799SMartin Matuska# If applicable, add the following below this CDDL HEADER, with the 178fc25799SMartin Matuska# fields enclosed by brackets "[]" replaced with your own identifying 188fc25799SMartin Matuska# information: Portions Copyright [yyyy] [name of copyright owner] 198fc25799SMartin Matuska# 208fc25799SMartin Matuska# CDDL HEADER END 218fc25799SMartin Matuska# 22*10b9d77bSPawel Jakub Dawidek# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 238fc25799SMartin Matuska# 248fc25799SMartin Matuska 258fc25799SMartin Matuska"""Implements the Dataset class, providing methods for manipulating ZFS 268fc25799SMartin Matuskadatasets. Also implements the Property class, which describes ZFS 278fc25799SMartin Matuskaproperties.""" 288fc25799SMartin Matuska 298fc25799SMartin Matuskaimport zfs.ioctl 308fc25799SMartin Matuskaimport zfs.util 318fc25799SMartin Matuskaimport errno 328fc25799SMartin Matuska 338fc25799SMartin Matuska_ = zfs.util._ 348fc25799SMartin Matuska 358fc25799SMartin Matuskaclass Property(object): 368fc25799SMartin Matuska """This class represents a ZFS property. It contains 378fc25799SMartin Matuska information about the property -- if it's readonly, a number vs 388fc25799SMartin Matuska string vs index, etc. Only native properties are represented by 398fc25799SMartin Matuska this class -- not user properties (eg "user:prop") or userspace 408fc25799SMartin Matuska properties (eg "userquota@joe").""" 418fc25799SMartin Matuska 428fc25799SMartin Matuska __slots__ = "name", "number", "type", "default", "attr", "validtypes", \ 438fc25799SMartin Matuska "values", "colname", "rightalign", "visible", "indextable" 448fc25799SMartin Matuska __repr__ = zfs.util.default_repr 458fc25799SMartin Matuska 468fc25799SMartin Matuska def __init__(self, t): 478fc25799SMartin Matuska """t is the tuple of information about this property 488fc25799SMartin Matuska from zfs.ioctl.get_proptable, which should match the 498fc25799SMartin Matuska members of zprop_desc_t (see zfs_prop.h).""" 508fc25799SMartin Matuska 518fc25799SMartin Matuska self.name = t[0] 528fc25799SMartin Matuska self.number = t[1] 538fc25799SMartin Matuska self.type = t[2] 548fc25799SMartin Matuska if self.type == "string": 558fc25799SMartin Matuska self.default = t[3] 568fc25799SMartin Matuska else: 578fc25799SMartin Matuska self.default = t[4] 588fc25799SMartin Matuska self.attr = t[5] 598fc25799SMartin Matuska self.validtypes = t[6] 608fc25799SMartin Matuska self.values = t[7] 618fc25799SMartin Matuska self.colname = t[8] 628fc25799SMartin Matuska self.rightalign = t[9] 638fc25799SMartin Matuska self.visible = t[10] 648fc25799SMartin Matuska self.indextable = t[11] 658fc25799SMartin Matuska 668fc25799SMartin Matuska def delegatable(self): 678fc25799SMartin Matuska """Return True if this property can be delegated with 688fc25799SMartin Matuska "zfs allow".""" 698fc25799SMartin Matuska return self.attr != "readonly" 708fc25799SMartin Matuska 718fc25799SMartin Matuskaproptable = dict() 728fc25799SMartin Matuskafor name, t in zfs.ioctl.get_proptable().iteritems(): 738fc25799SMartin Matuska proptable[name] = Property(t) 748fc25799SMartin Matuskadel name, t 758fc25799SMartin Matuska 768fc25799SMartin Matuskadef getpropobj(name): 778fc25799SMartin Matuska """Return the Property object that is identified by the given 788fc25799SMartin Matuska name string. It can be the full name, or the column name.""" 798fc25799SMartin Matuska try: 808fc25799SMartin Matuska return proptable[name] 818fc25799SMartin Matuska except KeyError: 828fc25799SMartin Matuska for p in proptable.itervalues(): 838fc25799SMartin Matuska if p.colname and p.colname.lower() == name: 848fc25799SMartin Matuska return p 858fc25799SMartin Matuska raise 868fc25799SMartin Matuska 878fc25799SMartin Matuskaclass Dataset(object): 888fc25799SMartin Matuska """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc). 898fc25799SMartin Matuska 908fc25799SMartin Matuska Generally, this class provides interfaces to the C functions in 918fc25799SMartin Matuska zfs.ioctl which actually interface with the kernel to manipulate 928fc25799SMartin Matuska datasets. 938fc25799SMartin Matuska 948fc25799SMartin Matuska Unless otherwise noted, any method can raise a ZFSError to 958fc25799SMartin Matuska indicate failure.""" 968fc25799SMartin Matuska 978fc25799SMartin Matuska __slots__ = "name", "__props" 988fc25799SMartin Matuska __repr__ = zfs.util.default_repr 998fc25799SMartin Matuska 1008fc25799SMartin Matuska def __init__(self, name, props=None, 1018fc25799SMartin Matuska types=("filesystem", "volume"), snaps=True): 1028fc25799SMartin Matuska """Open the named dataset, checking that it exists and 1038fc25799SMartin Matuska is of the specified type. 1048fc25799SMartin Matuska 1058fc25799SMartin Matuska name is the string name of this dataset. 1068fc25799SMartin Matuska 1078fc25799SMartin Matuska props is the property settings dict from zfs.ioctl.next_dataset. 1088fc25799SMartin Matuska 1098fc25799SMartin Matuska types is an iterable of strings specifying which types 1108fc25799SMartin Matuska of datasets are permitted. Accepted strings are 111*10b9d77bSPawel Jakub Dawidek "filesystem" and "volume". Defaults to accepting all 1128fc25799SMartin Matuska types. 1138fc25799SMartin Matuska 1148fc25799SMartin Matuska snaps is a boolean specifying if snapshots are acceptable. 1158fc25799SMartin Matuska 1168fc25799SMartin Matuska Raises a ZFSError if the dataset can't be accessed (eg 1178fc25799SMartin Matuska doesn't exist) or is not of the specified type. 1188fc25799SMartin Matuska """ 1198fc25799SMartin Matuska 1208fc25799SMartin Matuska self.name = name 1218fc25799SMartin Matuska 1228fc25799SMartin Matuska e = zfs.util.ZFSError(errno.EINVAL, 1238fc25799SMartin Matuska _("cannot open %s") % name, 1248fc25799SMartin Matuska _("operation not applicable to datasets of this type")) 1258fc25799SMartin Matuska if "@" in name and not snaps: 1268fc25799SMartin Matuska raise e 1278fc25799SMartin Matuska if not props: 1288fc25799SMartin Matuska props = zfs.ioctl.dataset_props(name) 1298fc25799SMartin Matuska self.__props = props 1308fc25799SMartin Matuska if "volume" not in types and self.getprop("type") == 3: 1318fc25799SMartin Matuska raise e 1328fc25799SMartin Matuska if "filesystem" not in types and self.getprop("type") == 2: 1338fc25799SMartin Matuska raise e 1348fc25799SMartin Matuska 1358fc25799SMartin Matuska def getprop(self, propname): 1368fc25799SMartin Matuska """Return the value of the given property for this dataset. 1378fc25799SMartin Matuska 1388fc25799SMartin Matuska Currently only works for native properties (those with a 1398fc25799SMartin Matuska Property object.) 1408fc25799SMartin Matuska 1418fc25799SMartin Matuska Raises KeyError if propname does not specify a native property. 1428fc25799SMartin Matuska Does not raise ZFSError. 1438fc25799SMartin Matuska """ 1448fc25799SMartin Matuska 1458fc25799SMartin Matuska p = getpropobj(propname) 1468fc25799SMartin Matuska try: 1478fc25799SMartin Matuska return self.__props[p.name]["value"] 1488fc25799SMartin Matuska except KeyError: 1498fc25799SMartin Matuska return p.default 1508fc25799SMartin Matuska 1518fc25799SMartin Matuska def parent(self): 1528fc25799SMartin Matuska """Return a Dataset representing the parent of this one.""" 1538fc25799SMartin Matuska return Dataset(self.name[:self.name.rindex("/")]) 1548fc25799SMartin Matuska 1558fc25799SMartin Matuska def descendents(self): 1568fc25799SMartin Matuska """A generator function which iterates over all 1578fc25799SMartin Matuska descendent Datasets (not including snapshots.""" 1588fc25799SMartin Matuska 1598fc25799SMartin Matuska cookie = 0 1608fc25799SMartin Matuska while True: 1618fc25799SMartin Matuska # next_dataset raises StopIteration when done 1628fc25799SMartin Matuska (name, cookie, props) = \ 1638fc25799SMartin Matuska zfs.ioctl.next_dataset(self.name, False, cookie) 1648fc25799SMartin Matuska ds = Dataset(name, props) 1658fc25799SMartin Matuska yield ds 1668fc25799SMartin Matuska for child in ds.descendents(): 1678fc25799SMartin Matuska yield child 1688fc25799SMartin Matuska 1698fc25799SMartin Matuska def userspace(self, prop): 1708fc25799SMartin Matuska """A generator function which iterates over a 1718fc25799SMartin Matuska userspace-type property. 1728fc25799SMartin Matuska 1738fc25799SMartin Matuska prop specifies which property ("userused@", 1748fc25799SMartin Matuska "userquota@", "groupused@", or "groupquota@"). 1758fc25799SMartin Matuska 1768fc25799SMartin Matuska returns 3-tuple of domain (string), rid (int), and space (int). 1778fc25799SMartin Matuska """ 1788fc25799SMartin Matuska 1798fc25799SMartin Matuska d = zfs.ioctl.userspace_many(self.name, prop) 1808fc25799SMartin Matuska for ((domain, rid), space) in d.iteritems(): 1818fc25799SMartin Matuska yield (domain, rid, space) 1828fc25799SMartin Matuska 1838fc25799SMartin Matuska def userspace_upgrade(self): 1848fc25799SMartin Matuska """Initialize the accounting information for 1858fc25799SMartin Matuska userused@... and groupused@... properties.""" 1868fc25799SMartin Matuska return zfs.ioctl.userspace_upgrade(self.name) 1878fc25799SMartin Matuska 1888fc25799SMartin Matuska def set_fsacl(self, un, d): 1898fc25799SMartin Matuska """Add to the "zfs allow"-ed permissions on this Dataset. 1908fc25799SMartin Matuska 1918fc25799SMartin Matuska un is True if the specified permissions should be removed. 1928fc25799SMartin Matuska 1938fc25799SMartin Matuska d is a dict specifying which permissions to add/remove: 1948fc25799SMartin Matuska { "whostr" -> None # remove all perms for this entity 1958fc25799SMartin Matuska "whostr" -> { "perm" -> None} # add/remove these perms 1968fc25799SMartin Matuska } """ 1978fc25799SMartin Matuska return zfs.ioctl.set_fsacl(self.name, un, d) 1988fc25799SMartin Matuska 1998fc25799SMartin Matuska def get_fsacl(self): 2008fc25799SMartin Matuska """Get the "zfs allow"-ed permissions on the Dataset. 2018fc25799SMartin Matuska 2028fc25799SMartin Matuska Return a dict("whostr": { "perm" -> None }).""" 2038fc25799SMartin Matuska 2048fc25799SMartin Matuska return zfs.ioctl.get_fsacl(self.name) 205*10b9d77bSPawel Jakub Dawidek 206*10b9d77bSPawel Jakub Dawidek def get_holds(self): 207*10b9d77bSPawel Jakub Dawidek """Get the user holds on this Dataset. 208*10b9d77bSPawel Jakub Dawidek 209*10b9d77bSPawel Jakub Dawidek Return a dict("tag": timestamp).""" 210*10b9d77bSPawel Jakub Dawidek 211*10b9d77bSPawel Jakub Dawidek return zfs.ioctl.get_holds(self.name) 212*10b9d77bSPawel Jakub Dawidek 213*10b9d77bSPawel Jakub Dawidekdef snapshots_fromcmdline(dsnames, recursive): 214*10b9d77bSPawel Jakub Dawidek for dsname in dsnames: 215*10b9d77bSPawel Jakub Dawidek if not "@" in dsname: 216*10b9d77bSPawel Jakub Dawidek raise zfs.util.ZFSError(errno.EINVAL, 217*10b9d77bSPawel Jakub Dawidek _("cannot open %s") % dsname, 218*10b9d77bSPawel Jakub Dawidek _("operation only applies to snapshots")) 219*10b9d77bSPawel Jakub Dawidek try: 220*10b9d77bSPawel Jakub Dawidek ds = Dataset(dsname) 221*10b9d77bSPawel Jakub Dawidek yield ds 222*10b9d77bSPawel Jakub Dawidek except zfs.util.ZFSError, e: 223*10b9d77bSPawel Jakub Dawidek if not recursive or e.errno != errno.ENOENT: 224*10b9d77bSPawel Jakub Dawidek raise 225*10b9d77bSPawel Jakub Dawidek if recursive: 226*10b9d77bSPawel Jakub Dawidek (base, snapname) = dsname.split('@') 227*10b9d77bSPawel Jakub Dawidek parent = Dataset(base) 228*10b9d77bSPawel Jakub Dawidek for child in parent.descendents(): 229*10b9d77bSPawel Jakub Dawidek try: 230*10b9d77bSPawel Jakub Dawidek yield Dataset(child.name + "@" + 231*10b9d77bSPawel Jakub Dawidek snapname) 232*10b9d77bSPawel Jakub Dawidek except zfs.util.ZFSError, e: 233*10b9d77bSPawel Jakub Dawidek if e.errno != errno.ENOENT: 234*10b9d77bSPawel Jakub Dawidek raise 235