1c7046f76SMartin Matuska#!/usr/bin/env @PYTHON_SHEBANG@ 2*61145dc2SMartin Matuska# SPDX-License-Identifier: CDDL-1.0 3c7046f76SMartin Matuska# 4c7046f76SMartin Matuska# Print out statistics for all zil stats. This information is 5c7046f76SMartin Matuska# available through the zil kstat. 6c7046f76SMartin Matuska# 7c7046f76SMartin Matuska# CDDL HEADER START 8c7046f76SMartin Matuska# 9c7046f76SMartin Matuska# The contents of this file are subject to the terms of the 10c7046f76SMartin Matuska# Common Development and Distribution License, Version 1.0 only 11c7046f76SMartin Matuska# (the "License"). You may not use this file except in compliance 12c7046f76SMartin Matuska# with the License. 13c7046f76SMartin Matuska# 14c7046f76SMartin Matuska# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 15c7046f76SMartin Matuska# or https://opensource.org/licenses/CDDL-1.0. 16c7046f76SMartin Matuska# See the License for the specific language governing permissions 17c7046f76SMartin Matuska# and limitations under the License. 18c7046f76SMartin Matuska# 19c7046f76SMartin Matuska# When distributing Covered Code, include this CDDL HEADER in each 20c7046f76SMartin Matuska# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 21c7046f76SMartin Matuska# If applicable, add the following below this CDDL HEADER, with the 22c7046f76SMartin Matuska# fields enclosed by brackets "[]" replaced with your own identifying 23c7046f76SMartin Matuska# information: Portions Copyright [yyyy] [name of copyright owner] 24c7046f76SMartin Matuska# 25c7046f76SMartin Matuska# This script must remain compatible with Python 3.6+. 26c7046f76SMartin Matuska# 27c7046f76SMartin Matuska 28c7046f76SMartin Matuskaimport sys 29c7046f76SMartin Matuskaimport subprocess 30c7046f76SMartin Matuskaimport time 31c7046f76SMartin Matuskaimport copy 32c7046f76SMartin Matuskaimport os 33c7046f76SMartin Matuskaimport re 34c7046f76SMartin Matuskaimport signal 35c7046f76SMartin Matuskafrom collections import defaultdict 36c7046f76SMartin Matuskaimport argparse 37c7046f76SMartin Matuskafrom argparse import RawTextHelpFormatter 38c7046f76SMartin Matuska 39c7046f76SMartin Matuskacols = { 40c7046f76SMartin Matuska # hdr: [size, scale, kstat name] 41c7046f76SMartin Matuska "time": [8, -1, "time"], 42c7046f76SMartin Matuska "pool": [12, -1, "pool"], 43c7046f76SMartin Matuska "ds": [12, -1, "dataset_name"], 44c7046f76SMartin Matuska "obj": [12, -1, "objset"], 454e8d558cSMartin Matuska "cc": [5, 1000, "zil_commit_count"], 464e8d558cSMartin Matuska "cwc": [5, 1000, "zil_commit_writer_count"], 47ce4dcb97SMartin Matuska "cec": [5, 1000, "zil_commit_error_count"], 48ce4dcb97SMartin Matuska "csc": [5, 1000, "zil_commit_stall_count"], 49ce4dcb97SMartin Matuska "cSc": [5, 1000, "zil_commit_suspend_count"], 504e8d558cSMartin Matuska "ic": [5, 1000, "zil_itx_count"], 514e8d558cSMartin Matuska "iic": [5, 1000, "zil_itx_indirect_count"], 524e8d558cSMartin Matuska "iib": [5, 1024, "zil_itx_indirect_bytes"], 534e8d558cSMartin Matuska "icc": [5, 1000, "zil_itx_copied_count"], 544e8d558cSMartin Matuska "icb": [5, 1024, "zil_itx_copied_bytes"], 554e8d558cSMartin Matuska "inc": [5, 1000, "zil_itx_needcopy_count"], 564e8d558cSMartin Matuska "inb": [5, 1024, "zil_itx_needcopy_bytes"], 574e8d558cSMartin Matuska "idc": [5, 1000, "icc+inc"], 584e8d558cSMartin Matuska "idb": [5, 1024, "icb+inb"], 594e8d558cSMartin Matuska "iwc": [5, 1000, "iic+idc"], 604e8d558cSMartin Matuska "iwb": [5, 1024, "iib+idb"], 614e8d558cSMartin Matuska "imnc": [6, 1000, "zil_itx_metaslab_normal_count"], 624e8d558cSMartin Matuska "imnb": [6, 1024, "zil_itx_metaslab_normal_bytes"], 634e8d558cSMartin Matuska "imnw": [6, 1024, "zil_itx_metaslab_normal_write"], 644e8d558cSMartin Matuska "imna": [6, 1024, "zil_itx_metaslab_normal_alloc"], 654e8d558cSMartin Matuska "imsc": [6, 1000, "zil_itx_metaslab_slog_count"], 664e8d558cSMartin Matuska "imsb": [6, 1024, "zil_itx_metaslab_slog_bytes"], 674e8d558cSMartin Matuska "imsw": [6, 1024, "zil_itx_metaslab_slog_write"], 684e8d558cSMartin Matuska "imsa": [6, 1024, "zil_itx_metaslab_slog_alloc"], 694e8d558cSMartin Matuska "imc": [5, 1000, "imnc+imsc"], 704e8d558cSMartin Matuska "imb": [5, 1024, "imnb+imsb"], 714e8d558cSMartin Matuska "imw": [5, 1024, "imnw+imsw"], 724e8d558cSMartin Matuska "ima": [5, 1024, "imna+imsa"], 734e8d558cSMartin Matuska "se%": [3, 100, "imb/ima"], 744e8d558cSMartin Matuska "sen%": [4, 100, "imnb/imna"], 754e8d558cSMartin Matuska "ses%": [4, 100, "imsb/imsa"], 764e8d558cSMartin Matuska "te%": [3, 100, "imb/imw"], 774e8d558cSMartin Matuska "ten%": [4, 100, "imnb/imnw"], 784e8d558cSMartin Matuska "tes%": [4, 100, "imsb/imsw"], 79c7046f76SMartin Matuska} 80c7046f76SMartin Matuska 814e8d558cSMartin Matuskahdr = ["time", "ds", "cc", "ic", "idc", "idb", "iic", "iib", 824e8d558cSMartin Matuska "imnc", "imnw", "imsc", "imsw"] 83c7046f76SMartin Matuska 844e8d558cSMartin Matuskaghdr = ["time", "cc", "ic", "idc", "idb", "iic", "iib", 854e8d558cSMartin Matuska "imnc", "imnw", "imsc", "imsw"] 86c7046f76SMartin Matuska 87c7046f76SMartin Matuskacmd = ("Usage: zilstat [-hgdv] [-i interval] [-p pool_name]") 88c7046f76SMartin Matuska 89c7046f76SMartin Matuskacurr = {} 90c7046f76SMartin Matuskadiff = {} 91c7046f76SMartin Matuskakstat = {} 92c7046f76SMartin Matuskads_pairs = {} 93c7046f76SMartin Matuskapool_name = None 94c7046f76SMartin Matuskadataset_name = None 95c7046f76SMartin Matuskainterval = 0 96c7046f76SMartin Matuskasep = " " 97c7046f76SMartin MatuskagFlag = True 98c7046f76SMartin MatuskadsFlag = False 99c7046f76SMartin Matuska 100c7046f76SMartin Matuskadef prettynum(sz, scale, num=0): 101c7046f76SMartin Matuska suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z'] 102c7046f76SMartin Matuska index = 0 103c7046f76SMartin Matuska save = 0 104c7046f76SMartin Matuska 105c7046f76SMartin Matuska if scale == -1: 106c7046f76SMartin Matuska return "%*s" % (sz, num) 107c7046f76SMartin Matuska 108c7046f76SMartin Matuska # Rounding error, return 0 109c7046f76SMartin Matuska elif 0 < num < 1: 110c7046f76SMartin Matuska num = 0 111c7046f76SMartin Matuska 112c7046f76SMartin Matuska while num > scale and index < 5: 113c7046f76SMartin Matuska save = num 114c7046f76SMartin Matuska num = num / scale 115c7046f76SMartin Matuska index += 1 116c7046f76SMartin Matuska 117c7046f76SMartin Matuska if index == 0: 118c7046f76SMartin Matuska return "%*d" % (sz, num) 119c7046f76SMartin Matuska 120c7046f76SMartin Matuska if (save / scale) < 10: 121c7046f76SMartin Matuska return "%*.1f%s" % (sz - 1, num, suffix[index]) 122c7046f76SMartin Matuska else: 123c7046f76SMartin Matuska return "%*d%s" % (sz - 1, num, suffix[index]) 124c7046f76SMartin Matuska 125c7046f76SMartin Matuskadef print_header(): 126c7046f76SMartin Matuska global hdr 127c7046f76SMartin Matuska global sep 128c7046f76SMartin Matuska for col in hdr: 129c7046f76SMartin Matuska new_col = col 1304e8d558cSMartin Matuska if interval > 0 and cols[col][1] > 100: 131c7046f76SMartin Matuska new_col += "/s" 132c7046f76SMartin Matuska sys.stdout.write("%*s%s" % (cols[col][0], new_col, sep)) 133c7046f76SMartin Matuska sys.stdout.write("\n") 134c7046f76SMartin Matuska 135c7046f76SMartin Matuskadef print_values(v): 136c7046f76SMartin Matuska global hdr 137c7046f76SMartin Matuska global sep 138c7046f76SMartin Matuska for col in hdr: 139c7046f76SMartin Matuska val = v[cols[col][2]] 1404e8d558cSMartin Matuska if interval > 0 and cols[col][1] > 100: 141c7046f76SMartin Matuska val = v[cols[col][2]] // interval 142c7046f76SMartin Matuska sys.stdout.write("%s%s" % ( 143c7046f76SMartin Matuska prettynum(cols[col][0], cols[col][1], val), sep)) 144c7046f76SMartin Matuska sys.stdout.write("\n") 145c7046f76SMartin Matuska 146c7046f76SMartin Matuskadef print_dict(d): 147c7046f76SMartin Matuska for pool in d: 148c7046f76SMartin Matuska for objset in d[pool]: 149c7046f76SMartin Matuska print_values(d[pool][objset]) 150c7046f76SMartin Matuska 151c7046f76SMartin Matuskadef detailed_usage(): 152c7046f76SMartin Matuska sys.stderr.write("%s\n" % cmd) 153c7046f76SMartin Matuska sys.stderr.write("Field definitions are as follows:\n") 154c7046f76SMartin Matuska for key in cols: 155c7046f76SMartin Matuska sys.stderr.write("%11s : %s\n" % (key, cols[key][2])) 156c7046f76SMartin Matuska sys.stderr.write("\n") 157c7046f76SMartin Matuska sys.exit(0) 158c7046f76SMartin Matuska 159c7046f76SMartin Matuskadef init(): 160c7046f76SMartin Matuska global pool_name 161c7046f76SMartin Matuska global dataset_name 162c7046f76SMartin Matuska global interval 163c7046f76SMartin Matuska global hdr 164c7046f76SMartin Matuska global curr 165c7046f76SMartin Matuska global gFlag 166c7046f76SMartin Matuska global sep 167c7046f76SMartin Matuska 168c7046f76SMartin Matuska curr = dict() 169c7046f76SMartin Matuska 170c7046f76SMartin Matuska parser = argparse.ArgumentParser(description='Program to print zilstats', 171c7046f76SMartin Matuska add_help=True, 172c7046f76SMartin Matuska formatter_class=RawTextHelpFormatter, 173c7046f76SMartin Matuska epilog="\nUsage Examples\n"\ 174c7046f76SMartin Matuska "Note: Global zilstats is shown by default,"\ 175c7046f76SMartin Matuska " if none of a|p|d option is not provided\n"\ 176c7046f76SMartin Matuska "\tzilstat -a\n"\ 177c7046f76SMartin Matuska '\tzilstat -v\n'\ 178c7046f76SMartin Matuska '\tzilstat -p tank\n'\ 179c7046f76SMartin Matuska '\tzilstat -d tank/d1,tank/d2,tank/zv1\n'\ 180c7046f76SMartin Matuska '\tzilstat -i 1\n'\ 181c7046f76SMartin Matuska '\tzilstat -s \"***\"\n'\ 182c7046f76SMartin Matuska '\tzilstat -f zcwc,zimnb,zimsb\n') 183c7046f76SMartin Matuska 184c7046f76SMartin Matuska parser.add_argument( 185c7046f76SMartin Matuska "-v", "--verbose", 186c7046f76SMartin Matuska action="store_true", 187c7046f76SMartin Matuska help="List field headers and definitions" 188c7046f76SMartin Matuska ) 189c7046f76SMartin Matuska 190c7046f76SMartin Matuska pool_grp = parser.add_mutually_exclusive_group() 191c7046f76SMartin Matuska 192c7046f76SMartin Matuska pool_grp.add_argument( 193c7046f76SMartin Matuska "-a", "--all", 194c7046f76SMartin Matuska action="store_true", 195c7046f76SMartin Matuska dest="all", 196c7046f76SMartin Matuska help="Print all dataset stats" 197c7046f76SMartin Matuska ) 198c7046f76SMartin Matuska 199c7046f76SMartin Matuska pool_grp.add_argument( 200c7046f76SMartin Matuska "-p", "--pool", 201c7046f76SMartin Matuska type=str, 202c7046f76SMartin Matuska help="Print stats for all datasets of a speicfied pool" 203c7046f76SMartin Matuska ) 204c7046f76SMartin Matuska 205c7046f76SMartin Matuska pool_grp.add_argument( 206c7046f76SMartin Matuska "-d", "--dataset", 207c7046f76SMartin Matuska type=str, 208c7046f76SMartin Matuska help="Print given dataset(s) (Comma separated)" 209c7046f76SMartin Matuska ) 210c7046f76SMartin Matuska 211c7046f76SMartin Matuska parser.add_argument( 212c7046f76SMartin Matuska "-f", "--columns", 213c7046f76SMartin Matuska type=str, 214c7046f76SMartin Matuska help="Specify specific fields to print (see -v)" 215c7046f76SMartin Matuska ) 216c7046f76SMartin Matuska 217c7046f76SMartin Matuska parser.add_argument( 218c7046f76SMartin Matuska "-s", "--separator", 219c7046f76SMartin Matuska type=str, 220c7046f76SMartin Matuska help="Override default field separator with custom " 221c7046f76SMartin Matuska "character or string" 222c7046f76SMartin Matuska ) 223c7046f76SMartin Matuska 224c7046f76SMartin Matuska parser.add_argument( 225c7046f76SMartin Matuska "-i", "--interval", 226c7046f76SMartin Matuska type=int, 227c7046f76SMartin Matuska dest="interval", 228c7046f76SMartin Matuska help="Print stats between specified interval" 229c7046f76SMartin Matuska " (in seconds)" 230c7046f76SMartin Matuska ) 231c7046f76SMartin Matuska 232c7046f76SMartin Matuska parsed_args = parser.parse_args() 233c7046f76SMartin Matuska 234c7046f76SMartin Matuska if parsed_args.verbose: 235c7046f76SMartin Matuska detailed_usage() 236c7046f76SMartin Matuska 237c7046f76SMartin Matuska if parsed_args.all: 238c7046f76SMartin Matuska gFlag = False 239c7046f76SMartin Matuska 240c7046f76SMartin Matuska if parsed_args.interval: 241c7046f76SMartin Matuska interval = parsed_args.interval 242c7046f76SMartin Matuska 243c7046f76SMartin Matuska if parsed_args.pool: 244c7046f76SMartin Matuska pool_name = parsed_args.pool 245c7046f76SMartin Matuska gFlag = False 246c7046f76SMartin Matuska 247c7046f76SMartin Matuska if parsed_args.dataset: 248c7046f76SMartin Matuska dataset_name = parsed_args.dataset 249c7046f76SMartin Matuska gFlag = False 250c7046f76SMartin Matuska 251c7046f76SMartin Matuska if parsed_args.separator: 252c7046f76SMartin Matuska sep = parsed_args.separator 253c7046f76SMartin Matuska 254c7046f76SMartin Matuska if gFlag: 255c7046f76SMartin Matuska hdr = ghdr 256c7046f76SMartin Matuska 257c7046f76SMartin Matuska if parsed_args.columns: 258c7046f76SMartin Matuska hdr = parsed_args.columns.split(",") 259c7046f76SMartin Matuska 260c7046f76SMartin Matuska invalid = [] 261c7046f76SMartin Matuska for ele in hdr: 2624e8d558cSMartin Matuska if ele not in cols: 263c7046f76SMartin Matuska invalid.append(ele) 264c7046f76SMartin Matuska 265c7046f76SMartin Matuska if len(invalid) > 0: 266c7046f76SMartin Matuska sys.stderr.write("Invalid column definition! -- %s\n" % invalid) 267c7046f76SMartin Matuska sys.exit(1) 268c7046f76SMartin Matuska 269c7046f76SMartin Matuska if pool_name and dataset_name: 270c7046f76SMartin Matuska print ("Error: Can not filter both dataset and pool") 271c7046f76SMartin Matuska sys.exit(1) 272c7046f76SMartin Matuska 273c7046f76SMartin Matuskadef FileCheck(fname): 274c7046f76SMartin Matuska try: 275c7046f76SMartin Matuska return (open(fname)) 276c7046f76SMartin Matuska except IOError: 277c7046f76SMartin Matuska print ("Unable to open zilstat proc file: " + fname) 278c7046f76SMartin Matuska sys.exit(1) 279c7046f76SMartin Matuska 280c7046f76SMartin Matuskaif sys.platform.startswith('freebsd'): 281c7046f76SMartin Matuska # Requires py-sysctl on FreeBSD 282c7046f76SMartin Matuska import sysctl 283c7046f76SMartin Matuska 284c7046f76SMartin Matuska def kstat_update(pool = None, objid = None): 285c7046f76SMartin Matuska global kstat 286c7046f76SMartin Matuska kstat = {} 287c7046f76SMartin Matuska if not pool: 288c7046f76SMartin Matuska file = "kstat.zfs.misc.zil" 289c7046f76SMartin Matuska k = [ctl for ctl in sysctl.filter(file) \ 290c7046f76SMartin Matuska if ctl.type != sysctl.CTLTYPE_NODE] 291c7046f76SMartin Matuska kstat_process_str(k, file, "GLOBAL", len(file + ".")) 292c7046f76SMartin Matuska elif objid: 293c7046f76SMartin Matuska file = "kstat.zfs." + pool + ".dataset.objset-" + objid 294c7046f76SMartin Matuska k = [ctl for ctl in sysctl.filter(file) if ctl.type \ 295c7046f76SMartin Matuska != sysctl.CTLTYPE_NODE] 296c7046f76SMartin Matuska kstat_process_str(k, file, objid, len(file + ".")) 297c7046f76SMartin Matuska else: 298c7046f76SMartin Matuska file = "kstat.zfs." + pool + ".dataset" 299c7046f76SMartin Matuska zil_start = len(file + ".") 300c7046f76SMartin Matuska obj_start = len("kstat.zfs." + pool + ".") 301c7046f76SMartin Matuska k = [ctl for ctl in sysctl.filter(file) 302c7046f76SMartin Matuska if ctl.type != sysctl.CTLTYPE_NODE] 303c7046f76SMartin Matuska for s in k: 304c7046f76SMartin Matuska if not s or (s.name.find("zil") == -1 and \ 305c7046f76SMartin Matuska s.name.find("dataset_name") == -1): 306c7046f76SMartin Matuska continue 307c7046f76SMartin Matuska name, value = s.name, s.value 308c7046f76SMartin Matuska objid = re.findall(r'0x[0-9A-F]+', \ 309c7046f76SMartin Matuska name[obj_start:], re.I)[0] 310c7046f76SMartin Matuska if objid not in kstat: 311c7046f76SMartin Matuska kstat[objid] = dict() 312c7046f76SMartin Matuska zil_start = len(file + ".objset-" + \ 313c7046f76SMartin Matuska objid + ".") 314c7046f76SMartin Matuska kstat[objid][name[zil_start:]] = value \ 315c7046f76SMartin Matuska if (name.find("dataset_name")) \ 316c7046f76SMartin Matuska else int(value) 317c7046f76SMartin Matuska 318c7046f76SMartin Matuska def kstat_process_str(k, file, objset = "GLOBAL", zil_start = 0): 319c7046f76SMartin Matuska global kstat 320c7046f76SMartin Matuska if not k: 321c7046f76SMartin Matuska print("Unable to process kstat for: " + file) 322c7046f76SMartin Matuska sys.exit(1) 323c7046f76SMartin Matuska kstat[objset] = dict() 324c7046f76SMartin Matuska for s in k: 325c7046f76SMartin Matuska if not s or (s.name.find("zil") == -1 and \ 326c7046f76SMartin Matuska s.name.find("dataset_name") == -1): 327c7046f76SMartin Matuska continue 328c7046f76SMartin Matuska name, value = s.name, s.value 329c7046f76SMartin Matuska kstat[objset][name[zil_start:]] = value \ 330c7046f76SMartin Matuska if (name.find("dataset_name")) else int(value) 331c7046f76SMartin Matuska 332c7046f76SMartin Matuskaelif sys.platform.startswith('linux'): 333c7046f76SMartin Matuska def kstat_update(pool = None, objid = None): 334c7046f76SMartin Matuska global kstat 335c7046f76SMartin Matuska kstat = {} 336c7046f76SMartin Matuska if not pool: 337c7046f76SMartin Matuska k = [line.strip() for line in \ 338c7046f76SMartin Matuska FileCheck("/proc/spl/kstat/zfs/zil")] 339c7046f76SMartin Matuska kstat_process_str(k, "/proc/spl/kstat/zfs/zil") 340c7046f76SMartin Matuska elif objid: 341c7046f76SMartin Matuska file = "/proc/spl/kstat/zfs/" + pool + "/objset-" + objid 342c7046f76SMartin Matuska k = [line.strip() for line in FileCheck(file)] 343c7046f76SMartin Matuska kstat_process_str(k, file, objid) 344c7046f76SMartin Matuska else: 345c7046f76SMartin Matuska if not os.path.exists(f"/proc/spl/kstat/zfs/{pool}"): 346c7046f76SMartin Matuska print("Pool \"" + pool + "\" does not exist, Exitting") 347c7046f76SMartin Matuska sys.exit(1) 348c7046f76SMartin Matuska objsets = os.listdir(f'/proc/spl/kstat/zfs/{pool}') 349c7046f76SMartin Matuska for objid in objsets: 350c7046f76SMartin Matuska if objid.find("objset-") == -1: 351c7046f76SMartin Matuska continue 352c7046f76SMartin Matuska file = "/proc/spl/kstat/zfs/" + pool + "/" + objid 353c7046f76SMartin Matuska k = [line.strip() for line in FileCheck(file)] 354c7046f76SMartin Matuska kstat_process_str(k, file, objid.replace("objset-", "")) 355c7046f76SMartin Matuska 356c7046f76SMartin Matuska def kstat_process_str(k, file, objset = "GLOBAL", zil_start = 0): 357c7046f76SMartin Matuska global kstat 358c7046f76SMartin Matuska if not k: 359c7046f76SMartin Matuska print("Unable to process kstat for: " + file) 360c7046f76SMartin Matuska sys.exit(1) 361c7046f76SMartin Matuska 362c7046f76SMartin Matuska kstat[objset] = dict() 363c7046f76SMartin Matuska for s in k: 364c7046f76SMartin Matuska if not s or (s.find("zil") == -1 and \ 365c7046f76SMartin Matuska s.find("dataset_name") == -1): 366c7046f76SMartin Matuska continue 367c7046f76SMartin Matuska name, unused, value = s.split() 368c7046f76SMartin Matuska kstat[objset][name] = value \ 369c7046f76SMartin Matuska if (name == "dataset_name") else int(value) 370c7046f76SMartin Matuska 371c7046f76SMartin Matuskadef zil_process_kstat(): 372c7046f76SMartin Matuska global curr, pool_name, dataset_name, dsFlag, ds_pairs 373c7046f76SMartin Matuska curr.clear() 374c7046f76SMartin Matuska if gFlag == True: 375c7046f76SMartin Matuska kstat_update() 376c7046f76SMartin Matuska zil_build_dict() 377c7046f76SMartin Matuska else: 378c7046f76SMartin Matuska if pool_name: 379c7046f76SMartin Matuska kstat_update(pool_name) 380c7046f76SMartin Matuska zil_build_dict(pool_name) 381c7046f76SMartin Matuska elif dataset_name: 382c7046f76SMartin Matuska if dsFlag == False: 383c7046f76SMartin Matuska dsFlag = True 384c7046f76SMartin Matuska datasets = dataset_name.split(',') 385c7046f76SMartin Matuska ds_pairs = defaultdict(list) 386c7046f76SMartin Matuska for ds in datasets: 387c7046f76SMartin Matuska try: 388c7046f76SMartin Matuska objid = subprocess.check_output(['zfs', 389c7046f76SMartin Matuska 'list', '-Hpo', 'objsetid', ds], \ 390c7046f76SMartin Matuska stderr=subprocess.DEVNULL) \ 391c7046f76SMartin Matuska .decode('utf-8').strip() 392c7046f76SMartin Matuska except subprocess.CalledProcessError as e: 393c7046f76SMartin Matuska print("Command: \"zfs list -Hpo objset "\ 394c7046f76SMartin Matuska + str(ds) + "\" failed with error code:"\ 395c7046f76SMartin Matuska + str(e.returncode)) 396c7046f76SMartin Matuska print("Please make sure that dataset \""\ 397c7046f76SMartin Matuska + str(ds) + "\" exists") 398c7046f76SMartin Matuska sys.exit(1) 399c7046f76SMartin Matuska if not objid: 400c7046f76SMartin Matuska continue 401c7046f76SMartin Matuska ds_pairs[ds.split('/')[0]]. \ 402c7046f76SMartin Matuska append(hex(int(objid))) 403c7046f76SMartin Matuska for pool, objids in ds_pairs.items(): 404c7046f76SMartin Matuska for objid in objids: 405c7046f76SMartin Matuska kstat_update(pool, objid) 406c7046f76SMartin Matuska zil_build_dict(pool) 407c7046f76SMartin Matuska else: 408c7046f76SMartin Matuska try: 409c7046f76SMartin Matuska pools = subprocess.check_output(['zpool', 'list', '-Hpo',\ 410c7046f76SMartin Matuska 'name']).decode('utf-8').split() 411c7046f76SMartin Matuska except subprocess.CalledProcessError as e: 412c7046f76SMartin Matuska print("Command: \"zpool list -Hpo name\" failed with error"\ 413c7046f76SMartin Matuska "code: " + str(e.returncode)) 414c7046f76SMartin Matuska sys.exit(1) 415c7046f76SMartin Matuska for pool in pools: 416c7046f76SMartin Matuska kstat_update(pool) 417c7046f76SMartin Matuska zil_build_dict(pool) 418c7046f76SMartin Matuska 419c7046f76SMartin Matuskadef calculate_diff(): 420c7046f76SMartin Matuska global curr, diff 421c7046f76SMartin Matuska prev = copy.deepcopy(curr) 422c7046f76SMartin Matuska zil_process_kstat() 423c7046f76SMartin Matuska diff = copy.deepcopy(curr) 424c7046f76SMartin Matuska for pool in curr: 425c7046f76SMartin Matuska for objset in curr[pool]: 4264e8d558cSMartin Matuska for key in curr[pool][objset]: 4274e8d558cSMartin Matuska if not isinstance(diff[pool][objset][key], int): 4284e8d558cSMartin Matuska continue 429c7046f76SMartin Matuska # If prev is NULL, this is the 430c7046f76SMartin Matuska # first time we are here 431c7046f76SMartin Matuska if not prev: 432c7046f76SMartin Matuska diff[pool][objset][key] = 0 433c7046f76SMartin Matuska else: 434c7046f76SMartin Matuska diff[pool][objset][key] \ 435c7046f76SMartin Matuska = curr[pool][objset][key] \ 436c7046f76SMartin Matuska - prev[pool][objset][key] 437c7046f76SMartin Matuska 438c7046f76SMartin Matuskadef zil_build_dict(pool = "GLOBAL"): 439c7046f76SMartin Matuska global kstat 440c7046f76SMartin Matuska for objset in kstat: 441c7046f76SMartin Matuska for key in kstat[objset]: 442c7046f76SMartin Matuska val = kstat[objset][key] 443c7046f76SMartin Matuska if pool not in curr: 444c7046f76SMartin Matuska curr[pool] = dict() 445c7046f76SMartin Matuska if objset not in curr[pool]: 446c7046f76SMartin Matuska curr[pool][objset] = dict() 447c7046f76SMartin Matuska curr[pool][objset][key] = val 4484e8d558cSMartin Matuska 4494e8d558cSMartin Matuskadef zil_extend_dict(): 4504e8d558cSMartin Matuska global diff 4514e8d558cSMartin Matuska for pool in diff: 4524e8d558cSMartin Matuska for objset in diff[pool]: 4534e8d558cSMartin Matuska diff[pool][objset]["pool"] = pool 4544e8d558cSMartin Matuska diff[pool][objset]["objset"] = objset 4554e8d558cSMartin Matuska diff[pool][objset]["time"] = time.strftime("%H:%M:%S", \ 456c7046f76SMartin Matuska time.localtime()) 4574e8d558cSMartin Matuska diff[pool][objset]["icc+inc"] = \ 4584e8d558cSMartin Matuska diff[pool][objset]["zil_itx_copied_count"] + \ 4594e8d558cSMartin Matuska diff[pool][objset]["zil_itx_needcopy_count"] 4604e8d558cSMartin Matuska diff[pool][objset]["icb+inb"] = \ 4614e8d558cSMartin Matuska diff[pool][objset]["zil_itx_copied_bytes"] + \ 4624e8d558cSMartin Matuska diff[pool][objset]["zil_itx_needcopy_bytes"] 4634e8d558cSMartin Matuska diff[pool][objset]["iic+idc"] = \ 4644e8d558cSMartin Matuska diff[pool][objset]["zil_itx_indirect_count"] + \ 4654e8d558cSMartin Matuska diff[pool][objset]["zil_itx_copied_count"] + \ 4664e8d558cSMartin Matuska diff[pool][objset]["zil_itx_needcopy_count"] 4674e8d558cSMartin Matuska diff[pool][objset]["iib+idb"] = \ 4684e8d558cSMartin Matuska diff[pool][objset]["zil_itx_indirect_bytes"] + \ 4694e8d558cSMartin Matuska diff[pool][objset]["zil_itx_copied_bytes"] + \ 4704e8d558cSMartin Matuska diff[pool][objset]["zil_itx_needcopy_bytes"] 4714e8d558cSMartin Matuska diff[pool][objset]["imnc+imsc"] = \ 4724e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_normal_count"] + \ 4734e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_slog_count"] 4744e8d558cSMartin Matuska diff[pool][objset]["imnb+imsb"] = \ 4754e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_normal_bytes"] + \ 4764e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_slog_bytes"] 4774e8d558cSMartin Matuska diff[pool][objset]["imnw+imsw"] = \ 4784e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_normal_write"] + \ 4794e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_slog_write"] 4804e8d558cSMartin Matuska diff[pool][objset]["imna+imsa"] = \ 4814e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_normal_alloc"] + \ 4824e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_slog_alloc"] 4834e8d558cSMartin Matuska if diff[pool][objset]["imna+imsa"] > 0: 4844e8d558cSMartin Matuska diff[pool][objset]["imb/ima"] = 100 * \ 4854e8d558cSMartin Matuska diff[pool][objset]["imnb+imsb"] // \ 4864e8d558cSMartin Matuska diff[pool][objset]["imna+imsa"] 4874e8d558cSMartin Matuska else: 4884e8d558cSMartin Matuska diff[pool][objset]["imb/ima"] = 100 4894e8d558cSMartin Matuska if diff[pool][objset]["zil_itx_metaslab_normal_alloc"] > 0: 4904e8d558cSMartin Matuska diff[pool][objset]["imnb/imna"] = 100 * \ 4914e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_normal_bytes"] // \ 4924e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_normal_alloc"] 4934e8d558cSMartin Matuska else: 4944e8d558cSMartin Matuska diff[pool][objset]["imnb/imna"] = 100 4954e8d558cSMartin Matuska if diff[pool][objset]["zil_itx_metaslab_slog_alloc"] > 0: 4964e8d558cSMartin Matuska diff[pool][objset]["imsb/imsa"] = 100 * \ 4974e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_slog_bytes"] // \ 4984e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_slog_alloc"] 4994e8d558cSMartin Matuska else: 5004e8d558cSMartin Matuska diff[pool][objset]["imsb/imsa"] = 100 5014e8d558cSMartin Matuska if diff[pool][objset]["imnw+imsw"] > 0: 5024e8d558cSMartin Matuska diff[pool][objset]["imb/imw"] = 100 * \ 5034e8d558cSMartin Matuska diff[pool][objset]["imnb+imsb"] // \ 5044e8d558cSMartin Matuska diff[pool][objset]["imnw+imsw"] 5054e8d558cSMartin Matuska else: 5064e8d558cSMartin Matuska diff[pool][objset]["imb/imw"] = 100 5074e8d558cSMartin Matuska if diff[pool][objset]["zil_itx_metaslab_normal_alloc"] > 0: 5084e8d558cSMartin Matuska diff[pool][objset]["imnb/imnw"] = 100 * \ 5094e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_normal_bytes"] // \ 5104e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_normal_write"] 5114e8d558cSMartin Matuska else: 5124e8d558cSMartin Matuska diff[pool][objset]["imnb/imnw"] = 100 5134e8d558cSMartin Matuska if diff[pool][objset]["zil_itx_metaslab_slog_alloc"] > 0: 5144e8d558cSMartin Matuska diff[pool][objset]["imsb/imsw"] = 100 * \ 5154e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_slog_bytes"] // \ 5164e8d558cSMartin Matuska diff[pool][objset]["zil_itx_metaslab_slog_write"] 5174e8d558cSMartin Matuska else: 5184e8d558cSMartin Matuska diff[pool][objset]["imsb/imsw"] = 100 519c7046f76SMartin Matuska 520c7046f76SMartin Matuskadef sign_handler_epipe(sig, frame): 521c7046f76SMartin Matuska print("Caught EPIPE signal: " + str(frame)) 522c7046f76SMartin Matuska print("Exitting...") 523c7046f76SMartin Matuska sys.exit(0) 524c7046f76SMartin Matuska 525c7046f76SMartin Matuskadef main(): 526c7046f76SMartin Matuska global interval 5274e8d558cSMartin Matuska global curr, diff 528c7046f76SMartin Matuska hprint = False 529c7046f76SMartin Matuska init() 530c7046f76SMartin Matuska signal.signal(signal.SIGINT, signal.SIG_DFL) 531c7046f76SMartin Matuska signal.signal(signal.SIGPIPE, sign_handler_epipe) 532c7046f76SMartin Matuska 533c7046f76SMartin Matuska zil_process_kstat() 534c7046f76SMartin Matuska if not curr: 535c7046f76SMartin Matuska print ("Error: No stats to show") 536c7046f76SMartin Matuska sys.exit(0) 537c7046f76SMartin Matuska print_header() 5384e8d558cSMartin Matuska if interval > 0: 5394e8d558cSMartin Matuska time.sleep(interval) 5404e8d558cSMartin Matuska while True: 5414e8d558cSMartin Matuska calculate_diff() 5424e8d558cSMartin Matuska if not diff: 5434e8d558cSMartin Matuska print ("Error: No stats to show") 5444e8d558cSMartin Matuska sys.exit(0) 5454e8d558cSMartin Matuska zil_extend_dict() 5464e8d558cSMartin Matuska print_dict(diff) 5474e8d558cSMartin Matuska time.sleep(interval) 5484e8d558cSMartin Matuska else: 5494e8d558cSMartin Matuska diff = curr 5504e8d558cSMartin Matuska zil_extend_dict() 5514e8d558cSMartin Matuska print_dict(diff) 552c7046f76SMartin Matuska 553c7046f76SMartin Matuskaif __name__ == '__main__': 554c7046f76SMartin Matuska main() 555c7046f76SMartin Matuska 556