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