Path: blob/main/cddl/contrib/opensolaris/lib/pyzfs/common/userspace.py
39563 views
#! /usr/bin/python2.61#2# CDDL HEADER START3#4# The contents of this file are subject to the terms of the5# Common Development and Distribution License (the "License").6# You may not use this file except in compliance with the License.7#8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9# or http://www.opensolaris.org/os/licensing.10# See the License for the specific language governing permissions11# and limitations under the License.12#13# When distributing Covered Code, include this CDDL HEADER in each14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.15# If applicable, add the following below this CDDL HEADER, with the16# fields enclosed by brackets "[]" replaced with your own identifying17# information: Portions Copyright [yyyy] [name of copyright owner]18#19# CDDL HEADER END20#21# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.22#2324"""This module implements the "zfs userspace" and "zfs groupspace" subcommands.25The only public interface is the zfs.userspace.do_userspace() function."""2627import optparse28import sys29import pwd30import grp31import errno32import solaris.misc33import zfs.util34import zfs.ioctl35import zfs.dataset36import zfs.table3738_ = zfs.util._3940# map from property name prefix -> (field name, isgroup)41props = {42"userused@": ("used", False),43"userquota@": ("quota", False),44"groupused@": ("used", True),45"groupquota@": ("quota", True),46}4748def skiptype(options, prop):49"""Return True if this property (eg "userquota@") should be skipped."""50(field, isgroup) = props[prop]51if field not in options.fields:52return True53if isgroup and "posixgroup" not in options.types and \54"smbgroup" not in options.types:55return True56if not isgroup and "posixuser" not in options.types and \57"smbuser" not in options.types:58return True59return False6061def new_entry(options, isgroup, domain, rid):62"""Return a dict("field": value) for this domain (string) + rid (int)"""6364if domain:65idstr = "%s-%u" % (domain, rid)66else:67idstr = "%u" % rid6869(typename, mapfunc) = {70(1, 1): ("SMB Group", lambda id: solaris.misc.sid_to_name(id, 0)),71(1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name),72(0, 1): ("SMB User", lambda id: solaris.misc.sid_to_name(id, 1)),73(0, 0): ("POSIX User", lambda id: pwd.getpwuid(int(id)).pw_name)74}[isgroup, bool(domain)]7576if typename.lower().replace(" ", "") not in options.types:77return None7879v = dict()80v["type"] = typename8182# python's getpwuid/getgrgid is confused by ephemeral uids83if not options.noname and rid < 1<<31:84try:85v["name"] = mapfunc(idstr)86except KeyError:87pass8889if "name" not in v:90v["name"] = idstr91if not domain:92# it's just a number, so pad it with spaces so93# that it will sort numerically94v["name.sort"] = "%20d" % rid95# fill in default values96v["used"] = "0"97v["used.sort"] = 098v["quota"] = "none"99v["quota.sort"] = 0100return v101102def process_one_raw(acct, options, prop, elem):103"""Update the acct dict to incorporate the104information from this elem from Dataset.userspace(prop)."""105106(domain, rid, value) = elem107(field, isgroup) = props[prop]108109if options.translate and domain:110try:111rid = solaris.misc.sid_to_id("%s-%u" % (domain, rid),112not isgroup)113domain = None114except KeyError:115pass;116key = (isgroup, domain, rid)117118try:119v = acct[key]120except KeyError:121v = new_entry(options, isgroup, domain, rid)122if not v:123return124acct[key] = v125126# Add our value to an existing value, which may be present if127# options.translate is set.128value = v[field + ".sort"] = value + v[field + ".sort"]129130if options.parsable:131v[field] = str(value)132else:133v[field] = zfs.util.nicenum(value)134135def do_userspace():136"""Implements the "zfs userspace" and "zfs groupspace" subcommands."""137138def usage(msg=None):139parser.print_help()140if msg:141142parser.exit("zfs: error: " + msg)143else:144parser.exit()145146if sys.argv[1] == "userspace":147defaulttypes = "posixuser,smbuser"148else:149defaulttypes = "posixgroup,smbgroup"150151fields = ("type", "name", "used", "quota")152rjustfields = ("used", "quota")153types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup")154155u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1]156u += _(" [-t type[,...]] <filesystem|snapshot>")157parser = optparse.OptionParser(usage=u, prog="zfs")158159parser.add_option("-n", action="store_true", dest="noname",160help=_("Print numeric ID instead of user/group name"))161parser.add_option("-i", action="store_true", dest="translate",162help=_("translate SID to posix (possibly ephemeral) ID"))163parser.add_option("-H", action="store_true", dest="noheaders",164help=_("no headers, tab delimited output"))165parser.add_option("-p", action="store_true", dest="parsable",166help=_("exact (parsable) numeric output"))167parser.add_option("-o", dest="fields", metavar="field[,...]",168default="type,name,used,quota",169help=_("print only these fields (eg type,name,used,quota)"))170parser.add_option("-s", dest="sortfields", metavar="field",171type="choice", choices=fields, default=list(),172action="callback", callback=zfs.util.append_with_opt,173help=_("sort field"))174parser.add_option("-S", dest="sortfields", metavar="field",175type="choice", choices=fields, #-s sets the default176action="callback", callback=zfs.util.append_with_opt,177help=_("reverse sort field"))178parser.add_option("-t", dest="types", metavar="type[,...]",179default=defaulttypes,180help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)"))181182(options, args) = parser.parse_args(sys.argv[2:])183if len(args) != 1:184usage(_("wrong number of arguments"))185dsname = args[0]186187options.fields = options.fields.split(",")188for f in options.fields:189if f not in fields:190usage(_("invalid field %s") % f)191192options.types = options.types.split(",")193for t in options.types:194if t not in types:195usage(_("invalid type %s") % t)196197if not options.sortfields:198options.sortfields = [("-s", "type"), ("-s", "name")]199200if "all" in options.types:201options.types = types[1:]202203ds = zfs.dataset.Dataset(dsname, types=("filesystem"))204205if ds.getprop("jailed") and solaris.misc.isglobalzone():206options.noname = True207208if not ds.getprop("useraccounting"):209print(_("Initializing accounting information on old filesystem, please wait..."))210ds.userspace_upgrade()211212# gather and process accounting information213# Due to -i, we need to keep a dict, so we can potentially add214# together the posix ID and SID's usage. Grr.215acct = dict()216for prop in props.keys():217if skiptype(options, prop):218continue;219for elem in ds.userspace(prop):220process_one_raw(acct, options, prop, elem)221222def cmpkey(val):223l = list()224for (opt, field) in options.sortfields:225try:226n = val[field + ".sort"]227except KeyError:228n = val[field]229if opt == "-S":230# reverse sorting231try:232n = -n233except TypeError:234# it's a string; decompose it235# into an array of integers,236# each one the negative of that237# character238n = [-ord(c) for c in n]239l.append(n)240return l241242t = zfs.table.Table(options.fields, rjustfields)243for val in acct.itervalues():244t.addline(cmpkey(val), val)245t.printme(not options.noheaders)246247248