- 运维
- 配置管理
- 后端
【免费下载链接】salt
Software to automate the management and configuration of infrastructure and applications at scale.
导读
salt.modules.smbios是 Salt 中负责读取 SMBIOS(System Management BIOS)与 DMI(Desktop Management Interface)硬件信息的执行模块,它通过调用系统自带的dmidecode(或smbios)命令完成底层解析,为 Minion 提供 BIOS 厂商、系统 UUID、主板序列号、机箱类型、处理器频率等硬件元数据。在 Salt 的实际运维场景中,它既是salt '*' smbios.get system-uuid这类命令式采集的直接入口,也是核心 grains(如uuid、biosversion、productname、serialnumber)的底层数据来源,广泛应用于资产盘点、硬件型号识别、虚拟化平台检测等自动化任务。读完本文,你将掌握smbios.get与smbios.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 加载器调用,只有系统中存在dmidecode或smbios可执行文件时模块才会注册成功,否则模块加载失败并记录原因:
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 参数 |
|---|---|
| BIOS | bios-vendor、bios-version、bios-release-date |
| System(系统) | system-manufacturer、system-product-name、system-version、system-serial-number、system-uuid |
| Baseboard(主板) | baseboard-manufacturer、baseboard-product-name、baseboard-version、baseboard-serial-number、baseboard-asset-tag |
| Chassis(机箱) | chassis-manufacturer、chassis-type、chassis-version、chassis-serial-number、chassis-asset-tag |
| Processor(处理器) | processor-family、processor-manufacturer、processor-version、processor-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这里有三点值得注意:
_dmidecoder底层调用的是salt.modules.cmdmod._run_quiet(dmidecoder + args)(源码中_dmidecoder函数),即静默执行dmidecode -s <string>并捕获标准输出;- 输出中的
#注释行会被剥除; - 若输出以
/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_count、thread_count、current_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_speedDMI 类型对照表(SMBIOS 规范定义)
records的rec_type参数接受以下 SMBIOS 规范定义的类型号(完整继承自模块 docstring):
| Type | 信息类别 | Type | 信息类别 |
|---|---|---|---|
| 0 | BIOS | 22 | Portable Battery |
| 1 | System | 23 | System Reset |
| 2 | Baseboard | 24 | Hardware Security |
| 3 | Chassis | 25 | System Power Controls |
| 4 | Processor | 26 | Voltage Probe |
| 5 | Memory Controller | 27 | Cooling Device |
| 6 | Memory Module | 28 | Temperature Probe |
| 7 | Cache | 29 | Electrical Current Probe |
| 8 | Port Connector | 30 | Out-of-band Remote Access |
| 9 | System Slots | 31 | Boot Integrity Services |
| 10 | On Board Devices | 32 | System Boot |
| 11 | OEM Strings | 33 | 64-bit Memory Error |
| 12 | System Configuration Options | 34 | Management Device |
| 13 | BIOS Language | 35 | Management Device Component |
| 14 | Group Associations | 36 | Management Device Threshold Data |
| 15 | System Event Log | 37 | Memory Channel |
| 16 | Physical Memory Array | 38 | IPMI Device |
| 17 | Memory Device | 39 | Power Supply |
| 18 | 32-bit Memory Error | 40 | Additional Information |
| 19 | Memory Array Mapped Address | 41 | Onboard Devices Extended Information |
| 20 | Memory Device Mapped Address | 42 | Management Controller Host Interface |
| 21 | Built-in Pointing Device | — | — |
例如smbios.records 17会返回所有内存条(Memory Device)的结构化记录,其中包含size、speed、manufacturer、part_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", ... } }- 顶层键
handle、description、type由_dmi_parse解析生成(见 salt/modules/smbios.py 中的_dmi_parse); - 内部字段键名为 dmidecode 输出属性名的小写下划线形式,例如输出中的
Product Name:会变成键product_name(_dmi_data中通过key.strip().lower().replace(" ", "_")完成转换); - 字段值会经过
_dmi_cast的类型转换:能转成整数的转成int,包含逗号的按逗号拆成列表(但serial、part、asset、product这类键保留字符串原样); - 同一键下的多行子项(例如
Characteristics:下面的PCI is supported、PNP is supported等)会被组装成列表。
底层解析链路:从 dmidecode 文本到结构化数据
records与get共享同一套底层解析工具函数,调用链为:
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家族假序列号以及含sernum、part_number、specified、filled、applicable等关键词的占位值; - 键匹配
asset|manufacturer→ 剔除含manufacturer、to be filled、available、asset、no(n|t)等模式的占位值; - 其他键 → 剔除
to be filled与un(known|specified)、not (asset|provided|...)之类的"未定义"描述。
- 空值或以
这套clean机制保证了默认情况下 Salt 返回的硬件信息是"干净的",避免把 OEM 占位符当真实资产数据写入配置管理流程。
与 grains 系统的集成:uuid 与硬件 grains 的底层来源
salt.modules.smbios不仅是一个可独立调用的执行模块,它还深度参与了核心 grains 的生成。在 salt/grains/core.py 中:
- 模块顶部直接导入并注册了
smbios.get/smbios.records到__salt__虚拟环境(第 135-136 行),这样 grains 函数才能调用它; - 在
_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从这段代码可以提炼出几个重要的实现事实:
uuidgrain由smbios.get("system-uuid")提供,且结果统一转小写;若获取失败则回退到已从/sys/hypervisor/uuid(Xen 半虚拟化)读取的值;serialnumbergrain依次尝试system-serial-number、chassis-serial-number、baseboard-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 记录)来断言virtual、virtual_subtype等 grains 的结果。这说明:
smbios.get/smbios.records在 grains 上下文中通过__salt__点号调用约定使用;- 测试以 mock
cmd.run_all输出的方式间接验证了 dmidecode 解析逻辑; - 对于本模块,也可以在目标 Minion 上直接执行
salt '*' smbios.records 1 clean=False与dmidecode -t 1对比结果来验证解析正确性。
实战场景小结
| 场景 | 推荐调用 |
|---|---|
| 获取机器唯一标识做资产入库 | salt '*' smbios.get system-uuid |
| 品牌型号盘点 | salt '*' smbios.get system-manufacturer、salt '*' 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.
相关推荐
Clover Bootloader SMBIOS信息生成:自定义硬件标识终极指南
Clover Bootloader SMBIOS信息生成:自定义硬件标识终极指南 Clover Bootloader 是一个强大的引导加载程序,支持在UEFI和
固件操作系统Salt 的 osquery 执行模块:用 SQL 驱动主机取证与系统信息采集
Salt 的 osquery 执行模块:用 SQL 驱动主机取证与系统信息采集 Salt(本项目即开源版本 salt)自 2015.8.0 起内置了 osque
运维配置管理后端Salt 执行模块 systemd_service 完全指南:基于 systemd 的服务管理实战
Salt 执行模块 systemd_service 完全指南:基于 systemd 的服务管理实战 本文是 Salt 官方参考文档 doc/ref/module
运维配置管理后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考