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