File: //usr/lib/fm-agent/plugins/io_stats.py
import agent_util
from sys import platform, exc_info
import logging
import re
from agent_util import float
class DevicePathManager:
def __init__(self, execute_function=None):
if not execute_function:
self._execute_function = agent_util.execute_command
else:
self._execute_function = execute_function
try:
command = "lsblk --pairs"
if "darwin" in platform:
disk_util = agent_util.which("diskutil")
command = "{} list".format(disk_util)
self._output = self._execute_function(command)
self._output = self._output[1].split("\n")
except Exception:
logging.exception("Error getting devices")
self._output = []
def find_path(self, device):
if "darwin" in platform:
for line in self._output:
if line.startswith("/"):
mp = line.split(" ")[0]
if mp[len("/dev/") :] == device:
return mp
return None
else:
for line in self._output:
expression = r'^NAME="(%s)".*MOUNTPOINT="(.*)"$' % (device)
match = re.match(expression, line)
if match:
matched_mountpoint = match.groups()[1]
if matched_mountpoint == "":
# Some devices don't show a mountpoint, in those
# cases, return the same device.
return device
else:
return matched_mountpoint
class IOStatPlugin(agent_util.Plugin):
textkey = "iostat"
label = "IO"
metrics_list = [
"rrqm/s",
"wrqm/s",
"r/s",
"w/s",
"svctm",
"rkB/s",
"wkB/s",
"%w",
"%b",
"wait",
"actv",
"kr/s",
"kw/s",
"svc_t",
"%util",
]
metrics_labels = {
"rrqm/s": "Read requests queued",
"wrqm/s": "Write requests queued",
"r/s": "Read requests",
"w/s": "Write requests",
"svctm": "Average I/O request service time",
"%w": "% of time transactions are waiting",
"%b": "Percent of time the disk is busy",
"wait": "Average transactions waiting for service",
"actv": "Average transactions being serviced",
"kr/s": "Data read rate",
"kw/s": "Data write rate",
"svc_t": "Average response time",
"%util": "% of I/O CPU time",
}
metrics_units = {
"rrqm/s": "requests/second",
"wrqm/s": "requests/second",
"r/s": "requests/second",
"w/s": "requests/second",
"svctm": "ms",
"%w": "percent",
"%b": "percent",
"wait": "transactions",
"actv": "transactions",
"kr/s": "KB/s",
"kw/s": "KB/s",
"svc_t": "Average response time",
"%util": "% of I/O CPU time",
}
darwinMetricsMap = {"kbpt": "KB/t", "tps": "tps", "mbps": "MB/s"}
@classmethod
def get_metadata(self, config):
status = agent_util.SUPPORTED
msg = None
iostat_bin = agent_util.which("iostat")
if not iostat_bin:
self.log.info("iostat not found")
status = agent_util.MISCONFIGURED
msg = "Install iostat."
return {}
self.log.debug("Starting IOStat")
# get our devices to monitor
devices = []
data = {}
output = None
header = []
options_schema = {"resource": "string", "mountpoint": "string"}
device_path_manager = DevicePathManager()
if "hp-ux" in platform:
ret, out = agent_util.execute_command("%s | sed '/^loop/d' " % iostat_bin)
lines = out.strip().splitlines()
for line in lines:
if line.startswith("device") or line == "" or not line:
continue
device = line.split()[0]
mountpoint = device_path_manager.find_path(device)
devices.append(
{
"resource": device,
"mountpoint": mountpoint,
}
)
self.log.debug("Devices found: %s" % devices)
metdata = {
"bps": {
"label": "kilobytes per second",
"options": devices,
"status": status,
"error_message": msg,
"unit": "kb",
"options_schema": options_schema,
},
"sps": {
"label": "disk seeks per second",
"options": devices,
"status": status,
"error_message": msg,
"unit": "",
"options_schema": options_schema,
},
}
return metdata
elif "darwin" in platform:
ret, out = agent_util.execute_command("%s -d" % iostat_bin)
if 0 != ret:
status = agent_util.MISCONFIGURED
msg = "iostat failure code {}".format(ret)
else:
lines = out.strip().splitlines()
devs = lines[0].strip().split()
for device in devs:
mp = device_path_manager.find_path(device)
if mp:
devices.append({"resource": device, "mountpoint": mp})
else:
status = agent_util.MISCONFIGURED
msg = "Could not map device"
break
metadata = {
"kbpt": {
"label": "Kilobytes per transfer",
"options": devices,
"status": status,
"error_msg": msg,
"unit": "KB/s",
"options_schema": options_schema,
},
"tps": {
"label": "Transfers per second",
"options": devices,
"status": status,
"error_msg": msg,
"unit": "transfers/s",
"options_schema": options_schema,
},
"mbps": {
"label": "Megabytes per second",
"options": devices,
"status": status,
"error_msg": msg,
"unit": "MB/s",
"options_schema": options_schema,
},
}
return metadata
else:
ret_code, output = agent_util.execute_command(
"%s -dx | sed '/^loop/d' " % iostat_bin
)
output = output.strip().splitlines()
self.log.debug("#####################################################")
self.log.debug("IO stats command '%s -dx' output:" % iostat_bin)
self.log.debug(str(output))
self.log.debug("#####################################################")
for line in reversed(output):
if line.lower().startswith("device"):
header = line.split()
break
fields = line.strip().split()
dev = fields[0]
if dev.lower().startswith("dm-"):
continue
mountpoint = device_path_manager.find_path(dev)
devices.append({"resource": dev, "mountpoint": mountpoint})
self.log.debug("Devices: %s" % str(devices))
# no devices? no resources to monitor then
if not devices:
status = agent_util.MISCONFIGURED
msg = "No devices found from iostat."
for metric in header:
self.log.debug("###########\nMetric: %s" % str(metric))
if metric in self.metrics_list:
self.log.debug(
"metric %s has the index value of %s"
% (str(metric), str(header.index(metric)))
)
data[str(metric)] = {
"label": self.metrics_labels.get(str(metric), str(metric)),
"options": devices,
"options_schema": options_schema,
"status": status,
"error_message": msg,
}
if str(metric) in self.metrics_units:
data[str(metric)]["unit"] = self.metrics_units.get(str(metric))
return data
def check(self, textkey, device, config):
metrics_index = {}
iostat_bin = agent_util.which("iostat")
second_line = False
header_line = 2
if "freebsd" in platform:
header_line = 1
if "sunos" in platform:
second_line = True
ret, output = agent_util.execute_command(
"%s -dx 1 2 | sed '/^loop/d'" % (iostat_bin),
cache_timeout=agent_util.DEFAULT_CACHE_TIMEOUT,
)
header = output.strip().splitlines()[1].split()
elif "hp-ux" in platform:
ret, out = agent_util.execute_command(
"iostat 1 2 | sed '/^loop/d'",
cache_timeout=agent_util.DEFAULT_CACHE_TIMEOUT,
)
lines = out.strip().splitlines()
previously_seen = False
for line in lines:
line = line.strip()
if previously_seen is False and line.startswith(device):
previously_seen = True
self.log.debug("Found first instance of disk %s" % device)
elif previously_seen is True and line.startswith(device):
self.log.debug("Found second instance of disk %s" % device)
self.log.debug(line)
l = line.split()
if textkey == "bps":
return float(l[1])
elif textkey == "sps":
return float(l[2])
else:
return None
elif "darwin" in platform:
try:
outputKey = self.darwinMetricsMap.get(textkey, None)
if outputKey is None:
raise Exception("Unrecognized textkey {}".format(textkey))
ret, output = agent_util.execute_command(
"%s -d -c 2" % iostat_bin,
cache_timeout=agent_util.DEFAULT_CACHE_TIMEOUT,
)
if 0 != ret:
raise Exception("iostat failure, error {}".format(ret))
metricsCount = len(self.darwinMetricsMap)
lines = output.strip().split("\n")
if 4 != len(lines):
self.log.error("Unrecognized iostat output")
self.log.error(output)
raise Exception("Unrecognized iostat output")
devices = lines[0].split()
metrics = lines[1].split()
metric_values = lines[3].split()
devIndex = devices.index(device)
si = devIndex * metricsCount
ei = si + metricsCount
deviceMetrics = metrics[si:ei]
di = deviceMetrics.index(outputKey)
return float(metric_values[si:ei][di])
except Exception:
err = exc_info()[1]
error = str(err)
self.log.error(
"Collection error {}, {}: {}".format(device, textkey, error)
)
return None
else:
second_line = True
cmd_to_run = "%s -dx 1 2 | sed '/^loop/d'" % (iostat_bin)
ret, output = agent_util.execute_command(
cmd_to_run, cache_timeout=agent_util.DEFAULT_CACHE_TIMEOUT
)
if ret != 0:
self.log.error("{} failed with status {}".format(cmd_to_run, ret))
return None
header = output.strip().splitlines()[header_line].split()
splitted_lines = output.strip().split("\n")
full = list(
filter(lambda x: re.match(r"^%s .*$" % (device), x), splitted_lines)
)
if full:
if second_line:
full = full[1]
else:
full = full[0]
else:
self.log.error("Device %s could not be found in output!" % device)
return None
if not header:
self.log.error("Device %s no longer exists!" % device)
return None
for metric in header:
self.log.debug("###########\nMetric: %s" % str(metric))
if metric in self.metrics_list:
metrics_index[str(metric)] = header.index(str(metric))
self.log.debug("#####################################################")
self.log.debug(
"IO stats command '%s -dx %s 1 2' output:" % (iostat_bin, device)
)
self.log.debug(str(full))
self.log.debug("#####################################################")
self.log.debug("iostat -dx output: %s" % str(output))
j = full.strip().split()
if device in j[0]:
return float(j[int(metrics_index[str(textkey.split(".")[-1])])])
return 0