diff options
author | Chris Kirby <chris.kirby@sun.com> | 2009-08-01 15:09:50 -0600 |
---|---|---|
committer | Chris Kirby <chris.kirby@sun.com> | 2009-08-01 15:09:50 -0600 |
commit | 842727c2f41f01b380de4f5e787d905702870f23 (patch) | |
tree | c0143417f1cb9c4385e0191a0d90682a98e6d81e /usr/src/lib/pyzfs/common | |
parent | 592106a23e99a1790d339bab84de7fa3474964a4 (diff) | |
download | illumos-gate-842727c2f41f01b380de4f5e787d905702870f23.tar.gz |
PSARC/2009/297 zfs snapshot holds
6803121 want user-settable refcounts on snapshots
6851824 zfs_ioc_rename() can be called with a NULL zc_name
Diffstat (limited to 'usr/src/lib/pyzfs/common')
-rw-r--r-- | usr/src/lib/pyzfs/common/allow.py | 4 | ||||
-rw-r--r-- | usr/src/lib/pyzfs/common/dataset.py | 28 | ||||
-rw-r--r-- | usr/src/lib/pyzfs/common/holds.py | 72 | ||||
-rw-r--r-- | usr/src/lib/pyzfs/common/ioctl.c | 20 | ||||
-rw-r--r-- | usr/src/lib/pyzfs/common/table.py | 71 | ||||
-rw-r--r-- | usr/src/lib/pyzfs/common/userspace.py | 63 |
6 files changed, 209 insertions, 49 deletions
diff --git a/usr/src/lib/pyzfs/common/allow.py b/usr/src/lib/pyzfs/common/allow.py index d3a03c7318..092fac07ea 100644 --- a/usr/src/lib/pyzfs/common/allow.py +++ b/usr/src/lib/pyzfs/common/allow.py @@ -217,6 +217,8 @@ perms_subcmd = dict( mount=_("Allows mount/umount of ZFS datasets"), share=_("Allows sharing file systems over NFS or SMB\n\t\t\t\tprotocols"), send="", + hold=_("Allows adding a user hold to a snapshot"), + release=_("Allows releasing a user hold which\n\t\t\t\tmight destroy the snapshot"), ) perms_other = dict( @@ -265,7 +267,7 @@ def print_perms(): print(fmt % (name, _("property"), "")) def do_allow(): - """Implementes the "zfs allow" and "zfs unallow" subcommands.""" + """Implements the "zfs allow" and "zfs unallow" subcommands.""" un = (sys.argv[1] == "unallow") def usage(msg=None): diff --git a/usr/src/lib/pyzfs/common/dataset.py b/usr/src/lib/pyzfs/common/dataset.py index b45173e01f..f3adc5c364 100644 --- a/usr/src/lib/pyzfs/common/dataset.py +++ b/usr/src/lib/pyzfs/common/dataset.py @@ -109,7 +109,7 @@ class Dataset(object): types is an iterable of strings specifying which types of datasets are permitted. Accepted strings are - "filesystem" and "volume". Defaults to acceptying all + "filesystem" and "volume". Defaults to accepting all types. snaps is a boolean specifying if snapshots are acceptable. @@ -203,3 +203,29 @@ class Dataset(object): Return a dict("whostr": { "perm" -> None }).""" return zfs.ioctl.get_fsacl(self.name) + + def get_holds(self): + """Get the user holds on this Dataset. + + Return a dict("tag": timestamp).""" + + return zfs.ioctl.get_holds(self.name) + +def snapshots_fromcmdline(dsnames, recursive): + for dsname in dsnames: + ds = Dataset(dsname) + if not "@" in dsname: + raise zfs.util.ZFSError(errno.EINVAL, + _("cannot open %s") % dsname, + _("operation only applies to snapshots")) + yield ds + if recursive: + (base, snapname) = dsname.split('@') + parent = Dataset(base) + for child in parent.descendents(): + try: + yield Dataset(child.name + "@" + + snapname) + except zfs.util.ZFSError, e: + if e.errno != errno.ENOENT: + raise diff --git a/usr/src/lib/pyzfs/common/holds.py b/usr/src/lib/pyzfs/common/holds.py new file mode 100644 index 0000000000..93db4a15e6 --- /dev/null +++ b/usr/src/lib/pyzfs/common/holds.py @@ -0,0 +1,72 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +"""This module implements the "zfs holds" subcommand. +The only public interface is the zfs.holds.do_holds() function.""" + +import optparse +import sys +import errno +import time +import zfs.util +import zfs.dataset +import zfs.table + +_ = zfs.util._ + +def do_holds(): + """Implements the "zfs holds" subcommand.""" + def usage(msg=None): + parser.print_help() + if msg: + print + parser.exit("zfs: error: " + msg) + else: + parser.exit() + + u = _("""holds [-r] <snapshot> ...""") + + parser = optparse.OptionParser(usage=u, prog="zfs") + + parser.add_option("-r", action="store_true", dest="recursive", + help=_("list holds recursively")) + + (options, args) = parser.parse_args(sys.argv[2:]) + + if len(args) < 1: + usage(_("missing snapshot argument")) + + fields = ("name", "tag", "timestamp") + rjustfields = () + printing = False + t = zfs.table.Table(fields, rjustfields) + for ds in zfs.dataset.snapshots_fromcmdline(args, options.recursive): + for tag, tm in ds.get_holds().iteritems(): + val = {"name": ds.name, "tag": tag, + "timestamp": time.ctime(tm)} + t.addline(ds.name, val) + printing = True + if printing: + t.printme() diff --git a/usr/src/lib/pyzfs/common/ioctl.c b/usr/src/lib/pyzfs/common/ioctl.c index ae3fa74dc2..0c838abcb4 100644 --- a/usr/src/lib/pyzfs/common/ioctl.c +++ b/usr/src/lib/pyzfs/common/ioctl.c @@ -350,6 +350,25 @@ py_set_fsacl(PyObject *self, PyObject *args) } static PyObject * +py_get_holds(PyObject *self, PyObject *args) +{ + zfs_cmd_t zc = { 0 }; + char *name; + PyObject *nvl; + + if (!PyArg_ParseTuple(args, "s", &name)) + return (NULL); + + (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); + + nvl = ioctl_with_dstnv(ZFS_IOC_GET_HOLDS, &zc); + if (nvl == NULL) + seterr(_("cannot get holds for %s"), name); + + return (nvl); +} + +static PyObject * py_userspace_many(PyObject *self, PyObject *args) { zfs_cmd_t zc = { 0 }; @@ -582,6 +601,7 @@ static PyMethodDef zfsmethods[] = { "Map SID to name@domain."}, {"isglobalzone", py_isglobalzone, METH_NOARGS, "Determine if this is the global zone."}, + {"get_holds", py_get_holds, METH_VARARGS, "Get user holds."}, {NULL, NULL, 0, NULL} }; diff --git a/usr/src/lib/pyzfs/common/table.py b/usr/src/lib/pyzfs/common/table.py new file mode 100644 index 0000000000..a96d8d73e5 --- /dev/null +++ b/usr/src/lib/pyzfs/common/table.py @@ -0,0 +1,71 @@ +#! /usr/bin/python2.4 +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +import zfs.util + +class Table: + __slots__ = "fields", "rjustfields", "maxfieldlen", "lines" + __repr__ = zfs.util.default_repr + + def __init__(self, fields, rjustfields=()): + # XXX maybe have a defaults, too? + self.fields = fields + self.rjustfields = rjustfields + self.maxfieldlen = dict.fromkeys(fields, 0) + self.lines = list() + + def __updatemax(self, k, v): + self.maxfieldlen[k] = max(self.maxfieldlen.get(k, None), v) + + def addline(self, sortkey, values): + """values is a dict from field name to value""" + + va = list() + for f in self.fields: + v = str(values[f]) + va.append(v) + self.__updatemax(f, len(v)) + self.lines.append((sortkey, va)) + + def printme(self, headers=True): + if headers: + d = dict([(f, f.upper()) for f in self.fields]) + self.addline(None, d) + + self.lines.sort() + for (k, va) in self.lines: + line = str() + for i in range(len(self.fields)): + if not headers: + line += va[i] + line += "\t" + else: + if self.fields[i] in self.rjustfields: + fmt = "%*s " + else: + fmt = "%-*s " + mfl = self.maxfieldlen[self.fields[i]] + line += fmt % (mfl, va[i]) + print(line) diff --git a/usr/src/lib/pyzfs/common/userspace.py b/usr/src/lib/pyzfs/common/userspace.py index 93c65ca59d..6444dbc8c1 100644 --- a/usr/src/lib/pyzfs/common/userspace.py +++ b/usr/src/lib/pyzfs/common/userspace.py @@ -26,14 +26,15 @@ """This module implements the "zfs userspace" and "zfs groupspace" subcommands. The only public interface is the zfs.userspace.do_userspace() function.""" -import zfs.util -import zfs.ioctl -import zfs.dataset import optparse import sys import pwd import grp import errno +import zfs.util +import zfs.ioctl +import zfs.dataset +import zfs.table _ = zfs.util._ @@ -58,9 +59,6 @@ def skiptype(options, prop): return True return False -def updatemax(d, k, v): - d[k] = max(d.get(k, None), v) - def new_entry(options, isgroup, domain, rid): """Return a dict("field": value) for this domain (string) + rid (int)""" @@ -102,8 +100,8 @@ def new_entry(options, isgroup, domain, rid): v["quota.sort"] = 0 return v -def process_one_raw(acct, maxfieldlen, options, prop, elem): - """Update the acct and maxfieldlen dicts to incorporate the +def process_one_raw(acct, options, prop, elem): + """Update the acct dict to incorporate the information from this elem from Dataset.userspace(prop).""" (domain, rid, value) = elem @@ -134,10 +132,6 @@ def process_one_raw(acct, maxfieldlen, options, prop, elem): v[field] = str(value) else: v[field] = zfs.util.nicenum(value) - for k in v.keys(): - # some of the .sort fields are integers, so have no len() - if isinstance(v[k], str): - updatemax(maxfieldlen, k, len(v[k])) def do_userspace(): """Implements the "zfs userspace" and "zfs groupspace" subcommands.""" @@ -156,7 +150,7 @@ def do_userspace(): defaulttypes = "posixgroup,smbgroup" fields = ("type", "name", "used", "quota") - ljustfields = ("type", "name") + rjustfields = ("used", "quota") types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup") u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1] @@ -216,31 +210,16 @@ def do_userspace(): print(_("Initializing accounting information on old filesystem, please wait...")) ds.userspace_upgrade() - acct = dict() - maxfieldlen = dict() - # gather and process accounting information + # Due to -i, we need to keep a dict, so we can potentially add + # together the posix ID and SID's usage. Grr. + acct = dict() for prop in props.keys(): if skiptype(options, prop): continue; for elem in ds.userspace(prop): - process_one_raw(acct, maxfieldlen, options, prop, elem) - - # print out headers - if not options.noheaders: - line = str() - for field in options.fields: - # make sure the field header will fit - updatemax(maxfieldlen, field, len(field)) - - if field in ljustfields: - fmt = "%-*s " - else: - fmt = "%*s " - line += fmt % (maxfieldlen[field], field.upper()) - print(line) - - # custom sorting func + process_one_raw(acct, options, prop, elem) + def cmpkey(val): l = list() for (opt, field) in options.sortfields: @@ -261,17 +240,7 @@ def do_userspace(): l.append(n) return l - # print out data lines - for val in sorted(acct.itervalues(), key=cmpkey): - line = str() - for field in options.fields: - if options.noheaders: - line += val[field] - line += "\t" - else: - if field in ljustfields: - fmt = "%-*s " - else: - fmt = "%*s " - line += fmt % (maxfieldlen[field], val[field]) - print(line) + t = zfs.table.Table(options.fields, rjustfields) + for val in acct.itervalues(): + t.addline(cmpkey(val), val) + t.printme(not options.noheaders) |