@cryptotaxi247 / netdata-1 / commits / 85ed4e17d

mdstat plugin: bugfix, fixes #2193

Ilya committed May 17, 2017 at 18:32 UTC 85ed4e17d7917769178fe2d66f7c347649bfe86c
1 file changed +97 -70
python.d/mdstat.chart.py
+97 -70
@@ -2,98 +2,125 @@
2 # Description: mdstat netdata python.d module
3 # Author: l2isbad
4
5 +from re import compile as re_compile
6 +from collections import defaultdict
7 from base import SimpleService
6 -from re import compile
8 +
9 priority = 60000
10 retries = 60
11 update_every = 1
12
13 +OPERATIONS = ('check', 'resync', 'reshape', 'recovery', 'finish', 'speed')
14 +
15
16 class Service(SimpleService):
17 def __init__(self, configuration=None, name=None):
18 SimpleService.__init__(self, configuration=configuration, name=name)
15 - self.order = ['agr_health']
16 - self.definitions = {'agr_health':
17 - {'options':
18 - [None, 'Faulty devices in MD', 'failed disks', 'health', 'md.health', 'line'],
19 - 'lines': []}}
20 - self.proc_mdstat = '/proc/mdstat'
21 - self.regex_disks = compile(r'((?<=\ )[a-zA-Z_0-9]+(?= : active)).*?((?<= \[)[0-9]+)/([0-9]+(?=\] ))')
22 - self.regex_status = compile(r'([a-zA-Z_0-9]+)( : active)[^:]*?([a-z]+) = ([0-9.]+(?=%)).*?((?<=finish=)[0-9.]+)min speed=([0-9]+)')
19 + self.regex = dict(disks=re_compile(r' (?P<array>[a-zA-Z_0-9]+) : active .+\['
20 + r'(?P<total_disks>[0-9]+)/'
21 + r'(?P<inuse_disks>[0-9])\]'),
22 + status=re_compile(r' (?P<array>[a-zA-Z_0-9]+) : active .+ '
23 + r'(?P<operation>[a-z]+) = '
24 + r'(?P<operation_status>[0-9.]+).+finish='
25 + r'(?P<finish>([0-9.]+))min speed='
26 + r'(?P<speed>[0-9]+)'))
27
28 def check(self):
25 - raw_data = self._get_raw_data()
26 - if not raw_data:
27 - self.error('Cant read mdstat data from %s' % (self.proc_mdstat))
28 - return False
29 -
30 - md_list = [md[0] for md in self.regex_disks.findall(raw_data)]
31 -
32 - if not md_list:
33 - self.error('No active arrays in %s' % (self.proc_mdstat))
34 - return False
35 - else:
36 - for md in md_list:
37 - self.order.append(md)
38 - self.order.append(''.join([md, '_status']))
39 - self.order.append(''.join([md, '_rate']))
40 - self.definitions['agr_health']['lines'].append([''.join([md, '_health']), md, 'absolute'])
41 - self.definitions[md] = {'options':
42 - [None, '%s disks stats' % md, 'disks', md, 'md.disks', 'stacked'],
43 - 'lines': [[''.join([md, '_total']), 'total', 'absolute'],
44 - [''.join([md, '_inuse']), 'inuse', 'absolute']]}
45 - self.definitions[''.join([md, '_status'])] = {'options':
46 - [None, '%s current status' % md, 'percent', md, 'md.status', 'line'],
47 - 'lines': [[''.join([md, '_resync']), 'resync', 'absolute', 1, 100],
48 - [''.join([md, '_recovery']), 'recovery', 'absolute', 1, 100],
49 - [''.join([md, '_reshape']), 'reshape', 'absolute', 1, 100],
50 - [''.join([md, '_check']), 'check', 'absolute', 1, 100]]}
51 - self.definitions[''.join([md, '_rate'])] = {'options':
52 - [None, '%s operation status' % md, 'rate', md, 'md.rate', 'line'],
53 - 'lines': [[''.join([md, '_finishin']), 'finish min', 'absolute', 1, 100],
54 - [''.join([md, '_rate']), 'megabyte/s', 'absolute', -1, 100]]}
55 - self.info('Plugin was started successfully. MDs to monitor %s' % (md_list))
56 -
57 - return True
58 -
59 - def _get_raw_data(self):
29 + arrays = find_arrays(self._get_raw_data(), self.regex)
30 + if not arrays:
31 + self.error('Failed to read data from /proc/mdstat or there is no active arrays')
32 + return None
33 +
34 + self.order, self.definitions = create_charts(arrays.keys())
35 + return True
36 +
37 + @staticmethod
38 + def _get_raw_data():
39 """
40 Read data from /proc/mdstat
41 :return: str
42 """
43 try:
65 - with open(self.proc_mdstat, 'rt') as proc_mdstat:
66 - raw_result = proc_mdstat.read()
67 - except Exception:
44 + with open('/proc/mdstat', 'rt') as proc_mdstat:
45 + return proc_mdstat.readlines() or None
46 + except (OSError, IOError):
47 return None
69 - else:
70 - raw_result = ' '.join(raw_result.split())
71 - return raw_result
48
49 def _get_data(self):
50 """
51 Parse data from _get_raw_data()
52 :return: dict
53 """
78 - raw_mdstat = self._get_raw_data()
79 - mdstat_disks = self.regex_disks.findall(raw_mdstat)
80 - mdstat_status = self.regex_status.findall(raw_mdstat)
81 - to_netdata = {}
82 -
83 - for md in mdstat_disks:
84 - to_netdata[''.join([md[0], '_total'])] = int(md[1])
85 - to_netdata[''.join([md[0], '_inuse'])] = int(md[2])
86 - to_netdata[''.join([md[0], '_health'])] = int(md[1]) - int(md[2])
87 - to_netdata[''.join([md[0], '_check'])] = 0
88 - to_netdata[''.join([md[0], '_resync'])] = 0
89 - to_netdata[''.join([md[0], '_reshape'])] = 0
90 - to_netdata[''.join([md[0], '_recovery'])] = 0
91 - to_netdata[''.join([md[0], '_finishin'])] = 0
92 - to_netdata[''.join([md[0], '_rate'])] = 0
93 -
94 - for md in mdstat_status:
95 - to_netdata[''.join([md[0], '_' + md[2]])] = round(float(md[3]) * 100)
96 - to_netdata[''.join([md[0], '_finishin'])] = round(float(md[4]) * 100)
97 - to_netdata[''.join([md[0], '_rate'])] = round(float(md[5]) / 1000 * 100)
54 + raw_data = self._get_raw_data()
55 + arrays = find_arrays(raw_data, self.regex)
56 + if not arrays:
57 + return None
58 +
59 + to_netdata = dict()
60 + for array, values in arrays.items():
61 + for key, value in values.items():
62 + to_netdata['_'.join([array, key])] = value
63
64 return to_netdata
65 +
66 +
67 +def find_arrays(raw_data, regex):
68 + if raw_data is None:
69 + return None
70 + data = defaultdict(str)
71 + counter = 1
72 +
73 + for row in (elem.strip() for elem in raw_data):
74 + if not row:
75 + counter += 1
76 + continue
77 + data[counter] = ' '.join([data[counter], row])
78 +
79 + arrays = dict()
80 + for value in data.values():
81 + match = regex['disks'].search(value)
82 + if not match:
83 + continue
84 +
85 + match = match.groupdict()
86 + array = match.pop('array')
87 + arrays[array] = match
88 + arrays[array]['health'] = int(match['total_disks']) - int(match['inuse_disks'])
89 + for operation in OPERATIONS:
90 + arrays[array][operation] = 0
91 +
92 + match = regex['status'].search(value)
93 + if match:
94 + match = match.groupdict()
95 + if match['operation'] in OPERATIONS:
96 + arrays[array][match['operation']] = float(match['operation_status']) * 100
97 + arrays[array]['finish'] = float(match['finish']) * 100
98 + arrays[array]['speed'] = float(match['speed']) / 1000 * 100
99 +
100 + return arrays or None
101 +
102 +
103 +def create_charts(arrays):
104 + order = ['mdstat_health']
105 + definitions = dict(mdstat_health={'options': [None, 'Faulty devices in MD', 'failed disks',
106 + 'health', 'md.health', 'line'],
107 + 'lines': []})
108 + for md in arrays:
109 + order.append(md)
110 + order.append(md + '_status')
111 + order.append(md + '_rate')
112 + definitions['mdstat_health']['lines'].append([md + '_health', md, 'absolute'])
113 + definitions[md] = {'options': [None, '%s disks stats' % md, 'disks', md, 'md.disks', 'stacked'],
114 + 'lines': [[md + '_total_disks', 'total', 'absolute'],
115 + [md + '_inuse_disks', 'inuse', 'absolute']]}
116 + definitions[md + '_status'] = {'options': [None, '%s current status' % md,
117 + 'percent', md, 'md.status', 'line'],
118 + 'lines': [[md + '_resync', 'resync', 'absolute', 1, 100],
119 + [md + '_recovery', 'recovery', 'absolute', 1, 100],
120 + [md + '_reshape', 'reshape', 'absolute', 1, 100],
121 + [md + '_check', 'check', 'absolute', 1, 100]]}
122 + definitions[md + '_rate'] = {'options': [None, '%s operation status' % md,
123 + 'rate', md, 'md.rate', 'line'],
124 + 'lines': [[md + '_finish', 'finish min', 'absolute', 1, 100],
125 + [md + '_speed', 'MB/s', 'absolute', -1, 100]]}
126 + return order, definitions