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