"""
MDAdm parsers
=============
Classes to parse ``mdadm`` commands information.
Parsers provided by this module include:
MDAdm - command ``/usr/sbin/mdadm -E {device}``
-----------------------------------------------
MDAdmDetail - command ``/usr/sbin/mdadm -D /dev/md*``
-----------------------------------------------------
"""
from insights.core import CommandParser
from insights.core.exceptions import ParseException, SkipComponent
from insights.core.plugins import parser
from insights.parsers import split_kv_pairs, parse_fixed_table
from insights.specs import Specs
[docs]
class MDAdmDetailDevice(dict):
"""
Parser for single MD device data from ``mdadm -D /dev/md*`` output.
MD arrry device's full path name will be reside in /dev/ and start with "md",
or reside in /dev/md/. For examples: /dev/md0, /dev/md/home .
The md device's properties in <property name> : <property value> format
will be stored seprately, and are accessable via <property name>.
"""
def parse_device(self, content, device_start_index, table_start_index, index):
self['device_name'] = content[device_start_index].split(':')[0]
has_device_table = bool(table_start_index > device_start_index)
kv_pairs_end_index = table_start_index if has_device_table else index
# Parse the key value pairs part in content
if kv_pairs_end_index - device_start_index > 1:
self.update(split_kv_pairs(content[device_start_index + 1:kv_pairs_end_index], split_on=':'))
# Parse the devices info table part in content
self['device_table'] = parse_fixed_table(content[table_start_index:index]) if has_device_table else []
# Empty prased data
if not (len(self) > 2 or self['device_table']):
raise ParseException('Empty parsed data')
@property
def is_internal_bitmap(self):
""" bool: True if using "Internal" for Intent Bitmap """
return self.get("Intent Bitmap") == "Internal"
@property
def device_name(self):
""" str: the name of the device, e.g. /dev/md0 """
return self.get("device_name")
@property
def device_table(self):
""" list: the devices info table """
return self.get("device_table")
[docs]
@parser(Specs.mdadm_D)
class MDAdmDetail(CommandParser, list):
"""
Parser for output of command ``mdadm -D /dev/md*``.
Each MD arrry device will be wrapped in :class:``MDAdmDetailDevice``.
The md device's properties in <property name> : <property value> format
will be stored seprately, and are accessable via <property name>.
Attributes:
unparsable_device_list (list): the name of unparsable devices
Sample output::
/dev/md2:
Version : 1.2
Creation Time : Sun Sep 5 23:19:18 2021
Raid Level : raid1
Array Size : 7501333824 (7153.83 GiB 7681.37 GB)
Used Dev Size : 7501333824 (7153.83 GiB 7681.37 GB)
Raid Devices : 2
Total Devices : 2
Persistence : Superblock is persistent
Intent Bitmap : Internal
Update Time : Sun Sep 26 22:18:13 2021
State : clean
Active Devices : 2
Working Devices : 2
Failed Devices : 0
Spare Devices : 0
Consistency Policy : bitmap
Name : hostname:2 (local to host hostname)
UUID : 245e1231:245e1231:245e1231:245e1231
Events : 1821
Number Major Minor RaidDevice State
0 259 1 0 active sync /dev/nvme2n1
1 259 0 1 active sync /dev/nvme3n1
/dev/md3:
Version : 1.2
Creation Time : Sun Sep 5 23:19:18 2021
...
Examples:
>>> len(mdadm_d)
2
>>> mdadm_d[0].device_name
'/dev/md2'
>>> mdadm_d[0]["UUID"]
'245e1231:245e1231:245e1231:245e1231'
>>> mdadm_d[0].is_internal_bitmap
True
>>> len(mdadm_d[0].device_table)
2
>>> mdadm_d[1].get("Version")
'1.2'
"""
MDADM_ERROR_MSG_PREFIX = "mdadm: "
[docs]
def parse_content(self, content):
if len(content) == 0:
raise SkipComponent("Empty content of command output")
self.unparsable_device_list = []
self.error_messages = []
def _handle_device(device_start_index, table_start_index, index):
if content[device_start_index].startswith(self.MDADM_ERROR_MSG_PREFIX):
self.error_messages.append(content[device_start_index])
return
try:
device_detail = MDAdmDetailDevice()
device_detail.parse_device(content, device_start_index, table_start_index, index)
self.append(device_detail)
except ParseException:
self.unparsable_device_list.append(content[device_start_index].split(':')[0])
# Split the devices content
device_start_index = 0
table_start_index = 0
for index, _line in enumerate(content):
line = _line.strip()
if not line:
continue
# Start line of a new device
if (line.startswith("/dev/md") and line.endswith(":") or
line.startswith(self.MDADM_ERROR_MSG_PREFIX)):
# Handle the last recongnized device
if index > device_start_index:
_handle_device(device_start_index, table_start_index, index)
device_start_index = index
elif "Number Major Minor" in line:
table_start_index = index
# Handle the final device
_handle_device(device_start_index, table_start_index, index + 1)
# Empty prased data
if len(self) < 1:
raise SkipComponent('Empty parsed device')