xref: /illumos-gate/usr/src/lib/pyzfs/common/userspace.py (revision 9f9230833b50b8271840dc2c12bd1e94d9df7d12)
1*9f923083SAlexander Pyhalov#!@PYTHON@
214843421SMatthew Ahrens#
314843421SMatthew Ahrens# CDDL HEADER START
414843421SMatthew Ahrens#
514843421SMatthew Ahrens# The contents of this file are subject to the terms of the
614843421SMatthew Ahrens# Common Development and Distribution License (the "License").
714843421SMatthew Ahrens# You may not use this file except in compliance with the License.
814843421SMatthew Ahrens#
914843421SMatthew Ahrens# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1014843421SMatthew Ahrens# or http://www.opensolaris.org/os/licensing.
1114843421SMatthew Ahrens# See the License for the specific language governing permissions
1214843421SMatthew Ahrens# and limitations under the License.
1314843421SMatthew Ahrens#
1414843421SMatthew Ahrens# When distributing Covered Code, include this CDDL HEADER in each
1514843421SMatthew Ahrens# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1614843421SMatthew Ahrens# If applicable, add the following below this CDDL HEADER, with the
1714843421SMatthew Ahrens# fields enclosed by brackets "[]" replaced with your own identifying
1814843421SMatthew Ahrens# information: Portions Copyright [yyyy] [name of copyright owner]
1914843421SMatthew Ahrens#
2014843421SMatthew Ahrens# CDDL HEADER END
2114843421SMatthew Ahrens#
226d52f363SLori Alt# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2314843421SMatthew Ahrens#
2414843421SMatthew Ahrens
2514843421SMatthew Ahrens"""This module implements the "zfs userspace" and "zfs groupspace" subcommands.
2614843421SMatthew AhrensThe only public interface is the zfs.userspace.do_userspace() function."""
2714843421SMatthew Ahrens
2814843421SMatthew Ahrensimport optparse
2914843421SMatthew Ahrensimport sys
3014843421SMatthew Ahrensimport pwd
3114843421SMatthew Ahrensimport grp
3214843421SMatthew Ahrensimport errno
33e4d060fbSSam Falknerimport solaris.misc
34842727c2SChris Kirbyimport zfs.util
35842727c2SChris Kirbyimport zfs.ioctl
36842727c2SChris Kirbyimport zfs.dataset
37842727c2SChris Kirbyimport zfs.table
3814843421SMatthew Ahrens
3914843421SMatthew Ahrens_ = zfs.util._
4014843421SMatthew Ahrens
4114843421SMatthew Ahrens# map from property name prefix -> (field name, isgroup)
4214843421SMatthew Ahrensprops = {
4314843421SMatthew Ahrens    "userused@": ("used", False),
4414843421SMatthew Ahrens    "userquota@": ("quota", False),
4514843421SMatthew Ahrens    "groupused@": ("used", True),
4614843421SMatthew Ahrens    "groupquota@": ("quota", True),
4714843421SMatthew Ahrens}
4814843421SMatthew Ahrens
4914843421SMatthew Ahrensdef skiptype(options, prop):
5014843421SMatthew Ahrens	"""Return True if this property (eg "userquota@") should be skipped."""
5114843421SMatthew Ahrens	(field, isgroup) = props[prop]
5214843421SMatthew Ahrens	if field not in options.fields:
5314843421SMatthew Ahrens		return True
5414843421SMatthew Ahrens	if isgroup and "posixgroup" not in options.types and \
5514843421SMatthew Ahrens	    "smbgroup" not in options.types:
5614843421SMatthew Ahrens		return True
5714843421SMatthew Ahrens	if not isgroup and "posixuser" not in options.types and \
5814843421SMatthew Ahrens	    "smbuser" not in options.types:
5914843421SMatthew Ahrens		return True
6014843421SMatthew Ahrens	return False
6114843421SMatthew Ahrens
6214843421SMatthew Ahrensdef new_entry(options, isgroup, domain, rid):
6314843421SMatthew Ahrens	"""Return a dict("field": value) for this domain (string) + rid (int)"""
6414843421SMatthew Ahrens
6514843421SMatthew Ahrens	if domain:
6614843421SMatthew Ahrens		idstr = "%s-%u" % (domain, rid)
6714843421SMatthew Ahrens	else:
6814843421SMatthew Ahrens		idstr = "%u" % rid
6914843421SMatthew Ahrens
7014843421SMatthew Ahrens	(typename, mapfunc) = {
71e4d060fbSSam Falkner	    (1, 1): ("SMB Group",   lambda id: solaris.misc.sid_to_name(id, 0)),
7214843421SMatthew Ahrens	    (1, 0): ("POSIX Group", lambda id: grp.getgrgid(int(id)).gr_name),
73e4d060fbSSam Falkner	    (0, 1): ("SMB User",    lambda id: solaris.misc.sid_to_name(id, 1)),
7414843421SMatthew Ahrens	    (0, 0): ("POSIX User",  lambda id: pwd.getpwuid(int(id)).pw_name)
7514843421SMatthew Ahrens	}[isgroup, bool(domain)]
7614843421SMatthew Ahrens
7714843421SMatthew Ahrens	if typename.lower().replace(" ", "") not in options.types:
7814843421SMatthew Ahrens		return None
7914843421SMatthew Ahrens
8014843421SMatthew Ahrens	v = dict()
8114843421SMatthew Ahrens	v["type"] = typename
8214843421SMatthew Ahrens
8314843421SMatthew Ahrens	# python's getpwuid/getgrgid is confused by ephemeral uids
8414843421SMatthew Ahrens	if not options.noname and rid < 1<<31:
8514843421SMatthew Ahrens		try:
8614843421SMatthew Ahrens			v["name"] = mapfunc(idstr)
8714843421SMatthew Ahrens		except KeyError:
8814843421SMatthew Ahrens			pass
8914843421SMatthew Ahrens
9014843421SMatthew Ahrens	if "name" not in v:
9114843421SMatthew Ahrens		v["name"] = idstr
9214843421SMatthew Ahrens		if not domain:
9314843421SMatthew Ahrens			# it's just a number, so pad it with spaces so
9414843421SMatthew Ahrens			# that it will sort numerically
9514843421SMatthew Ahrens			v["name.sort"] = "%20d" % rid
9614843421SMatthew Ahrens	# fill in default values
9714843421SMatthew Ahrens	v["used"] = "0"
9814843421SMatthew Ahrens	v["used.sort"] = 0
9914843421SMatthew Ahrens	v["quota"] = "none"
10014843421SMatthew Ahrens	v["quota.sort"] = 0
10114843421SMatthew Ahrens	return v
10214843421SMatthew Ahrens
103842727c2SChris Kirbydef process_one_raw(acct, options, prop, elem):
104842727c2SChris Kirby	"""Update the acct dict to incorporate the
10514843421SMatthew Ahrens	information from this elem from Dataset.userspace(prop)."""
10614843421SMatthew Ahrens
10714843421SMatthew Ahrens	(domain, rid, value) = elem
10814843421SMatthew Ahrens	(field, isgroup) = props[prop]
10914843421SMatthew Ahrens
11014843421SMatthew Ahrens	if options.translate and domain:
11114843421SMatthew Ahrens		try:
112e4d060fbSSam Falkner			rid = solaris.misc.sid_to_id("%s-%u" % (domain, rid),
11314843421SMatthew Ahrens			    not isgroup)
11414843421SMatthew Ahrens			domain = None
11514843421SMatthew Ahrens		except KeyError:
11614843421SMatthew Ahrens			pass;
11714843421SMatthew Ahrens	key = (isgroup, domain, rid)
11814843421SMatthew Ahrens
11914843421SMatthew Ahrens	try:
12014843421SMatthew Ahrens		v = acct[key]
12114843421SMatthew Ahrens	except KeyError:
12214843421SMatthew Ahrens		v = new_entry(options, isgroup, domain, rid)
12314843421SMatthew Ahrens		if not v:
12414843421SMatthew Ahrens			return
12514843421SMatthew Ahrens		acct[key] = v
12614843421SMatthew Ahrens
12714843421SMatthew Ahrens	# Add our value to an existing value, which may be present if
12814843421SMatthew Ahrens	# options.translate is set.
12914843421SMatthew Ahrens	value = v[field + ".sort"] = value + v[field + ".sort"]
13014843421SMatthew Ahrens
13114843421SMatthew Ahrens	if options.parsable:
13214843421SMatthew Ahrens		v[field] = str(value)
13314843421SMatthew Ahrens	else:
13414843421SMatthew Ahrens		v[field] = zfs.util.nicenum(value)
13514843421SMatthew Ahrens
13614843421SMatthew Ahrensdef do_userspace():
13714843421SMatthew Ahrens	"""Implements the "zfs userspace" and "zfs groupspace" subcommands."""
13814843421SMatthew Ahrens
13914843421SMatthew Ahrens	def usage(msg=None):
14014843421SMatthew Ahrens		parser.print_help()
14114843421SMatthew Ahrens		if msg:
14214843421SMatthew Ahrens			print
14314843421SMatthew Ahrens			parser.exit("zfs: error: " + msg)
14414843421SMatthew Ahrens		else:
14514843421SMatthew Ahrens			parser.exit()
14614843421SMatthew Ahrens
14714843421SMatthew Ahrens	if sys.argv[1] == "userspace":
14814843421SMatthew Ahrens		defaulttypes = "posixuser,smbuser"
14914843421SMatthew Ahrens	else:
15014843421SMatthew Ahrens		defaulttypes = "posixgroup,smbgroup"
15114843421SMatthew Ahrens
15214843421SMatthew Ahrens	fields = ("type", "name", "used", "quota")
153842727c2SChris Kirby	rjustfields = ("used", "quota")
15414843421SMatthew Ahrens	types = ("all", "posixuser", "smbuser", "posixgroup", "smbgroup")
15514843421SMatthew Ahrens
15614843421SMatthew Ahrens	u = _("%s [-niHp] [-o field[,...]] [-sS field] ... \n") % sys.argv[1]
15714843421SMatthew Ahrens	u += _("    [-t type[,...]] <filesystem|snapshot>")
15814843421SMatthew Ahrens	parser = optparse.OptionParser(usage=u, prog="zfs")
15914843421SMatthew Ahrens
16014843421SMatthew Ahrens	parser.add_option("-n", action="store_true", dest="noname",
16114843421SMatthew Ahrens	    help=_("Print numeric ID instead of user/group name"))
16214843421SMatthew Ahrens	parser.add_option("-i", action="store_true", dest="translate",
16314843421SMatthew Ahrens	    help=_("translate SID to posix (possibly ephemeral) ID"))
16414843421SMatthew Ahrens	parser.add_option("-H", action="store_true", dest="noheaders",
16514843421SMatthew Ahrens	    help=_("no headers, tab delimited output"))
16614843421SMatthew Ahrens	parser.add_option("-p", action="store_true", dest="parsable",
16714843421SMatthew Ahrens	    help=_("exact (parsable) numeric output"))
16814843421SMatthew Ahrens	parser.add_option("-o", dest="fields", metavar="field[,...]",
16914843421SMatthew Ahrens	    default="type,name,used,quota",
17014843421SMatthew Ahrens	    help=_("print only these fields (eg type,name,used,quota)"))
17114843421SMatthew Ahrens	parser.add_option("-s", dest="sortfields", metavar="field",
17214843421SMatthew Ahrens	    type="choice", choices=fields, default=list(),
17314843421SMatthew Ahrens	    action="callback", callback=zfs.util.append_with_opt,
17414843421SMatthew Ahrens	    help=_("sort field"))
17514843421SMatthew Ahrens	parser.add_option("-S", dest="sortfields", metavar="field",
17614843421SMatthew Ahrens	    type="choice", choices=fields, #-s sets the default
17714843421SMatthew Ahrens	    action="callback", callback=zfs.util.append_with_opt,
17814843421SMatthew Ahrens	    help=_("reverse sort field"))
17914843421SMatthew Ahrens	parser.add_option("-t", dest="types", metavar="type[,...]",
18014843421SMatthew Ahrens	    default=defaulttypes,
18114843421SMatthew Ahrens	    help=_("print only these types (eg posixuser,smbuser,posixgroup,smbgroup,all)"))
18214843421SMatthew Ahrens
18314843421SMatthew Ahrens	(options, args) = parser.parse_args(sys.argv[2:])
18414843421SMatthew Ahrens	if len(args) != 1:
18514843421SMatthew Ahrens		usage(_("wrong number of arguments"))
18614843421SMatthew Ahrens	dsname = args[0]
18714843421SMatthew Ahrens
18814843421SMatthew Ahrens	options.fields = options.fields.split(",")
18914843421SMatthew Ahrens	for f in options.fields:
19014843421SMatthew Ahrens		if f not in fields:
19114843421SMatthew Ahrens			usage(_("invalid field %s") % f)
19214843421SMatthew Ahrens
19314843421SMatthew Ahrens	options.types = options.types.split(",")
19414843421SMatthew Ahrens	for t in options.types:
19514843421SMatthew Ahrens		if t not in types:
19614843421SMatthew Ahrens			usage(_("invalid type %s") % t)
19714843421SMatthew Ahrens
19814843421SMatthew Ahrens	if not options.sortfields:
19914843421SMatthew Ahrens		options.sortfields = [("-s", "type"), ("-s", "name")]
20014843421SMatthew Ahrens
20114843421SMatthew Ahrens	if "all" in options.types:
20214843421SMatthew Ahrens		options.types = types[1:]
20314843421SMatthew Ahrens
20414843421SMatthew Ahrens	ds = zfs.dataset.Dataset(dsname, types=("filesystem"))
20514843421SMatthew Ahrens
206e4d060fbSSam Falkner	if ds.getprop("zoned") and solaris.misc.isglobalzone():
20714843421SMatthew Ahrens		options.noname = True
20814843421SMatthew Ahrens
20914843421SMatthew Ahrens	if not ds.getprop("useraccounting"):
21014843421SMatthew Ahrens		print(_("Initializing accounting information on old filesystem, please wait..."))
21114843421SMatthew Ahrens		ds.userspace_upgrade()
21214843421SMatthew Ahrens
21314843421SMatthew Ahrens	# gather and process accounting information
214842727c2SChris Kirby	# Due to -i, we need to keep a dict, so we can potentially add
215842727c2SChris Kirby	# together the posix ID and SID's usage.  Grr.
216842727c2SChris Kirby	acct = dict()
21714843421SMatthew Ahrens	for prop in props.keys():
21814843421SMatthew Ahrens		if skiptype(options, prop):
21914843421SMatthew Ahrens			continue;
22014843421SMatthew Ahrens		for elem in ds.userspace(prop):
221842727c2SChris Kirby			process_one_raw(acct, options, prop, elem)
222842727c2SChris Kirby
22314843421SMatthew Ahrens	def cmpkey(val):
22414843421SMatthew Ahrens		l = list()
22514843421SMatthew Ahrens		for (opt, field) in options.sortfields:
22614843421SMatthew Ahrens			try:
22714843421SMatthew Ahrens				n = val[field + ".sort"]
22814843421SMatthew Ahrens			except KeyError:
22914843421SMatthew Ahrens				n = val[field]
23014843421SMatthew Ahrens			if opt == "-S":
23114843421SMatthew Ahrens				# reverse sorting
23214843421SMatthew Ahrens				try:
23314843421SMatthew Ahrens					n = -n
23414843421SMatthew Ahrens				except TypeError:
23514843421SMatthew Ahrens					# it's a string; decompose it
23614843421SMatthew Ahrens					# into an array of integers,
23714843421SMatthew Ahrens					# each one the negative of that
23814843421SMatthew Ahrens					# character
23914843421SMatthew Ahrens					n = [-ord(c) for c in n]
24014843421SMatthew Ahrens			l.append(n)
24114843421SMatthew Ahrens		return l
24214843421SMatthew Ahrens
243842727c2SChris Kirby	t = zfs.table.Table(options.fields, rjustfields)
244842727c2SChris Kirby	for val in acct.itervalues():
245842727c2SChris Kirby		t.addline(cmpkey(val), val)
246842727c2SChris Kirby	t.printme(not options.noheaders)
247