Руководство по переносу Ansible 2.0
В данном разделе рассматриваются изменения поведения между Ansible 1.x и Ansible 2.0.
Он предназначен для помощи в обновлении ваших playbooks, плагинов и других частей вашей инфраструктуры Ansible, чтобы они работали с этой версией Ansible.
Рекомендуется ознакомиться с этой страницей вместе с Журналом изменений Ansible 2.0, чтобы понять, какие обновления вам могут потребоваться.
Этот документ является частью коллекции по переносу. Полный список руководств по переносу можно найти по адресу руководствам по переносу.
Playbook
В этом разделе обсуждаются изменения, которые вам могут потребоваться внести в ваши playbooks.
- Синтаксис в 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') ) }}"
-
Конец строки. Когда строка с концом строки указывалась в playbook через формат словаря yaml, конец строки удалялся. При указании в формате key=value концы строк сохранялись. В версии 2 оба способа указания строки сохраняют концы строк. Если вы полагались на удаление конца строки, вы можете изменить свой playbook, используя следующий пример:
* 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"
-
Изменяется поведение шаблонизации текстовых файлов типа DOS в Ansible v2.
Ошибка в Ansible v1 приводит к тому, что текстовые файлы типа DOS (использующие возврат каретки и новую строку) шаблонизируются в текстовые файлы типа Unix (использующие только новую строку). В Ansible v2 эта давняя ошибка, наконец, исправлена, и текстовые файлы типа DOS сохраняются правильно. Это может быть запутанно, когда вы ожидаете, что ваш playbook не покажет никаких различий при переходе на 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
- Шаблонизация (переменные в playbooks и шаблонах поиска) улучшена в отношении сохранения оригинального значения вместо преобразования всего в строку. Если вам нужно старое поведение, оформите значение в кавычки, чтобы передать его как строку.
- Пустые переменные и переменные, установленные в null в YAML, больше не преобразуются в пустые строки. Они сохранят значение
None. Вы можете переопределитьnull_representationзначение на пустую строку в файле конфигурации, установив переменную окруженияANSIBLE_NULL_REPRESENTATION. - Плагины дополнительных обратных вызовов должны быть включены в ansible.cfg. Копирование больше не требуется, но их нужно включить в ansible.cfg.
- Модуль dnf был переписан. Могут наблюдаться некоторые незначительные изменения в поведении.
- Модуль win_updates был переписан и теперь работает как ожидается.
- Начиная с версии 2.0.1, неявная задача настройки из gather_facts теперь корректно наследует все от playbook, но это может вызвать проблемы для тех, кто устанавливает
environmentна уровне playbook и зависит от наличияansible_env. Ранее это игнорировалось, но теперь может выдать ошибку «Undefined».
Устаревшие элементы
Хотя все перечисленные здесь элементы будут отображать сообщение об устаревании, они по-прежнему работают так же, как и в 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].
- Playbooks, использующие повышение привилегий, должны всегда использовать опции «become*» вместо устаревших опций su*/sudo*.
-
«Короткий» вариант для vars_prompt больше не поддерживается. Например:
vars_prompt: variable_name: "Prompt string" -
Указание переменных на верхнем уровне заявления о включении задачи больше не поддерживается. Например:
- include_tasks: foo.yml a: 1
Теперь должно быть:
- include_tasks: foo.yml
vars:
a: 1
- Установка any_errors_fatal в задаче больше не поддерживается. Это должно быть установлено только на уровне playbook.
- Необработанные переменные в словаре
environment(для playbooks/задач и т. д.) больше не поддерживаются. Переменные, указанные там, должны использовать полный синтаксис переменной: ‘{{foo}}’. -
Теги (или любые директивы) больше не должны указываться вместе с другими параметрами во включении задачи. Вместо этого они должны быть указаны как параметр задачи. Например:
- 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), но также молча игнорировалась, поэтому playbook «работал», хотя этого не должно было происходить. Сейчас это ошибка парсинга. -
Повторные директивы:
- task: dostuf when: True when: False
Первая
whenбыла проигнорирована, и использовалась только вторая, так как playbook выполнялся без предупреждения об игнорировании одной из директив. Сейчас это ошибка парсинга. -
Смешивание переменных и директив:
- 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 обратного вызова, старый продолжает работать для большинства плагинов обратного вызова. Однако, если ваш плагин обратного вызова использует 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. Так же, как и при переносе плагинов из версии 1 в версию 2, необходимо понимать, как плагины работают в каждой версии, и поддерживать оба требования.
Поскольку система плагинов ansible-2.0 более продвинута, проще адаптировать ваш плагин, чтобы предоставить аналогичные части (подклассы, методы) для ansible-1.9.x, как ожидается от ansible-2.0. Таким образом, ваш код будет выглядеть намного чище.
Следующие советы могут быть полезны:
- Проверьте, доступны ли классы ansible-2.0, и если их нет (ansible-1.9.x), имитируйте их с необходимыми методами (например,
__init__) - Когда импортируются модули Python ansible-2.0, и они терпят неудачу (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(), так как у нас не было прямого использования для него в конечной версии. Тем не менее, мы оставили этот код, чтобы люди могли использовать эту часть во время разработки/переноса/использования.
Плагины подключения
- плагины подключения
Плагины действий
- плагины действий
Плагины обратного вызова
- плагины обратного вызова
Плагины подключения
- плагины подключения
Перенос пользовательских скриптов
Пользовательские скрипты, которые использовали API ansible.runner.Runner в версии 1.x, должны быть перенесены в версию 2.x. Обратитесь к: Python API
© 2012–2018 Michael DeHaan
© 2018–2024 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/latest/porting_guides/porting_guide_2.0.html