1#!/usr/bin/env python2 2# 3# $Id: arc_summary.pl,v 388:e27800740aa2 2011-07-08 02:53:29Z jhell $ 4# 5# Copyright (c) 2008 Ben Rockwood <[email protected]>, 6# Copyright (c) 2010 Martin Matuska <[email protected]>, 7# Copyright (c) 2010-2011 Jason J. Hellenthal <[email protected]>, 8# All rights reserved. 9# 10# Redistribution and use in source and binary forms, with or without 11# modification, are permitted provided that the following conditions 12# are met: 13# 14# 1. Redistributions of source code must retain the above copyright 15# notice, this list of conditions and the following disclaimer. 16# 2. Redistributions in binary form must reproduce the above copyright 17# notice, this list of conditions and the following disclaimer in the 18# documentation and/or other materials provided with the distribution. 19# 20# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30# SUCH DAMAGE. 31# 32# If you are having troubles when using this script from cron(8) please try 33# adjusting your PATH before reporting problems. 34# 35# Note some of this code uses older code (eg getopt instead of argparse, 36# subprocess.Popen() instead of subprocess.run()) because we need to support 37# some very old versions of Python. 38# 39 40"""Print statistics on the ZFS Adjustable Replacement Cache (ARC) 41 42Provides basic information on the ARC, its efficiency, the L2ARC (if present), 43the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See the 44in-source documentation and code at 45https://github.com/openzfs/zfs/blob/master/module/zfs/arc.c for details. 46""" 47 48import getopt 49import os 50import sys 51import time 52import errno 53 54from subprocess import Popen, PIPE 55from decimal import Decimal as D 56 57 58if sys.platform.startswith('freebsd'): 59 # Requires py27-sysctl on FreeBSD 60 import sysctl 61 62 def is_value(ctl): 63 return ctl.type != sysctl.CTLTYPE_NODE 64 65 def load_kstats(namespace): 66 """Collect information on a specific subsystem of the ARC""" 67 68 base = 'kstat.zfs.misc.%s.' % namespace 69 fmt = lambda kstat: (kstat.name, D(kstat.value)) 70 kstats = sysctl.filter(base) 71 return [fmt(kstat) for kstat in kstats if is_value(kstat)] 72 73 def load_tunables(): 74 ctls = sysctl.filter('vfs.zfs') 75 return dict((ctl.name, ctl.value) for ctl in ctls if is_value(ctl)) 76 77elif sys.platform.startswith('linux'): 78 79 def load_kstats(namespace): 80 """Collect information on a specific subsystem of the ARC""" 81 82 kstat = 'kstat.zfs.misc.%s.%%s' % namespace 83 path = '/proc/spl/kstat/zfs/%s' % namespace 84 with open(path) as f: 85 entries = [line.strip().split() for line in f][2:] # Skip header 86 return [(kstat % name, D(value)) for name, _, value in entries] 87 88 def load_tunables(): 89 basepath = '/sys/module/zfs/parameters' 90 tunables = {} 91 for name in os.listdir(basepath): 92 if not name: 93 continue 94 path = '%s/%s' % (basepath, name) 95 with open(path) as f: 96 value = f.read() 97 tunables[name] = value.strip() 98 return tunables 99 100 101show_tunable_descriptions = False 102alternate_tunable_layout = False 103 104 105def handle_Exception(ex_cls, ex, tb): 106 if ex is IOError: 107 if ex.errno == errno.EPIPE: 108 sys.exit() 109 110 if ex is KeyboardInterrupt: 111 sys.exit() 112 113 114sys.excepthook = handle_Exception 115 116 117def get_Kstat(): 118 """Collect information on the ZFS subsystem from the /proc virtual 119 file system. The name "kstat" is a holdover from the Solaris utility 120 of the same name. 121 """ 122 123 Kstat = {} 124 Kstat.update(load_kstats('arcstats')) 125 Kstat.update(load_kstats('zfetchstats')) 126 Kstat.update(load_kstats('vdev_cache_stats')) 127 return Kstat 128 129 130def fBytes(b=0): 131 """Return human-readable representation of a byte value in 132 powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal 133 points. Values smaller than one KiB are returned without 134 decimal points. 135 """ 136 137 prefixes = [ 138 [2**80, "YiB"], # yobibytes (yotta) 139 [2**70, "ZiB"], # zebibytes (zetta) 140 [2**60, "EiB"], # exbibytes (exa) 141 [2**50, "PiB"], # pebibytes (peta) 142 [2**40, "TiB"], # tebibytes (tera) 143 [2**30, "GiB"], # gibibytes (giga) 144 [2**20, "MiB"], # mebibytes (mega) 145 [2**10, "KiB"]] # kibibytes (kilo) 146 147 if b >= 2**10: 148 149 for limit, unit in prefixes: 150 151 if b >= limit: 152 value = b / limit 153 break 154 155 result = "%0.2f\t%s" % (value, unit) 156 157 else: 158 159 result = "%d\tBytes" % b 160 161 return result 162 163 164def fHits(hits=0): 165 """Create a human-readable representation of the number of hits. 166 The single-letter symbols used are SI to avoid the confusion caused 167 by the different "short scale" and "long scale" representations in 168 English, which use the same words for different values. See 169 https://en.wikipedia.org/wiki/Names_of_large_numbers and 170 https://physics.nist.gov/cuu/Units/prefixes.html 171 """ 172 173 numbers = [ 174 [10**24, 'Y'], # yotta (septillion) 175 [10**21, 'Z'], # zetta (sextillion) 176 [10**18, 'E'], # exa (quintrillion) 177 [10**15, 'P'], # peta (quadrillion) 178 [10**12, 'T'], # tera (trillion) 179 [10**9, 'G'], # giga (billion) 180 [10**6, 'M'], # mega (million) 181 [10**3, 'k']] # kilo (thousand) 182 183 if hits >= 1000: 184 185 for limit, symbol in numbers: 186 187 if hits >= limit: 188 value = hits/limit 189 break 190 191 result = "%0.2f%s" % (value, symbol) 192 193 else: 194 195 result = "%d" % hits 196 197 return result 198 199 200def fPerc(lVal=0, rVal=0, Decimal=2): 201 """Calculate percentage value and return in human-readable format""" 202 203 if rVal > 0: 204 return str("%0." + str(Decimal) + "f") % (100 * (lVal / rVal)) + "%" 205 else: 206 return str("%0." + str(Decimal) + "f") % 100 + "%" 207 208 209def get_arc_summary(Kstat): 210 """Collect general data on the ARC""" 211 212 output = {} 213 memory_throttle_count = Kstat[ 214 "kstat.zfs.misc.arcstats.memory_throttle_count" 215 ] 216 217 if memory_throttle_count > 0: 218 output['health'] = 'THROTTLED' 219 else: 220 output['health'] = 'HEALTHY' 221 222 output['memory_throttle_count'] = fHits(memory_throttle_count) 223 224 # ARC Misc. 225 deleted = Kstat["kstat.zfs.misc.arcstats.deleted"] 226 mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"] 227 evict_skip = Kstat["kstat.zfs.misc.arcstats.evict_skip"] 228 evict_l2_cached = Kstat["kstat.zfs.misc.arcstats.evict_l2_cached"] 229 evict_l2_eligible = Kstat["kstat.zfs.misc.arcstats.evict_l2_eligible"] 230 evict_l2_eligible_mfu = Kstat["kstat.zfs.misc.arcstats.evict_l2_eligible_mfu"] 231 evict_l2_eligible_mru = Kstat["kstat.zfs.misc.arcstats.evict_l2_eligible_mru"] 232 evict_l2_ineligible = Kstat["kstat.zfs.misc.arcstats.evict_l2_ineligible"] 233 evict_l2_skip = Kstat["kstat.zfs.misc.arcstats.evict_l2_skip"] 234 235 # ARC Misc. 236 output["arc_misc"] = {} 237 output["arc_misc"]["deleted"] = fHits(deleted) 238 output["arc_misc"]["mutex_miss"] = fHits(mutex_miss) 239 output["arc_misc"]["evict_skips"] = fHits(evict_skip) 240 output["arc_misc"]["evict_l2_skip"] = fHits(evict_l2_skip) 241 output["arc_misc"]["evict_l2_cached"] = fBytes(evict_l2_cached) 242 output["arc_misc"]["evict_l2_eligible"] = fBytes(evict_l2_eligible) 243 output["arc_misc"]["evict_l2_eligible_mfu"] = { 244 'per': fPerc(evict_l2_eligible_mfu, evict_l2_eligible), 245 'num': fBytes(evict_l2_eligible_mfu), 246 } 247 output["arc_misc"]["evict_l2_eligible_mru"] = { 248 'per': fPerc(evict_l2_eligible_mru, evict_l2_eligible), 249 'num': fBytes(evict_l2_eligible_mru), 250 } 251 output["arc_misc"]["evict_l2_ineligible"] = fBytes(evict_l2_ineligible) 252 253 # ARC Sizing 254 arc_size = Kstat["kstat.zfs.misc.arcstats.size"] 255 mru_size = Kstat["kstat.zfs.misc.arcstats.mru_size"] 256 mfu_size = Kstat["kstat.zfs.misc.arcstats.mfu_size"] 257 meta_limit = Kstat["kstat.zfs.misc.arcstats.arc_meta_limit"] 258 meta_size = Kstat["kstat.zfs.misc.arcstats.arc_meta_used"] 259 dnode_limit = Kstat["kstat.zfs.misc.arcstats.arc_dnode_limit"] 260 dnode_size = Kstat["kstat.zfs.misc.arcstats.dnode_size"] 261 target_max_size = Kstat["kstat.zfs.misc.arcstats.c_max"] 262 target_min_size = Kstat["kstat.zfs.misc.arcstats.c_min"] 263 target_size = Kstat["kstat.zfs.misc.arcstats.c"] 264 265 target_size_ratio = (target_max_size / target_min_size) 266 267 # ARC Sizing 268 output['arc_sizing'] = {} 269 output['arc_sizing']['arc_size'] = { 270 'per': fPerc(arc_size, target_max_size), 271 'num': fBytes(arc_size), 272 } 273 output['arc_sizing']['target_max_size'] = { 274 'ratio': target_size_ratio, 275 'num': fBytes(target_max_size), 276 } 277 output['arc_sizing']['target_min_size'] = { 278 'per': fPerc(target_min_size, target_max_size), 279 'num': fBytes(target_min_size), 280 } 281 output['arc_sizing']['target_size'] = { 282 'per': fPerc(target_size, target_max_size), 283 'num': fBytes(target_size), 284 } 285 output['arc_sizing']['meta_limit'] = { 286 'per': fPerc(meta_limit, target_max_size), 287 'num': fBytes(meta_limit), 288 } 289 output['arc_sizing']['meta_size'] = { 290 'per': fPerc(meta_size, meta_limit), 291 'num': fBytes(meta_size), 292 } 293 output['arc_sizing']['dnode_limit'] = { 294 'per': fPerc(dnode_limit, meta_limit), 295 'num': fBytes(dnode_limit), 296 } 297 output['arc_sizing']['dnode_size'] = { 298 'per': fPerc(dnode_size, dnode_limit), 299 'num': fBytes(dnode_size), 300 } 301 302 # ARC Hash Breakdown 303 output['arc_hash_break'] = {} 304 output['arc_hash_break']['hash_chain_max'] = Kstat[ 305 "kstat.zfs.misc.arcstats.hash_chain_max" 306 ] 307 output['arc_hash_break']['hash_chains'] = Kstat[ 308 "kstat.zfs.misc.arcstats.hash_chains" 309 ] 310 output['arc_hash_break']['hash_collisions'] = Kstat[ 311 "kstat.zfs.misc.arcstats.hash_collisions" 312 ] 313 output['arc_hash_break']['hash_elements'] = Kstat[ 314 "kstat.zfs.misc.arcstats.hash_elements" 315 ] 316 output['arc_hash_break']['hash_elements_max'] = Kstat[ 317 "kstat.zfs.misc.arcstats.hash_elements_max" 318 ] 319 320 output['arc_size_break'] = {} 321 output['arc_size_break']['recently_used_cache_size'] = { 322 'per': fPerc(mru_size, mru_size + mfu_size), 323 'num': fBytes(mru_size), 324 } 325 output['arc_size_break']['frequently_used_cache_size'] = { 326 'per': fPerc(mfu_size, mru_size + mfu_size), 327 'num': fBytes(mfu_size), 328 } 329 330 # ARC Hash Breakdown 331 hash_chain_max = Kstat["kstat.zfs.misc.arcstats.hash_chain_max"] 332 hash_chains = Kstat["kstat.zfs.misc.arcstats.hash_chains"] 333 hash_collisions = Kstat["kstat.zfs.misc.arcstats.hash_collisions"] 334 hash_elements = Kstat["kstat.zfs.misc.arcstats.hash_elements"] 335 hash_elements_max = Kstat["kstat.zfs.misc.arcstats.hash_elements_max"] 336 337 output['arc_hash_break'] = {} 338 output['arc_hash_break']['elements_max'] = fHits(hash_elements_max) 339 output['arc_hash_break']['elements_current'] = { 340 'per': fPerc(hash_elements, hash_elements_max), 341 'num': fHits(hash_elements), 342 } 343 output['arc_hash_break']['collisions'] = fHits(hash_collisions) 344 output['arc_hash_break']['chain_max'] = fHits(hash_chain_max) 345 output['arc_hash_break']['chains'] = fHits(hash_chains) 346 347 return output 348 349 350def _arc_summary(Kstat): 351 """Print information on the ARC""" 352 353 # ARC Sizing 354 arc = get_arc_summary(Kstat) 355 356 sys.stdout.write("ARC Summary: (%s)\n" % arc['health']) 357 358 sys.stdout.write("\tMemory Throttle Count:\t\t\t%s\n" % 359 arc['memory_throttle_count']) 360 sys.stdout.write("\n") 361 362 # ARC Misc. 363 sys.stdout.write("ARC Misc:\n") 364 sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted']) 365 sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" % 366 arc['arc_misc']['mutex_miss']) 367 sys.stdout.write("\tEviction Skips:\t\t\t\t%s\n" % 368 arc['arc_misc']['evict_skips']) 369 sys.stdout.write("\tEviction Skips Due to L2 Writes:\t%s\n" % 370 arc['arc_misc']['evict_l2_skip']) 371 sys.stdout.write("\tL2 Cached Evictions:\t\t\t%s\n" % 372 arc['arc_misc']['evict_l2_cached']) 373 sys.stdout.write("\tL2 Eligible Evictions:\t\t\t%s\n" % 374 arc['arc_misc']['evict_l2_eligible']) 375 sys.stdout.write("\tL2 Eligible MFU Evictions:\t%s\t%s\n" % ( 376 arc['arc_misc']['evict_l2_eligible_mfu']['per'], 377 arc['arc_misc']['evict_l2_eligible_mfu']['num'], 378 ) 379 ) 380 sys.stdout.write("\tL2 Eligible MRU Evictions:\t%s\t%s\n" % ( 381 arc['arc_misc']['evict_l2_eligible_mru']['per'], 382 arc['arc_misc']['evict_l2_eligible_mru']['num'], 383 ) 384 ) 385 sys.stdout.write("\tL2 Ineligible Evictions:\t\t%s\n" % 386 arc['arc_misc']['evict_l2_ineligible']) 387 sys.stdout.write("\n") 388 389 # ARC Sizing 390 sys.stdout.write("ARC Size:\t\t\t\t%s\t%s\n" % ( 391 arc['arc_sizing']['arc_size']['per'], 392 arc['arc_sizing']['arc_size']['num'] 393 ) 394 ) 395 sys.stdout.write("\tTarget Size: (Adaptive)\t\t%s\t%s\n" % ( 396 arc['arc_sizing']['target_size']['per'], 397 arc['arc_sizing']['target_size']['num'], 398 ) 399 ) 400 401 sys.stdout.write("\tMin Size (Hard Limit):\t\t%s\t%s\n" % ( 402 arc['arc_sizing']['target_min_size']['per'], 403 arc['arc_sizing']['target_min_size']['num'], 404 ) 405 ) 406 407 sys.stdout.write("\tMax Size (High Water):\t\t%d:1\t%s\n" % ( 408 arc['arc_sizing']['target_max_size']['ratio'], 409 arc['arc_sizing']['target_max_size']['num'], 410 ) 411 ) 412 413 sys.stdout.write("\nARC Size Breakdown:\n") 414 sys.stdout.write("\tRecently Used Cache Size:\t%s\t%s\n" % ( 415 arc['arc_size_break']['recently_used_cache_size']['per'], 416 arc['arc_size_break']['recently_used_cache_size']['num'], 417 ) 418 ) 419 sys.stdout.write("\tFrequently Used Cache Size:\t%s\t%s\n" % ( 420 arc['arc_size_break']['frequently_used_cache_size']['per'], 421 arc['arc_size_break']['frequently_used_cache_size']['num'], 422 ) 423 ) 424 sys.stdout.write("\tMetadata Size (Hard Limit):\t%s\t%s\n" % ( 425 arc['arc_sizing']['meta_limit']['per'], 426 arc['arc_sizing']['meta_limit']['num'], 427 ) 428 ) 429 sys.stdout.write("\tMetadata Size:\t\t\t%s\t%s\n" % ( 430 arc['arc_sizing']['meta_size']['per'], 431 arc['arc_sizing']['meta_size']['num'], 432 ) 433 ) 434 sys.stdout.write("\tDnode Size (Hard Limit):\t%s\t%s\n" % ( 435 arc['arc_sizing']['dnode_limit']['per'], 436 arc['arc_sizing']['dnode_limit']['num'], 437 ) 438 ) 439 sys.stdout.write("\tDnode Size:\t\t\t%s\t%s\n" % ( 440 arc['arc_sizing']['dnode_size']['per'], 441 arc['arc_sizing']['dnode_size']['num'], 442 ) 443 ) 444 445 sys.stdout.write("\n") 446 447 # ARC Hash Breakdown 448 sys.stdout.write("ARC Hash Breakdown:\n") 449 sys.stdout.write("\tElements Max:\t\t\t\t%s\n" % 450 arc['arc_hash_break']['elements_max']) 451 sys.stdout.write("\tElements Current:\t\t%s\t%s\n" % ( 452 arc['arc_hash_break']['elements_current']['per'], 453 arc['arc_hash_break']['elements_current']['num'], 454 ) 455 ) 456 sys.stdout.write("\tCollisions:\t\t\t\t%s\n" % 457 arc['arc_hash_break']['collisions']) 458 sys.stdout.write("\tChain Max:\t\t\t\t%s\n" % 459 arc['arc_hash_break']['chain_max']) 460 sys.stdout.write("\tChains:\t\t\t\t\t%s\n" % 461 arc['arc_hash_break']['chains']) 462 463 464def get_arc_efficiency(Kstat): 465 """Collect information on the efficiency of the ARC""" 466 467 output = {} 468 469 arc_hits = Kstat["kstat.zfs.misc.arcstats.hits"] 470 arc_misses = Kstat["kstat.zfs.misc.arcstats.misses"] 471 demand_data_hits = Kstat["kstat.zfs.misc.arcstats.demand_data_hits"] 472 demand_data_misses = Kstat["kstat.zfs.misc.arcstats.demand_data_misses"] 473 demand_metadata_hits = Kstat[ 474 "kstat.zfs.misc.arcstats.demand_metadata_hits" 475 ] 476 demand_metadata_misses = Kstat[ 477 "kstat.zfs.misc.arcstats.demand_metadata_misses" 478 ] 479 mfu_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mfu_ghost_hits"] 480 mfu_hits = Kstat["kstat.zfs.misc.arcstats.mfu_hits"] 481 mru_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mru_ghost_hits"] 482 mru_hits = Kstat["kstat.zfs.misc.arcstats.mru_hits"] 483 prefetch_data_hits = Kstat["kstat.zfs.misc.arcstats.prefetch_data_hits"] 484 prefetch_data_misses = Kstat[ 485 "kstat.zfs.misc.arcstats.prefetch_data_misses" 486 ] 487 prefetch_metadata_hits = Kstat[ 488 "kstat.zfs.misc.arcstats.prefetch_metadata_hits" 489 ] 490 prefetch_metadata_misses = Kstat[ 491 "kstat.zfs.misc.arcstats.prefetch_metadata_misses" 492 ] 493 494 anon_hits = arc_hits - ( 495 mfu_hits + mru_hits + mfu_ghost_hits + mru_ghost_hits 496 ) 497 arc_accesses_total = (arc_hits + arc_misses) 498 demand_data_total = (demand_data_hits + demand_data_misses) 499 prefetch_data_total = (prefetch_data_hits + prefetch_data_misses) 500 real_hits = (mfu_hits + mru_hits) 501 502 output["total_accesses"] = fHits(arc_accesses_total) 503 output["cache_hit_ratio"] = { 504 'per': fPerc(arc_hits, arc_accesses_total), 505 'num': fHits(arc_hits), 506 } 507 output["cache_miss_ratio"] = { 508 'per': fPerc(arc_misses, arc_accesses_total), 509 'num': fHits(arc_misses), 510 } 511 output["actual_hit_ratio"] = { 512 'per': fPerc(real_hits, arc_accesses_total), 513 'num': fHits(real_hits), 514 } 515 output["data_demand_efficiency"] = { 516 'per': fPerc(demand_data_hits, demand_data_total), 517 'num': fHits(demand_data_total), 518 } 519 520 if prefetch_data_total > 0: 521 output["data_prefetch_efficiency"] = { 522 'per': fPerc(prefetch_data_hits, prefetch_data_total), 523 'num': fHits(prefetch_data_total), 524 } 525 526 if anon_hits > 0: 527 output["cache_hits_by_cache_list"] = {} 528 output["cache_hits_by_cache_list"]["anonymously_used"] = { 529 'per': fPerc(anon_hits, arc_hits), 530 'num': fHits(anon_hits), 531 } 532 533 output["most_recently_used"] = { 534 'per': fPerc(mru_hits, arc_hits), 535 'num': fHits(mru_hits), 536 } 537 output["most_frequently_used"] = { 538 'per': fPerc(mfu_hits, arc_hits), 539 'num': fHits(mfu_hits), 540 } 541 output["most_recently_used_ghost"] = { 542 'per': fPerc(mru_ghost_hits, arc_hits), 543 'num': fHits(mru_ghost_hits), 544 } 545 output["most_frequently_used_ghost"] = { 546 'per': fPerc(mfu_ghost_hits, arc_hits), 547 'num': fHits(mfu_ghost_hits), 548 } 549 550 output["cache_hits_by_data_type"] = {} 551 output["cache_hits_by_data_type"]["demand_data"] = { 552 'per': fPerc(demand_data_hits, arc_hits), 553 'num': fHits(demand_data_hits), 554 } 555 output["cache_hits_by_data_type"]["prefetch_data"] = { 556 'per': fPerc(prefetch_data_hits, arc_hits), 557 'num': fHits(prefetch_data_hits), 558 } 559 output["cache_hits_by_data_type"]["demand_metadata"] = { 560 'per': fPerc(demand_metadata_hits, arc_hits), 561 'num': fHits(demand_metadata_hits), 562 } 563 output["cache_hits_by_data_type"]["prefetch_metadata"] = { 564 'per': fPerc(prefetch_metadata_hits, arc_hits), 565 'num': fHits(prefetch_metadata_hits), 566 } 567 568 output["cache_misses_by_data_type"] = {} 569 output["cache_misses_by_data_type"]["demand_data"] = { 570 'per': fPerc(demand_data_misses, arc_misses), 571 'num': fHits(demand_data_misses), 572 } 573 output["cache_misses_by_data_type"]["prefetch_data"] = { 574 'per': fPerc(prefetch_data_misses, arc_misses), 575 'num': fHits(prefetch_data_misses), 576 } 577 output["cache_misses_by_data_type"]["demand_metadata"] = { 578 'per': fPerc(demand_metadata_misses, arc_misses), 579 'num': fHits(demand_metadata_misses), 580 } 581 output["cache_misses_by_data_type"]["prefetch_metadata"] = { 582 'per': fPerc(prefetch_metadata_misses, arc_misses), 583 'num': fHits(prefetch_metadata_misses), 584 } 585 586 return output 587 588 589def _arc_efficiency(Kstat): 590 """Print information on the efficiency of the ARC""" 591 592 arc = get_arc_efficiency(Kstat) 593 594 sys.stdout.write("ARC Total accesses:\t\t\t\t\t%s\n" % 595 arc['total_accesses']) 596 sys.stdout.write("\tCache Hit Ratio:\t\t%s\t%s\n" % ( 597 arc['cache_hit_ratio']['per'], 598 arc['cache_hit_ratio']['num'], 599 ) 600 ) 601 sys.stdout.write("\tCache Miss Ratio:\t\t%s\t%s\n" % ( 602 arc['cache_miss_ratio']['per'], 603 arc['cache_miss_ratio']['num'], 604 ) 605 ) 606 607 sys.stdout.write("\tActual Hit Ratio:\t\t%s\t%s\n" % ( 608 arc['actual_hit_ratio']['per'], 609 arc['actual_hit_ratio']['num'], 610 ) 611 ) 612 613 sys.stdout.write("\n") 614 sys.stdout.write("\tData Demand Efficiency:\t\t%s\t%s\n" % ( 615 arc['data_demand_efficiency']['per'], 616 arc['data_demand_efficiency']['num'], 617 ) 618 ) 619 620 if 'data_prefetch_efficiency' in arc: 621 sys.stdout.write("\tData Prefetch Efficiency:\t%s\t%s\n" % ( 622 arc['data_prefetch_efficiency']['per'], 623 arc['data_prefetch_efficiency']['num'], 624 ) 625 ) 626 sys.stdout.write("\n") 627 628 sys.stdout.write("\tCACHE HITS BY CACHE LIST:\n") 629 if 'cache_hits_by_cache_list' in arc: 630 sys.stdout.write("\t Anonymously Used:\t\t%s\t%s\n" % ( 631 arc['cache_hits_by_cache_list']['anonymously_used']['per'], 632 arc['cache_hits_by_cache_list']['anonymously_used']['num'], 633 ) 634 ) 635 sys.stdout.write("\t Most Recently Used:\t\t%s\t%s\n" % ( 636 arc['most_recently_used']['per'], 637 arc['most_recently_used']['num'], 638 ) 639 ) 640 sys.stdout.write("\t Most Frequently Used:\t\t%s\t%s\n" % ( 641 arc['most_frequently_used']['per'], 642 arc['most_frequently_used']['num'], 643 ) 644 ) 645 sys.stdout.write("\t Most Recently Used Ghost:\t%s\t%s\n" % ( 646 arc['most_recently_used_ghost']['per'], 647 arc['most_recently_used_ghost']['num'], 648 ) 649 ) 650 sys.stdout.write("\t Most Frequently Used Ghost:\t%s\t%s\n" % ( 651 arc['most_frequently_used_ghost']['per'], 652 arc['most_frequently_used_ghost']['num'], 653 ) 654 ) 655 656 sys.stdout.write("\n\tCACHE HITS BY DATA TYPE:\n") 657 sys.stdout.write("\t Demand Data:\t\t\t%s\t%s\n" % ( 658 arc["cache_hits_by_data_type"]['demand_data']['per'], 659 arc["cache_hits_by_data_type"]['demand_data']['num'], 660 ) 661 ) 662 sys.stdout.write("\t Prefetch Data:\t\t%s\t%s\n" % ( 663 arc["cache_hits_by_data_type"]['prefetch_data']['per'], 664 arc["cache_hits_by_data_type"]['prefetch_data']['num'], 665 ) 666 ) 667 sys.stdout.write("\t Demand Metadata:\t\t%s\t%s\n" % ( 668 arc["cache_hits_by_data_type"]['demand_metadata']['per'], 669 arc["cache_hits_by_data_type"]['demand_metadata']['num'], 670 ) 671 ) 672 sys.stdout.write("\t Prefetch Metadata:\t\t%s\t%s\n" % ( 673 arc["cache_hits_by_data_type"]['prefetch_metadata']['per'], 674 arc["cache_hits_by_data_type"]['prefetch_metadata']['num'], 675 ) 676 ) 677 678 sys.stdout.write("\n\tCACHE MISSES BY DATA TYPE:\n") 679 sys.stdout.write("\t Demand Data:\t\t\t%s\t%s\n" % ( 680 arc["cache_misses_by_data_type"]['demand_data']['per'], 681 arc["cache_misses_by_data_type"]['demand_data']['num'], 682 ) 683 ) 684 sys.stdout.write("\t Prefetch Data:\t\t%s\t%s\n" % ( 685 arc["cache_misses_by_data_type"]['prefetch_data']['per'], 686 arc["cache_misses_by_data_type"]['prefetch_data']['num'], 687 ) 688 ) 689 sys.stdout.write("\t Demand Metadata:\t\t%s\t%s\n" % ( 690 arc["cache_misses_by_data_type"]['demand_metadata']['per'], 691 arc["cache_misses_by_data_type"]['demand_metadata']['num'], 692 ) 693 ) 694 sys.stdout.write("\t Prefetch Metadata:\t\t%s\t%s\n" % ( 695 arc["cache_misses_by_data_type"]['prefetch_metadata']['per'], 696 arc["cache_misses_by_data_type"]['prefetch_metadata']['num'], 697 ) 698 ) 699 700 701def get_l2arc_summary(Kstat): 702 """Collection information on the L2ARC""" 703 704 output = {} 705 706 l2_abort_lowmem = Kstat["kstat.zfs.misc.arcstats.l2_abort_lowmem"] 707 l2_cksum_bad = Kstat["kstat.zfs.misc.arcstats.l2_cksum_bad"] 708 l2_evict_lock_retry = Kstat["kstat.zfs.misc.arcstats.l2_evict_lock_retry"] 709 l2_evict_reading = Kstat["kstat.zfs.misc.arcstats.l2_evict_reading"] 710 l2_feeds = Kstat["kstat.zfs.misc.arcstats.l2_feeds"] 711 l2_free_on_write = Kstat["kstat.zfs.misc.arcstats.l2_free_on_write"] 712 l2_hdr_size = Kstat["kstat.zfs.misc.arcstats.l2_hdr_size"] 713 l2_hits = Kstat["kstat.zfs.misc.arcstats.l2_hits"] 714 l2_io_error = Kstat["kstat.zfs.misc.arcstats.l2_io_error"] 715 l2_misses = Kstat["kstat.zfs.misc.arcstats.l2_misses"] 716 l2_rw_clash = Kstat["kstat.zfs.misc.arcstats.l2_rw_clash"] 717 l2_size = Kstat["kstat.zfs.misc.arcstats.l2_size"] 718 l2_asize = Kstat["kstat.zfs.misc.arcstats.l2_asize"] 719 l2_writes_done = Kstat["kstat.zfs.misc.arcstats.l2_writes_done"] 720 l2_writes_error = Kstat["kstat.zfs.misc.arcstats.l2_writes_error"] 721 l2_writes_sent = Kstat["kstat.zfs.misc.arcstats.l2_writes_sent"] 722 l2_mfu_asize = Kstat["kstat.zfs.misc.arcstats.l2_mfu_asize"] 723 l2_mru_asize = Kstat["kstat.zfs.misc.arcstats.l2_mru_asize"] 724 l2_prefetch_asize = Kstat["kstat.zfs.misc.arcstats.l2_prefetch_asize"] 725 l2_bufc_data_asize = Kstat["kstat.zfs.misc.arcstats.l2_bufc_data_asize"] 726 l2_bufc_metadata_asize = Kstat["kstat.zfs.misc.arcstats.l2_bufc_metadata_asize"] 727 728 l2_access_total = (l2_hits + l2_misses) 729 output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error) 730 731 output['l2_access_total'] = l2_access_total 732 output['l2_size'] = l2_size 733 output['l2_asize'] = l2_asize 734 735 if l2_size > 0 and l2_access_total > 0: 736 737 if output['l2_health_count'] > 0: 738 output["health"] = "DEGRADED" 739 else: 740 output["health"] = "HEALTHY" 741 742 output["low_memory_aborts"] = fHits(l2_abort_lowmem) 743 output["free_on_write"] = fHits(l2_free_on_write) 744 output["rw_clashes"] = fHits(l2_rw_clash) 745 output["bad_checksums"] = fHits(l2_cksum_bad) 746 output["io_errors"] = fHits(l2_io_error) 747 748 output["l2_arc_size"] = {} 749 output["l2_arc_size"]["adaptive"] = fBytes(l2_size) 750 output["l2_arc_size"]["actual"] = { 751 'per': fPerc(l2_asize, l2_size), 752 'num': fBytes(l2_asize) 753 } 754 output["l2_arc_size"]["head_size"] = { 755 'per': fPerc(l2_hdr_size, l2_size), 756 'num': fBytes(l2_hdr_size), 757 } 758 output["l2_arc_size"]["mfu_asize"] = { 759 'per': fPerc(l2_mfu_asize, l2_asize), 760 'num': fBytes(l2_mfu_asize), 761 } 762 output["l2_arc_size"]["mru_asize"] = { 763 'per': fPerc(l2_mru_asize, l2_asize), 764 'num': fBytes(l2_mru_asize), 765 } 766 output["l2_arc_size"]["prefetch_asize"] = { 767 'per': fPerc(l2_prefetch_asize, l2_asize), 768 'num': fBytes(l2_prefetch_asize), 769 } 770 output["l2_arc_size"]["bufc_data_asize"] = { 771 'per': fPerc(l2_bufc_data_asize, l2_asize), 772 'num': fBytes(l2_bufc_data_asize), 773 } 774 output["l2_arc_size"]["bufc_metadata_asize"] = { 775 'per': fPerc(l2_bufc_metadata_asize, l2_asize), 776 'num': fBytes(l2_bufc_metadata_asize), 777 } 778 779 output["l2_arc_evicts"] = {} 780 output["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry) 781 output["l2_arc_evicts"]['reading'] = fHits(l2_evict_reading) 782 783 output['l2_arc_breakdown'] = {} 784 output['l2_arc_breakdown']['value'] = fHits(l2_access_total) 785 output['l2_arc_breakdown']['hit_ratio'] = { 786 'per': fPerc(l2_hits, l2_access_total), 787 'num': fHits(l2_hits), 788 } 789 output['l2_arc_breakdown']['miss_ratio'] = { 790 'per': fPerc(l2_misses, l2_access_total), 791 'num': fHits(l2_misses), 792 } 793 output['l2_arc_breakdown']['feeds'] = fHits(l2_feeds) 794 795 output['l2_arc_buffer'] = {} 796 797 output['l2_arc_writes'] = {} 798 output['l2_writes_done'] = l2_writes_done 799 output['l2_writes_sent'] = l2_writes_sent 800 if l2_writes_done != l2_writes_sent: 801 output['l2_arc_writes']['writes_sent'] = { 802 'value': "FAULTED", 803 'num': fHits(l2_writes_sent), 804 } 805 output['l2_arc_writes']['done_ratio'] = { 806 'per': fPerc(l2_writes_done, l2_writes_sent), 807 'num': fHits(l2_writes_done), 808 } 809 output['l2_arc_writes']['error_ratio'] = { 810 'per': fPerc(l2_writes_error, l2_writes_sent), 811 'num': fHits(l2_writes_error), 812 } 813 else: 814 output['l2_arc_writes']['writes_sent'] = { 815 'per': fPerc(100), 816 'num': fHits(l2_writes_sent), 817 } 818 819 return output 820 821 822def _l2arc_summary(Kstat): 823 """Print information on the L2ARC""" 824 825 arc = get_l2arc_summary(Kstat) 826 827 if arc['l2_size'] > 0 and arc['l2_access_total'] > 0: 828 sys.stdout.write("L2 ARC Summary: ") 829 if arc['l2_health_count'] > 0: 830 sys.stdout.write("(DEGRADED)\n") 831 else: 832 sys.stdout.write("(HEALTHY)\n") 833 sys.stdout.write("\tLow Memory Aborts:\t\t\t%s\n" % 834 arc['low_memory_aborts']) 835 sys.stdout.write("\tFree on Write:\t\t\t\t%s\n" % arc['free_on_write']) 836 sys.stdout.write("\tR/W Clashes:\t\t\t\t%s\n" % arc['rw_clashes']) 837 sys.stdout.write("\tBad Checksums:\t\t\t\t%s\n" % arc['bad_checksums']) 838 sys.stdout.write("\tIO Errors:\t\t\t\t%s\n" % arc['io_errors']) 839 sys.stdout.write("\n") 840 841 sys.stdout.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" % 842 arc["l2_arc_size"]["adaptive"]) 843 sys.stdout.write("\tCompressed:\t\t\t%s\t%s\n" % ( 844 arc["l2_arc_size"]["actual"]["per"], 845 arc["l2_arc_size"]["actual"]["num"], 846 ) 847 ) 848 sys.stdout.write("\tHeader Size:\t\t\t%s\t%s\n" % ( 849 arc["l2_arc_size"]["head_size"]["per"], 850 arc["l2_arc_size"]["head_size"]["num"], 851 ) 852 ) 853 sys.stdout.write("\tMFU Alloc. Size:\t\t%s\t%s\n" % ( 854 arc["l2_arc_size"]["mfu_asize"]["per"], 855 arc["l2_arc_size"]["mfu_asize"]["num"], 856 ) 857 ) 858 sys.stdout.write("\tMRU Alloc. Size:\t\t%s\t%s\n" % ( 859 arc["l2_arc_size"]["mru_asize"]["per"], 860 arc["l2_arc_size"]["mru_asize"]["num"], 861 ) 862 ) 863 sys.stdout.write("\tPrefetch Alloc. Size:\t\t%s\t%s\n" % ( 864 arc["l2_arc_size"]["prefetch_asize"]["per"], 865 arc["l2_arc_size"]["prefetch_asize"]["num"], 866 ) 867 ) 868 sys.stdout.write("\tData (buf content) Alloc. Size:\t%s\t%s\n" % ( 869 arc["l2_arc_size"]["bufc_data_asize"]["per"], 870 arc["l2_arc_size"]["bufc_data_asize"]["num"], 871 ) 872 ) 873 sys.stdout.write("\tMetadata (buf content) Size:\t%s\t%s\n" % ( 874 arc["l2_arc_size"]["bufc_metadata_asize"]["per"], 875 arc["l2_arc_size"]["bufc_metadata_asize"]["num"], 876 ) 877 ) 878 sys.stdout.write("\n") 879 880 if arc["l2_arc_evicts"]['lock_retries'] != '0' or \ 881 arc["l2_arc_evicts"]["reading"] != '0': 882 sys.stdout.write("L2 ARC Evictions:\n") 883 sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" % 884 arc["l2_arc_evicts"]['lock_retries']) 885 sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" % 886 arc["l2_arc_evicts"]["reading"]) 887 sys.stdout.write("\n") 888 889 sys.stdout.write("L2 ARC Breakdown:\t\t\t\t%s\n" % 890 arc['l2_arc_breakdown']['value']) 891 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % ( 892 arc['l2_arc_breakdown']['hit_ratio']['per'], 893 arc['l2_arc_breakdown']['hit_ratio']['num'], 894 ) 895 ) 896 897 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % ( 898 arc['l2_arc_breakdown']['miss_ratio']['per'], 899 arc['l2_arc_breakdown']['miss_ratio']['num'], 900 ) 901 ) 902 903 sys.stdout.write("\tFeeds:\t\t\t\t\t%s\n" % 904 arc['l2_arc_breakdown']['feeds']) 905 sys.stdout.write("\n") 906 907 sys.stdout.write("L2 ARC Writes:\n") 908 if arc['l2_writes_done'] != arc['l2_writes_sent']: 909 sys.stdout.write("\tWrites Sent: (%s)\t\t\t\t%s\n" % ( 910 arc['l2_arc_writes']['writes_sent']['value'], 911 arc['l2_arc_writes']['writes_sent']['num'], 912 ) 913 ) 914 sys.stdout.write("\t Done Ratio:\t\t\t%s\t%s\n" % ( 915 arc['l2_arc_writes']['done_ratio']['per'], 916 arc['l2_arc_writes']['done_ratio']['num'], 917 ) 918 ) 919 sys.stdout.write("\t Error Ratio:\t\t\t%s\t%s\n" % ( 920 arc['l2_arc_writes']['error_ratio']['per'], 921 arc['l2_arc_writes']['error_ratio']['num'], 922 ) 923 ) 924 else: 925 sys.stdout.write("\tWrites Sent:\t\t\t%s\t%s\n" % ( 926 arc['l2_arc_writes']['writes_sent']['per'], 927 arc['l2_arc_writes']['writes_sent']['num'], 928 ) 929 ) 930 931 932def get_dmu_summary(Kstat): 933 """Collect information on the DMU""" 934 935 output = {} 936 937 zfetch_hits = Kstat["kstat.zfs.misc.zfetchstats.hits"] 938 zfetch_misses = Kstat["kstat.zfs.misc.zfetchstats.misses"] 939 940 zfetch_access_total = (zfetch_hits + zfetch_misses) 941 output['zfetch_access_total'] = zfetch_access_total 942 943 if zfetch_access_total > 0: 944 output['dmu'] = {} 945 output['dmu']['efficiency'] = {} 946 output['dmu']['efficiency']['value'] = fHits(zfetch_access_total) 947 output['dmu']['efficiency']['hit_ratio'] = { 948 'per': fPerc(zfetch_hits, zfetch_access_total), 949 'num': fHits(zfetch_hits), 950 } 951 output['dmu']['efficiency']['miss_ratio'] = { 952 'per': fPerc(zfetch_misses, zfetch_access_total), 953 'num': fHits(zfetch_misses), 954 } 955 956 return output 957 958 959def _dmu_summary(Kstat): 960 """Print information on the DMU""" 961 962 arc = get_dmu_summary(Kstat) 963 964 if arc['zfetch_access_total'] > 0: 965 sys.stdout.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" % 966 arc['dmu']['efficiency']['value']) 967 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % ( 968 arc['dmu']['efficiency']['hit_ratio']['per'], 969 arc['dmu']['efficiency']['hit_ratio']['num'], 970 ) 971 ) 972 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % ( 973 arc['dmu']['efficiency']['miss_ratio']['per'], 974 arc['dmu']['efficiency']['miss_ratio']['num'], 975 ) 976 ) 977 978 sys.stdout.write("\n") 979 980 981def get_vdev_summary(Kstat): 982 """Collect information on the VDEVs""" 983 984 output = {} 985 986 vdev_cache_delegations = \ 987 Kstat["kstat.zfs.misc.vdev_cache_stats.delegations"] 988 vdev_cache_misses = Kstat["kstat.zfs.misc.vdev_cache_stats.misses"] 989 vdev_cache_hits = Kstat["kstat.zfs.misc.vdev_cache_stats.hits"] 990 vdev_cache_total = (vdev_cache_misses + vdev_cache_hits + 991 vdev_cache_delegations) 992 993 output['vdev_cache_total'] = vdev_cache_total 994 995 if vdev_cache_total > 0: 996 output['summary'] = fHits(vdev_cache_total) 997 output['hit_ratio'] = { 998 'per': fPerc(vdev_cache_hits, vdev_cache_total), 999 'num': fHits(vdev_cache_hits), 1000 } 1001 output['miss_ratio'] = { 1002 'per': fPerc(vdev_cache_misses, vdev_cache_total), 1003 'num': fHits(vdev_cache_misses), 1004 } 1005 output['delegations'] = { 1006 'per': fPerc(vdev_cache_delegations, vdev_cache_total), 1007 'num': fHits(vdev_cache_delegations), 1008 } 1009 1010 return output 1011 1012 1013def _vdev_summary(Kstat): 1014 """Print information on the VDEVs""" 1015 1016 arc = get_vdev_summary(Kstat) 1017 1018 if arc['vdev_cache_total'] > 0: 1019 sys.stdout.write("VDEV Cache Summary:\t\t\t\t%s\n" % arc['summary']) 1020 sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % ( 1021 arc['hit_ratio']['per'], 1022 arc['hit_ratio']['num'], 1023 )) 1024 sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % ( 1025 arc['miss_ratio']['per'], 1026 arc['miss_ratio']['num'], 1027 )) 1028 sys.stdout.write("\tDelegations:\t\t\t%s\t%s\n" % ( 1029 arc['delegations']['per'], 1030 arc['delegations']['num'], 1031 )) 1032 1033 1034def _tunable_summary(Kstat): 1035 """Print information on tunables, including descriptions if requested""" 1036 1037 global show_tunable_descriptions 1038 global alternate_tunable_layout 1039 1040 tunables = load_tunables() 1041 descriptions = {} 1042 1043 if show_tunable_descriptions: 1044 1045 command = ["/sbin/modinfo", "zfs", "-0"] 1046 1047 try: 1048 p = Popen(command, stdin=PIPE, stdout=PIPE, 1049 stderr=PIPE, shell=False, close_fds=True) 1050 p.wait() 1051 1052 # By default, Python 2 returns a string as the first element of the 1053 # tuple from p.communicate(), while Python 3 returns bytes which 1054 # must be decoded first. The better way to do this would be with 1055 # subprocess.run() or at least .check_output(), but this fails on 1056 # CentOS 6 because of its old version of Python 2 1057 desc = bytes.decode(p.communicate()[0]) 1058 description_list = desc.strip().split('\0') 1059 1060 if p.returncode == 0: 1061 for tunable in description_list: 1062 if tunable[0:5] == 'parm:': 1063 tunable = tunable[5:].strip() 1064 name, description = tunable.split(':', 1) 1065 if not description: 1066 description = "Description unavailable" 1067 descriptions[name] = description 1068 else: 1069 sys.stderr.write("%s: '%s' exited with code %i\n" % 1070 (sys.argv[0], command[0], p.returncode)) 1071 sys.stderr.write("Tunable descriptions will be disabled.\n") 1072 except OSError as e: 1073 sys.stderr.write("%s: Cannot run '%s': %s\n" % 1074 (sys.argv[0], command[0], e.strerror)) 1075 sys.stderr.write("Tunable descriptions will be disabled.\n") 1076 1077 sys.stdout.write("ZFS Tunables:\n") 1078 1079 if alternate_tunable_layout: 1080 fmt = "\t%s=%s\n" 1081 else: 1082 fmt = "\t%-50s%s\n" 1083 1084 for name in sorted(tunables.keys()): 1085 if show_tunable_descriptions and name in descriptions: 1086 sys.stdout.write("\t# %s\n" % descriptions[name]) 1087 1088 sys.stdout.write(fmt % (name, tunables[name])) 1089 1090 1091unSub = [ 1092 _arc_summary, 1093 _arc_efficiency, 1094 _l2arc_summary, 1095 _dmu_summary, 1096 _vdev_summary, 1097 _tunable_summary 1098] 1099 1100 1101def zfs_header(): 1102 """Print title string with date""" 1103 1104 daydate = time.strftime('%a %b %d %H:%M:%S %Y') 1105 1106 sys.stdout.write('\n'+'-'*72+'\n') 1107 sys.stdout.write('ZFS Subsystem Report\t\t\t\t%s' % daydate) 1108 sys.stdout.write('\n') 1109 1110 1111def usage(): 1112 """Print usage information""" 1113 1114 sys.stdout.write("Usage: arc_summary [-h] [-a] [-d] [-p PAGE]\n\n") 1115 sys.stdout.write("\t -h, --help : " 1116 "Print this help message and exit\n") 1117 sys.stdout.write("\t -a, --alternate : " 1118 "Show an alternate sysctl layout\n") 1119 sys.stdout.write("\t -d, --description : " 1120 "Show the sysctl descriptions\n") 1121 sys.stdout.write("\t -p PAGE, --page=PAGE : " 1122 "Select a single output page to display,\n") 1123 sys.stdout.write("\t " 1124 "should be an integer between 1 and " + 1125 str(len(unSub)) + "\n\n") 1126 sys.stdout.write("Examples:\n") 1127 sys.stdout.write("\tarc_summary -a\n") 1128 sys.stdout.write("\tarc_summary -p 4\n") 1129 sys.stdout.write("\tarc_summary -ad\n") 1130 sys.stdout.write("\tarc_summary --page=2\n") 1131 1132 1133def main(): 1134 """Main function""" 1135 1136 global show_tunable_descriptions 1137 global alternate_tunable_layout 1138 1139 try: 1140 opts, args = getopt.getopt( 1141 sys.argv[1:], 1142 "adp:h", ["alternate", "description", "page=", "help"] 1143 ) 1144 except getopt.error as e: 1145 sys.stderr.write("Error: %s\n" % e.msg) 1146 usage() 1147 sys.exit(1) 1148 1149 args = {} 1150 for opt, arg in opts: 1151 if opt in ('-a', '--alternate'): 1152 args['a'] = True 1153 if opt in ('-d', '--description'): 1154 args['d'] = True 1155 if opt in ('-p', '--page'): 1156 args['p'] = arg 1157 if opt in ('-h', '--help'): 1158 usage() 1159 sys.exit(0) 1160 1161 Kstat = get_Kstat() 1162 1163 alternate_tunable_layout = 'a' in args 1164 show_tunable_descriptions = 'd' in args 1165 1166 pages = [] 1167 1168 if 'p' in args: 1169 try: 1170 pages.append(unSub[int(args['p']) - 1]) 1171 except IndexError: 1172 sys.stderr.write('the argument to -p must be between 1 and ' + 1173 str(len(unSub)) + '\n') 1174 sys.exit(1) 1175 else: 1176 pages = unSub 1177 1178 zfs_header() 1179 for page in pages: 1180 page(Kstat) 1181 sys.stdout.write("\n") 1182 1183 1184if __name__ == '__main__': 1185 main() 1186