Salt 的 pkgutil 执行模块:基于 OpenCSW 管理 Solaris 软件包全指南
【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址: https://gitcode.com/gh_mirrors/sa/salt
导读
本文深入讲解 Salt 中面向 Solaris 操作系统的pkgutil执行模块(salt/modules/pkgutil.py)。该模块是 Salt 虚拟pkg模块在 Solaris + OpenCSW 场景下的具体实现,通过调用 OpenCSW 的pkgutil(1)与系统自带的pkginfo(1)完成软件包的刷新、查询、安装、升级与卸载。读完本文,你将掌握pkgutil模块的加载条件、全部函数与参数、底层命令映射关系、返回值结构,并能直接在 Salt 命令行与 SLS 状态文件中正确使用它管理 Solaris 上的软件包。
模块定位:pkg 虚拟模块的 Solaris Provider
Salt 的包管理入口是一个名为pkg的虚拟模块:用户在命令行或状态文件中写pkg.install、pkg.latest时,Salt 的加载器会根据目标系统的特性自动选择真正干活的执行模块。doc/ref/modules/all/salt.modules.pkg.rst 中明确列出,pkg虚拟模块由以下模块之一提供,其中:
:py:mod:`~salt.modules.pkgutil` Solaris-based OSes using OpenCSW's ``pkgutil(1)``也就是说,当被管理的主机是基于 Solaris 且使用 OpenCSW 的软件仓库时,pkg.xxx系列命令实际由pkgutil模块承接。同属 Solaris 阵营的还有使用pkgadd(1M)的solarispkg模块、使用 IPSpkg(1)的solarisipspkg模块——三者分工不同,选择依据是目标主机采用的包工具。
加载条件与虚拟名
pkgutil模块定义了虚拟名__virtualname__ = "pkgutil",并通过__virtual__()决定是否加载(salt/modules/pkgutil.py):
def __virtual__(): """ Set the virtual pkg module if the os is Solaris """ if __grains__["os_family"] == "Solaris": return __virtualname__ return ( False, "The pkgutil execution module cannot be loaded: " "only available on Solaris systems.", )关键点:
- 判断依据是 grain
os_family == "Solaris",与具体 Solaris 版本无关(Solaris 10、11 及 OpenSolaris 衍生系统均适用)。 - 当不满足条件时返回
(False, "错误说明"),模块不会加载,此时执行pkg.install会得到类似'pkg.install' is not available的错误。 - 模块 docstring 特别提醒:如果 Salt 没有选择本模块而用了其他模块(或报出上述错误),可以参考 Salt 文档中的module-provider-override(模块提供者覆盖)机制,在 minion 配置中用
providers强制指定pkg由pkgutil提供。
底层命令依赖
该模块不自行实现包仓库逻辑,而是封装了 OpenCSW 环境下的两个外部命令:
| 命令 | 用途 | 模块内使用点 |
|---|---|---|
/opt/csw/bin/pkgutil | OpenCSW 包管理工具,负责刷新仓库、查询可用/可升级包、安装与升级 | refresh_db、upgrade_available、list_upgrades、upgrade、latest_version、install、remove |
/usr/bin/pkginfo | Solaris 系统自带命令,用于枚举当前已安装的包 | list_pkgs |
模块源码对每个函数都给出了对应的底层命令与参数(如pkgutil -U、pkgutil -A --parse、pkgutil -yu、pkgutil -yr),使用该模块前应确认目标主机已安装 OpenCSW 且/opt/csw/bin下存在pkgutil。
仓库数据刷新:refresh_db
salt '*' pkgutil.refresh_dbrefresh_db 执行/opt/csw/bin/pkgutil -U更新 OpenCSW 仓库数据库,返回布尔值表示是否成功:
def refresh_db(): salt.utils.pkg.clear_rtag(__opts__) return __salt__"cmd.retcode" == 0两个实现细节值得注意:
- 通过
cmd.retcode判断退出码是否为 0 来确定成功与否; - 调用
salt.utils.pkg.clear_rtag(__opts__)清除 Salt 内部的 rtag 标记文件。这是 Salt 包管理体系的通用机制:rtag 用于记录"最近一次刷新"的时间戳,避免在同一个 state 运行中多个 state 反复触发仓库刷新。手动调用refresh_db前清除 rtag,保证后续pkgstate 中refresh: True的语义仍然成立。在 tests/pytests/unit/modules/test_pkgutil.py 中,test_refresh_db同时 mock 了cmd.retcode与clear_rtag来验证这一行为。
已安装包清单:list_pkgs 与 version
list_pkgs
salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs versions_as_list=Truelist_pkgs 返回{'<package_name>': '<version>'}形式的字典。其数据来源是系统命令/usr/bin/pkginfo -x,源码中对此有精妙的解析逻辑(salt/modules/pkgutil.py):
# Package information returned two lines per package. On even-offset # lines, the package name is in the first column. On odd-offset lines, the # package version is in the second column. lines = __salt__"cmd.run".splitlines() for index, line in enumerate(lines): if index % 2 == 0: name = line.split()[0].strip() if index % 2 == 1: version_num = line.split()[1].strip() __salt__"pkg_resource.add_pkg"也就是说,pkginfo -x每个包输出两行(偶数行包名、奇数行版本号),模块按行号奇偶配对解析。解析结果通过pkg_resource.add_pkg汇总、pkg_resource.sort_pkglist排序,并深拷贝写入__context__["pkg.list_pkgs"]缓存。
其他行为:
versions_as_list=True时,返回的每个包版本以列表形式呈现(兼容同版本多实例场景);removed=True参数在当前实现中直接返回{}(源码注释注明 "removed not yet implemented or not applicable");use_context参数控制是否优先读取上下文缓存,配合_list_pkgs_from_context(salt/modules/pkgutil.py)避免在同一进程内重复执行pkginfo命令——测试 test_list_pkgs_no_context 专门验证了use_context=False时不会调用上下文读取逻辑。
version
salt '*' pkgutil.version CSWpythonversion 直接委托给pkg_resource.version,返回已安装包的版本字符串;未安装则返回空字符串。
版本查询:latest_version 与 upgrade_available
latest_version
salt '*' pkgutil.latest_version CSWpython salt '*' pkgutil.latest_version <package1> <package2> <package3> ...latest_version 返回指定包当前可安装/可升级的最新版本:
- 支持一次传入多个包名,返回
{name: version}字典;只传一个包名时直接返回字符串; - 若包已是最新版本,对应返回空字符串;
refresh=True(默认)时先调用refresh_db();- 底层命令为
/opt/csw/bin/pkgutil -a --parse <names>,并对每行输出做健壮性处理(ValueError时跳过); - 版本比较使用
salt.utils.versions.compare(ver1=cver, oper="<", ver2=nver),仅当已装版本低于可用版本时才返回新版本,且比较前会去掉version_rev中的修订号(split(",")[0])。
模块同时提供了一个被标记为废弃的别名:
# available_version is being deprecated available_version = salt.utils.functools.alias_function( latest_version, "available_version" )(salt/modules/pkgutil.py)。新代码应使用latest_version。也正因如此,API 文档salt.modules.pkgutil.rst通过:exclude-members: available_version排除了该成员的自动文档生成。
upgrade_available
salt '*' pkgutil.upgrade_available CSWpythonupgrade_available 是轻量级的"是否有升级"检查,执行pkgutil -c --parse --single <name>,取输出第三列(out.split()[2]):
- 若为
SAME或命令无输出,返回空字符串(无可升级); - 否则返回可用版本号。
单元测试 test_upgrade_available 覆盖了SAME、可升级、无输出三种分支。
升级管理:list_upgrades 与 upgrade
list_upgrades
salt '*' pkgutil.list_upgradeslist_upgrades 列出系统上所有可升级的包:
- 默认
refresh=True,先刷新仓库; - 执行
/opt/csw/bin/pkgutil -A --parse,逐行按\t切分; - 跳过状态为
SAME(已最新)与not installed(未安装)的行,其余记入upgrades[包名] = 版本; - 返回形如
{'<pkg>': '<version>'}的字典。
upgrade
salt '*' pkgutil.upgradeupgrade 升级系统上全部软件包:
- 默认先
refresh_db(); - 调用
list_pkgs()记录升级前的包清单old; - 执行
/opt/csw/bin/pkgutil -yu完成全量升级; - 弹出
__context__中的pkg.list_pkgs缓存,重新调用list_pkgs()得到new; - 通过
salt.utils.data.compare_dicts(old, new)返回变更字典,结构为:
{'<package>': {'old': '<old-version>', 'new': '<new-version>'}}注意第 4 步的"弹缓存再重查"是模块内所有变更类操作(upgrade、install、remove)的统一模式,确保返回值反映的是真实变更而非过期缓存。
安装:install
salt '*' pkg.install <package_name> salt '*' pkg.install SMClgcc346install 使用pkgutil工具安装软件包,参数如下:
| 参数 | 说明 |
|---|---|
name | 要安装的包名 |
refresh | 是否先刷新仓库,默认False |
version | 指定安装版本(与pkgs二选一使用) |
pkgs | 批量安装列表,必须是 Python 列表,可混入{"name": "version"}形式的版本约束;传入时name会被忽略 |
批量安装示例:
salt '*' pkg.install pkgs='["foo", "bar"]' salt '*' pkg.install pkgs='["foo", {"bar": "1.2.3"}]'执行流程(源码级):
refresh=True时先refresh_db();- 调用
pkg_resource.parse_targets(name, pkgs, **kwargs)解析安装目标,MinionError会被包装为CommandExecutionError抛出;解析为空则直接返回{}; - 若
pkgs为None且指定了version且只有单个目标,则把版本并入目标({name: version}); - 对每个目标拼接目标串:无版本时直接是包名,有版本时格式为
pkgname-version; - 执行
/opt/csw/bin/pkgutil -yu <targets...>; - 弹缓存、重新
list_pkgs()、compare_dicts(old, new)返回变更。
源码注释中 "Ignore 'sources' argument" 表明sources等多余参数会被parse_targets忽略。
卸载:remove 与 purge
salt '*' pkg.remove <package name> salt '*' pkg.remove <package1>,<package2>,<package3> salt '*' pkg.remove pkgs='["foo", "bar"]'remove 卸载包及其不再被其他包使用的依赖:
name为要删除的包名;pkgs为批量删除列表,传入时name被忽略(pkgs选项自版本 0.16.0 起可用);- 先
list_pkgs()得到old,只对当前确实已安装的目标(x in old)执行/opt/csw/bin/pkgutil -yr <targets...>;没有已安装目标时直接返回{}; - 同样走"弹缓存→重查→compare_dicts"流程返回变更字典。
purge 与remove完全相同——Solaris 的pkgutil不区分卸载与清除,因此purge只是remove的别名式转发(return remove(name=name, pkgs=pkgs)),二者参数与返回结构一致。
与 Salt pkg 状态模块的联动
pkgutil模块不只用于命令行,它还作为底层支撑服务于pkg状态模块(salt/states/pkg.py)。状态模块在运行时通过__salt__["pkg.latest_version"](如 salt/states/pkg.py、salt/states/pkg.py)与__salt__["pkg.list_upgrades"](如 salt/states/pkg.py)与执行模块协作——这些调用在 Solaris 主机上即落到pkgutil的latest_version/list_upgrades。
因此,在 SLS 状态文件中可以这样声明式地管理 OpenCSW 包:
install_opencsw_python: pkg.installed: - name: CSWpython upgrade_all: pkg.uptodate: - refresh: Truepkg.uptodate会在目标主机上调用pkg.list_upgrades后执行升级,与命令行salt '*' pkgutil.upgrade语义一致。
注意事项与边界
- 平台限制:
pkgutil模块仅在os_family == Solaris的主机上加载,且依赖 OpenCSW 提供的/opt/csw/bin/pkgutil;若使用 Solaris 原生pkgadd或 IPS,则应分别选择solarispkg/solarisipspkg模块。 - 返回格式:所有变更类函数统一返回
{'<pkg>': {'old': ..., 'new': ...}},便于状态模块做变更判断。 - 缓存一致性:模块通过
__context__["pkg.list_pkgs"]缓存已装包清单以提升性能,所有修改类操作都会主动弹出该缓存,保证查询结果始终准确。 purge语义:purge与remove等价,不要期望它会额外删除配置文件。- 废弃别名:
available_version已被标记为废弃,应统一使用latest_version。 - 版本比较:
latest_version会剔除修订号再做大小比较,避免因1.0,REV=2016之类的修订后缀影响结果。
参考实现与测试
- 模块实现:salt/modules/pkgutil.py
- 单元测试(覆盖全部函数):tests/pytests/unit/modules/test_pkgutil.py
- 虚拟模块
pkg的 provider 说明:doc/ref/modules/all/salt.modules.pkg.rst - 状态层联动:salt/states/pkg.py
- API 文档源文件:doc/ref/modules/all/salt.modules.pkgutil.rst
对每个函数(refresh_db、upgrade_available、list_upgrades、upgrade、list_pkgs、version、latest_version、install、remove、purge),单元测试中都有一一对应的测试用例,可作为理解边界行为与返回值约定的最直接参考。
【免费下载链接】saltSoftware to automate the management and configuration of infrastructure and applications at scale.项目地址: https://gitcode.com/gh_mirrors/sa/salt
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考