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