xref: /freebsd/sys/contrib/openzfs/cmd/zilstat.in (revision c7046f76c2c027b00c0e6ba57cfd28f1a78f5e23)
1*c7046f76SMartin Matuska#!/usr/bin/env @PYTHON_SHEBANG@
2*c7046f76SMartin Matuska#
3*c7046f76SMartin Matuska# Print out statistics for all zil stats. This information is
4*c7046f76SMartin Matuska# available through the zil kstat.
5*c7046f76SMartin Matuska#
6*c7046f76SMartin Matuska# CDDL HEADER START
7*c7046f76SMartin Matuska#
8*c7046f76SMartin Matuska# The contents of this file are subject to the terms of the
9*c7046f76SMartin Matuska# Common Development and Distribution License, Version 1.0 only
10*c7046f76SMartin Matuska# (the "License").  You may not use this file except in compliance
11*c7046f76SMartin Matuska# with the License.
12*c7046f76SMartin Matuska#
13*c7046f76SMartin Matuska# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
14*c7046f76SMartin Matuska# or https://opensource.org/licenses/CDDL-1.0.
15*c7046f76SMartin Matuska# See the License for the specific language governing permissions
16*c7046f76SMartin Matuska# and limitations under the License.
17*c7046f76SMartin Matuska#
18*c7046f76SMartin Matuska# When distributing Covered Code, include this CDDL HEADER in each
19*c7046f76SMartin Matuska# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
20*c7046f76SMartin Matuska# If applicable, add the following below this CDDL HEADER, with the
21*c7046f76SMartin Matuska# fields enclosed by brackets "[]" replaced with your own identifying
22*c7046f76SMartin Matuska# information: Portions Copyright [yyyy] [name of copyright owner]
23*c7046f76SMartin Matuska#
24*c7046f76SMartin Matuska# This script must remain compatible with Python 3.6+.
25*c7046f76SMartin Matuska#
26*c7046f76SMartin Matuska
27*c7046f76SMartin Matuskaimport sys
28*c7046f76SMartin Matuskaimport subprocess
29*c7046f76SMartin Matuskaimport time
30*c7046f76SMartin Matuskaimport copy
31*c7046f76SMartin Matuskaimport os
32*c7046f76SMartin Matuskaimport re
33*c7046f76SMartin Matuskaimport signal
34*c7046f76SMartin Matuskafrom collections import defaultdict
35*c7046f76SMartin Matuskaimport argparse
36*c7046f76SMartin Matuskafrom argparse import RawTextHelpFormatter
37*c7046f76SMartin Matuska
38*c7046f76SMartin Matuskacols = {
39*c7046f76SMartin Matuska	# hdr:       [size,      scale, 	 kstat name]
40*c7046f76SMartin Matuska	"time":      [8,         -1,         "time"],
41*c7046f76SMartin Matuska	"pool":      [12,        -1,         "pool"],
42*c7046f76SMartin Matuska	"ds":        [12,        -1,         "dataset_name"],
43*c7046f76SMartin Matuska	"obj":       [12,        -1,         "objset"],
44*c7046f76SMartin Matuska	"zcc":       [10,        1000,       "zil_commit_count"],
45*c7046f76SMartin Matuska	"zcwc":      [10,        1000,       "zil_commit_writer_count"],
46*c7046f76SMartin Matuska	"ziic":      [10,        1000,       "zil_itx_indirect_count"],
47*c7046f76SMartin Matuska	"zic":       [10,        1000,       "zil_itx_count"],
48*c7046f76SMartin Matuska	"ziib":      [10,        1024,       "zil_itx_indirect_bytes"],
49*c7046f76SMartin Matuska	"zicc":      [10,        1000,       "zil_itx_copied_count"],
50*c7046f76SMartin Matuska	"zicb":      [10,        1024,       "zil_itx_copied_bytes"],
51*c7046f76SMartin Matuska	"zinc":      [10,        1000,       "zil_itx_needcopy_count"],
52*c7046f76SMartin Matuska	"zinb":      [10,        1024,       "zil_itx_needcopy_bytes"],
53*c7046f76SMartin Matuska	"zimnc":     [10,        1000,       "zil_itx_metaslab_normal_count"],
54*c7046f76SMartin Matuska	"zimnb":     [10,        1024,       "zil_itx_metaslab_normal_bytes"],
55*c7046f76SMartin Matuska	"zimsc":     [10,        1000,       "zil_itx_metaslab_slog_count"],
56*c7046f76SMartin Matuska	"zimsb":     [10,        1024,       "zil_itx_metaslab_slog_bytes"],
57*c7046f76SMartin Matuska}
58*c7046f76SMartin Matuska
59*c7046f76SMartin Matuskahdr = ["time", "pool", "ds", "obj", "zcc", "zcwc", "ziic", "zic", "ziib", \
60*c7046f76SMartin Matuska	"zicc", "zicb", "zinc", "zinb", "zimnc", "zimnb", "zimsc", "zimsb"]
61*c7046f76SMartin Matuska
62*c7046f76SMartin Matuskaghdr = ["time", "zcc", "zcwc", "ziic", "zic", "ziib", "zicc", "zicb",
63*c7046f76SMartin Matuska	"zinc", "zinb", "zimnc", "zimnb", "zimsc", "zimsb"]
64*c7046f76SMartin Matuska
65*c7046f76SMartin Matuskacmd = ("Usage: zilstat [-hgdv] [-i interval] [-p pool_name]")
66*c7046f76SMartin Matuska
67*c7046f76SMartin Matuskacurr = {}
68*c7046f76SMartin Matuskadiff = {}
69*c7046f76SMartin Matuskakstat = {}
70*c7046f76SMartin Matuskads_pairs = {}
71*c7046f76SMartin Matuskapool_name = None
72*c7046f76SMartin Matuskadataset_name = None
73*c7046f76SMartin Matuskainterval = 0
74*c7046f76SMartin Matuskasep = "  "
75*c7046f76SMartin MatuskagFlag = True
76*c7046f76SMartin MatuskadsFlag = False
77*c7046f76SMartin Matuska
78*c7046f76SMartin Matuskadef prettynum(sz, scale, num=0):
79*c7046f76SMartin Matuska	suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
80*c7046f76SMartin Matuska	index = 0
81*c7046f76SMartin Matuska	save = 0
82*c7046f76SMartin Matuska
83*c7046f76SMartin Matuska	if scale == -1:
84*c7046f76SMartin Matuska		return "%*s" % (sz, num)
85*c7046f76SMartin Matuska
86*c7046f76SMartin Matuska	# Rounding error, return 0
87*c7046f76SMartin Matuska	elif 0 < num < 1:
88*c7046f76SMartin Matuska		num = 0
89*c7046f76SMartin Matuska
90*c7046f76SMartin Matuska	while num > scale and index < 5:
91*c7046f76SMartin Matuska		save = num
92*c7046f76SMartin Matuska		num = num / scale
93*c7046f76SMartin Matuska		index += 1
94*c7046f76SMartin Matuska
95*c7046f76SMartin Matuska	if index == 0:
96*c7046f76SMartin Matuska		return "%*d" % (sz, num)
97*c7046f76SMartin Matuska
98*c7046f76SMartin Matuska	if (save / scale) < 10:
99*c7046f76SMartin Matuska		return "%*.1f%s" % (sz - 1, num, suffix[index])
100*c7046f76SMartin Matuska	else:
101*c7046f76SMartin Matuska		return "%*d%s" % (sz - 1, num, suffix[index])
102*c7046f76SMartin Matuska
103*c7046f76SMartin Matuskadef print_header():
104*c7046f76SMartin Matuska	global hdr
105*c7046f76SMartin Matuska	global sep
106*c7046f76SMartin Matuska	for col in hdr:
107*c7046f76SMartin Matuska		new_col = col
108*c7046f76SMartin Matuska		if interval > 0 and col not in ['time', 'pool', 'ds', 'obj']:
109*c7046f76SMartin Matuska			new_col += "/s"
110*c7046f76SMartin Matuska		sys.stdout.write("%*s%s" % (cols[col][0], new_col, sep))
111*c7046f76SMartin Matuska	sys.stdout.write("\n")
112*c7046f76SMartin Matuska
113*c7046f76SMartin Matuskadef print_values(v):
114*c7046f76SMartin Matuska	global hdr
115*c7046f76SMartin Matuska	global sep
116*c7046f76SMartin Matuska	for col in hdr:
117*c7046f76SMartin Matuska		val = v[cols[col][2]]
118*c7046f76SMartin Matuska		if col not in ['time', 'pool', 'ds', 'obj'] and interval > 0:
119*c7046f76SMartin Matuska			val = v[cols[col][2]] // interval
120*c7046f76SMartin Matuska		sys.stdout.write("%s%s" % (
121*c7046f76SMartin Matuska			prettynum(cols[col][0], cols[col][1], val), sep))
122*c7046f76SMartin Matuska	sys.stdout.write("\n")
123*c7046f76SMartin Matuska
124*c7046f76SMartin Matuskadef print_dict(d):
125*c7046f76SMartin Matuska	for pool in d:
126*c7046f76SMartin Matuska		for objset in d[pool]:
127*c7046f76SMartin Matuska			print_values(d[pool][objset])
128*c7046f76SMartin Matuska
129*c7046f76SMartin Matuskadef detailed_usage():
130*c7046f76SMartin Matuska	sys.stderr.write("%s\n" % cmd)
131*c7046f76SMartin Matuska	sys.stderr.write("Field definitions are as follows:\n")
132*c7046f76SMartin Matuska	for key in cols:
133*c7046f76SMartin Matuska		sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
134*c7046f76SMartin Matuska	sys.stderr.write("\n")
135*c7046f76SMartin Matuska	sys.exit(0)
136*c7046f76SMartin Matuska
137*c7046f76SMartin Matuskadef init():
138*c7046f76SMartin Matuska	global pool_name
139*c7046f76SMartin Matuska	global dataset_name
140*c7046f76SMartin Matuska	global interval
141*c7046f76SMartin Matuska	global hdr
142*c7046f76SMartin Matuska	global curr
143*c7046f76SMartin Matuska	global gFlag
144*c7046f76SMartin Matuska	global sep
145*c7046f76SMartin Matuska
146*c7046f76SMartin Matuska	curr = dict()
147*c7046f76SMartin Matuska
148*c7046f76SMartin Matuska	parser = argparse.ArgumentParser(description='Program to print zilstats',
149*c7046f76SMartin Matuska                                	 add_help=True,
150*c7046f76SMartin Matuska					 formatter_class=RawTextHelpFormatter,
151*c7046f76SMartin Matuska					 epilog="\nUsage Examples\n"\
152*c7046f76SMartin Matuska				 		"Note: Global zilstats is shown by default,"\
153*c7046f76SMartin Matuska						" if none of a|p|d option is not provided\n"\
154*c7046f76SMartin Matuska				 		"\tzilstat -a\n"\
155*c7046f76SMartin Matuska						'\tzilstat -v\n'\
156*c7046f76SMartin Matuska						'\tzilstat -p tank\n'\
157*c7046f76SMartin Matuska						'\tzilstat -d tank/d1,tank/d2,tank/zv1\n'\
158*c7046f76SMartin Matuska						'\tzilstat -i 1\n'\
159*c7046f76SMartin Matuska						'\tzilstat -s \"***\"\n'\
160*c7046f76SMartin Matuska						'\tzilstat -f zcwc,zimnb,zimsb\n')
161*c7046f76SMartin Matuska
162*c7046f76SMartin Matuska	parser.add_argument(
163*c7046f76SMartin Matuska		"-v", "--verbose",
164*c7046f76SMartin Matuska		action="store_true",
165*c7046f76SMartin Matuska		help="List field headers and definitions"
166*c7046f76SMartin Matuska	)
167*c7046f76SMartin Matuska
168*c7046f76SMartin Matuska	pool_grp = parser.add_mutually_exclusive_group()
169*c7046f76SMartin Matuska
170*c7046f76SMartin Matuska	pool_grp.add_argument(
171*c7046f76SMartin Matuska		"-a", "--all",
172*c7046f76SMartin Matuska		action="store_true",
173*c7046f76SMartin Matuska		dest="all",
174*c7046f76SMartin Matuska		help="Print all dataset stats"
175*c7046f76SMartin Matuska	)
176*c7046f76SMartin Matuska
177*c7046f76SMartin Matuska	pool_grp.add_argument(
178*c7046f76SMartin Matuska		"-p", "--pool",
179*c7046f76SMartin Matuska		type=str,
180*c7046f76SMartin Matuska		help="Print stats for all datasets of a speicfied pool"
181*c7046f76SMartin Matuska	)
182*c7046f76SMartin Matuska
183*c7046f76SMartin Matuska	pool_grp.add_argument(
184*c7046f76SMartin Matuska		"-d", "--dataset",
185*c7046f76SMartin Matuska		type=str,
186*c7046f76SMartin Matuska		help="Print given dataset(s) (Comma separated)"
187*c7046f76SMartin Matuska	)
188*c7046f76SMartin Matuska
189*c7046f76SMartin Matuska	parser.add_argument(
190*c7046f76SMartin Matuska		"-f", "--columns",
191*c7046f76SMartin Matuska		type=str,
192*c7046f76SMartin Matuska		help="Specify specific fields to print (see -v)"
193*c7046f76SMartin Matuska	)
194*c7046f76SMartin Matuska
195*c7046f76SMartin Matuska	parser.add_argument(
196*c7046f76SMartin Matuska		"-s", "--separator",
197*c7046f76SMartin Matuska		type=str,
198*c7046f76SMartin Matuska		help="Override default field separator with custom "
199*c7046f76SMartin Matuska			 "character or string"
200*c7046f76SMartin Matuska	)
201*c7046f76SMartin Matuska
202*c7046f76SMartin Matuska	parser.add_argument(
203*c7046f76SMartin Matuska		"-i", "--interval",
204*c7046f76SMartin Matuska		type=int,
205*c7046f76SMartin Matuska		dest="interval",
206*c7046f76SMartin Matuska		help="Print stats between specified interval"
207*c7046f76SMartin Matuska			 " (in seconds)"
208*c7046f76SMartin Matuska	)
209*c7046f76SMartin Matuska
210*c7046f76SMartin Matuska	parsed_args = parser.parse_args()
211*c7046f76SMartin Matuska
212*c7046f76SMartin Matuska	if parsed_args.verbose:
213*c7046f76SMartin Matuska		detailed_usage()
214*c7046f76SMartin Matuska
215*c7046f76SMartin Matuska	if parsed_args.all:
216*c7046f76SMartin Matuska		gFlag = False
217*c7046f76SMartin Matuska
218*c7046f76SMartin Matuska	if parsed_args.interval:
219*c7046f76SMartin Matuska		interval = parsed_args.interval
220*c7046f76SMartin Matuska
221*c7046f76SMartin Matuska	if parsed_args.pool:
222*c7046f76SMartin Matuska		pool_name = parsed_args.pool
223*c7046f76SMartin Matuska		gFlag = False
224*c7046f76SMartin Matuska
225*c7046f76SMartin Matuska	if parsed_args.dataset:
226*c7046f76SMartin Matuska		dataset_name = parsed_args.dataset
227*c7046f76SMartin Matuska		gFlag = False
228*c7046f76SMartin Matuska
229*c7046f76SMartin Matuska	if parsed_args.separator:
230*c7046f76SMartin Matuska		sep = parsed_args.separator
231*c7046f76SMartin Matuska
232*c7046f76SMartin Matuska	if gFlag:
233*c7046f76SMartin Matuska		hdr = ghdr
234*c7046f76SMartin Matuska
235*c7046f76SMartin Matuska	if parsed_args.columns:
236*c7046f76SMartin Matuska		hdr = parsed_args.columns.split(",")
237*c7046f76SMartin Matuska
238*c7046f76SMartin Matuska		invalid = []
239*c7046f76SMartin Matuska		for ele in hdr:
240*c7046f76SMartin Matuska			if gFlag and ele not in ghdr:
241*c7046f76SMartin Matuska				invalid.append(ele)
242*c7046f76SMartin Matuska			elif ele not in cols:
243*c7046f76SMartin Matuska				invalid.append(ele)
244*c7046f76SMartin Matuska
245*c7046f76SMartin Matuska		if len(invalid) > 0:
246*c7046f76SMartin Matuska			sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
247*c7046f76SMartin Matuska			sys.exit(1)
248*c7046f76SMartin Matuska
249*c7046f76SMartin Matuska	if pool_name and dataset_name:
250*c7046f76SMartin Matuska		print ("Error: Can not filter both dataset and pool")
251*c7046f76SMartin Matuska		sys.exit(1)
252*c7046f76SMartin Matuska
253*c7046f76SMartin Matuskadef FileCheck(fname):
254*c7046f76SMartin Matuska	try:
255*c7046f76SMartin Matuska		return (open(fname))
256*c7046f76SMartin Matuska	except IOError:
257*c7046f76SMartin Matuska		print ("Unable to open zilstat proc file: " + fname)
258*c7046f76SMartin Matuska		sys.exit(1)
259*c7046f76SMartin Matuska
260*c7046f76SMartin Matuskaif sys.platform.startswith('freebsd'):
261*c7046f76SMartin Matuska	# Requires py-sysctl on FreeBSD
262*c7046f76SMartin Matuska	import sysctl
263*c7046f76SMartin Matuska
264*c7046f76SMartin Matuska	def kstat_update(pool = None, objid = None):
265*c7046f76SMartin Matuska		global kstat
266*c7046f76SMartin Matuska		kstat = {}
267*c7046f76SMartin Matuska		if not pool:
268*c7046f76SMartin Matuska			file = "kstat.zfs.misc.zil"
269*c7046f76SMartin Matuska			k = [ctl for ctl in sysctl.filter(file) \
270*c7046f76SMartin Matuska				if ctl.type != sysctl.CTLTYPE_NODE]
271*c7046f76SMartin Matuska			kstat_process_str(k, file, "GLOBAL", len(file + "."))
272*c7046f76SMartin Matuska		elif objid:
273*c7046f76SMartin Matuska			file = "kstat.zfs." + pool + ".dataset.objset-" + objid
274*c7046f76SMartin Matuska			k = [ctl for ctl in sysctl.filter(file) if ctl.type \
275*c7046f76SMartin Matuska				!= sysctl.CTLTYPE_NODE]
276*c7046f76SMartin Matuska			kstat_process_str(k, file, objid, len(file + "."))
277*c7046f76SMartin Matuska		else:
278*c7046f76SMartin Matuska			file = "kstat.zfs." + pool + ".dataset"
279*c7046f76SMartin Matuska			zil_start = len(file + ".")
280*c7046f76SMartin Matuska			obj_start = len("kstat.zfs." + pool + ".")
281*c7046f76SMartin Matuska			k = [ctl for ctl in sysctl.filter(file)
282*c7046f76SMartin Matuska				if ctl.type != sysctl.CTLTYPE_NODE]
283*c7046f76SMartin Matuska			for s in k:
284*c7046f76SMartin Matuska				if not s or (s.name.find("zil") == -1 and \
285*c7046f76SMartin Matuska					s.name.find("dataset_name") == -1):
286*c7046f76SMartin Matuska					continue
287*c7046f76SMartin Matuska				name, value = s.name, s.value
288*c7046f76SMartin Matuska				objid = re.findall(r'0x[0-9A-F]+', \
289*c7046f76SMartin Matuska					name[obj_start:], re.I)[0]
290*c7046f76SMartin Matuska				if objid not in kstat:
291*c7046f76SMartin Matuska					kstat[objid] = dict()
292*c7046f76SMartin Matuska				zil_start = len(file + ".objset-" + \
293*c7046f76SMartin Matuska					objid + ".")
294*c7046f76SMartin Matuska				kstat[objid][name[zil_start:]] = value \
295*c7046f76SMartin Matuska					if (name.find("dataset_name")) \
296*c7046f76SMartin Matuska					else int(value)
297*c7046f76SMartin Matuska
298*c7046f76SMartin Matuska	def kstat_process_str(k, file, objset = "GLOBAL", zil_start = 0):
299*c7046f76SMartin Matuska			global kstat
300*c7046f76SMartin Matuska			if not k:
301*c7046f76SMartin Matuska				print("Unable to process kstat for: " + file)
302*c7046f76SMartin Matuska				sys.exit(1)
303*c7046f76SMartin Matuska			kstat[objset] = dict()
304*c7046f76SMartin Matuska			for s in k:
305*c7046f76SMartin Matuska				if not s or (s.name.find("zil") == -1 and \
306*c7046f76SMartin Matuska				    s.name.find("dataset_name") == -1):
307*c7046f76SMartin Matuska					continue
308*c7046f76SMartin Matuska				name, value = s.name, s.value
309*c7046f76SMartin Matuska				kstat[objset][name[zil_start:]] = value \
310*c7046f76SMartin Matuska				    if (name.find("dataset_name")) else int(value)
311*c7046f76SMartin Matuska
312*c7046f76SMartin Matuskaelif sys.platform.startswith('linux'):
313*c7046f76SMartin Matuska	def kstat_update(pool = None, objid = None):
314*c7046f76SMartin Matuska		global kstat
315*c7046f76SMartin Matuska		kstat = {}
316*c7046f76SMartin Matuska		if not pool:
317*c7046f76SMartin Matuska			k = [line.strip() for line in \
318*c7046f76SMartin Matuska				FileCheck("/proc/spl/kstat/zfs/zil")]
319*c7046f76SMartin Matuska			kstat_process_str(k, "/proc/spl/kstat/zfs/zil")
320*c7046f76SMartin Matuska		elif objid:
321*c7046f76SMartin Matuska			file = "/proc/spl/kstat/zfs/" + pool + "/objset-" + objid
322*c7046f76SMartin Matuska			k = [line.strip() for line in FileCheck(file)]
323*c7046f76SMartin Matuska			kstat_process_str(k, file, objid)
324*c7046f76SMartin Matuska		else:
325*c7046f76SMartin Matuska			if not os.path.exists(f"/proc/spl/kstat/zfs/{pool}"):
326*c7046f76SMartin Matuska				print("Pool \"" + pool + "\" does not exist, Exitting")
327*c7046f76SMartin Matuska				sys.exit(1)
328*c7046f76SMartin Matuska			objsets = os.listdir(f'/proc/spl/kstat/zfs/{pool}')
329*c7046f76SMartin Matuska			for objid in objsets:
330*c7046f76SMartin Matuska				if objid.find("objset-") == -1:
331*c7046f76SMartin Matuska					continue
332*c7046f76SMartin Matuska				file = "/proc/spl/kstat/zfs/" + pool + "/" + objid
333*c7046f76SMartin Matuska				k = [line.strip() for line in FileCheck(file)]
334*c7046f76SMartin Matuska				kstat_process_str(k, file, objid.replace("objset-", ""))
335*c7046f76SMartin Matuska
336*c7046f76SMartin Matuska	def kstat_process_str(k, file, objset = "GLOBAL", zil_start = 0):
337*c7046f76SMartin Matuska			global kstat
338*c7046f76SMartin Matuska			if not k:
339*c7046f76SMartin Matuska				print("Unable to process kstat for: " + file)
340*c7046f76SMartin Matuska				sys.exit(1)
341*c7046f76SMartin Matuska
342*c7046f76SMartin Matuska			kstat[objset] = dict()
343*c7046f76SMartin Matuska			for s in k:
344*c7046f76SMartin Matuska				if not s or (s.find("zil") == -1 and \
345*c7046f76SMartin Matuska				    s.find("dataset_name") == -1):
346*c7046f76SMartin Matuska					continue
347*c7046f76SMartin Matuska				name, unused, value = s.split()
348*c7046f76SMartin Matuska				kstat[objset][name] = value \
349*c7046f76SMartin Matuska				    if (name == "dataset_name") else int(value)
350*c7046f76SMartin Matuska
351*c7046f76SMartin Matuskadef zil_process_kstat():
352*c7046f76SMartin Matuska	global curr, pool_name, dataset_name, dsFlag, ds_pairs
353*c7046f76SMartin Matuska	curr.clear()
354*c7046f76SMartin Matuska	if gFlag == True:
355*c7046f76SMartin Matuska		kstat_update()
356*c7046f76SMartin Matuska		zil_build_dict()
357*c7046f76SMartin Matuska	else:
358*c7046f76SMartin Matuska		if pool_name:
359*c7046f76SMartin Matuska			kstat_update(pool_name)
360*c7046f76SMartin Matuska			zil_build_dict(pool_name)
361*c7046f76SMartin Matuska		elif dataset_name:
362*c7046f76SMartin Matuska			if dsFlag == False:
363*c7046f76SMartin Matuska				dsFlag = True
364*c7046f76SMartin Matuska				datasets = dataset_name.split(',')
365*c7046f76SMartin Matuska				ds_pairs = defaultdict(list)
366*c7046f76SMartin Matuska				for ds in datasets:
367*c7046f76SMartin Matuska					try:
368*c7046f76SMartin Matuska						objid = subprocess.check_output(['zfs',
369*c7046f76SMartin Matuska						    'list', '-Hpo', 'objsetid', ds], \
370*c7046f76SMartin Matuska						    stderr=subprocess.DEVNULL) \
371*c7046f76SMartin Matuska						    .decode('utf-8').strip()
372*c7046f76SMartin Matuska					except subprocess.CalledProcessError as e:
373*c7046f76SMartin Matuska						print("Command: \"zfs list -Hpo objset "\
374*c7046f76SMartin Matuska						+ str(ds) + "\" failed with error code:"\
375*c7046f76SMartin Matuska						+ str(e.returncode))
376*c7046f76SMartin Matuska						print("Please make sure that dataset \""\
377*c7046f76SMartin Matuska						+ str(ds) + "\" exists")
378*c7046f76SMartin Matuska						sys.exit(1)
379*c7046f76SMartin Matuska					if not objid:
380*c7046f76SMartin Matuska						continue
381*c7046f76SMartin Matuska					ds_pairs[ds.split('/')[0]]. \
382*c7046f76SMartin Matuska						append(hex(int(objid)))
383*c7046f76SMartin Matuska			for pool, objids in ds_pairs.items():
384*c7046f76SMartin Matuska				for objid in objids:
385*c7046f76SMartin Matuska					kstat_update(pool, objid)
386*c7046f76SMartin Matuska					zil_build_dict(pool)
387*c7046f76SMartin Matuska		else:
388*c7046f76SMartin Matuska			try:
389*c7046f76SMartin Matuska				pools = subprocess.check_output(['zpool', 'list', '-Hpo',\
390*c7046f76SMartin Matuska				    'name']).decode('utf-8').split()
391*c7046f76SMartin Matuska			except subprocess.CalledProcessError as e:
392*c7046f76SMartin Matuska				print("Command: \"zpool list -Hpo name\" failed with error"\
393*c7046f76SMartin Matuska				    "code: " + str(e.returncode))
394*c7046f76SMartin Matuska				sys.exit(1)
395*c7046f76SMartin Matuska			for pool in pools:
396*c7046f76SMartin Matuska				kstat_update(pool)
397*c7046f76SMartin Matuska				zil_build_dict(pool)
398*c7046f76SMartin Matuska
399*c7046f76SMartin Matuskadef calculate_diff():
400*c7046f76SMartin Matuska	global curr, diff
401*c7046f76SMartin Matuska	prev = copy.deepcopy(curr)
402*c7046f76SMartin Matuska	zil_process_kstat()
403*c7046f76SMartin Matuska	diff = copy.deepcopy(curr)
404*c7046f76SMartin Matuska	for pool in curr:
405*c7046f76SMartin Matuska		for objset in curr[pool]:
406*c7046f76SMartin Matuska			for col in hdr:
407*c7046f76SMartin Matuska				if col not in ['time', 'pool', 'ds', 'obj']:
408*c7046f76SMartin Matuska					key = cols[col][2]
409*c7046f76SMartin Matuska					# If prev is NULL, this is the
410*c7046f76SMartin Matuska					# first time we are here
411*c7046f76SMartin Matuska					if not prev:
412*c7046f76SMartin Matuska						diff[pool][objset][key] = 0
413*c7046f76SMartin Matuska					else:
414*c7046f76SMartin Matuska						diff[pool][objset][key] \
415*c7046f76SMartin Matuska							= curr[pool][objset][key] \
416*c7046f76SMartin Matuska							- prev[pool][objset][key]
417*c7046f76SMartin Matuska
418*c7046f76SMartin Matuskadef zil_build_dict(pool = "GLOBAL"):
419*c7046f76SMartin Matuska	global kstat
420*c7046f76SMartin Matuska	for objset in kstat:
421*c7046f76SMartin Matuska		for key in kstat[objset]:
422*c7046f76SMartin Matuska			val = kstat[objset][key]
423*c7046f76SMartin Matuska			if pool not in curr:
424*c7046f76SMartin Matuska				curr[pool] = dict()
425*c7046f76SMartin Matuska			if objset not in curr[pool]:
426*c7046f76SMartin Matuska				curr[pool][objset] = dict()
427*c7046f76SMartin Matuska			curr[pool][objset][key] = val
428*c7046f76SMartin Matuska		curr[pool][objset]["pool"] = pool
429*c7046f76SMartin Matuska		curr[pool][objset]["objset"] = objset
430*c7046f76SMartin Matuska		curr[pool][objset]["time"] = time.strftime("%H:%M:%S", \
431*c7046f76SMartin Matuska			time.localtime())
432*c7046f76SMartin Matuska
433*c7046f76SMartin Matuskadef sign_handler_epipe(sig, frame):
434*c7046f76SMartin Matuska	print("Caught EPIPE signal: " + str(frame))
435*c7046f76SMartin Matuska	print("Exitting...")
436*c7046f76SMartin Matuska	sys.exit(0)
437*c7046f76SMartin Matuska
438*c7046f76SMartin Matuskadef main():
439*c7046f76SMartin Matuska	global interval
440*c7046f76SMartin Matuska	global curr
441*c7046f76SMartin Matuska	hprint = False
442*c7046f76SMartin Matuska	init()
443*c7046f76SMartin Matuska	signal.signal(signal.SIGINT, signal.SIG_DFL)
444*c7046f76SMartin Matuska	signal.signal(signal.SIGPIPE, sign_handler_epipe)
445*c7046f76SMartin Matuska
446*c7046f76SMartin Matuska	if interval > 0:
447*c7046f76SMartin Matuska		while True:
448*c7046f76SMartin Matuska			calculate_diff()
449*c7046f76SMartin Matuska			if not diff:
450*c7046f76SMartin Matuska				print ("Error: No stats to show")
451*c7046f76SMartin Matuska				sys.exit(0)
452*c7046f76SMartin Matuska			if hprint == False:
453*c7046f76SMartin Matuska				print_header()
454*c7046f76SMartin Matuska				hprint = True
455*c7046f76SMartin Matuska			print_dict(diff)
456*c7046f76SMartin Matuska			time.sleep(interval)
457*c7046f76SMartin Matuska	else:
458*c7046f76SMartin Matuska		zil_process_kstat()
459*c7046f76SMartin Matuska		if not curr:
460*c7046f76SMartin Matuska			print ("Error: No stats to show")
461*c7046f76SMartin Matuska			sys.exit(0)
462*c7046f76SMartin Matuska		print_header()
463*c7046f76SMartin Matuska		print_dict(curr)
464*c7046f76SMartin Matuska
465*c7046f76SMartin Matuskaif __name__ == '__main__':
466*c7046f76SMartin Matuska	main()
467*c7046f76SMartin Matuska
468