python-diamond / Diamond

Diamond is a python daemon that collects system metrics and publishes them to Graphite (and others). It is capable of collecting cpu, memory, network, i/o, load and disk metrics. Additionally, it features an API for implementing custom collectors for gathering metrics from almost any source.
http://diamond.readthedocs.org/
MIT License
1.74k stars 599 forks source link

mdstat may fail on some _parse_array_member_state situations #739

Closed labeneator closed 2 years ago

labeneator commented 4 years ago

On my debian stretch box, I have the collector failing due to a regular expression match failing.

To be exact

In [1]: from mdstat import MdStatCollector

In [2]: md = MdStatCollector()

In [3]: md
Out[3]: <mdstat.MdStatCollector at 0x7efefe219dd0>

In [4]: md._parse_mdstat()
sda5[0]
sdc5[1]
raid1
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-9566e1384fa8> in <module>()
----> 1 md._parse_mdstat()

/usr/share/diamond/collectors/mdstat/mdstat.py in _parse_mdstat(self)
    141                 # 'member_count' and 'status' are mandatory keys
    142                 arrays[md_device_name] = {
--> 143                     'member_count': self._parse_array_member_state(block),
    144                     'status': self._parse_array_status(block),
    145                 }

/usr/share/diamond/collectors/mdstat/mdstat.py in _parse_array_member_state(self, block)
    206         for member in members:
    207             print(member)
--> 208             member_dict = device_regexp.match(member).groupdict()
    209
    210             if member_dict['member_state'] == 'S':

AttributeError: 'NoneType' object has no attribute 'groupdict'

device_regexp.match(member) is opportunistically expecting a match. However, there are cases where there might be no match. A quick patch is to verify whether the match succeeds before attempting a groupdict.

labeneator commented 4 years ago

PR above offers a fix for this issue. Here's a quick test off my box.

In [1]: from mdstat import MdStatCollector

In [2]: import re

In [3]: md = MdStatCollector()

In [4]: md._parse_mdstat()
sda5[0]
sdc5[1]
raid1
sda2[0]
sdc2[1]
Out[4]:
{'md1': {'member_count': {'active': 2, 'faulty': 0, 'spare': 0},
  'status': {'actual_members': 2,
   'blocks': 3904448,
   'superblock_version': 1.2,
   'total_members': 2}},
 'md2': {'bitmap': {'allocated_pages': '2',
   'chunk_size': '65536',
   'page_size': '8',
   'total_pages': '2'},
  'member_count': {'active': 2, 'faulty': 0, 'spare': 0},
  'status': {'actual_members': 2,
   'blocks': 239124288,
   'superblock_version': 1.2,
   'total_members': 2}}}

In [5]:

In [5]: !cat /proc/mdstat
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md2 : active raid1 sda5[0] sdc5[1]
      239124288 blocks super 1.2 [2/2] [UU]
      bitmap: 2/2 pages [8KB], 65536KB chunk

md1 : active (auto-read-only) raid1 sda2[0] sdc2[1]
      3904448 blocks super 1.2 [2/2] [UU]

md0 : active raid1 sda1[0] sdc1[1]
      975296 blocks super 1.2 [2/2] [UU]

unused devices: <none>
shortdudey123 commented 2 years ago

Solved by #740