news 2026/9/23 22:39:06

Salt smbios 执行模块:基于 DMIdecode 的 SMBIOS 硬件信息采集实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Salt smbios 执行模块:基于 DMIdecode 的 SMBIOS 硬件信息采集实战指南
  • 运维
  • 配置管理
  • 后端

【免费下载链接】salt

Software to automate the management and configuration of infrastructure and applications at scale.

项目地址:https://gitcode.com/gh_mirrors/sa/salt
点击查看免费下载

导读

salt.modules.smbios是 Salt 中负责读取 SMBIOS(System Management BIOS)与 DMI(Desktop Management Interface)硬件信息的执行模块,它通过调用系统自带的dmidecode(或smbios)命令完成底层解析,为 Minion 提供 BIOS 厂商、系统 UUID、主板序列号、机箱类型、处理器频率等硬件元数据。在 Salt 的实际运维场景中,它既是salt '*' smbios.get system-uuid这类命令式采集的直接入口,也是核心 grains(如uuidbiosversionproductnameserialnumber)的底层数据来源,广泛应用于资产盘点、硬件型号识别、虚拟化平台检测等自动化任务。读完本文,你将掌握smbios.getsmbios.records的完整用法、DMI 类型对照、clean过滤机制,以及该模块与 grains 系统之间的调用关系。

模块概览:SMBIOS/DMI 与 dmidecode

SMBIOS 与 DMI 是由 DMTF(Distributed Management Task Force)定义的固件标准,主板 BIOS 会将这些硬件信息写入内存中的 SMBIOS 表,操作系统则通过dmidecode之类的工具读取展示。salt.modules.smbios的设计思路很直接:不做底层硬件访问,而是解析dmidecode命令的文本输出。这一点在模块 docstring 中写得非常明确——"Interface to SMBIOS/DMI (Parsing through dmidecode)",模块源码位于 salt/modules/smbios.py。

加载条件:__virtual__

该模块不是一个"必然加载"的模块,它的__virtual__()函数会在 Minion 启动时被 Salt 加载器调用,只有系统中存在dmidecodesmbios可执行文件时模块才会注册成功,否则模块加载失败并记录原因:

def __virtual__(): """ Only work when dmidecode is installed. """ return ( bool(salt.utils.path.which_bin(["dmidecode", "smbios"])), "The smbios execution module failed to load: neither dmidecode nor smbios in" " the path.", )

从源码结构可以推断,which_bin按顺序在两个候选命令中查找可用的那个,dmidecode优先。这也意味着:

  • 在大多数 Linux 发行版上,需要先安装dmidecode(如 Debian/Ubuntu 的dmidecode包、RHEL/CentOS 的dmidecode包)才能使用本模块;
  • 在某些 SmartOS/SunOS 系统上,原生smbios命令可被用作替代品。

smbios.get:按 DMI 字符串名获取单项硬件信息

smbios.get(string, clean=True)是模块最常用的入口,它从 SMBIOS 信息中抓取单个 DMI 字符串,底层等价于执行dmidecode -s <string>

支持的 DMI 字符串

该函数完整支持的字符串参数如下(源自模块源码 docstring):

分类支持的 string 参数
BIOSbios-vendorbios-versionbios-release-date
System(系统)system-manufacturersystem-product-namesystem-versionsystem-serial-numbersystem-uuid
Baseboard(主板)baseboard-manufacturerbaseboard-product-namebaseboard-versionbaseboard-serial-numberbaseboard-asset-tag
Chassis(机箱)chassis-manufacturerchassis-typechassis-versionchassis-serial-numberchassis-asset-tag
Processor(处理器)processor-familyprocessor-manufacturerprocessor-versionprocessor-frequency

clean参数:过滤厂商占位假数据

clean默认值为True,其作用是剔除众所周知的假信息,例如全零 UUID、000000000之类的假序列号、以及 OEM 厂商预置的占位字符串(如 "To be filled by O.E.M."、"Not Specified" 等)。当clean=True且检测到这类脏值时,函数直接返回None

CLI 用法

salt '*' smbios.get system-uuid clean=False salt '*' smbios.get system-manufacturer salt '*' smbios.get bios-version salt '*' smbios.get processor-frequency

配合clean=False可以拿到未过滤的原始值(例如在排查固件预置脏数据时很有用)。

实现原理

get的核心实现非常短小精悍(见 salt/modules/smbios.py 中的get函数):

def get(string, clean=True): val = _dmidecoder(f"-s {string}").strip() # Cleanup possible comments in strings. val = "\n".join([v for v in val.split("\n") if not v.startswith("#")]) if val.startswith("/dev/mem") or clean and not _dmi_isclean(string, val): val = None return val

这里有三点值得注意:

  1. _dmidecoder底层调用的是salt.modules.cmdmod._run_quiet(dmidecoder + args)(源码中_dmidecoder函数),即静默执行dmidecode -s <string>并捕获标准输出;
  2. 输出中的#注释行会被剥除;
  3. 若输出以/dev/mem开头(dmidecode 无法访问内存时的报错信息),或clean=True_dmi_isclean判定为脏值,则统一返回None

smbios.records:结构化返回完整 DMI 记录

smbios.records(rec_type=None, fields=None, clean=True)返回结构化的 DMI 记录列表,适合做批量采集与资产分析。它支持三个参数:

  • rec_type:只返回指定 DMI 类型的记录,传整数类型号(如4表示处理器),None时返回全部记录;
  • fields:只保留指定字段的键值对(字段名为 dmidecode 输出中"属性名转小写、空格转下划线"后的形式,如core_countthread_countcurrent_speed);
  • clean:与get中的语义一致,默认True

CLI 用法

# 返回全部 DMI 记录 salt '*' smbios.records clean=False # 只返回 DMI type 14(Group Associations)的记录 salt '*' smbios.records 14 # 只返回处理器记录中的部分字段 salt '*' smbios.records 4 core_count,thread_count,current_speed

DMI 类型对照表(SMBIOS 规范定义)

recordsrec_type参数接受以下 SMBIOS 规范定义的类型号(完整继承自模块 docstring):

Type信息类别Type信息类别
0BIOS22Portable Battery
1System23System Reset
2Baseboard24Hardware Security
3Chassis25System Power Controls
4Processor26Voltage Probe
5Memory Controller27Cooling Device
6Memory Module28Temperature Probe
7Cache29Electrical Current Probe
8Port Connector30Out-of-band Remote Access
9System Slots31Boot Integrity Services
10On Board Devices32System Boot
11OEM Strings3364-bit Memory Error
12System Configuration Options34Management Device
13BIOS Language35Management Device Component
14Group Associations36Management Device Threshold Data
15System Event Log37Memory Channel
16Physical Memory Array38IPMI Device
17Memory Device39Power Supply
1832-bit Memory Error40Additional Information
19Memory Array Mapped Address41Onboard Devices Extended Information
20Memory Device Mapped Address42Management Controller Host Interface
21Built-in Pointing Device

例如smbios.records 17会返回所有内存条(Memory Device)的结构化记录,其中包含sizespeedmanufacturerpart_number等字段,可用于内存资产盘点。

返回的数据结构

records的输出是"一条 DMI 记录对应一个字典"的列表,每条记录形如:

{ "handle": "0x0001", # dmidecode 的 Handle 编号 "description": "System Information", # 记录类型描述 "type": 1, # DMI 类型号(int) "data": { "manufacturer": "Dell Inc.", "product_name": "PowerEdge R740", "serial_number": "ABC123", ... } }
  • 顶层键handledescriptiontype_dmi_parse解析生成(见 salt/modules/smbios.py 中的_dmi_parse);
  • 内部字段键名为 dmidecode 输出属性名的小写下划线形式,例如输出中的Product Name:会变成键product_name_dmi_data中通过key.strip().lower().replace(" ", "_")完成转换);
  • 字段值会经过_dmi_cast的类型转换:能转成整数的转成int,包含逗号的按逗号拆成列表(但serialpartassetproduct这类键保留字符串原样);
  • 同一键下的多行子项(例如Characteristics:下面的PCI is supportedPNP is supported等)会被组装成列表。

底层解析链路:从 dmidecode 文本到结构化数据

recordsget共享同一套底层解析工具函数,调用链为:

smbios.records / smbios.get └─> _dmidecoder(args) # 静默执行 dmidecode [-s/-t args] └─> _dmi_parse(data) # 按 "Handle ..." 正则切分记录 └─> _dmi_data(...) # 将单个 handle 的文本行解析为 dict └─> _dmi_cast(...) # 值类型转换(int / list / str) └─> _dmi_isclean(...) # 脏值判定(clean 模式)

各工具函数的作用与实现要点(全部位于 salt/modules/smbios.py):

  • _dmidecoder(args=None):通过salt.utils.path.which_bin(["dmidecode", "smbios"])定位可执行文件,再以salt.modules.cmdmod._run_quiet静默执行并返回输出;无参数时执行裸dmidecode(全量 dump),有参数时执行dmidecode <args>
  • _dmi_parse(data, clean=True, fields=None):用正则(handle [0-9]x[0-9a-f]+[^\n]+)\n把全量输出按 Handle 切分成多条记录,逐条提取handle编号与type类型号,第一条描述行作为description,剩余内容交给_dmi_data。空记录与过滤后无数据的记录在clean=False时仍会保留(仅有 handle/description/type)。
  • _dmi_data(dmi_raw, clean, fields):逐行识别\t(一级键值)与\t\t(子项列表)两种缩进层级,支持"同一键既有单值又有列表"的罕见情况(源码注释提到在所有系统上只遇到过 1 次)。
  • _dmi_cast(key, val, clean=True):先做_dmi_isclean脏值检查,再按规则把值转为 int 或逗号分隔列表。
  • _dmi_isclean(key, val):脏值判定规则集,包括:
    • 空值或以none开头 → 判脏;
    • 键含uuid→ 依次用 RFC 4122 的 version 1~4 尝试解析,全部失败则判脏(该逻辑在源码中明确为Try each version (1-5) of RFC4122,实际循环范围为 1~4);
    • 键匹配serial|part|version→ 剔除全零串、1234567家族假序列号以及含sernumpart_numberspecifiedfilledapplicable等关键词的占位值;
    • 键匹配asset|manufacturer→ 剔除含manufacturerto be filledavailableassetno(n|t)等模式的占位值;
    • 其他键 → 剔除to be filledun(known|specified)not (asset|provided|...)之类的"未定义"描述。

这套clean机制保证了默认情况下 Salt 返回的硬件信息是"干净的",避免把 OEM 占位符当真实资产数据写入配置管理流程。

与 grains 系统的集成:uuid 与硬件 grains 的底层来源

salt.modules.smbios不仅是一个可独立调用的执行模块,它还深度参与了核心 grains 的生成。在 salt/grains/core.py 中:

  1. 模块顶部直接导入并注册了smbios.get/smbios.records__salt__虚拟环境(第 135-136 行),这样 grains 函数才能调用它;
  2. _hw_data相关的 grains 生成逻辑中(第 3370-3400 行附近),当 Linux 上不存在/sys/class/dmi/id(内核未开启 CONFIG_DMIID 的 sysfs 接口)时,会回退到 dmidecode 路径:
elif salt.utils.path.which_bin(["dmidecode", "smbios"]) is not None and not (...): uuid = __salt__"smbios.get" ... grains = { "biosversion": __salt__"smbios.get", "biosvendor": __salt__"smbios.get", "productname": __salt__"smbios.get", "manufacturer": __salt__"smbios.get", "biosreleasedate": __salt__"smbios.get", "uuid": uuid, } grains = {key: val for key, val in grains.items() if val is not None} for serial in ( "system-serial-number", "chassis-serial-number", "baseboard-serial-number", ): serial = __salt__"smbios.get" if serial is not None: grains["serialnumber"] = serial break

从这段代码可以提炼出几个重要的实现事实:

  • uuidgrainsmbios.get("system-uuid")提供,且结果统一转小写;若获取失败则回退到已从/sys/hypervisor/uuid(Xen 半虚拟化)读取的值;
  • serialnumbergrain依次尝试system-serial-numberchassis-serial-numberbaseboard-serial-number,取第一个非空值;
  • clean=True的默认行为在这里自动过滤了占位序列号,所以最终 grains 中通常不会出现 "To be filled by O.E.M." 之类的脏值;
  • 在 SmartOS(smbios 仅全局区可用)以及 SunOS/SPARC(系统不导出 SMBIOS 表)上会跳过该路径,这正是__virtual__中把smbios命令纳入候选的原因。

此外,核心 grains 中的虚拟化检测_virtual()也会在部分平台(如 FreeBSD 等)借助dmidecode/smbios输出识别虚拟机类型。

测试覆盖与验证方式

虽然仓库中暂无针对smbios模块本身的独立单元测试文件,但其在 grains 体系中的行为已被测试覆盖。在 tests/pytests/unit/grains/test_core.py 中,多个虚拟化 grains 测试(如test_virtual_nutanix_*系列)通过patch.dict(core.__salt__, {"smbios.get": salt.modules.smbios.get, ...})的方式把真实的smbios.get注册进__salt__虚拟环境,再模拟dmidecode输出(包括 "System Boot Information / Status: No errors detected" 等 DMI type 32 记录)来断言virtualvirtual_subtype等 grains 的结果。这说明:

  • smbios.get/smbios.records在 grains 上下文中通过__salt__点号调用约定使用;
  • 测试以 mockcmd.run_all输出的方式间接验证了 dmidecode 解析逻辑;
  • 对于本模块,也可以在目标 Minion 上直接执行salt '*' smbios.records 1 clean=Falsedmidecode -t 1对比结果来验证解析正确性。

实战场景小结

场景推荐调用
获取机器唯一标识做资产入库salt '*' smbios.get system-uuid
品牌型号盘点salt '*' smbios.get system-manufacturersalt '*' smbios.get system-product-name
排查内存条配置salt '*' smbios.records 17
处理器信息汇总salt '*' smbios.records 4 core_count,thread_count,current_speed
查看原始未过滤值(排查脏数据)salt '*' smbios.get system-serial-number clean=False
判断是否虚拟化/容器环境salt '*' smbios.records 1(比对system-manufacturer

使用前提与限制

  • 需要 Minion 系统安装dmidecode(或存在可用的smbios命令),否则模块无法加载,报错信息为 "The smbios execution module failed to load: neither dmidecode nor smbios in the path.";
  • 读取 SMBIOS 表通常需要 root 权限,Minion 进程应以具备足够权限的用户运行;
  • 在容器、部分云主机(无物理 BIOS 暴露)上,dmidecode可能输出/dev/mem访问错误或被虚拟化平台填充为占位值,此时clean=True会把这些值过滤为None
  • 不同固件厂商的占位符文本风格差异很大,clean过滤规则基于模块中_dmi_isclean的正则集合,若遇到未被覆盖的厂商脏值,可在调用时使用clean=False获取原始输出后自行清洗。
  • 运维
  • 配置管理
  • 后端

【免费下载链接】salt

Software to automate the management and configuration of infrastructure and applications at scale.

项目地址:https://gitcode.com/gh_mirrors/sa/salt
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 22:38:30

《算法导论》没有第四版:识别CLRS第三版真伪与构建可验证学习环境

简介&#xff1a;《算法导论》第四版&#xff08;2022年MIT Press原版&#xff09;是计算机科学领域公认的权威教材&#xff0c;面向高校本科生、研究生及算法工程师&#xff0c;系统解决算法设计、分析与实现的核心能力培养问题。全书涵盖算法基础、排序与选择、数据结构、图算…

作者头像 李华
网站建设 2026/9/23 22:37:26

Vue 动态路由加载实战:从权限控制到路由守卫原理与实现

做后台管理系统这几年&#xff0c;我几乎每个项目都会碰到“动态路由加载”这个需求。很多刚接触 Vue 的同学以为动态路由就是把路由表从静态改成动态&#xff0c;其实远没这么简单。它背后牵扯到权限控制、菜单渲染、路由守卫的执行时机、刷新后状态恢复等一系列问题。今天我就…

作者头像 李华
网站建设 2026/9/23 22:33:57

C/C++宏定义:原理、技巧与最佳实践

1. 宏定义基础与核心概念在C/C开发者的日常工作中&#xff0c;宏定义&#xff08;#define&#xff09;就像瑞士军刀中的万能工具&#xff0c;看似简单却蕴含巨大能量。作为预处理器指令&#xff0c;它会在编译器看到代码之前完成文本替换工作。这种机制虽然原始&#xff0c;却为…

作者头像 李华
网站建设 2026/9/23 22:32:59

日语动词活用规则全解析:分类、变形与音便规律一次学透

日语学到动词活用&#xff0c;很多人的心态会从"我好像能看懂日语"瞬间变成"我怎么一个词都不认识了"。五十音图背得滚瓜烂熟&#xff0c;结果课文里同一个动词&#xff0c;一会儿是書かない&#xff0c;一会儿是書いて&#xff0c;一会儿是書けば&#xf…

作者头像 李华