Ansible 弃用机制详解:4 个版本的弃用周期与 Display.deprecated / AnsibleModule.deprecate 实践
【免费下载链接】ansibleAnsible is a radically simple IT automation platform that makes your applications and systems easier to deploy and maintain. Automate everything from code deployment to network configuration to cloud management, in a language that approaches plain English, using SSH, with no agents to install on remote systems. https://docs.ansible.com.项目地址: https://gitcode.com/GitHub_Trending/ans/ansible
本文围绕 Ansible 仓库中的向后兼容与弃用(Deprecation)政策展开:先讲清楚“弃用 4 个版本后才移除”的版本节奏如何计算,再结合仓库源码剖析Display.deprecated与AnsibleModule.deprecate两个标记 API 的真实签名、消息格式化逻辑以及警告去重与任务级捕获机制。读完之后,你将知道如何在贡献代码时正确标注一个即将被移除的功能,并理解用户侧看到的[DEPRECATION WARNING]消息是怎么一步步生成的。
为什么向后兼容优先
context/deprecation.md 开篇只有一句话,但它是整个政策的总纲:
Backward compatibility is prioritized over most other concerns.(向后兼容性优先于绝大多数其他考量。)
Ansible 是一个面向生产环境的自动化平台,Playbook、Role 和模块往往在团队与组织间长期复用。一旦某个接口说删就删,所有依赖它的用户都会被静默打断。因此仓库采用了一个明确的、可预测的弃用周期:先给用户充足的迁移窗口期,再执行移除。理解这个周期,是正确使用下文两个 API 的前提。
弃用周期:deprecation + 2 个过渡版本 + removal
文档给出的规则是:
- 弃用周期为4 个版本:
deprecation + 2 releases + removal(标记弃用 + 2 个中间发布 + 移除)。 - 移除版本的计算方式:当前版本 + 3。当前版本以 lib/ansible/release.py 中的
__version__为准。 - 文档中的示例:在 2.19 标记弃用,意味着在 2.22 移除。
也就是说,一个功能从“还能正常使用但开始报警告”到“彻底消失”,中间有 3 个完整版本的缓冲期。用一个简表来描述时间线:
| 版本(以 release.py 当前版本 C 计) | 状态 | 用户可见行为 |
|---|---|---|
| C(deprecation 版本) | 标记弃用 | 运行触发该功能的 Playbook 时打印[DEPRECATION WARNING],功能仍可用 |
| C+1、C+2(过渡版本) | 过渡期 | 持续打印弃用警告,功能仍可用 |
| C+3(removal 版本) | 移除 | 触发该功能直接报错,消息变为“was removed” |
以当前仓库快照验证这条规则:lib/ansible/release.py 中__version__ = '2.22.0.dev0',即当前开发版本为 2.22,按“+3”规则,现在标记弃用的功能应写移除版本2.25。这也解释了为什么仓库源码中能看到大量version='2.23'、version='2.24'之类的弃用调用——它们是前几个版本标记、尚未到达移除点的功能。
标记弃用代码的两个官方入口
文档“Deprecating code”一节的原话是:
Use
Display.deprecatedorAnsibleModule.deprecatewith the removal version.(使用Display.deprecated或AnsibleModule.deprecate并带上移除版本。)
两个 API 分别对应 Ansible 的两类代码场景,必须“with the removal version”——即version参数填的是移除版本而不是当前版本,这正是上一条“+3”规则落到代码里的写法。
控制器侧:Display.deprecated
Display.deprecated定义在 lib/ansible/utils/display.py,完整签名为:
def deprecated( self, msg: str, version: str | None = None, removed: bool = False, date: str | None = None, collection_name: str | None = None, *, deprecator: _messages.PluginInfo | None = None, help_text: str | None = None, obj: t.Any = None, ) -> None:关键参数说明(来自源码 docstring 与实现):
msg:面向用户的弃用说明,应说明“什么变了、应该改成什么”。version/date:二者只能给其一,不能同时给;date若使用必须是YYYY-MM-DD格式(针对以日期为节奏发布的外部集合)。removed:True表示该功能已到移除点,此时消息前缀从[DEPRECATION WARNING]:变为[DEPRECATED]:,并且不是打警告而是直接抛出AnsibleError——即“警告”升级为硬错误,见 display.py 的实现。help_text:给用户的具体迁移指引(如替代写法、替代参数名)。obj:触发弃用的源码对象,用于在错误上下文中标注来源位置。collection_name/deprecator:大多数调用方无需提供;确需指定时二者只给一个,内部由_deprecator.get_best_deprecator归一化。
仓库中一个真实的调用例子在模板引擎的条件表达式处理里,lib/ansible/_internal/_templating/_engine.py:
if conditional in (None, ''): # deprecated backward-compatible behavior; None/empty input conditionals are always True if _TemplateConfig.allow_broken_conditionals: _display.deprecated( msg='Empty conditional expression was evaluated as True.', help_text=self._BROKEN_CONDITIONAL_ALLOWED_FRAGMENT, obj=conditional, version='2.23', ) return True raise AnsibleBrokenConditionalError("Empty conditional expressions are not allowed.", obj=conditional)这段代码完整演示了文档要求的全部要素:msg说明行为、help_text给出替代方案、obj指向触发源、version='2.23'是移除版本(当前 2.22 基础上 +3 以内)。同时可以看到典型的“弃用期 + 移除点”双分支写法:过渡期内打弃用警告并保留旧行为,移除后同一位置抛出正式异常。
模块侧:AnsibleModule.deprecate
当弃用的代码运行在远端(被打包成模块执行的 Python 代码)时,控制器侧的Display不可用,文档因此给出第二个入口AnsibleModule.deprecate,定义在 lib/ansible/module_utils/basic.py:
def deprecate( self, msg: str, version: str | None = None, date: str | None = None, collection_name: str | None = None, *, deprecator: _messages.PluginInfo | None = None, help_text: str | None = None, ) -> None: """ Record a deprecation warning to be returned with the module result. ... Specify `version` or `date`, but not both. If `date` is a string, it must be in the form `YYYY-MM-DD`. """与控制器侧的差异在于:模块是“执行完把 JSON 结果发回”的模型,没有实时终端可写,所以 docstring 明确说明其语义是“Record a deprecation warning to be returned with the module result”——把警告记录进模块结果,随结果一起传回控制器,最终由回调层展示。参数规则与Display.deprecated一致:version与date二选一、日期格式YYYY-MM-DD、version填移除版本。
弃用消息的内部处理链
知道两个入口之后,再看这些警告在 Ansible 内部如何被格式化与分发,能解释用户在终端看到的每一段文字。
1. 前缀与措辞的拼装。lib/ansible/_internal/_display_utils.py 的get_deprecation_message_with_plugin_info负责生成最终文案:未移除时前缀为[DEPRECATION WARNING]:,已移除时为[DEPRECATED]:;时间片段按“有date→ in a release after <date>;有version→ version <version>;都没有 → in a future release”的规则生成;如果removed=True,措辞从 “This feature will be removed” 切换为 “This feature was removed”。函数还会根据deprecator信息把消息归属到具体的集合与插件(例如module 'xxx' in collection 'ansible.builtin',builtin 集合统一显示为 “ansible-core”)。
2. 移除点即报错。如前文所述,Display._deprecated_with_plugin_info在removed=True分支中直接raise AnsibleError(formatted_msg),保证移除版本中旧功能一触即错,而不是继续静默工作。
3. 展示期去重。display.py 中的_deduplicate在“打印时”才对消息做去重:同一警告在一个运行内重复触发时只在终端打印一次,但注释特别说明去重发生在很晚的阶段,“Duplicates included in task results will always be visible to registered variables and callbacks”——注册变量和回调中看到的重复警告不受去重影响,避免丢失警告与具体任务的对应关系。
4. 任务级延迟捕获。lib/ansible/_internal/_display_utils.py 定义了DeferredWarningContext:在该上下文内调用Display.warning()/Display.deprecated()时,警告不会被立即打印,而是被捕获并附加到任务结果上(区分get_warnings()与get_deprecation_warnings()两类),由当前激活的显示回调统一呈现给用户。这是“警告必须能被register到的结果看到、也能被任务归属”这一设计目标的具体实现。
给贡献者的落地清单
综合 context/deprecation.md 的政策与上述源码实现,在 Ansible 中标记一个功能弃用时应做到:
- 算对移除版本:读 lib/ansible/release.py 的
__version__,移除版本 = 当前版本 + 3;写version=参数时填这个值,不要填当前版本。 - 选对入口:控制器侧代码(解析、执行器、模板引擎等)用
Display.deprecated;模块内代码(会在远端执行的lib/ansible/modules/或module_utils)用AnsibleModule.deprecate。 - 写清 msg 与 help_text:
msg说清被弃用的行为,help_text给出替代做法;version与date只提供一个。 - 为移除版本准备分支:参考 _engine.py 的条件表达式处理 的写法,过渡期走弃用警告,移除点抛出正式异常或彻底删除旧行为。
- 注意警告的去重与捕获边界:终端打印会去重,但任务结果、注册变量与回调中仍会保留完整警告;不要把“终端只出现一次”当作“只发生了一次”的证据。
这套“4 版本周期 + 两个标记 API + 展示期去重/任务级捕获”的组合,构成了 Ansible 在快速演进的代码库中仍然能给出稳定迁移路径的机制基础。
【免费下载链接】ansibleAnsible is a radically simple IT automation platform that makes your applications and systems easier to deploy and maintain. Automate everything from code deployment to network configuration to cloud management, in a language that approaches plain English, using SSH, with no agents to install on remote systems. https://docs.ansible.com.项目地址: https://gitcode.com/GitHub_Trending/ans/ansible
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考