|  | #!/usr/bin/env python2 | 
|  | # | 
|  | # $Id: arc_summary.pl,v 388:e27800740aa2 2011-07-08 02:53:29Z jhell $ | 
|  | # | 
|  | # Copyright (c) 2008 Ben Rockwood <benr@cuddletech.com>, | 
|  | # Copyright (c) 2010 Martin Matuska <mm@FreeBSD.org>, | 
|  | # Copyright (c) 2010-2011 Jason J. Hellenthal <jhell@DataIX.net>, | 
|  | # All rights reserved. | 
|  | # | 
|  | # Redistribution and use in source and binary forms, with or without | 
|  | # modification, are permitted provided that the following conditions | 
|  | # are met: | 
|  | # | 
|  | # 1. Redistributions of source code must retain the above copyright | 
|  | #    notice, this list of conditions and the following disclaimer. | 
|  | # 2. Redistributions in binary form must reproduce the above copyright | 
|  | #    notice, this list of conditions and the following disclaimer in the | 
|  | #    documentation and/or other materials provided with the distribution. | 
|  | # | 
|  | # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND | 
|  | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | # ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE | 
|  | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | # SUCH DAMAGE. | 
|  | # | 
|  | # If you are having troubles when using this script from cron(8) please try | 
|  | # adjusting your PATH before reporting problems. | 
|  | # | 
|  | # Note some of this code uses older code (eg getopt instead of argparse, | 
|  | # subprocess.Popen() instead of subprocess.run()) because we need to support | 
|  | # some very old versions of Python. | 
|  | # | 
|  |  | 
|  | """Print statistics on the ZFS Adjustable Replacement Cache (ARC) | 
|  |  | 
|  | Provides basic information on the ARC, its efficiency, the L2ARC (if present), | 
|  | the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See the | 
|  | in-source documentation and code at | 
|  | https://github.com/zfsonlinux/zfs/blob/master/module/zfs/arc.c for details. | 
|  | """ | 
|  |  | 
|  | import getopt | 
|  | import os | 
|  | import sys | 
|  | import time | 
|  | import errno | 
|  |  | 
|  | from subprocess import Popen, PIPE | 
|  | from decimal import Decimal as D | 
|  |  | 
|  | show_tunable_descriptions = False | 
|  | alternate_tunable_layout = False | 
|  |  | 
|  |  | 
|  | def handle_Exception(ex_cls, ex, tb): | 
|  | if ex is IOError: | 
|  | if ex.errno == errno.EPIPE: | 
|  | sys.exit() | 
|  |  | 
|  | if ex is KeyboardInterrupt: | 
|  | sys.exit() | 
|  |  | 
|  |  | 
|  | sys.excepthook = handle_Exception | 
|  |  | 
|  |  | 
|  | def get_Kstat(): | 
|  | """Collect information on the ZFS subsystem from the /proc virtual | 
|  | file system. The name "kstat" is a holdover from the Solaris utility | 
|  | of the same name. | 
|  | """ | 
|  |  | 
|  | def load_proc_kstats(fn, namespace): | 
|  | """Collect information on a specific subsystem of the ARC""" | 
|  |  | 
|  | kstats = [line.strip() for line in open(fn)] | 
|  | del kstats[0:2] | 
|  | for kstat in kstats: | 
|  | kstat = kstat.strip() | 
|  | name, _, value = kstat.split() | 
|  | Kstat[namespace + name] = D(value) | 
|  |  | 
|  | Kstat = {} | 
|  | load_proc_kstats('/proc/spl/kstat/zfs/arcstats', | 
|  | 'kstat.zfs.misc.arcstats.') | 
|  | load_proc_kstats('/proc/spl/kstat/zfs/zfetchstats', | 
|  | 'kstat.zfs.misc.zfetchstats.') | 
|  | load_proc_kstats('/proc/spl/kstat/zfs/vdev_cache_stats', | 
|  | 'kstat.zfs.misc.vdev_cache_stats.') | 
|  |  | 
|  | return Kstat | 
|  |  | 
|  |  | 
|  | def fBytes(b=0): | 
|  | """Return human-readable representation of a byte value in | 
|  | powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal | 
|  | points. Values smaller than one KiB are returned without | 
|  | decimal points. | 
|  | """ | 
|  |  | 
|  | prefixes = [ | 
|  | [2**80, "YiB"],   # yobibytes (yotta) | 
|  | [2**70, "ZiB"],   # zebibytes (zetta) | 
|  | [2**60, "EiB"],   # exbibytes (exa) | 
|  | [2**50, "PiB"],   # pebibytes (peta) | 
|  | [2**40, "TiB"],   # tebibytes (tera) | 
|  | [2**30, "GiB"],   # gibibytes (giga) | 
|  | [2**20, "MiB"],   # mebibytes (mega) | 
|  | [2**10, "KiB"]]   # kibibytes (kilo) | 
|  |  | 
|  | if b >= 2**10: | 
|  |  | 
|  | for limit, unit in prefixes: | 
|  |  | 
|  | if b >= limit: | 
|  | value = b / limit | 
|  | break | 
|  |  | 
|  | result = "%0.2f\t%s" % (value, unit) | 
|  |  | 
|  | else: | 
|  |  | 
|  | result = "%d\tBytes" % b | 
|  |  | 
|  | return result | 
|  |  | 
|  |  | 
|  | def fHits(hits=0): | 
|  | """Create a human-readable representation of the number of hits. | 
|  | The single-letter symbols used are SI to avoid the confusion caused | 
|  | by the different "short scale" and "long scale" representations in | 
|  | English, which use the same words for different values. See | 
|  | https://en.wikipedia.org/wiki/Names_of_large_numbers and | 
|  | https://physics.nist.gov/cuu/Units/prefixes.html | 
|  | """ | 
|  |  | 
|  | numbers = [ | 
|  | [10**24, 'Y'],  # yotta (septillion) | 
|  | [10**21, 'Z'],  # zetta (sextillion) | 
|  | [10**18, 'E'],  # exa   (quintrillion) | 
|  | [10**15, 'P'],  # peta  (quadrillion) | 
|  | [10**12, 'T'],  # tera  (trillion) | 
|  | [10**9, 'G'],   # giga  (billion) | 
|  | [10**6, 'M'],   # mega  (million) | 
|  | [10**3, 'k']]   # kilo  (thousand) | 
|  |  | 
|  | if hits >= 1000: | 
|  |  | 
|  | for limit, symbol in numbers: | 
|  |  | 
|  | if hits >= limit: | 
|  | value = hits/limit | 
|  | break | 
|  |  | 
|  | result = "%0.2f%s" % (value, symbol) | 
|  |  | 
|  | else: | 
|  |  | 
|  | result = "%d" % hits | 
|  |  | 
|  | return result | 
|  |  | 
|  |  | 
|  | def fPerc(lVal=0, rVal=0, Decimal=2): | 
|  | """Calculate percentage value and return in human-readable format""" | 
|  |  | 
|  | if rVal > 0: | 
|  | return str("%0." + str(Decimal) + "f") % (100 * (lVal / rVal)) + "%" | 
|  | else: | 
|  | return str("%0." + str(Decimal) + "f") % 100 + "%" | 
|  |  | 
|  |  | 
|  | def get_arc_summary(Kstat): | 
|  | """Collect general data on the ARC""" | 
|  |  | 
|  | output = {} | 
|  | memory_throttle_count = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.memory_throttle_count" | 
|  | ] | 
|  |  | 
|  | if memory_throttle_count > 0: | 
|  | output['health'] = 'THROTTLED' | 
|  | else: | 
|  | output['health'] = 'HEALTHY' | 
|  |  | 
|  | output['memory_throttle_count'] = fHits(memory_throttle_count) | 
|  |  | 
|  | # ARC Misc. | 
|  | deleted = Kstat["kstat.zfs.misc.arcstats.deleted"] | 
|  | mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"] | 
|  | evict_skip = Kstat["kstat.zfs.misc.arcstats.evict_skip"] | 
|  |  | 
|  | # ARC Misc. | 
|  | output["arc_misc"] = {} | 
|  | output["arc_misc"]["deleted"] = fHits(deleted) | 
|  | output["arc_misc"]['mutex_miss'] = fHits(mutex_miss) | 
|  | output["arc_misc"]['evict_skips'] = fHits(evict_skip) | 
|  |  | 
|  | # ARC Sizing | 
|  | arc_size = Kstat["kstat.zfs.misc.arcstats.size"] | 
|  | mru_size = Kstat["kstat.zfs.misc.arcstats.mru_size"] | 
|  | mfu_size = Kstat["kstat.zfs.misc.arcstats.mfu_size"] | 
|  | meta_limit = Kstat["kstat.zfs.misc.arcstats.arc_meta_limit"] | 
|  | meta_size = Kstat["kstat.zfs.misc.arcstats.arc_meta_used"] | 
|  | dnode_limit = Kstat["kstat.zfs.misc.arcstats.arc_dnode_limit"] | 
|  | dnode_size = Kstat["kstat.zfs.misc.arcstats.dnode_size"] | 
|  | target_max_size = Kstat["kstat.zfs.misc.arcstats.c_max"] | 
|  | target_min_size = Kstat["kstat.zfs.misc.arcstats.c_min"] | 
|  | target_size = Kstat["kstat.zfs.misc.arcstats.c"] | 
|  |  | 
|  | target_size_ratio = (target_max_size / target_min_size) | 
|  |  | 
|  | # ARC Sizing | 
|  | output['arc_sizing'] = {} | 
|  | output['arc_sizing']['arc_size'] = { | 
|  | 'per': fPerc(arc_size, target_max_size), | 
|  | 'num': fBytes(arc_size), | 
|  | } | 
|  | output['arc_sizing']['target_max_size'] = { | 
|  | 'ratio': target_size_ratio, | 
|  | 'num': fBytes(target_max_size), | 
|  | } | 
|  | output['arc_sizing']['target_min_size'] = { | 
|  | 'per': fPerc(target_min_size, target_max_size), | 
|  | 'num': fBytes(target_min_size), | 
|  | } | 
|  | output['arc_sizing']['target_size'] = { | 
|  | 'per': fPerc(target_size, target_max_size), | 
|  | 'num': fBytes(target_size), | 
|  | } | 
|  | output['arc_sizing']['meta_limit'] = { | 
|  | 'per': fPerc(meta_limit, target_max_size), | 
|  | 'num': fBytes(meta_limit), | 
|  | } | 
|  | output['arc_sizing']['meta_size'] = { | 
|  | 'per': fPerc(meta_size, meta_limit), | 
|  | 'num': fBytes(meta_size), | 
|  | } | 
|  | output['arc_sizing']['dnode_limit'] = { | 
|  | 'per': fPerc(dnode_limit, meta_limit), | 
|  | 'num': fBytes(dnode_limit), | 
|  | } | 
|  | output['arc_sizing']['dnode_size'] = { | 
|  | 'per': fPerc(dnode_size, dnode_limit), | 
|  | 'num': fBytes(dnode_size), | 
|  | } | 
|  |  | 
|  | # ARC Hash Breakdown | 
|  | output['arc_hash_break'] = {} | 
|  | output['arc_hash_break']['hash_chain_max'] = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.hash_chain_max" | 
|  | ] | 
|  | output['arc_hash_break']['hash_chains'] = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.hash_chains" | 
|  | ] | 
|  | output['arc_hash_break']['hash_collisions'] = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.hash_collisions" | 
|  | ] | 
|  | output['arc_hash_break']['hash_elements'] = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.hash_elements" | 
|  | ] | 
|  | output['arc_hash_break']['hash_elements_max'] = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.hash_elements_max" | 
|  | ] | 
|  |  | 
|  | output['arc_size_break'] = {} | 
|  | output['arc_size_break']['recently_used_cache_size'] = { | 
|  | 'per': fPerc(mru_size, mru_size + mfu_size), | 
|  | 'num': fBytes(mru_size), | 
|  | } | 
|  | output['arc_size_break']['frequently_used_cache_size'] = { | 
|  | 'per': fPerc(mfu_size, mru_size + mfu_size), | 
|  | 'num': fBytes(mfu_size), | 
|  | } | 
|  |  | 
|  | # ARC Hash Breakdown | 
|  | hash_chain_max = Kstat["kstat.zfs.misc.arcstats.hash_chain_max"] | 
|  | hash_chains = Kstat["kstat.zfs.misc.arcstats.hash_chains"] | 
|  | hash_collisions = Kstat["kstat.zfs.misc.arcstats.hash_collisions"] | 
|  | hash_elements = Kstat["kstat.zfs.misc.arcstats.hash_elements"] | 
|  | hash_elements_max = Kstat["kstat.zfs.misc.arcstats.hash_elements_max"] | 
|  |  | 
|  | output['arc_hash_break'] = {} | 
|  | output['arc_hash_break']['elements_max'] = fHits(hash_elements_max) | 
|  | output['arc_hash_break']['elements_current'] = { | 
|  | 'per': fPerc(hash_elements, hash_elements_max), | 
|  | 'num': fHits(hash_elements), | 
|  | } | 
|  | output['arc_hash_break']['collisions'] = fHits(hash_collisions) | 
|  | output['arc_hash_break']['chain_max'] = fHits(hash_chain_max) | 
|  | output['arc_hash_break']['chains'] = fHits(hash_chains) | 
|  |  | 
|  | return output | 
|  |  | 
|  |  | 
|  | def _arc_summary(Kstat): | 
|  | """Print information on the ARC""" | 
|  |  | 
|  | # ARC Sizing | 
|  | arc = get_arc_summary(Kstat) | 
|  |  | 
|  | sys.stdout.write("ARC Summary: (%s)\n" % arc['health']) | 
|  |  | 
|  | sys.stdout.write("\tMemory Throttle Count:\t\t\t%s\n" % | 
|  | arc['memory_throttle_count']) | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  | # ARC Misc. | 
|  | sys.stdout.write("ARC Misc:\n") | 
|  | sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted']) | 
|  | sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" % | 
|  | arc['arc_misc']['mutex_miss']) | 
|  | sys.stdout.write("\tEvict Skips:\t\t\t\t%s\n" % | 
|  | arc['arc_misc']['evict_skips']) | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  | # ARC Sizing | 
|  | sys.stdout.write("ARC Size:\t\t\t\t%s\t%s\n" % ( | 
|  | arc['arc_sizing']['arc_size']['per'], | 
|  | arc['arc_sizing']['arc_size']['num'] | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tTarget Size: (Adaptive)\t\t%s\t%s\n" % ( | 
|  | arc['arc_sizing']['target_size']['per'], | 
|  | arc['arc_sizing']['target_size']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\tMin Size (Hard Limit):\t\t%s\t%s\n" % ( | 
|  | arc['arc_sizing']['target_min_size']['per'], | 
|  | arc['arc_sizing']['target_min_size']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\tMax Size (High Water):\t\t%d:1\t%s\n" % ( | 
|  | arc['arc_sizing']['target_max_size']['ratio'], | 
|  | arc['arc_sizing']['target_max_size']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\nARC Size Breakdown:\n") | 
|  | sys.stdout.write("\tRecently Used Cache Size:\t%s\t%s\n" % ( | 
|  | arc['arc_size_break']['recently_used_cache_size']['per'], | 
|  | arc['arc_size_break']['recently_used_cache_size']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tFrequently Used Cache Size:\t%s\t%s\n" % ( | 
|  | arc['arc_size_break']['frequently_used_cache_size']['per'], | 
|  | arc['arc_size_break']['frequently_used_cache_size']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tMetadata Size (Hard Limit):\t%s\t%s\n" % ( | 
|  | arc['arc_sizing']['meta_limit']['per'], | 
|  | arc['arc_sizing']['meta_limit']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tMetadata Size:\t\t\t%s\t%s\n" % ( | 
|  | arc['arc_sizing']['meta_size']['per'], | 
|  | arc['arc_sizing']['meta_size']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tDnode Size (Hard Limit):\t%s\t%s\n" % ( | 
|  | arc['arc_sizing']['dnode_limit']['per'], | 
|  | arc['arc_sizing']['dnode_limit']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tDnode Size:\t\t\t%s\t%s\n" % ( | 
|  | arc['arc_sizing']['dnode_size']['per'], | 
|  | arc['arc_sizing']['dnode_size']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  | # ARC Hash Breakdown | 
|  | sys.stdout.write("ARC Hash Breakdown:\n") | 
|  | sys.stdout.write("\tElements Max:\t\t\t\t%s\n" % | 
|  | arc['arc_hash_break']['elements_max']) | 
|  | sys.stdout.write("\tElements Current:\t\t%s\t%s\n" % ( | 
|  | arc['arc_hash_break']['elements_current']['per'], | 
|  | arc['arc_hash_break']['elements_current']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tCollisions:\t\t\t\t%s\n" % | 
|  | arc['arc_hash_break']['collisions']) | 
|  | sys.stdout.write("\tChain Max:\t\t\t\t%s\n" % | 
|  | arc['arc_hash_break']['chain_max']) | 
|  | sys.stdout.write("\tChains:\t\t\t\t\t%s\n" % | 
|  | arc['arc_hash_break']['chains']) | 
|  |  | 
|  |  | 
|  | def get_arc_efficiency(Kstat): | 
|  | """Collect information on the efficiency of the ARC""" | 
|  |  | 
|  | output = {} | 
|  |  | 
|  | arc_hits = Kstat["kstat.zfs.misc.arcstats.hits"] | 
|  | arc_misses = Kstat["kstat.zfs.misc.arcstats.misses"] | 
|  | demand_data_hits = Kstat["kstat.zfs.misc.arcstats.demand_data_hits"] | 
|  | demand_data_misses = Kstat["kstat.zfs.misc.arcstats.demand_data_misses"] | 
|  | demand_metadata_hits = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.demand_metadata_hits" | 
|  | ] | 
|  | demand_metadata_misses = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.demand_metadata_misses" | 
|  | ] | 
|  | mfu_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mfu_ghost_hits"] | 
|  | mfu_hits = Kstat["kstat.zfs.misc.arcstats.mfu_hits"] | 
|  | mru_ghost_hits = Kstat["kstat.zfs.misc.arcstats.mru_ghost_hits"] | 
|  | mru_hits = Kstat["kstat.zfs.misc.arcstats.mru_hits"] | 
|  | prefetch_data_hits = Kstat["kstat.zfs.misc.arcstats.prefetch_data_hits"] | 
|  | prefetch_data_misses = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.prefetch_data_misses" | 
|  | ] | 
|  | prefetch_metadata_hits = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.prefetch_metadata_hits" | 
|  | ] | 
|  | prefetch_metadata_misses = Kstat[ | 
|  | "kstat.zfs.misc.arcstats.prefetch_metadata_misses" | 
|  | ] | 
|  |  | 
|  | anon_hits = arc_hits - ( | 
|  | mfu_hits + mru_hits + mfu_ghost_hits + mru_ghost_hits | 
|  | ) | 
|  | arc_accesses_total = (arc_hits + arc_misses) | 
|  | demand_data_total = (demand_data_hits + demand_data_misses) | 
|  | prefetch_data_total = (prefetch_data_hits + prefetch_data_misses) | 
|  | real_hits = (mfu_hits + mru_hits) | 
|  |  | 
|  | output["total_accesses"] = fHits(arc_accesses_total) | 
|  | output["cache_hit_ratio"] = { | 
|  | 'per': fPerc(arc_hits, arc_accesses_total), | 
|  | 'num': fHits(arc_hits), | 
|  | } | 
|  | output["cache_miss_ratio"] = { | 
|  | 'per': fPerc(arc_misses, arc_accesses_total), | 
|  | 'num': fHits(arc_misses), | 
|  | } | 
|  | output["actual_hit_ratio"] = { | 
|  | 'per': fPerc(real_hits, arc_accesses_total), | 
|  | 'num': fHits(real_hits), | 
|  | } | 
|  | output["data_demand_efficiency"] = { | 
|  | 'per': fPerc(demand_data_hits, demand_data_total), | 
|  | 'num': fHits(demand_data_total), | 
|  | } | 
|  |  | 
|  | if prefetch_data_total > 0: | 
|  | output["data_prefetch_efficiency"] = { | 
|  | 'per': fPerc(prefetch_data_hits, prefetch_data_total), | 
|  | 'num': fHits(prefetch_data_total), | 
|  | } | 
|  |  | 
|  | if anon_hits > 0: | 
|  | output["cache_hits_by_cache_list"] = {} | 
|  | output["cache_hits_by_cache_list"]["anonymously_used"] = { | 
|  | 'per': fPerc(anon_hits, arc_hits), | 
|  | 'num': fHits(anon_hits), | 
|  | } | 
|  |  | 
|  | output["most_recently_used"] = { | 
|  | 'per': fPerc(mru_hits, arc_hits), | 
|  | 'num': fHits(mru_hits), | 
|  | } | 
|  | output["most_frequently_used"] = { | 
|  | 'per': fPerc(mfu_hits, arc_hits), | 
|  | 'num': fHits(mfu_hits), | 
|  | } | 
|  | output["most_recently_used_ghost"] = { | 
|  | 'per': fPerc(mru_ghost_hits, arc_hits), | 
|  | 'num': fHits(mru_ghost_hits), | 
|  | } | 
|  | output["most_frequently_used_ghost"] = { | 
|  | 'per': fPerc(mfu_ghost_hits, arc_hits), | 
|  | 'num': fHits(mfu_ghost_hits), | 
|  | } | 
|  |  | 
|  | output["cache_hits_by_data_type"] = {} | 
|  | output["cache_hits_by_data_type"]["demand_data"] = { | 
|  | 'per': fPerc(demand_data_hits, arc_hits), | 
|  | 'num': fHits(demand_data_hits), | 
|  | } | 
|  | output["cache_hits_by_data_type"]["prefetch_data"] = { | 
|  | 'per': fPerc(prefetch_data_hits, arc_hits), | 
|  | 'num': fHits(prefetch_data_hits), | 
|  | } | 
|  | output["cache_hits_by_data_type"]["demand_metadata"] = { | 
|  | 'per': fPerc(demand_metadata_hits, arc_hits), | 
|  | 'num': fHits(demand_metadata_hits), | 
|  | } | 
|  | output["cache_hits_by_data_type"]["prefetch_metadata"] = { | 
|  | 'per': fPerc(prefetch_metadata_hits, arc_hits), | 
|  | 'num': fHits(prefetch_metadata_hits), | 
|  | } | 
|  |  | 
|  | output["cache_misses_by_data_type"] = {} | 
|  | output["cache_misses_by_data_type"]["demand_data"] = { | 
|  | 'per': fPerc(demand_data_misses, arc_misses), | 
|  | 'num': fHits(demand_data_misses), | 
|  | } | 
|  | output["cache_misses_by_data_type"]["prefetch_data"] = { | 
|  | 'per': fPerc(prefetch_data_misses, arc_misses), | 
|  | 'num': fHits(prefetch_data_misses), | 
|  | } | 
|  | output["cache_misses_by_data_type"]["demand_metadata"] = { | 
|  | 'per': fPerc(demand_metadata_misses, arc_misses), | 
|  | 'num': fHits(demand_metadata_misses), | 
|  | } | 
|  | output["cache_misses_by_data_type"]["prefetch_metadata"] = { | 
|  | 'per': fPerc(prefetch_metadata_misses, arc_misses), | 
|  | 'num': fHits(prefetch_metadata_misses), | 
|  | } | 
|  |  | 
|  | return output | 
|  |  | 
|  |  | 
|  | def _arc_efficiency(Kstat): | 
|  | """Print information on the efficiency of the ARC""" | 
|  |  | 
|  | arc = get_arc_efficiency(Kstat) | 
|  |  | 
|  | sys.stdout.write("ARC Total accesses:\t\t\t\t\t%s\n" % | 
|  | arc['total_accesses']) | 
|  | sys.stdout.write("\tCache Hit Ratio:\t\t%s\t%s\n" % ( | 
|  | arc['cache_hit_ratio']['per'], | 
|  | arc['cache_hit_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tCache Miss Ratio:\t\t%s\t%s\n" % ( | 
|  | arc['cache_miss_ratio']['per'], | 
|  | arc['cache_miss_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\tActual Hit Ratio:\t\t%s\t%s\n" % ( | 
|  | arc['actual_hit_ratio']['per'], | 
|  | arc['actual_hit_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\n") | 
|  | sys.stdout.write("\tData Demand Efficiency:\t\t%s\t%s\n" % ( | 
|  | arc['data_demand_efficiency']['per'], | 
|  | arc['data_demand_efficiency']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | if 'data_prefetch_efficiency' in arc: | 
|  | sys.stdout.write("\tData Prefetch Efficiency:\t%s\t%s\n" % ( | 
|  | arc['data_prefetch_efficiency']['per'], | 
|  | arc['data_prefetch_efficiency']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  | sys.stdout.write("\tCACHE HITS BY CACHE LIST:\n") | 
|  | if 'cache_hits_by_cache_list' in arc: | 
|  | sys.stdout.write("\t  Anonymously Used:\t\t%s\t%s\n" % ( | 
|  | arc['cache_hits_by_cache_list']['anonymously_used']['per'], | 
|  | arc['cache_hits_by_cache_list']['anonymously_used']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Most Recently Used:\t\t%s\t%s\n" % ( | 
|  | arc['most_recently_used']['per'], | 
|  | arc['most_recently_used']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Most Frequently Used:\t\t%s\t%s\n" % ( | 
|  | arc['most_frequently_used']['per'], | 
|  | arc['most_frequently_used']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Most Recently Used Ghost:\t%s\t%s\n" % ( | 
|  | arc['most_recently_used_ghost']['per'], | 
|  | arc['most_recently_used_ghost']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Most Frequently Used Ghost:\t%s\t%s\n" % ( | 
|  | arc['most_frequently_used_ghost']['per'], | 
|  | arc['most_frequently_used_ghost']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\n\tCACHE HITS BY DATA TYPE:\n") | 
|  | sys.stdout.write("\t  Demand Data:\t\t\t%s\t%s\n" % ( | 
|  | arc["cache_hits_by_data_type"]['demand_data']['per'], | 
|  | arc["cache_hits_by_data_type"]['demand_data']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Prefetch Data:\t\t%s\t%s\n" % ( | 
|  | arc["cache_hits_by_data_type"]['prefetch_data']['per'], | 
|  | arc["cache_hits_by_data_type"]['prefetch_data']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Demand Metadata:\t\t%s\t%s\n" % ( | 
|  | arc["cache_hits_by_data_type"]['demand_metadata']['per'], | 
|  | arc["cache_hits_by_data_type"]['demand_metadata']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Prefetch Metadata:\t\t%s\t%s\n" % ( | 
|  | arc["cache_hits_by_data_type"]['prefetch_metadata']['per'], | 
|  | arc["cache_hits_by_data_type"]['prefetch_metadata']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\n\tCACHE MISSES BY DATA TYPE:\n") | 
|  | sys.stdout.write("\t  Demand Data:\t\t\t%s\t%s\n" % ( | 
|  | arc["cache_misses_by_data_type"]['demand_data']['per'], | 
|  | arc["cache_misses_by_data_type"]['demand_data']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Prefetch Data:\t\t%s\t%s\n" % ( | 
|  | arc["cache_misses_by_data_type"]['prefetch_data']['per'], | 
|  | arc["cache_misses_by_data_type"]['prefetch_data']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Demand Metadata:\t\t%s\t%s\n" % ( | 
|  | arc["cache_misses_by_data_type"]['demand_metadata']['per'], | 
|  | arc["cache_misses_by_data_type"]['demand_metadata']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Prefetch Metadata:\t\t%s\t%s\n" % ( | 
|  | arc["cache_misses_by_data_type"]['prefetch_metadata']['per'], | 
|  | arc["cache_misses_by_data_type"]['prefetch_metadata']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  |  | 
|  | def get_l2arc_summary(Kstat): | 
|  | """Collection information on the L2ARC""" | 
|  |  | 
|  | output = {} | 
|  |  | 
|  | l2_abort_lowmem = Kstat["kstat.zfs.misc.arcstats.l2_abort_lowmem"] | 
|  | l2_cksum_bad = Kstat["kstat.zfs.misc.arcstats.l2_cksum_bad"] | 
|  | l2_evict_lock_retry = Kstat["kstat.zfs.misc.arcstats.l2_evict_lock_retry"] | 
|  | l2_evict_reading = Kstat["kstat.zfs.misc.arcstats.l2_evict_reading"] | 
|  | l2_feeds = Kstat["kstat.zfs.misc.arcstats.l2_feeds"] | 
|  | l2_free_on_write = Kstat["kstat.zfs.misc.arcstats.l2_free_on_write"] | 
|  | l2_hdr_size = Kstat["kstat.zfs.misc.arcstats.l2_hdr_size"] | 
|  | l2_hits = Kstat["kstat.zfs.misc.arcstats.l2_hits"] | 
|  | l2_io_error = Kstat["kstat.zfs.misc.arcstats.l2_io_error"] | 
|  | l2_misses = Kstat["kstat.zfs.misc.arcstats.l2_misses"] | 
|  | l2_rw_clash = Kstat["kstat.zfs.misc.arcstats.l2_rw_clash"] | 
|  | l2_size = Kstat["kstat.zfs.misc.arcstats.l2_size"] | 
|  | l2_asize = Kstat["kstat.zfs.misc.arcstats.l2_asize"] | 
|  | l2_writes_done = Kstat["kstat.zfs.misc.arcstats.l2_writes_done"] | 
|  | l2_writes_error = Kstat["kstat.zfs.misc.arcstats.l2_writes_error"] | 
|  | l2_writes_sent = Kstat["kstat.zfs.misc.arcstats.l2_writes_sent"] | 
|  |  | 
|  | l2_access_total = (l2_hits + l2_misses) | 
|  | output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error) | 
|  |  | 
|  | output['l2_access_total'] = l2_access_total | 
|  | output['l2_size'] = l2_size | 
|  | output['l2_asize'] = l2_asize | 
|  |  | 
|  | if l2_size > 0 and l2_access_total > 0: | 
|  |  | 
|  | if output['l2_health_count'] > 0: | 
|  | output["health"] = "DEGRADED" | 
|  | else: | 
|  | output["health"] = "HEALTHY" | 
|  |  | 
|  | output["low_memory_aborts"] = fHits(l2_abort_lowmem) | 
|  | output["free_on_write"] = fHits(l2_free_on_write) | 
|  | output["rw_clashes"] = fHits(l2_rw_clash) | 
|  | output["bad_checksums"] = fHits(l2_cksum_bad) | 
|  | output["io_errors"] = fHits(l2_io_error) | 
|  |  | 
|  | output["l2_arc_size"] = {} | 
|  | output["l2_arc_size"]["adative"] = fBytes(l2_size) | 
|  | output["l2_arc_size"]["actual"] = { | 
|  | 'per': fPerc(l2_asize, l2_size), | 
|  | 'num': fBytes(l2_asize) | 
|  | } | 
|  | output["l2_arc_size"]["head_size"] = { | 
|  | 'per': fPerc(l2_hdr_size, l2_size), | 
|  | 'num': fBytes(l2_hdr_size), | 
|  | } | 
|  |  | 
|  | output["l2_arc_evicts"] = {} | 
|  | output["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry) | 
|  | output["l2_arc_evicts"]['reading'] = fHits(l2_evict_reading) | 
|  |  | 
|  | output['l2_arc_breakdown'] = {} | 
|  | output['l2_arc_breakdown']['value'] = fHits(l2_access_total) | 
|  | output['l2_arc_breakdown']['hit_ratio'] = { | 
|  | 'per': fPerc(l2_hits, l2_access_total), | 
|  | 'num': fHits(l2_hits), | 
|  | } | 
|  | output['l2_arc_breakdown']['miss_ratio'] = { | 
|  | 'per': fPerc(l2_misses, l2_access_total), | 
|  | 'num': fHits(l2_misses), | 
|  | } | 
|  | output['l2_arc_breakdown']['feeds'] = fHits(l2_feeds) | 
|  |  | 
|  | output['l2_arc_buffer'] = {} | 
|  |  | 
|  | output['l2_arc_writes'] = {} | 
|  | output['l2_writes_done'] = l2_writes_done | 
|  | output['l2_writes_sent'] = l2_writes_sent | 
|  | if l2_writes_done != l2_writes_sent: | 
|  | output['l2_arc_writes']['writes_sent'] = { | 
|  | 'value': "FAULTED", | 
|  | 'num': fHits(l2_writes_sent), | 
|  | } | 
|  | output['l2_arc_writes']['done_ratio'] = { | 
|  | 'per': fPerc(l2_writes_done, l2_writes_sent), | 
|  | 'num': fHits(l2_writes_done), | 
|  | } | 
|  | output['l2_arc_writes']['error_ratio'] = { | 
|  | 'per': fPerc(l2_writes_error, l2_writes_sent), | 
|  | 'num': fHits(l2_writes_error), | 
|  | } | 
|  | else: | 
|  | output['l2_arc_writes']['writes_sent'] = { | 
|  | 'per': fPerc(100), | 
|  | 'num': fHits(l2_writes_sent), | 
|  | } | 
|  |  | 
|  | return output | 
|  |  | 
|  |  | 
|  | def _l2arc_summary(Kstat): | 
|  | """Print information on the L2ARC""" | 
|  |  | 
|  | arc = get_l2arc_summary(Kstat) | 
|  |  | 
|  | if arc['l2_size'] > 0 and arc['l2_access_total'] > 0: | 
|  | sys.stdout.write("L2 ARC Summary: ") | 
|  | if arc['l2_health_count'] > 0: | 
|  | sys.stdout.write("(DEGRADED)\n") | 
|  | else: | 
|  | sys.stdout.write("(HEALTHY)\n") | 
|  | sys.stdout.write("\tLow Memory Aborts:\t\t\t%s\n" % | 
|  | arc['low_memory_aborts']) | 
|  | sys.stdout.write("\tFree on Write:\t\t\t\t%s\n" % arc['free_on_write']) | 
|  | sys.stdout.write("\tR/W Clashes:\t\t\t\t%s\n" % arc['rw_clashes']) | 
|  | sys.stdout.write("\tBad Checksums:\t\t\t\t%s\n" % arc['bad_checksums']) | 
|  | sys.stdout.write("\tIO Errors:\t\t\t\t%s\n" % arc['io_errors']) | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  | sys.stdout.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" % | 
|  | arc["l2_arc_size"]["adative"]) | 
|  | sys.stdout.write("\tCompressed:\t\t\t%s\t%s\n" % ( | 
|  | arc["l2_arc_size"]["actual"]["per"], | 
|  | arc["l2_arc_size"]["actual"]["num"], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tHeader Size:\t\t\t%s\t%s\n" % ( | 
|  | arc["l2_arc_size"]["head_size"]["per"], | 
|  | arc["l2_arc_size"]["head_size"]["num"], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  | if arc["l2_arc_evicts"]['lock_retries'] != '0' or \ | 
|  | arc["l2_arc_evicts"]["reading"] != '0': | 
|  | sys.stdout.write("L2 ARC Evicts:\n") | 
|  | sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" % | 
|  | arc["l2_arc_evicts"]['lock_retries']) | 
|  | sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" % | 
|  | arc["l2_arc_evicts"]["reading"]) | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  | sys.stdout.write("L2 ARC Breakdown:\t\t\t\t%s\n" % | 
|  | arc['l2_arc_breakdown']['value']) | 
|  | sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % ( | 
|  | arc['l2_arc_breakdown']['hit_ratio']['per'], | 
|  | arc['l2_arc_breakdown']['hit_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % ( | 
|  | arc['l2_arc_breakdown']['miss_ratio']['per'], | 
|  | arc['l2_arc_breakdown']['miss_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\tFeeds:\t\t\t\t\t%s\n" % | 
|  | arc['l2_arc_breakdown']['feeds']) | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  | sys.stdout.write("L2 ARC Writes:\n") | 
|  | if arc['l2_writes_done'] != arc['l2_writes_sent']: | 
|  | sys.stdout.write("\tWrites Sent: (%s)\t\t\t\t%s\n" % ( | 
|  | arc['l2_arc_writes']['writes_sent']['value'], | 
|  | arc['l2_arc_writes']['writes_sent']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Done Ratio:\t\t\t%s\t%s\n" % ( | 
|  | arc['l2_arc_writes']['done_ratio']['per'], | 
|  | arc['l2_arc_writes']['done_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\t  Error Ratio:\t\t\t%s\t%s\n" % ( | 
|  | arc['l2_arc_writes']['error_ratio']['per'], | 
|  | arc['l2_arc_writes']['error_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  | else: | 
|  | sys.stdout.write("\tWrites Sent:\t\t\t%s\t%s\n" % ( | 
|  | arc['l2_arc_writes']['writes_sent']['per'], | 
|  | arc['l2_arc_writes']['writes_sent']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  |  | 
|  | def get_dmu_summary(Kstat): | 
|  | """Collect information on the DMU""" | 
|  |  | 
|  | output = {} | 
|  |  | 
|  | zfetch_hits = Kstat["kstat.zfs.misc.zfetchstats.hits"] | 
|  | zfetch_misses = Kstat["kstat.zfs.misc.zfetchstats.misses"] | 
|  |  | 
|  | zfetch_access_total = (zfetch_hits + zfetch_misses) | 
|  | output['zfetch_access_total'] = zfetch_access_total | 
|  |  | 
|  | if zfetch_access_total > 0: | 
|  | output['dmu'] = {} | 
|  | output['dmu']['efficiency'] = {} | 
|  | output['dmu']['efficiency']['value'] = fHits(zfetch_access_total) | 
|  | output['dmu']['efficiency']['hit_ratio'] = { | 
|  | 'per': fPerc(zfetch_hits, zfetch_access_total), | 
|  | 'num': fHits(zfetch_hits), | 
|  | } | 
|  | output['dmu']['efficiency']['miss_ratio'] = { | 
|  | 'per': fPerc(zfetch_misses, zfetch_access_total), | 
|  | 'num': fHits(zfetch_misses), | 
|  | } | 
|  |  | 
|  | return output | 
|  |  | 
|  |  | 
|  | def _dmu_summary(Kstat): | 
|  | """Print information on the DMU""" | 
|  |  | 
|  | arc = get_dmu_summary(Kstat) | 
|  |  | 
|  | if arc['zfetch_access_total'] > 0: | 
|  | sys.stdout.write("DMU Prefetch Efficiency:\t\t\t\t\t%s\n" % | 
|  | arc['dmu']['efficiency']['value']) | 
|  | sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % ( | 
|  | arc['dmu']['efficiency']['hit_ratio']['per'], | 
|  | arc['dmu']['efficiency']['hit_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  | sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % ( | 
|  | arc['dmu']['efficiency']['miss_ratio']['per'], | 
|  | arc['dmu']['efficiency']['miss_ratio']['num'], | 
|  | ) | 
|  | ) | 
|  |  | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  |  | 
|  | def get_vdev_summary(Kstat): | 
|  | """Collect information on the VDEVs""" | 
|  |  | 
|  | output = {} | 
|  |  | 
|  | vdev_cache_delegations = \ | 
|  | Kstat["kstat.zfs.misc.vdev_cache_stats.delegations"] | 
|  | vdev_cache_misses = Kstat["kstat.zfs.misc.vdev_cache_stats.misses"] | 
|  | vdev_cache_hits = Kstat["kstat.zfs.misc.vdev_cache_stats.hits"] | 
|  | vdev_cache_total = (vdev_cache_misses + vdev_cache_hits + | 
|  | vdev_cache_delegations) | 
|  |  | 
|  | output['vdev_cache_total'] = vdev_cache_total | 
|  |  | 
|  | if vdev_cache_total > 0: | 
|  | output['summary'] = fHits(vdev_cache_total) | 
|  | output['hit_ratio'] = { | 
|  | 'per': fPerc(vdev_cache_hits, vdev_cache_total), | 
|  | 'num': fHits(vdev_cache_hits), | 
|  | } | 
|  | output['miss_ratio'] = { | 
|  | 'per': fPerc(vdev_cache_misses, vdev_cache_total), | 
|  | 'num': fHits(vdev_cache_misses), | 
|  | } | 
|  | output['delegations'] = { | 
|  | 'per': fPerc(vdev_cache_delegations, vdev_cache_total), | 
|  | 'num': fHits(vdev_cache_delegations), | 
|  | } | 
|  |  | 
|  | return output | 
|  |  | 
|  |  | 
|  | def _vdev_summary(Kstat): | 
|  | """Print information on the VDEVs""" | 
|  |  | 
|  | arc = get_vdev_summary(Kstat) | 
|  |  | 
|  | if arc['vdev_cache_total'] > 0: | 
|  | sys.stdout.write("VDEV Cache Summary:\t\t\t\t%s\n" % arc['summary']) | 
|  | sys.stdout.write("\tHit Ratio:\t\t\t%s\t%s\n" % ( | 
|  | arc['hit_ratio']['per'], | 
|  | arc['hit_ratio']['num'], | 
|  | )) | 
|  | sys.stdout.write("\tMiss Ratio:\t\t\t%s\t%s\n" % ( | 
|  | arc['miss_ratio']['per'], | 
|  | arc['miss_ratio']['num'], | 
|  | )) | 
|  | sys.stdout.write("\tDelegations:\t\t\t%s\t%s\n" % ( | 
|  | arc['delegations']['per'], | 
|  | arc['delegations']['num'], | 
|  | )) | 
|  |  | 
|  |  | 
|  | def _tunable_summary(Kstat): | 
|  | """Print information on tunables, including descriptions if requested""" | 
|  |  | 
|  | global show_tunable_descriptions | 
|  | global alternate_tunable_layout | 
|  |  | 
|  | names = os.listdir("/sys/module/zfs/parameters/") | 
|  |  | 
|  | values = {} | 
|  | for name in names: | 
|  | with open("/sys/module/zfs/parameters/" + name) as f: | 
|  | value = f.read() | 
|  | values[name] = value.strip() | 
|  |  | 
|  | descriptions = {} | 
|  |  | 
|  | if show_tunable_descriptions: | 
|  |  | 
|  | command = ["/sbin/modinfo", "zfs", "-0"] | 
|  |  | 
|  | try: | 
|  | p = Popen(command, stdin=PIPE, stdout=PIPE, | 
|  | stderr=PIPE, shell=False, close_fds=True) | 
|  | p.wait() | 
|  |  | 
|  | # By default, Python 2 returns a string as the first element of the | 
|  | # tuple from p.communicate(), while Python 3 returns bytes which | 
|  | # must be decoded first. The better way to do this would be with | 
|  | # subprocess.run() or at least .check_output(), but this fails on | 
|  | # CentOS 6 because of its old version of Python 2 | 
|  | desc = bytes.decode(p.communicate()[0]) | 
|  | description_list = desc.strip().split('\0') | 
|  |  | 
|  | if p.returncode == 0: | 
|  | for tunable in description_list: | 
|  | if tunable[0:5] == 'parm:': | 
|  | tunable = tunable[5:].strip() | 
|  | name, description = tunable.split(':', 1) | 
|  | if not description: | 
|  | description = "Description unavailable" | 
|  | descriptions[name] = description | 
|  | else: | 
|  | sys.stderr.write("%s: '%s' exited with code %i\n" % | 
|  | (sys.argv[0], command[0], p.returncode)) | 
|  | sys.stderr.write("Tunable descriptions will be disabled.\n") | 
|  | except OSError as e: | 
|  | sys.stderr.write("%s: Cannot run '%s': %s\n" % | 
|  | (sys.argv[0], command[0], e.strerror)) | 
|  | sys.stderr.write("Tunable descriptions will be disabled.\n") | 
|  |  | 
|  | sys.stdout.write("ZFS Tunables:\n") | 
|  | names.sort() | 
|  |  | 
|  | if alternate_tunable_layout: | 
|  | fmt = "\t%s=%s\n" | 
|  | else: | 
|  | fmt = "\t%-50s%s\n" | 
|  |  | 
|  | for name in names: | 
|  |  | 
|  | if not name: | 
|  | continue | 
|  |  | 
|  | if show_tunable_descriptions and name in descriptions: | 
|  | sys.stdout.write("\t# %s\n" % descriptions[name]) | 
|  |  | 
|  | sys.stdout.write(fmt % (name, values[name])) | 
|  |  | 
|  |  | 
|  | unSub = [ | 
|  | _arc_summary, | 
|  | _arc_efficiency, | 
|  | _l2arc_summary, | 
|  | _dmu_summary, | 
|  | _vdev_summary, | 
|  | _tunable_summary | 
|  | ] | 
|  |  | 
|  |  | 
|  | def zfs_header(): | 
|  | """Print title string with date""" | 
|  |  | 
|  | daydate = time.strftime('%a %b %d %H:%M:%S %Y') | 
|  |  | 
|  | sys.stdout.write('\n'+'-'*72+'\n') | 
|  | sys.stdout.write('ZFS Subsystem Report\t\t\t\t%s' % daydate) | 
|  | sys.stdout.write('\n') | 
|  |  | 
|  |  | 
|  | def usage(): | 
|  | """Print usage information""" | 
|  |  | 
|  | sys.stdout.write("Usage: arc_summary [-h] [-a] [-d] [-p PAGE]\n\n") | 
|  | sys.stdout.write("\t -h, --help           : " | 
|  | "Print this help message and exit\n") | 
|  | sys.stdout.write("\t -a, --alternate      : " | 
|  | "Show an alternate sysctl layout\n") | 
|  | sys.stdout.write("\t -d, --description    : " | 
|  | "Show the sysctl descriptions\n") | 
|  | sys.stdout.write("\t -p PAGE, --page=PAGE : " | 
|  | "Select a single output page to display,\n") | 
|  | sys.stdout.write("\t                        " | 
|  | "should be an integer between 1 and " + | 
|  | str(len(unSub)) + "\n\n") | 
|  | sys.stdout.write("Examples:\n") | 
|  | sys.stdout.write("\tarc_summary -a\n") | 
|  | sys.stdout.write("\tarc_summary -p 4\n") | 
|  | sys.stdout.write("\tarc_summary -ad\n") | 
|  | sys.stdout.write("\tarc_summary --page=2\n") | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | """Main function""" | 
|  |  | 
|  | global show_tunable_descriptions | 
|  | global alternate_tunable_layout | 
|  |  | 
|  | try: | 
|  | opts, args = getopt.getopt( | 
|  | sys.argv[1:], | 
|  | "adp:h", ["alternate", "description", "page=", "help"] | 
|  | ) | 
|  | except getopt.error as e: | 
|  | sys.stderr.write("Error: %s\n" % e.msg) | 
|  | usage() | 
|  | sys.exit(1) | 
|  |  | 
|  | args = {} | 
|  | for opt, arg in opts: | 
|  | if opt in ('-a', '--alternate'): | 
|  | args['a'] = True | 
|  | if opt in ('-d', '--description'): | 
|  | args['d'] = True | 
|  | if opt in ('-p', '--page'): | 
|  | args['p'] = arg | 
|  | if opt in ('-h', '--help'): | 
|  | usage() | 
|  | sys.exit(0) | 
|  |  | 
|  | Kstat = get_Kstat() | 
|  |  | 
|  | alternate_tunable_layout = 'a' in args | 
|  | show_tunable_descriptions = 'd' in args | 
|  |  | 
|  | pages = [] | 
|  |  | 
|  | if 'p' in args: | 
|  | try: | 
|  | pages.append(unSub[int(args['p']) - 1]) | 
|  | except IndexError: | 
|  | sys.stderr.write('the argument to -p must be between 1 and ' + | 
|  | str(len(unSub)) + '\n') | 
|  | sys.exit(1) | 
|  | else: | 
|  | pages = unSub | 
|  |  | 
|  | zfs_header() | 
|  | for page in pages: | 
|  | page(Kstat) | 
|  | sys.stdout.write("\n") | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | main() |