Руководство по переносу Ansible 2.0
В этом разделе рассматриваются изменения в поведении между Ansible 1.x и Ansible 2.0.
Цель этого раздела – помочь обновить ваши playbook, плагины и другие части вашей инфраструктуры Ansible, чтобы они работали с этой версией Ansible.
Рекомендуется прочитать эту страницу вместе с Журналом изменений Ansible 2.0, чтобы понять, какие обновления вам могут потребоваться.
Этот документ является частью коллекции по переносу. Полный список руководств по переносу можно найти в руководствах по переносу.
Playbook
В этом разделе обсуждаются изменения, которые вам могут потребоваться внести в ваши playbook.
# Syntax in 1.9.x
- debug:
msg: "{{ 'test1_junk 1\\\\3' | regex_replace('(.*)_junk (.*)', '\\\\1 \\\\2') }}"
# Syntax in 2.0.x
- debug:
msg: "{{ 'test1_junk 1\\3' | regex_replace('(.*)_junk (.*)', '\\1 \\2') }}"
# Output:
"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 dict, конец строки удалялся. Если она была указана в формате key=value, окончания строк сохранялись. В версии 2 оба способа сохраняют окончания строк. Если вы полагались на удаление окончания строки, вы можете изменить свой playbook, используя следующий пример:
# Syntax in 1.9.x vars: message: > Testing some things tasks: - debug: msg: "{{ message }}" # Syntax in 2.0.x vars: old_message: > Testing some things message: "{{ old_messsage[:-1] }}" - debug: msg: "{{ message }}" # Output "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}}" # <- args here uses the full variable syntax with_items: "{{my_dirs}}" - порт задачи включает
- Более динамичный. Случаи, которые раньше не должны были работать, теперь не работают, как ожидалось.
- переменные, определенные в формате yaml dict https://github.com/ansible/ansible/issues/13324
- Шаблонизация (переменные в playbook и поисках шаблонов) улучшена в отношении сохранения исходного значения вместо преобразования всего в строку. Если вам нужно старое поведение, укажите значение в кавычках, чтобы передать его как строку.
- Пустые переменные и переменные, установленные в null в формате yaml, больше не преобразуются в пустые строки. Они сохранят значение
None. Вы можете переопределитьnull_representationнастройку на пустую строку в вашем файле конфигурации, установив переменную окруженияANSIBLE_NULL_REPRESENTATION. - Плагины обратного вызова должны быть разрешены в ansible.cfg. Копирование больше не требуется, но необходимо выполнить разрешение в ansible.cfg.
- Модуль dnf был переписан. Могут наблюдаться некоторые незначительные изменения в поведении.
- Модуль win_updates был переписан и теперь работает как ожидается.
- С версии 2.0.1 и выше неявная задача setup из gather_facts теперь корректно наследует все от play, но это может вызвать проблемы для тех, кто устанавливает
environmentна уровне play и зависит от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: # These are both deprecated: - debug: "{{debug_params}}" - debug: args: "{{debug_params}}" # Use this instead: - debug: msg: "{{debug_params['msg']}}" - В шаблонах хостов следует использовать запятую (,) или двоеточие (:) вместо точки с запятой (;), чтобы разделять хосты/группы в шаблоне.
- Диапазоны, указанные в шаблонах хостов, должны использовать синтаксис [x:y] вместо [x-y].
- В playbook с повышением привилегий следует всегда использовать параметры «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 в задаче больше не поддерживается. Его следует указывать только на уровне play.
- Простые переменные в словаре
environment(для play/задач и т. д.) больше не поддерживаются. Переменные, указанные там, должны использовать полный синтаксис переменной: ‘{{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), но также молча игнорировалась, поэтому игра «запускалась», хотя этого не должно было быть; теперь это ошибка синтаксического анализа. -
Повторные директивы:
- task: dostuf when: True when: False
Первая
whenбыла проигнорирована, и использовалась только вторая, так как игра запускалась без предупреждения, что одна из директив игнорируется. Теперь это ошибка синтаксического анализа. -
Смешивание переменных и директив:
- role: {name=rosy, port=435 } # in tasks/main.yml - wait_for: port={{port}}Переменная
portзарезервирована как директива play/задачи для переопределения порта подключения. В предыдущих версиях она была смешана с переменной, названнойport, и была доступна позже в игре, что создавало проблемы, если хост пытался подключиться повторно или использовал не кешируемое соединение. Теперь она будет правильно распознана как директива, и переменная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__) - Когда импортируются модули 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–2019 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/2.8/porting_guides/porting_guide_2.0.html