import re import json from itertools import takewhile from enum import Enum __all__ = ['DmiParser'] DmiParserState = Enum ( 'DmiParserState', ( 'GET_SECT', 'GET_PROP', 'GET_PROP_ITEM', ) ) class DmiParserSectionHandle(object): '''A handle looks like this Handle 0x0066, DMI type 148, 48 bytes ''' def __init__(self): self.id= '' self.type = '' self.bytes = 0 def __str__(self): return json.dumps(self.__dict__) class DmiParserSectionProp(object): '''A property looks like this Characteristics: 3.3 V is provided PME signal is supported SMBus signal is supported ''' def __init__(self, value:str): self.values = [] if value: self.append(value) def __str__(self): return json.dumps(self.__dict__) def append(self, item:str): self.values.append(item) class DmiParserSection(object): '''A section looks like this On Board Device 1 Information Type: Video Status: Enabled Description: ServerEngines Pilot III ''' def __init__(self): self.handle = None self.name = '' self.props = {} def __str__(self): return json.dumps(self.__dict__) def append(self, key:str, prop:str): self.props[key] = prop class DmiParser(object): '''This parse dmidecode output to JSON ''' def __init__(self, text:str, **kwargs): ''' text: output of command dmidecode kwargs: these will pass to json.dumps ''' self._text = text self._kwargs = kwargs self._indentLv = lambda l: len(list(takewhile(lambda c: "\t" == c, l))) self._sections = [] if type(text) is not str: raise TypeError("%s want a %s but got %s" %( self.__class__, type(__name__), type(text))) self._parse(text) def __str__(self): return json.dumps(self._sections, **self._kwargs) def _parse(self, text:str): lines = self._text.splitlines() rhandle = r'^Handle\s(.+?),\sDMI\stype\s(\d+?),\s(\d+?)\sbytes$' section = None prop = None state = None k, v = None, None for i, l in enumerate(lines): if i == len(lines) - 1 or DmiParserState.GET_SECT == state: # Add previous section if exist if section: # Add previous prop if exist if prop: section.append(k, json.loads(str(prop))) prop = None self._sections.append(json.loads(str(section))) section = None if not l: continue if l.startswith('Handle'): state = DmiParserState.GET_SECT handle = DmiParserSectionHandle() match = re.match(rhandle, l) handle.id, handle.type, handle.bytes = match.groups() continue if DmiParserState.GET_SECT == state: section = DmiParserSection() section.handle = json.loads(str(handle)) section.name = l state = DmiParserState.GET_PROP continue if DmiParserState.GET_PROP == state: k, v = [x.strip() for x in l.split(':', 1)] prop = DmiParserSectionProp(v) lv = self._indentLv(l) - self._indentLv(lines[i+1]) if v: if not lv: section.append(k, json.loads(str(prop))) prop = None elif -1 == lv: state = DmiParserState.GET_PROP_ITEM continue else: if -1 == lv: state = DmiParserState.GET_PROP_ITEM continue # Next section for this handle if not self._indentLv(lines[i+1]): state = DmiParserState.GET_SECT if DmiParserState.GET_PROP_ITEM == state: prop.append(l.strip()) lv = self._indentLv(l) - self._indentLv(lines[i+1]) if lv: section.append(k, json.loads(str(prop))) prop = None if lv > 1: state = DmiParserState.GET_SECT else: state = DmiParserState.GET_PROP if '__main__' == __name__: text='''# dmidecode 3.0 Getting SMBIOS data from sysfs. SMBIOS 2.7 present. Handle 0x0003, DMI type 2, 17 bytes Base Board Information Manufacturer: Intel Corporation Product Name: S2600WT2R Version: H21573-372 Serial Number: BQWL81150522 Asset Tag: Base Board Asset Tag Features: Board is a hosting board Board is replaceable Location In Chassis: Part Component Chassis Handle: 0x0000 Type: Motherboard Contained Object Handles: 0 ''' # just print parser = DmiParser(text) #parser = DmiParser(text, sort_keys=True, indent=2) print("parser is %s" %(type(parser))) print(parser) # if you want a string dmistr = str(parser) print("dmistr is %s" %(type(dmistr))) print(dmistr) # if you want a data structure dmidata = json.loads(str(parser)) print("dmidata is %s" %(type(dmidata))) print(dmidata)