Условные операторы
- Оператор When
- Циклы и условные операторы
- Загрузка пользовательских фактов
- Применение ‘when’ к ролям, импортам и включениям
- Условные импорты
- Выбор файлов и шаблонов на основе переменных
- Регистрация переменных
- Часто используемые факты
Часто результат выполнения задачи зависит от значения переменной, факта (информации о удалённой системе) или результата предыдущей задачи. В некоторых случаях значения переменных могут зависеть от других переменных. Дополнительные группы могут быть созданы для управления хостами на основе соответствия хостов определённым критериям. Эта тема описывает использование условных операторов в плейбуках.
Примечание
Существует множество способов управления потоком выполнения в Ansible. Дополнительные примеры поддерживаемых условных операторов можно найти здесь: http://jinja.pocoo.org/docs/dev/templates/#comparisons.
Оператор When
Иногда вам нужно пропустить определённый шаг на конкретном хосте. Это может быть что-то простое, например, не устанавливать определённый пакет, если операционная система имеет определённую версию, или выполнение некоторых этапов очистки, если файловая система заполняется.
В Ansible это легко сделать с помощью условия when, которое содержит выражение Jinja2 в сыром виде без двойных фигурных скобок (см. Переменные). На самом деле это довольно просто:
tasks:
- name: "shut down Debian flavored systems"
command: /sbin/shutdown -t now
when: ansible_os_family == "Debian"
# note that Ansible facts and vars like ansible_os_family can be used
# directly in conditionals without double curly braces
Также можно использовать скобки для группировки условий:
tasks:
- name: "shut down CentOS 6 and Debian 7 systems"
command: /sbin/shutdown -t now
when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or
(ansible_distribution == "Debian" and ansible_distribution_major_version == "7")
Несколько условий, которые должны быть истинными (логическое «и»), также можно указать как список:
tasks:
- name: "shut down CentOS 6 systems"
command: /sbin/shutdown -t now
when:
- ansible_distribution == "CentOS"
- ansible_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, и т. д.).
Напоминание: чтобы увидеть, какие факты доступны на определённой системе, вы можете сделать следующее:
ansible hostname.example.com -m setup
Подсказка: Иногда вы получаете переменную, которая является строкой, и хотите выполнить математическое сравнение с ней. Вы можете сделать это так:
tasks:
- shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6
Примечание
для приведённого выше примера требуется пакет lsb_release на целевом хосте для возвращения факта ansible_lsb.major_release.
Также можно использовать переменные, определённые в плейбуках или инвентаре. Примером может быть выполнение задачи на основе булевого значения переменной:
vars: epic: true
Тогда условное выполнение может выглядеть так:
tasks:
- shell: echo "This certainly is epic!"
when: epic
или:
tasks:
- shell: echo "This certainly isn't epic!"
when: not epic
Если необходимая переменная не задана, вы можете пропустить или прервать выполнение, используя тест Jinja2 defined. Например:
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 это работало с включениями задач, но не с включениями плейбуков. Версия 2.0 позволяет использовать это с обоими типами.
Или с ролью:
- hosts: webservers
roles:
- { role: debian_stock_config, when: ansible_os_family == 'Debian' }
Вы заметите, что по умолчанию Ansible выводит много информации о пропущенных шагах при использовании этого подхода на системах, которые не соответствуют критериям. Ознакомьтесь с модулем «group_by» в документации по Работа с модулями, чтобы найти более эффективный способ достижения той же цели.
При использовании с include_* задачами вместо импортов, условие применяется _только_ к самой задаче включения, а не к другим задачам в включённом файле(ах). Общая ситуация, где это различие важно, выглядит следующим образом:
# include a file to define a variable when it is not already defined
# main.yml
- include_tasks: other_tasks.yml
when: x is not defined
# other_tasks.yml
- set_fact:
x: foo
- debug:
var: x
В приведённом выше примере, если import_tasks было бы использовано, обе включенные задачи также были бы пропущены. В случае использования include_tasks вместо этого задачи выполняются как ожидается, потому что условие не применяется к ним.
Условные импорты
Примечание
Это продвинутая тема, которая используется нечасто.
Иногда вам нужно выполнить разные действия в плейбуке в зависимости от определённых критериев. Пример — создание одного плейбука, работающего на нескольких платформах и с разными версиями операционных систем.
Например, имя пакета Apache может отличаться между CentOS и Debian, но это легко обрабатывается с минимальным количеством синтаксиса в Ansible Playbook:
---
- hosts: all
remote_user: root
vars_files:
- "vars/common.yml"
- [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
tasks:
- name: make sure apache is started
service: name={{ apache }} state=started
Примечание
Переменная ‘ansible_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 к конфигурации — разделение переменных и задач, предотвращение превращения плейбуков в произвольный код с вложенными условиями — приводит к более структурированным и проверяемым правилам конфигурации, так как точек принятия решений для отслеживания меньше.
Выбор файлов и шаблонов на основе переменных
Примечание
Это продвинутая тема, которая используется нечасто. Вы можете пропустить этот раздел.
Иногда конфигурационный файл, который вы хотите скопировать, или шаблон, который будете использовать, могут зависеть от переменной. Следующая конструкция выбирает первый доступный файл, подходящий для переменных данного хоста, что часто намного чище, чем вложение множества операторов if в шаблон.
Следующий пример демонстрирует, как создать шаблон конфигурационного файла, который значительно отличается между, например, 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_distribution}}.conf"
- default.conf
mypaths: ['search_location_one/somedir/', '/opt/other_location/somedir/']
Регистрация переменных
Часто в плейбуке может быть полезно сохранить результат определённой команды в переменной и использовать её позже. Использование модуля command таким образом может в значительной степени устранить необходимость написания сайту-специфичных фактов, например, можно проверить наличие конкретной программы.
Ключевое слово ‘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_distribution
Возможные значения:
Alpine Altlinux Amazon Archlinux ClearLinux Coreos Debian Gentoo Mandriva NA OpenWrt OracleLinux RedHat Slackware SMGL SUSE VMwareESX
ansible_distribution_major_version
Это будет основная версия операционной системы. Например, значение будет 16 для Ubuntu 16.04.
ansible_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.5/user_guide/playbooks_conditionals.html