dmidecode的Python解析

昀溪發表於2019-01-23
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
解析dmidecode命令輸出結果,返回JSON格式資料
測試伺服器Dell VMware虛擬機器
測試系統為CentOS 6.x 7.x
Python版本為Python3.6
原版參考的是 https://pypi.org/project/dmidecode/ 但是在Python3上無法直接使用而且我覺得它有些邏輯問題需要做一些修改
"""

import sys
import subprocess

__version__ = "0.1.0"
__author__ = "rex.chen"


class Dmidecode(object):
    def __init__(self):
        # 註釋掉裡面的鍵值意味著忽略某些型別
        self._DMI_TYPE = {
            0: "BIOS Information",        # BIOS資訊 提供商、版本等
            1: "System Information",      # 系統資訊包括伺服器廠商、伺服器的星號、伺服器序列號
            2: "Base Board Information",
            3: "Chassis Information",     # 可以獲取伺服器的高度,比如1U等。
            4: "Processor Information",    # 每個邏輯CPU的資訊,物理CPU數量*核心數量=邏輯CPU數量
            6: "Memory Module Information",
            7: "Cache Information",
            8: "Port Connector Information",
            9: "System Slot Information",
            10: "On Board Device Information",
            11: "OEM Strings",
            15: "System Event Log",
            16: "Physical Memory Array",
            17: "Memory Device",          # 每個記憶體槽位上查的記憶體條資訊,型別、容量、主頻、序列號等
            18: "32-bit Memory Error Information",
            19: "Memory Array Mapped Address",
            20: "Memory Device Mapped Address",
            23: "System Reset",
            24: "Hardware Security",
            30: "Out-of-band Remote Access",
            32: "System Boot Information",
            33: "64-bit Memory Error Information",
            38: "IPMI Device Information",
            41: "Onboard Device",
            # 126: "Inactive",
            # 127: "End Of Table"
        }

    def parse_dmi(self, content):
        """
        解析dmidecode命令輸出
        :param content: 傳遞進來dmidecode命令執行的原始輸出結果
        :return: 重新組裝後的資料字典
        """
        info = {}
        try:
            """
            這裡是一個關鍵點,dmidecode命令輸出其實是層級結構的,它使用製表符\t來表示層級,lines可以列表,但後續處理會比較麻煩
            所以這裡使用迭代器一個是佔用空間少,另外是你每一次傳遞lines到其他地方當呼叫next()或者for迴圈時它都是從上一次的位置
            繼續向下,這樣對於處理這種dmidecode輸出的有層級關係的非結構化資料比較方便。
            """
            lines = iter(content.strip().splitlines())
            while True:
                try:
                    line = next(lines)
                    if line.startswith(b'Handle 0x'):
                        typ = int(str(line).split(',', 2)[1].strip()[len('DMI type'):])
                        if typ in self._DMI_TYPE:
                            if typ not in info:
                                info[self._DMI_TYPE[typ]] = []
                                info[self._DMI_TYPE[typ]].append(self._parse_handle_section(lines))
                            else:
                                info[self._DMI_TYPE[typ]].append(self._parse_handle_section(lines))
                except StopIteration:
                    break
            return info
        except Exception as err:
            print("Error occured in Function parse_dmi")
            print(err)

    def _parse_handle_section(self, lines):
        """
        解析每個type下面的資訊,也就是每一個 Handle 0x 下面的資訊
        :param lines: 傳遞所有的內容進來,也就是之前生成的迭代器,而且這個迭代器是接著上面的位置繼續向後迭代
        :return: 字典,每個type下面的子資訊組成的字典
        """
        data = {}
        title = str(next(lines).strip().decode(encoding='utf-8'))
        try:
            for line in lines:
                line = line.rstrip()
                strline = str(line.strip().decode(encoding='utf-8'))
                if line.startswith(b'\t\t'):
                    data[k].append(strline)
                elif line.startswith(b'\t'):
                    k, v = str(line.strip().decode(encoding='utf-8')).split(":", 1)
                    if v:
                        data[k] = v.strip()
                    else:
                        data[k] = []
                else:
                    break
        except Exception as err:
            print("Error occured in Function parse_handle_section")
            print("Data section is %s " % title)
            print(err)
        return data


def main():
    cmd = "sshpass -p 123456 ssh root@172.16.48.171 '/usr/sbin/dmidecode'"
    mydmi = Dmidecode()
    try:
        completed_process = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        if completed_process.returncode == 0:
            data = completed_process.stdout
            print(mydmi.parse_dmi(data))
        else:
            print("Returncode is not 0")
    except Exception as err:
        print(err)

if __name__ == "__main__":
    try:
        main()
    finally:
        sys.exit()

 

相關文章