mdstat.chart.py 4.9 KB

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