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