Ansible 2.0 移植指南
本节讨论 Ansible 1.x 和 Ansible 2.0 之间的行为变化。
旨在帮助您更新您的剧本、插件和 Ansible 基础架构的其他部分,以便它们能够与此版本的 Ansible 协同工作。
我们建议您阅读此页面以及 Ansible 2.0 的变更日志,以了解您可能需要进行哪些更新。
本文档是移植集合的一部分。可以在 移植指南 中找到完整的移植指南列表。
剧本
本节讨论您可能需要对剧本进行的任何更改。
1.9.x 中的语法
- debug:
msg: "{{ 'test1_junk 1\\\\3' | regex_replace('(.*)_junk (.*)', '\\\\1 \\\\2') }}"
2.0.x 中的语法
- debug:
msg: "{{ 'test1_junk 1\\3' | regex_replace('(.*)_junk (.*)', '\\1 \\2') }}"
输出
"msg": "test1 1\\3"
要创建可在所有版本上使用的转义字符串,您有两个选项
- debug: msg="{{ 'test1_junk 1\\3' | regex_replace('(.*)_junk (.*)', '\\1 \\2') }}"
使用 key=value 转义,该转义没有更改。另一个选项是检查 ansible 版本
"{{ (ansible_version|version_compare('2.0', 'ge'))|ternary( 'test1_junk 1\\3' | regex_replace('(.*)_junk (.*)', '\\1 \\2') , 'test1_junk 1\\\\3' | regex_replace('(.*)_junk (.*)', '\\\\1 \\\\2') ) }}"
尾随换行符 当通过 yaml 字典格式在剧本中指定带有尾随换行符的字符串时,尾随换行符会被去除。当以 key=value 格式指定时,尾随换行符会被保留。在 v2 中,这两种指定字符串的方法都将保留尾随换行符。如果您依赖于尾随换行符被去除,您可以使用以下示例更改您的剧本
* Syntax in 1.9.x
vars: message: > Testing some things tasks: - debug: msg: "{{ message }}"
2.0.x 中的语法
vars: old_message: > Testing some things message: "{{ old_message[:-1] }}" - debug: msg: "{{ message }}"
输出
"msg": "Testing some things"
Ansible v2 中模板化 DOS 类型文本文件的行为发生了变化。
Ansible v1 中的一个错误会导致 DOS 类型文本文件(使用回车符和换行符)被模板化为 Unix 类型文本文件(仅使用换行符)。在 Ansible v2 中,这个长期存在的错误终于得到了修复,并且 DOS 类型文本文件被正确地保留了。当您期望您的剧本在迁移到 Ansible v2 时不会显示任何差异时,这可能会令人困惑,而实际上您会看到每个 DOS 类型文件都被完全替换(内容看起来完全相同)。
当将复杂参数指定为变量时,变量必须使用完整的 jinja2 变量语法(
`{{var_name}}`
) - 这里不再接受裸变量名。事实上,即使使用变量指定参数也被弃用,并且在将来的版本中将不被允许
---
- hosts: localhost
connection: local
gather_facts: false
vars:
my_dirs:
- { path: /tmp/3a, state: directory, mode: 0755 }
- { path: /tmp/3b, state: directory, mode: 0700 }
tasks:
- file:
args: "{{item}}"
with_items: "{{my_dirs}}"
移植任务包含
更具动态性。原本不应该工作的极端情况格式现在按预期不再工作。
在 yaml 字典格式中定义的变量,请参阅 问题 13324
模板化(剧本中的变量和模板查找)在保留原始值而不是将所有内容转换为字符串方面得到了改进。如果您需要旧的行为,请引用该值以将其作为字符串传递。
在 yaml 中,空变量和设置为 null 的变量不再转换为空字符串。它们将保留 None 的值。您可以通过在配置文件中将
ANSIBLE_NULL_REPRESENTATION
环境变量设置为空字符串来覆盖 null_representation 设置。必须在 ansible.cfg 中启用额外回调。不再需要复制,但必须在 ansible.cfg 中启用它们。
dnf 模块已重写。可能会观察到一些细微的行为变化。
win_updates 已重写,现在按预期工作。
从 2.0.1 开始,gather_facts 中的隐式设置任务现在可以正确地继承来自 playbook 的所有内容,但这可能会导致那些在 playbook 级别设置 environment 并依赖于 ansible_env 存在的问题。以前这是被忽略的,但现在可能会发出“未定义”错误。
已弃用
虽然此处列出的所有项目都将显示弃用警告消息,但它们仍然像在 1.9.x 中一样工作。请注意,它们将在 2.2 中删除(Ansible 始终等待两个主要版本才能删除弃用的功能)。
with_
循环中的裸变量应改为使用"{{ var }}"
语法,这有助于消除歧义。ansible-galaxy 文本格式需求文件。用户应改为使用 YAML 格式的需求文件。
with_
循环列表中的未定义变量目前不会中断循环,但会发出警告;将来,它们将发出错误。使用字典变量设置所有任务参数是不安全的,将在将来的版本中删除。已弃用变体的示例
- hosts: localhost
gather_facts: no
vars:
debug_params:
msg: "hello there"
tasks:
- debug: "{{debug_params}}"
- debug:
args: "{{debug_params}}"
推荐变体的示例
- hosts: localhost
gather_facts: no
vars:
debug_params:
msg: "hello there"
tasks:
- debug:
msg: "{{debug_params['msg']}}"
主机模式应使用逗号 (,) 或冒号 (:) 而不是分号 (;) 来分隔模式中的主机/组。
主机模式中指定的范围应使用 [x:y] 语法,而不是 [x-y]。
使用权限提升的剧本应始终使用“become*”选项而不是旧的 su*/sudo* 选项。
vars_prompt 的“简写形式”不再受支持。例如
vars_prompt: variable_name: "Prompt string"
不再支持在任务 include 语句的顶层指定变量。例如
- include_tasks: foo.yml a: 1
现在应该
- include_tasks: foo.yml
vars:
a: 1
不再支持在任务上设置 any_errors_fatal。这应该仅在 playbook 级别设置。
不再支持 environment 字典(对于 playbook/任务等)中的裸变量。在那里指定的变量应使用完整的变量语法:'{{foo}}'。
标签(或任何指令)不再应与任务 include 中的其他参数一起指定。相反,它们应作为任务上的选项指定。例如
- include_tasks: foo.yml tags=a,b,c
应该是
- include_tasks: foo.yml tags: [a, b, c]
任务上的 first_available_file 选项已弃用。用户应使用 with_first_found 选项或查找('first_found', ...)插件。
其他注意事项
以下是在更新时遇到的一些极端情况。这些主要是由更严格的解析器验证和捕获以前被忽略的错误引起的。
错误的变量组合
with_items: myvar_{{rest_of_name}}
这“偶然”起作用了,因为错误被重新模板化并最终解析了变量,它从未被认为是有效的语法,现在会正确地返回错误,请改为使用以下语法。
hostvars[inventory_hostname]['myvar_' + rest_of_name]
拼写错误的指令
- task: dostuf becom: yes
该任务始终在不使用权限提升的情况下运行(为此,您需要 become),但也静默忽略,因此即使该任务不应该运行,play 也会“运行”,现在这是一个解析错误。
重复指令
- task: dostuf when: True when: False
第一个 when 被忽略,只有第二个被使用,因为 play 在没有警告的情况下运行,它忽略了其中一个指令,现在这会产生解析错误。
混淆变量和指令
- role: {name=rosy, port=435 } # in tasks/main.yml - wait_for: port={{port}}
port 变量被保留为覆盖连接端口的 playbook/任务指令,在以前的版本中,这与名为 port 的变量混淆,并且可以在稍后的 playbook 中使用,如果主机尝试重新连接或使用非缓存连接,这会导致问题。现在它将被正确地识别为指令,并且 port 变量将显示为未定义,这现在强制使用不冲突的名称,并在向角色调用添加设置和变量时消除歧义。
with_ 上的裸操作
with_items: var1 + var2
“裸变量”功能存在一个问题,该功能仅应在不需要花括号({{ }})的情况下模板化单个变量,但在 Ansible 的某些版本中会模板化完整的表达式。现在您需要对所有表达式使用正确的模板化和花括号,除了条件语句(when)之外。
with_items: "{{var1 + var2}}"
裸功能本身已被弃用,因为未定义的变量与字符串无法区分,这使得难以显示正确的错误。
移植插件
在 Ansible-1.9.x 中,通常会复制一个现有的插件来创建一个新的插件。只需实现插件调用者期望的方法和属性,就可以将其变成该类型的插件。在 Ansible-2.0 中,大多数插件都是通过为每种插件类型继承一个基类来实现的。这样,自定义插件就不需要包含未自定义的方法。
查找插件
查找插件;导入版本
连接插件
连接插件
动作插件
动作插件
回调插件
虽然 Ansible 2.0 提供了一个新的回调 API,但旧的 API 仍然适用于大多数回调插件。但是,如果您的回调插件使用了self.playbook
、self.play
或 self.task
,那么您将必须自己存储这些值,因为 Ansible 不再自动填充回调。
import os
from ansible.plugins.callback import CallbackBase
class CallbackModule(CallbackBase):
def __init__(self):
self.playbook = None
self.playbook_name = None
self.play = None
self.task = None
def v2_playbook_on_start(self, playbook):
self.playbook = playbook
self.playbook_name = os.path.basename(self.playbook._file_name)
def v2_playbook_on_play_start(self, play):
self.play = play
def v2_playbook_on_task_start(self, task, is_conditional):
self.task = task
def v2_on_any(self, *args, **kwargs):
self._display.display('%s: %s: %s' % (self.playbook_name,
self.play.name, self.task))
连接插件
连接插件
混合插件
在某些特定情况下,您可能希望一个插件同时支持 Ansible-1.9.x 和 Ansible-2.0。就像将插件从 v1 移植到 v2 一样,您需要了解插件在每个版本中的工作方式,并满足这两个版本的要求。
由于 Ansible-2.0 插件系统更加高级,因此更容易调整您的插件以针对 Ansible-1.9.x 提供类似的部分(子类、方法),就像 Ansible-2.0 期望的那样。这样,您的代码看起来会更简洁。
您可能会发现以下提示很有用
检查 Ansible-2.0 类是否可用,如果它们丢失(Ansible-1.9.x),则使用所需的方法(例如,
__init__
)模仿它们。当导入 Ansible-2.0 Python 模块时,如果它们失败(Ansible-1.9.x),请捕获
ImportError
异常并对 Ansible-1.9.x 执行等效的导入。并可能进行转换(例如,导入特定方法)。使用这些方法的存在作为您正在运行的 Ansible 版本的限定符。因此,与其使用版本检查,不如执行功能检查。(请参阅下面的示例)
为每个 if-then-else 案例记录每个块需要哪个特定版本。这将帮助其他人了解他们如何调整自己的插件,但它也将帮助您在弃用 Ansible-1.9.x 支持时将其删除。
在进行插件开发时,在开发过程中使用
warning()
方法非常有用,但发出死胡同(您期望永远不会触发的案例)或极端情况(例如,您期望错误配置的案例)的警告也很重要。查看 Ansible-1.9.x 和 Ansible-2.0 中的其他插件以了解 API 的工作原理以及可用的模块、类和方法,这将很有帮助。
查找插件
作为一个简单的示例,我们将创建一个混合的 fileglob
查找插件。
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import glob
try:
# ansible-2.0
from ansible.plugins.lookup import LookupBase
except ImportError:
# ansible-1.9.x
class LookupBase(object):
def __init__(self, basedir=None, runner=None, **kwargs):
self.runner = runner
self.basedir = self.runner.basedir
def get_basedir(self, variables):
return self.basedir
try:
# ansible-1.9.x
from ansible.utils import (listify_lookup_plugin_terms, path_dwim, warning)
except ImportError:
# ansible-2.0
from ansible.utils.display import Display
warning = Display().warning
class LookupModule(LookupBase):
# For ansible-1.9.x, we added inject=None as valid argument
def run(self, terms, inject=None, variables=None, **kwargs):
# ansible-2.0, but we made this work for ansible-1.9.x too !
basedir = self.get_basedir(variables)
# ansible-1.9.x
if 'listify_lookup_plugin_terms' in globals():
terms = listify_lookup_plugin_terms(terms, basedir, inject)
ret = []
for term in terms:
term_file = os.path.basename(term)
# For ansible-1.9.x, we imported path_dwim() from ansible.utils
if 'path_dwim' in globals():
# ansible-1.9.x
dwimmed_path = path_dwim(basedir, os.path.dirname(term))
else:
# ansible-2.0
dwimmed_path = self._loader.path_dwim_relative(basedir, 'files', os.path.dirname(term))
globbed = glob.glob(os.path.join(dwimmed_path, term_file))
ret.extend(g for g in globbed if os.path.isfile(g))
return ret
注意
在上面的示例中,我们没有使用 warning()
方法,因为我们在最终版本中没有直接使用它。但是,我们保留了这段代码,以便人们在开发/移植/使用过程中可以使用它。
连接插件
连接插件
动作插件
动作插件
回调插件
回调插件
连接插件
连接插件
移植自定义脚本
在 1.x 中使用 ansible.runner.Runner
API 的自定义脚本必须在 2.x 中移植。请参考:Python API