diff options
author | lgz <ilyamaschenko@gmail.com> | 2018-06-17 21:48:11 +0900 |
---|---|---|
committer | lgz <ilyamaschenko@gmail.com> | 2018-06-17 21:48:11 +0900 |
commit | dc3529ef145db0931108242566bfaa2e237cac69 (patch) | |
tree | ceb59bce8fdbbd6fb74c8b4f92f6ba83417cfde3 /python.d/mdstat.chart.py | |
parent | 9598db24cae63fd6241bb236e2fac55ae7b92d2f (diff) |
mdstat: refactor + read mismatch_cnt feature added
Diffstat (limited to 'python.d/mdstat.chart.py')
-rw-r--r-- | python.d/mdstat.chart.py | 206 |
1 files changed, 110 insertions, 96 deletions
diff --git a/python.d/mdstat.chart.py b/python.d/mdstat.chart.py index 756f0b94df..410c4ffa38 100644 --- a/python.d/mdstat.chart.py +++ b/python.d/mdstat.chart.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Description: mdstat netdata python.d module -# Author: l2isbad +# Author: Ilya Mashchenko (l2isbad) # SPDX-License-Identifier: GPL-3.0+ import re @@ -9,11 +9,11 @@ from collections import defaultdict from bases.FrameworkServices.SimpleService import SimpleService -priority = 60000 -retries = 60 -update_every = 1 +MDSTAT = '/proc/mdstat' +MISMATCH_CNT = '/sys/block/{0}/md/mismatch_cnt' ORDER = ['mdstat_health'] + CHARTS = { 'mdstat_health': { 'options': [None, 'Faulty Devices In MD', 'failed disks', 'health', 'md.health', 'line'], @@ -21,8 +21,6 @@ CHARTS = { } } -OPERATIONS = ('check', 'resync', 'reshape', 'recovery', 'finish', 'speed') - RE_DISKS = re.compile(r' (?P<array>[a-zA-Z_0-9]+) : active .+\[' r'(?P<total_disks>[0-9]+)/' r'(?P<inuse_disks>[0-9]+)\]') @@ -30,47 +28,55 @@ RE_DISKS = re.compile(r' (?P<array>[a-zA-Z_0-9]+) : active .+\[' RE_STATUS = re.compile(r' (?P<array>[a-zA-Z_0-9]+) : active .+ ' r'(?P<operation>[a-z]+) =[ ]{1,2}' r'(?P<operation_status>[0-9.]+).+finish=' - r'(?P<finish>([0-9.]+))min speed=' + r'(?P<finish_in>([0-9.]+))min speed=' r'(?P<speed>[0-9]+)') -def md_charts(md): - order = ['{0}_disks'.format(md.name), - '{0}_operation'.format(md.name), - '{0}_finish'.format(md.name), - '{0}_speed'.format(md.name) +def md_charts(name): + order = ['{0}_disks'.format(name), + '{0}_operation'.format(name), + '{0}_mismatch_cnt'.format(name), + '{0}_finish'.format(name), + '{0}_speed'.format(name) ] charts = dict() charts[order[0]] = { - 'options': [None, 'Disks Stats', 'disks', md.name, 'md.disks', 'stacked'], + 'options': [None, 'Disks Stats', 'disks', name, 'md.disks', 'stacked'], + 'lines': [ + ['{0}_total_disks'.format(name), 'total', 'absolute'], + ['{0}_inuse_disks'.format(name), 'inuse', 'absolute'] + ] + } + + charts[order[1]] = { + 'options': [None, 'Current Status', 'percent', name, 'md.status', 'line'], 'lines': [ - ['{0}_total_disks'.format(md.name), 'total', 'absolute'], - ['{0}_inuse_disks'.format(md.name), 'inuse', 'absolute'] + ['{0}_resync'.format(name), 'resync', 'absolute', 1, 100], + ['{0}_recovery'.format(name), 'recovery', 'absolute', 1, 100], + ['{0}_reshape'.format(name), 'reshape', 'absolute', 1, 100], + ['{0}_check'.format(name), 'check', 'absolute', 1, 100], ] } - charts['_'.join([md.name, 'operation'])] = { - 'options': [None, 'Current Status', 'percent', md.name, 'md.status', 'line'], + charts[order[2]] = { + 'options': [None, 'Mismatch Count', 'unsynchronized blocks', name, 'md.mismatch_cnt', 'line'], 'lines': [ - ['{0}_resync'.format(md.name), 'resync', 'absolute', 1, 100], - ['{0}_recovery'.format(md.name), 'recovery', 'absolute', 1, 100], - ['{0}_reshape'.format(md.name), 'reshape', 'absolute', 1, 100], - ['{0}_check'.format(md.name), 'check', 'absolute', 1, 100] + ['{0}_mismatch_cnt'.format(name), 'count', 'absolute'] ] } - charts['_'.join([md.name, 'finish'])] = { - 'options': [None, 'Approximate Time Until Finish', 'seconds', md.name, 'md.rate', 'line'], + charts[order[3]] = { + 'options': [None, 'Approximate Time Until Finish', 'seconds', name, 'md.rate', 'line'], 'lines': [ - ['{0}_finish'.format(md.name), 'finish in', 'absolute', 1, 1000] + ['{0}_finish_in'.format(name), 'finish in', 'absolute', 1, 1000] ] } - charts['_'.join([md.name, 'speed'])] = { - 'options': [None, 'Operation Speed', 'KB/s', md.name, 'md.rate', 'line'], + charts[order[4]] = { + 'options': [None, 'Operation Speed', 'KB/s', name, 'md.rate', 'line'], 'lines': [ - ['{0}_speed'.format(md.name), 'speed', 'absolute', 1, 1000] + ['{0}_speed'.format(name), 'speed', 'absolute', 1, 1000] ] } @@ -78,17 +84,33 @@ def md_charts(md): class MD: - def __init__(self, name, stats): - self.name = name - self.stats = stats - - def update_stats(self, stats): - self.stats = stats + def __init__(self, raw_data): + self.name = raw_data["array"] + self.d = raw_data def data(self): - stats = dict(('_'.join([self.name, k]), v) for k, v in self.stats.items()) - stats['{0}_health'.format(self.name)] = int(self.stats['total_disks']) - int(self.stats['inuse_disks']) - return stats + rv = { + 'total_disks': self.d['total_disks'], + 'inuse_disks': self.d['inuse_disks'], + 'health': int(self.d['total_disks']) - int(self.d['inuse_disks']), + 'resync': 0, + 'recovery': 0, + 'reshape': 0, + 'check': 0, + 'finish_in': 0, + 'speed': 0, + } + + v = read_lines(MISMATCH_CNT.format(self.name)) + if v: + rv['mismatch_cnt'] = v + + if self.d.get('operation'): + rv[self.d['operation']] = float(self.d['operation_status']) * 100 + rv['finish_in'] = float(self.d['finish_in']) * 1000 * 60 + rv['speed'] = float(self.d['speed']) * 1000 + + return dict(('{0}_{1}'.format(self.name, k), v) for k, v in rv.items()) class Service(SimpleService): @@ -96,67 +118,56 @@ class Service(SimpleService): SimpleService.__init__(self, configuration=configuration, name=name) self.order = ORDER self.definitions = CHARTS - self.mds = dict() - - def check(self): - arrays = find_arrays(self._get_raw_data()) - if not arrays: - self.error('Failed to read data from /proc/mdstat or there is no active arrays') - return None - return True + self.mds = list() @staticmethod - def _get_raw_data(): - """ - Read data from /proc/mdstat - :return: str - """ - try: - with open('/proc/mdstat', 'rt') as proc_mdstat: - return proc_mdstat.readlines() or None - except (OSError, IOError): + def get_mds(): + raw = read_lines(MDSTAT) + + if not raw: return None + return find_mds(raw) + def get_data(self): """ Parse data from _get_raw_data() :return: dict """ - arrays = find_arrays(self._get_raw_data()) - if not arrays: + mds = self.get_mds() + + if not mds: return None data = dict() - for array, values in arrays.items(): - - if array not in self.mds: - md = MD(array, values) - self.mds[md.name] = md - self.create_new_array_charts(md) - else: - md = self.mds[array] - md.update_stats(values) - + for md in mds: + if md.name not in self.mds: + self.mds.append(md.name) + self.add_new_md_charts(md.name) data.update(md.data()) - return data - def create_new_array_charts(self, md): - order, charts = md_charts(md) + def check(self): + if not self.get_mds(): + self.error('Failed to read data from {0} or there is no active arrays'.format(MDSTAT)) + return False + return True + + def add_new_md_charts(self, name): + order, charts = md_charts(name) + + self.charts['mdstat_health'].add_dimension(['{0}_health'.format(name), name]) - self.charts['mdstat_health'].add_dimension(['{0}_health'.format(md.name), md.name]) for chart_name in order: params = [chart_name] + charts[chart_name]['options'] - dimensions = charts[chart_name]['lines'] + dims = charts[chart_name]['lines'] - new_chart = self.charts.add_chart(params) - for dimension in dimensions: - new_chart.add_dimension(dimension) + chart = self.charts.add_chart(params) + for dim in dims: + chart.add_dimension(dim) -def find_arrays(raw_data): - if raw_data is None: - return None +def find_mds(raw_data): data = defaultdict(str) counter = 1 @@ -166,25 +177,28 @@ def find_arrays(raw_data): continue data[counter] = ' '.join([data[counter], row]) - arrays = dict() - for value in data.values(): - match = RE_DISKS.search(value) - if not match: + mds = list() + + for v in data.values(): + m = RE_DISKS.search(v) + + if not m: continue - match = match.groupdict() - array = match.pop('array') - arrays[array] = match - for operation in OPERATIONS: - arrays[array][operation] = 0 - - match = RE_STATUS.search(value) - if match: - match = match.groupdict() - if match['operation'] in OPERATIONS: - arrays[array]['operation'] = match['operation'] - arrays[array][match['operation']] = float(match['operation_status']) * 100 - arrays[array]['finish'] = float(match['finish']) * 1000 * 60 - arrays[array]['speed'] = float(match['speed']) * 1000 - - return arrays or None + d = m.groupdict() + + m = RE_STATUS.search(v) + if m: + d.update(m.groupdict()) + + mds.append(MD(d)) + + return sorted(mds, key=lambda md: md.name) + + +def read_lines(path): + try: + with open(path) as f: + return f.readlines() + except (IOError, OSError): + return None |