diff options
Diffstat (limited to 'python.d/mdstat.chart.py')
-rw-r--r-- | python.d/mdstat.chart.py | 167 |
1 files changed, 97 insertions, 70 deletions
diff --git a/python.d/mdstat.chart.py b/python.d/mdstat.chart.py index 0f7d2b44..ca9aba56 100644 --- a/python.d/mdstat.chart.py +++ b/python.d/mdstat.chart.py @@ -2,98 +2,125 @@ # Description: mdstat netdata python.d module # Author: l2isbad +from re import compile as re_compile +from collections import defaultdict from base import SimpleService -from re import compile + priority = 60000 retries = 60 update_every = 1 +OPERATIONS = ('check', 'resync', 'reshape', 'recovery', 'finish', 'speed') + class Service(SimpleService): def __init__(self, configuration=None, name=None): SimpleService.__init__(self, configuration=configuration, name=name) - self.order = ['agr_health'] - self.definitions = {'agr_health': - {'options': - [None, 'Faulty devices in MD', 'failed disks', 'health', 'md.health', 'line'], - 'lines': []}} - self.proc_mdstat = '/proc/mdstat' - self.regex_disks = compile(r'((?<=\ )[a-zA-Z_0-9]+(?= : active)).*?((?<= \[)[0-9]+)/([0-9]+(?=\] ))') - self.regex_status = compile(r'([a-zA-Z_0-9]+)( : active)[^:]*?([a-z]+) = ([0-9.]+(?=%)).*?((?<=finish=)[0-9.]+)min speed=([0-9]+)') + self.regex = dict(disks=re_compile(r' (?P<array>[a-zA-Z_0-9]+) : active .+\[' + r'(?P<total_disks>[0-9]+)/' + r'(?P<inuse_disks>[0-9])\]'), + status=re_compile(r' (?P<array>[a-zA-Z_0-9]+) : active .+ ' + r'(?P<operation>[a-z]+) = ' + r'(?P<operation_status>[0-9.]+).+finish=' + r'(?P<finish>([0-9.]+))min speed=' + r'(?P<speed>[0-9]+)')) def check(self): - raw_data = self._get_raw_data() - if not raw_data: - self.error('Cant read mdstat data from %s' % (self.proc_mdstat)) - return False - - md_list = [md[0] for md in self.regex_disks.findall(raw_data)] - - if not md_list: - self.error('No active arrays in %s' % (self.proc_mdstat)) - return False - else: - for md in md_list: - self.order.append(md) - self.order.append(''.join([md, '_status'])) - self.order.append(''.join([md, '_rate'])) - self.definitions['agr_health']['lines'].append([''.join([md, '_health']), md, 'absolute']) - self.definitions[md] = {'options': - [None, '%s disks stats' % md, 'disks', md, 'md.disks', 'stacked'], - 'lines': [[''.join([md, '_total']), 'total', 'absolute'], - [''.join([md, '_inuse']), 'inuse', 'absolute']]} - self.definitions[''.join([md, '_status'])] = {'options': - [None, '%s current status' % md, 'percent', md, 'md.status', 'line'], - 'lines': [[''.join([md, '_resync']), 'resync', 'absolute', 1, 100], - [''.join([md, '_recovery']), 'recovery', 'absolute', 1, 100], - [''.join([md, '_reshape']), 'reshape', 'absolute', 1, 100], - [''.join([md, '_check']), 'check', 'absolute', 1, 100]]} - self.definitions[''.join([md, '_rate'])] = {'options': - [None, '%s operation status' % md, 'rate', md, 'md.rate', 'line'], - 'lines': [[''.join([md, '_finishin']), 'finish min', 'absolute', 1, 100], - [''.join([md, '_rate']), 'megabyte/s', 'absolute', -1, 100]]} - self.info('Plugin was started successfully. MDs to monitor %s' % (md_list)) - - return True - - def _get_raw_data(self): + arrays = find_arrays(self._get_raw_data(), self.regex) + if not arrays: + self.error('Failed to read data from /proc/mdstat or there is no active arrays') + return None + + self.order, self.definitions = create_charts(arrays.keys()) + return True + + @staticmethod + def _get_raw_data(): """ Read data from /proc/mdstat :return: str """ try: - with open(self.proc_mdstat, 'rt') as proc_mdstat: - raw_result = proc_mdstat.read() - except Exception: + with open('/proc/mdstat', 'rt') as proc_mdstat: + return proc_mdstat.readlines() or None + except (OSError, IOError): return None - else: - raw_result = ' '.join(raw_result.split()) - return raw_result def _get_data(self): """ Parse data from _get_raw_data() :return: dict """ - raw_mdstat = self._get_raw_data() - mdstat_disks = self.regex_disks.findall(raw_mdstat) - mdstat_status = self.regex_status.findall(raw_mdstat) - to_netdata = {} - - for md in mdstat_disks: - to_netdata[''.join([md[0], '_total'])] = int(md[1]) - to_netdata[''.join([md[0], '_inuse'])] = int(md[2]) - to_netdata[''.join([md[0], '_health'])] = int(md[1]) - int(md[2]) - to_netdata[''.join([md[0], '_check'])] = 0 - to_netdata[''.join([md[0], '_resync'])] = 0 - to_netdata[''.join([md[0], '_reshape'])] = 0 - to_netdata[''.join([md[0], '_recovery'])] = 0 - to_netdata[''.join([md[0], '_finishin'])] = 0 - to_netdata[''.join([md[0], '_rate'])] = 0 - - for md in mdstat_status: - to_netdata[''.join([md[0], '_' + md[2]])] = round(float(md[3]) * 100) - to_netdata[''.join([md[0], '_finishin'])] = round(float(md[4]) * 100) - to_netdata[''.join([md[0], '_rate'])] = round(float(md[5]) / 1000 * 100) + raw_data = self._get_raw_data() + arrays = find_arrays(raw_data, self.regex) + if not arrays: + return None + + to_netdata = dict() + for array, values in arrays.items(): + for key, value in values.items(): + to_netdata['_'.join([array, key])] = value return to_netdata + + +def find_arrays(raw_data, regex): + if raw_data is None: + return None + data = defaultdict(str) + counter = 1 + + for row in (elem.strip() for elem in raw_data): + if not row: + counter += 1 + continue + data[counter] = ' '.join([data[counter], row]) + + arrays = dict() + for value in data.values(): + match = regex['disks'].search(value) + if not match: + continue + + match = match.groupdict() + array = match.pop('array') + arrays[array] = match + arrays[array]['health'] = int(match['total_disks']) - int(match['inuse_disks']) + for operation in OPERATIONS: + arrays[array][operation] = 0 + + match = regex['status'].search(value) + if match: + match = match.groupdict() + if match['operation'] in OPERATIONS: + arrays[array][match['operation']] = float(match['operation_status']) * 100 + arrays[array]['finish'] = float(match['finish']) * 100 + arrays[array]['speed'] = float(match['speed']) / 1000 * 100 + + return arrays or None + + +def create_charts(arrays): + order = ['mdstat_health'] + definitions = dict(mdstat_health={'options': [None, 'Faulty devices in MD', 'failed disks', + 'health', 'md.health', 'line'], + 'lines': []}) + for md in arrays: + order.append(md) + order.append(md + '_status') + order.append(md + '_rate') + definitions['mdstat_health']['lines'].append([md + '_health', md, 'absolute']) + definitions[md] = {'options': [None, '%s disks stats' % md, 'disks', md, 'md.disks', 'stacked'], + 'lines': [[md + '_total_disks', 'total', 'absolute'], + [md + '_inuse_disks', 'inuse', 'absolute']]} + definitions[md + '_status'] = {'options': [None, '%s current status' % md, + 'percent', md, 'md.status', 'line'], + 'lines': [[md + '_resync', 'resync', 'absolute', 1, 100], + [md + '_recovery', 'recovery', 'absolute', 1, 100], + [md + '_reshape', 'reshape', 'absolute', 1, 100], + [md + '_check', 'check', 'absolute', 1, 100]]} + definitions[md + '_rate'] = {'options': [None, '%s operation status' % md, + 'rate', md, 'md.rate', 'line'], + 'lines': [[md + '_finish', 'finish min', 'absolute', 1, 100], + [md + '_speed', 'MB/s', 'absolute', -1, 100]]} + return order, definitions |