2N/A#! /usr/bin/python2.6
2N/A#
2N/A# CDDL HEADER START
2N/A#
2N/A# The contents of this file are subject to the terms of the
2N/A# Common Development and Distribution License (the "License").
2N/A# You may not use this file except in compliance with the License.
2N/A#
2N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A# or http://www.opensolaris.org/os/licensing.
2N/A# See the License for the specific language governing permissions
2N/A# and limitations under the License.
2N/A#
2N/A# When distributing Covered Code, include this CDDL HEADER in each
2N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A# If applicable, add the following below this CDDL HEADER, with the
2N/A# fields enclosed by brackets "[]" replaced with your own identifying
2N/A# information: Portions Copyright [yyyy] [name of copyright owner]
2N/A#
2N/A# CDDL HEADER END
2N/A#
2N/A# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2N/A#
2N/A
2N/A"""Implements the Dataset class, providing methods for manipulating ZFS
2N/Adatasets. Also implements the Property class, which describes ZFS
2N/Aproperties."""
2N/A
2N/Aimport zfs.ioctl
2N/Aimport zfs.util
2N/Aimport errno
2N/A
2N/A_ = zfs.util._
2N/A
2N/Aclass Property(object):
2N/A """This class represents a ZFS property. It contains
2N/A information about the property -- if it's readonly, a number vs
2N/A string vs index, etc. Only native properties are represented by
2N/A this class -- not user properties (eg "user:prop") or userspace
2N/A properties (eg "userquota@joe")."""
2N/A
2N/A __slots__ = "name", "number", "type", "default", "attr", "validtypes", \
2N/A "values", "colname", "rightalign", "visible", "indextable"
2N/A __repr__ = zfs.util.default_repr
2N/A
2N/A def __init__(self, t):
2N/A """t is the tuple of information about this property
2N/A from zfs.ioctl.get_proptable, which should match the
2N/A members of zprop_desc_t (see zfs_prop.h)."""
2N/A
2N/A self.name = t[0]
2N/A self.number = t[1]
2N/A self.type = t[2]
2N/A if self.type == "string":
2N/A self.default = t[3]
2N/A else:
2N/A self.default = t[4]
2N/A self.attr = t[5]
2N/A self.validtypes = t[6]
2N/A self.values = t[7]
2N/A self.colname = t[8]
2N/A self.rightalign = t[9]
2N/A self.visible = t[10]
2N/A self.indextable = t[11]
2N/A
2N/A def delegatable(self):
2N/A """Return True if this property can be delegated with
2N/A "zfs allow"."""
2N/A return self.attr != "readonly"
2N/A
2N/Aproptable = dict()
2N/Afor name, t in zfs.ioctl.get_proptable().iteritems():
2N/A proptable[name] = Property(t)
2N/Adel name, t
2N/A
2N/Adef getpropobj(name):
2N/A """Return the Property object that is identified by the given
2N/A name string. It can be the full name, or the column name."""
2N/A try:
2N/A return proptable[name]
2N/A except KeyError:
2N/A for p in proptable.itervalues():
2N/A if p.colname and p.colname.lower() == name:
2N/A return p
2N/A raise
2N/A
2N/Aclass Dataset(object):
2N/A """Represents a ZFS dataset (filesystem, snapshot, zvol, clone, etc).
2N/A
2N/A Generally, this class provides interfaces to the C functions in
2N/A zfs.ioctl which actually interface with the kernel to manipulate
2N/A datasets.
2N/A
2N/A Unless otherwise noted, any method can raise a ZFSError to
2N/A indicate failure."""
2N/A
2N/A __slots__ = "name", "__props"
2N/A __repr__ = zfs.util.default_repr
2N/A
2N/A def __init__(self, name, props=None,
2N/A types=("filesystem", "volume"), snaps=True):
2N/A """Open the named dataset, checking that it exists and
2N/A is of the specified type.
2N/A
2N/A name is the string name of this dataset.
2N/A
2N/A props is the property settings dict from zfs.ioctl.next_dataset.
2N/A
2N/A types is an iterable of strings specifying which types
2N/A of datasets are permitted. Accepted strings are
2N/A "filesystem" and "volume". Defaults to accepting all
2N/A types.
2N/A
2N/A snaps is a boolean specifying if snapshots are acceptable.
2N/A
2N/A Raises a ZFSError if the dataset can't be accessed (eg
2N/A doesn't exist) or is not of the specified type.
2N/A """
2N/A
2N/A self.name = name
2N/A
2N/A e = zfs.util.ZFSError(errno.EINVAL,
2N/A _("cannot open %s") % name,
2N/A _("operation not applicable to datasets of this type"))
2N/A if "@" in name and not snaps:
2N/A raise e
2N/A if not props:
2N/A props = zfs.ioctl.dataset_props(name)
2N/A self.__props = props
2N/A if "volume" not in types and self.getprop("type") == 3:
2N/A raise e
2N/A if "filesystem" not in types and self.getprop("type") == 2:
2N/A raise e
2N/A
2N/A def getprop(self, propname):
2N/A """Return the value of the given property for this dataset.
2N/A
2N/A Currently only works for native properties (those with a
2N/A Property object.)
2N/A
2N/A Raises KeyError if propname does not specify a native property.
2N/A Does not raise ZFSError.
2N/A """
2N/A
2N/A p = getpropobj(propname)
2N/A try:
2N/A return self.__props[p.name]["value"]
2N/A except KeyError:
2N/A return p.default
2N/A
2N/A def parent(self):
2N/A """Return a Dataset representing the parent of this one."""
2N/A return Dataset(self.name[:self.name.rindex("/")])
2N/A
2N/A def descendents(self):
2N/A """A generator function which iterates over all
2N/A descendent Datasets (not including snapshots."""
2N/A
2N/A cookie = 0
2N/A while True:
2N/A # next_dataset raises StopIteration when done
2N/A (name, cookie, props) = \
2N/A zfs.ioctl.next_dataset(self.name, False, cookie)
2N/A ds = Dataset(name, props)
2N/A yield ds
2N/A for child in ds.descendents():
2N/A yield child
2N/A
2N/A def userspace(self, prop):
2N/A """A generator function which iterates over a
2N/A userspace-type property.
2N/A
2N/A prop specifies which property ("userused@",
2N/A "userquota@", "groupused@", or "groupquota@").
2N/A
2N/A returns 3-tuple of domain (string), rid (int), and space (int).
2N/A """
2N/A
2N/A d = zfs.ioctl.userspace_many(self.name, prop)
2N/A for ((domain, rid), space) in d.iteritems():
2N/A yield (domain, rid, space)
2N/A
2N/A def userspace_upgrade(self):
2N/A """Initialize the accounting information for
2N/A userused@... and groupused@... properties."""
2N/A return zfs.ioctl.userspace_upgrade(self.name)
2N/A
2N/A def set_fsacl(self, un, d):
2N/A """Add to the "zfs allow"-ed permissions on this Dataset.
2N/A
2N/A un is True if the specified permissions should be removed.
2N/A
2N/A d is a dict specifying which permissions to add/remove:
2N/A { "whostr" -> None # remove all perms for this entity
2N/A "whostr" -> { "perm" -> None} # add/remove these perms
2N/A } """
2N/A return zfs.ioctl.set_fsacl(self.name, un, d)
2N/A
2N/A def get_fsacl(self):
2N/A """Get the "zfs allow"-ed permissions on the Dataset.
2N/A
2N/A Return a dict("whostr": { "perm" -> None })."""
2N/A
2N/A return zfs.ioctl.get_fsacl(self.name)
2N/A
2N/A def get_holds(self):
2N/A """Get the user holds on this Dataset.
2N/A
2N/A Return a dict("tag": timestamp)."""
2N/A
2N/A return zfs.ioctl.get_holds(self.name)
2N/A
2N/Adef snapshots_fromcmdline(dsnames, recursive):
2N/A for dsname in dsnames:
2N/A if not "@" in dsname:
2N/A raise zfs.util.ZFSError(errno.EINVAL,
2N/A _("cannot open %s") % dsname,
2N/A _("operation only applies to snapshots"))
2N/A try:
2N/A ds = Dataset(dsname)
2N/A yield ds
2N/A except zfs.util.ZFSError, e:
2N/A if not recursive or e.errno != errno.ENOENT:
2N/A raise
2N/A if recursive:
2N/A (base, snapname) = dsname.split('@')
2N/A parent = Dataset(base)
2N/A for child in parent.descendents():
2N/A try:
2N/A yield Dataset(child.name + "@" +
2N/A snapname)
2N/A except zfs.util.ZFSError, e:
2N/A if e.errno != errno.ENOENT:
2N/A raise