Условные операторы
- Оператор When
- Циклы и условные операторы
- Загрузка пользовательских фактов
- Применение ‘when’ к ролям, импортам и включениям
- Условные импорты
- Выбор файлов и шаблонов на основе переменных
- Регистрация переменных
- Часто используемые факты
Часто результат выполнения задачи зависит от значения переменной, факта (данных о удалённой системе) или результата предыдущей задачи. В некоторых случаях значения переменных могут зависеть от других переменных. Дополнительные группы могут быть созданы для управления хостами на основе соответствия хостов другим критериям. Эта тема описывает использование условных операторов в playbook.
Примечание
Существует множество способов управления потоком выполнения в Ansible. Дополнительные примеры поддерживаемых условных операторов можно найти здесь: http://jinja.pocoo.org/docs/dev/templates/#comparisons.
Оператор When
Иногда вам нужно пропустить определённый шаг на определённом хосте. Это может быть что-то простое, например, не устанавливать определённый пакет, если операционная система имеет определённую версию, или что-то вроде выполнения шагов очистки, если файловая система заполняется.
Это легко сделать в Ansible с помощью when оператора, который содержит необработанное выражение Jinja2 без двойных фигурных скобок (см. group_by — создание групп Ansible на основе фактов). На самом деле это довольно просто:
tasks:
- name: "shut down Debian flavored systems"
command: /sbin/shutdown -t now
when: ansible_facts['os_family'] == "Debian"
# note that all variables can be directly in conditionals without double curly braces
Вы также можете использовать скобки для группировки условий:
tasks:
- name: "shut down CentOS 6 and Debian 7 systems"
command: /sbin/shutdown -t now
when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
(ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
Несколько условий, которые должны быть истинными одновременно (логическое «и»), также могут быть указаны как список:
tasks:
- name: "shut down CentOS 6 systems"
command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"
В операторах when также можно использовать ряд «тестов» и «фильтров» Jinja2, некоторые из которых уникальны и предоставляются Ansible. Предположим, мы хотим проигнорировать ошибку одного оператора, а затем решить что-то сделать условно в зависимости от успеха или неудачи:
tasks:
- command: /bin/false
register: result
ignore_errors: True
- command: /bin/something
when: result is failed
# In older versions of ansible use ``success``, now both are valid but succeeded uses the correct tense.
- command: /bin/something_else
when: result is succeeded
- command: /bin/still/something_else
when: result is skipped
Примечание
как success , так и succeeded работают (fail/failed, и т.д.).
Чтобы узнать, какие факты доступны на определённой системе, вы можете сделать следующее в playbook:
- debug: var=ansible_facts
Подсказка: Иногда вы получите переменную, которая является строкой, и вам нужно выполнить сравнение математической операции на ней. Вы можете сделать это следующим образом:
tasks:
- shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release']|int >= 6
Примечание
В приведённом выше примере требуется пакет lsb_release на целевом хосте для возврата факта ‘lsb major_release’.
Переменные, определённые в playbook или инвентаре, также могут быть использованы. Примером может быть выполнение задачи на основе булевого значения переменной:
vars: epic: true
Затем условное выполнение может выглядеть следующим образом:
tasks:
- shell: echo "This certainly is epic!"
when: epic
или:
tasks:
- shell: echo "This certainly isn't epic!"
when: not epic
Если необходимая переменная не задана, вы можете пропустить или прервать выполнение, используя Jinja2’s defined test. Например:
tasks:
- shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
when: foo is defined
- fail: msg="Bailing out. this play requires 'bar'"
when: bar is undefined
Это особенно полезно в сочетании с условным импортом файлов vars (см. ниже). Как показывают примеры, вам не нужно использовать {{ }} для использования переменных в условных операторах, так как они уже подразумеваются.
Циклы и условные операторы
Комбинируя when с циклами (см. Циклы), имейте в виду, что when оператор обрабатывается отдельно для каждого элемента. Это сделано по дизайну:
tasks:
- command: echo {{ item }}
loop: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5
Если нужно пропустить всю задачу в зависимости от определения переменной цикла, используйте |default фильтр для предоставления пустого итератора:
- command: echo {{ item }}
loop: "{{ mylist|default([]) }}"
when: item > 5
Если используется словарь в цикле:
- command: echo {{ item.key }}
loop: "{{ query('dict', mydict|default({})) }}"
when: item.value > 5
Загрузка пользовательских фактов
Также легко предоставить собственные факты, если нужно, что описано в Разработка модуля. Чтобы запустить их, просто выполните вызов своего собственного модуля сбора фактов вверху вашего списка задач, и переменные, возвращённые там, будут доступны для последующих задач:
tasks:
- name: gather site specific fact data
action: site_facts
- command: /usr/bin/thingy
when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'
Применение ‘when’ к ролям, импортам и включениям
Обратите внимание, что если у вас есть несколько задач, которые все используют одно и то же условное выражение, вы можете добавить это условие к инструкции включения задачи, как показано ниже. Все задачи будут оценены, но условие применяется к каждой отдельной задаче:
- import_tasks: tasks/sometasks.yml when: "'reticulating splines' in output"
Примечание
В версиях до 2.0 это работало с включениями задач, но не с включениями playbook. Версия 2.0 позволяет это использовать в обоих случаях.
Или с ролью:
- hosts: webservers
roles:
- role: debian_stock_config
when: ansible_facts['os_family'] == 'Debian'
Вы заметите, что по умолчанию в Ansible появляется много вывода «skipped» при использовании этого подхода на системах, которые не соответствуют критериям. Во многих случаях модуль group_by (см. Работа с модулями) может быть более эффективным способом достижения той же цели; см. Отличия в операционных системах и дистрибутивах.
Когда условие используется с include_* задачами вместо импортов, оно применяется only к самой задаче включения, а не к другим задачам в включённом файле(ах). Типичный случай, когда это различие важно, следующий:
# We wish to include a file to define a variable when it is not
# already defined
# main.yml
- import_tasks: other_tasks.yml # note "import"
when: x is not defined
# other_tasks.yml
- set_fact:
x: foo
- debug:
var: x
Это расширяется во время включения до эквивалента:
- set_fact:
x: foo
when: x is not defined
- debug:
var: x
when: x is not defined
Таким образом, если x изначально не определено, задача debug будет пропущена. Используя include_tasks вместо import_tasks, обе задачи из other_tasks.yml будут выполнены как ожидается.
Дополнительную информацию о различиях между include и import см. в Создание повторно используемых playbooks.
Условные импорты
Примечание
Это сложная тема, которая используется редко.
Иногда в playbook вы хотите делать разные вещи в зависимости от определённых критериев. Примером может служить один playbook, работающий на нескольких платформах и версиях ОС.
В качестве примера имя пакета Apache может отличаться между CentOS и Debian, но это легко обрабатывается с минимальной синтаксической нагрузкой в Ansible Playbook:
---
- hosts: all
remote_user: root
vars_files:
- "vars/common.yml"
- [ "vars/{{ ansible_facts['os_family'] }}.yml", "vars/os_defaults.yml" ]
tasks:
- name: make sure apache is started
service: name={{ apache }} state=started
Примечание
Переменная «ansible_facts[‘os_family’]» интерполируется в список имён файлов, определяемых для vars_files.
Напоминаем, что различные YAML-файлы содержат только ключи и значения:
--- # for vars/RedHat.yml apache: httpd somethingelse: 42
Как это работает? Для операционных систем Red Hat (‘CentOS’, например) Ansible пытается импортировать первый файл ‘vars/RedHat.yml’. Если этот файл не существует, Ansible пытается загрузить ‘vars/os_defaults.yml’. Если файлы в списке не найдены, возникает ошибка.
В Debian Ansible сначала ищет ‘vars/Debian.yml’ вместо ‘vars/RedHat.yml’, прежде чем перейти к ‘vars/os_defaults.yml’.
Подход Ansible к конфигурации — разделение переменных и задач, сохранение playbooks от превращения в произвольный код с вложенными условными операторами — приводит к более структурированным и проверяемым правилам конфигурации, так как точек принятия решений меньше.
Выбор файлов и шаблонов на основе переменных
Примечание
Это сложная тема, которая используется редко. Вы можете пропустить этот раздел.
Иногда конфигурационный файл, который вы хотите скопировать, или шаблон, который вы будете использовать, может зависеть от переменной. Следующий конструкт выбирает первый доступный файл, соответствующий переменным данного хоста, что часто намного чище, чем размещение большого количества условных операторов в шаблоне.
Следующий пример демонстрирует, как создать шаблон конфигурационного файла, сильно различающегося между, например, CentOS и Debian:
- name: template a file
template:
src: "{{ item }}"
dest: /etc/myapp/foo.conf
loop: "{{ query('first_found', { 'files': myfiles, 'paths': mypaths}) }}"
vars:
myfiles:
- "{{ansible_facts['distribution']}}.conf"
- default.conf
mypaths: ['search_location_one/somedir/', '/opt/other_location/somedir/']
Регистрация переменных
Часто в playbook может быть полезно сохранить результат определённой команды в переменной и получить к ней доступ позднее. Использование модуля команды таким образом во многих отношениях может устранить необходимость писать специфичные для сайта факты, например, вы можете проверить наличие определённой программы.
Ключевое слово ‘register’ определяет, в какой переменной сохранить результат. Результатные переменные могут быть использованы в шаблонах, строках действий или в операторах when. Это выглядит так (в явно тривиальном примере):
- name: test play
hosts: all
tasks:
- shell: cat /etc/motd
register: motd_contents
- shell: echo "motd contains the word hi"
when: motd_contents.stdout.find('hi') != -1
Как показано ранее, содержимое зарегистрированной переменной доступно с помощью значения ‘stdout’. Регистрируемый результат может быть использован в цикле задачи, если он преобразован в список (или уже является списком), как показано ниже. «stdout_lines» также доступен в объекте, хотя вы также можете вызвать «home_dirs.stdout.split()», если хотите, и можете разделить по другим полям:
- name: registered variable usage as a loop list
hosts: all
tasks:
- name: retrieve the list of home directories
command: ls /home
register: home_dirs
- name: add home dirs to the backup spooler
file:
path: /mnt/bkspool/{{ item }}
src: /home/{{ item }}
state: link
loop: "{{ home_dirs.stdout_lines }}"
# same as loop: "{{ home_dirs.stdout.split() }}"
Как показано ранее, содержимое зарегистрированной переменной доступно с помощью значения ‘stdout’. Вы можете проверить пустоту зарегистрированной переменной:
- name: check registered variable for emptiness
hosts: all
tasks:
- name: list contents of directory
command: ls mydir
register: contents
- name: check contents for emptiness
debug:
msg: "Directory is empty"
when: contents.stdout == ""
Часто используемые факты
Следующие факты часто используются в условных операторах — см. примеры выше.
ansible_facts[‘distribution’]
Возможные значения (пример, неполный список):
Alpine Altlinux Amazon Archlinux ClearLinux Coreos Debian Fedora Gentoo Mandriva NA OpenWrt OracleLinux RedHat Slackware SMGL SUSE VMwareESX
ansible_facts[‘distribution_major_version’]
Это будет основная версия операционной системы. Например, значение будет 16 для Ubuntu 16.04.
ansible_facts[‘os_family’]
Возможные значения (пример, неполный список):
AIX Alpine Altlinux Archlinux Darwin Debian FreeBSD Gentoo HP-UX Mandrake RedHat SGML Slackware Solaris Suse
См. также
- Работа с playbook
- Введение в playbook
- Роли
- Организация playbook по ролям
- Рекомендации по наилучшей практике
- Рекомендации по наилучшей практике в playbook
- Использование переменных
- Все о переменных
- Список рассылки пользователей
- Есть вопрос? Зайдите на форум Google!
- irc.freenode.net
- Канал IRC-чата #ansible
© 2012–2018 Michael DeHaan
© 2018–2019 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/2.7/user_guide/playbooks_conditionals.html