python 围绕iostat的Python包装器,具有适合Nagios使用的调整格式。
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 围绕iostat的Python包装器,具有适合Nagios使用的调整格式。相关的知识,希望对你有一定的参考价值。
#!/usr/bin/env python
__author__ = 'RackTop Systems'
import shlex
import subprocess
from subprocess import PIPE
from string import digits
import datetime
import time
import signal
from sys import argv, exit
DEBUG = False
REPEAT = 60
to_print = ""
disks = {}
disks_final = {}
class LimitsTuple(object):
"""
Since Python 2.5.x is limited in terms of namedtuple(s), we
just implement our own. Properties cannot be modified once set,
and three methods expose values on variables which remain private.
"""
# Basic defaults are set, but really, should always be overridden.
def __init__(self, min_value=0.0, max_value=100.0, default=0.0):
self.__min_value = min_value
self.__max_value = max_value
self.__default = default
@property # This is minimum value
def min_value(self):
return self.__min_value
@property # This is maximum value
def max_value(self):
return self.__max_value
@property # This is default value
def default(self):
return self.__default
class RODict(dict):
"""
Enums don't really exist, so we make our own.
"""
__readonly = True
def readonly(self, allow=1):
# Do we allow modification of dict?
self.__readonly = bool(allow)
def __setitem__(self, key, value):
if self.__readonly:
raise TypeError("__setitem__ is not supported")
return dict.__setitem__(self, key, value)
def __delitem__(self, key):
if self.__readonly:
raise TypeError("__delitem__ is not supported")
return dict.__delitem__(self, key)
LIMITS = \
RODict({
"svc_t": LimitsTuple(0.0, 10000.0, 0.0), # (min, max, default)
"queue_sz": LimitsTuple(0.0, 100.0, 0.0), # (min, max, default)
"pct": LimitsTuple(0, 100, 0), # (min, max, default)
})
def usage():
print "Usage: %s [--forever] [--batch]" % argv[0]
def signal_handler(signal, frame):
print('Stopping on Ctrl+C Interrupt.')
exit(0)
def read_iostat_metrics(count):
disks = {}
disks_final = {}
string_to_real = lambda y: [float(each) for each in y]
params = shlex.split("/usr/bin/iostat -xnz 1 %d" % count)
stats = subprocess.Popen(params, shell=False, bufsize=4096,
stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
stdout, stderr = stats.communicate()
lines = stdout.split("\n")
for line in lines:
line = line.lstrip(' \t\n\r')
if not line == "" and line[0] in digits:
elements = line.rstrip("\n").split()
disk = elements[10]
metrics = elements[0:10]
real_metrics = string_to_real(metrics) # Convert all metrics to floats.
# If disk is already in dictionary, for each metric do a comparison
# to see whether this new value is greater. We want to always keep
# largest value.
if disk in disks.keys():
for pos, value in enumerate(real_metrics):
last_pos = disks[disk][pos]
disks[disk][pos] = max(disks[disk][pos], value)
new_pos = disks[disk][pos]
if DEBUG:
if new_pos > last_pos:
print "New MAX => [%f]" % new_pos
# If this is a brand new entry and the disk is not yet in the dict,
# we do not need to compare current value against old value, just add entry.
else:
disks[disk] = real_metrics
# Normalize
for disk in disks.keys():
disks_final[disk] = [0 for _ in xrange(10)]
for pos, value in enumerate(disks[disk]):
# Store last value, and if sample is weird, set value to max(last_value, default).
last_value = disks_final[disk][pos]
# If for some reason these numbers are not within range set by min_value and max_value
# in the LIMITS["type"] dict, we choose the max of last value, stored in the last_value var.
# and default. This is not a great approach, but better than others I could come-up with, atm.
if pos == 2 or pos == 3:
disks_final[disk][pos] = value * 1024 # Convert to bytes from Kbytes
elif pos == 4 or pos == 5:
disks_final[disk][pos] = \
value if LIMITS["queue_sz"].min_value <= int(value) <= LIMITS["queue_sz"].max_value \
else max(last_value, LIMITS["queue_sz"].default)
elif pos == 6 or pos == 7:
disks_final[disk][pos] = \
value if LIMITS["svc_t"].min_value <= value <= LIMITS["svc_t"].max_value \
else max(last_value, LIMITS["svc_t"].default)
elif pos == 8 or pos == 9:
disks_final[disk][pos] = \
int(value) if LIMITS["pct"].min_value <= int(value) <= LIMITS["pct"].max_value \
else max(last_value, LIMITS["pct"].default)
else:
disks_final[disk][pos] = value
dt = datetime.datetime.now()
unix_time = int(time.mktime(dt.timetuple()))
for disk in disks_final:
x = dict()
x["reads"] = disks_final[disk][0]
x["writes"] = disks_final[disk][1]
x["bytes_r"] = disks_final[disk][2]
x["bytes_w"] = disks_final[disk][3]
x["wait"] = disks_final[disk][4]
x["activ"] = disks_final[disk][5]
x["wsvc_t"] = disks_final[disk][6]
x["asvc_t"] = disks_final[disk][7]
x["pct_w"] = disks_final[disk][8]
x["pct_b"] = disks_final[disk][9]
to_print = ",".join([
str(unix_time),
disk,
"%.2f" % x["reads"],
"%.2f" % x["writes"],
"%.2f" % x["bytes_r"],
"%.2f" % x["bytes_w"],
"%.2f" % x["wait"],
"%.2f" % x["activ"],
"%.2f" % x["wsvc_t"],
"%.2f" % x["asvc_t"],
"%d" % x["pct_w"],
"%d" % x["pct_b"]
])
print to_print
if __name__ == "__main__":
if not len(argv[:]) > 1:
try:
print("Press Ctrl+C to interrupt process.")
read_iostat_metrics(REPEAT)
except KeyboardInterrupt:
print "Interrupted by user with Ctrl+C."
exit(0)
else:
signal.signal(signal.SIGINT, signal_handler)
if not "--batch" in argv:
print("Press Ctrl+C to interrupt process.")
if "--forever" in argv:
while True:
try:
read_iostat_metrics(REPEAT)
except KeyboardInterrupt:
exit(0)
else:
usage()
以上是关于python 围绕iostat的Python包装器,具有适合Nagios使用的调整格式。的主要内容,如果未能解决你的问题,请参考以下文章
如何将围绕 C++ 函数的 R 包装器转换为 Python/Numpy