Spec-Zone.ru › Ansible 2.5

Условные операторы

  • Оператор When
  • Циклы и условные операторы
  • Загрузка пользовательских фактов
  • Применение ‘when’ к ролям, импортам и включениям
  • Условные импорты
  • Выбор файлов и шаблонов на основе переменных
  • Регистрация переменных
  • Часто используемые факты
    • ansible_distribution
    • ansible_distribution_major_version
    • ansible_os_family

Часто результат выполнения задачи зависит от значения переменной, факта (информации о удалённой системе) или результата предыдущей задачи. В некоторых случаях значения переменных могут зависеть от других переменных. Дополнительные группы могут быть созданы для управления хостами на основе соответствия хостов определённым критериям. Эта тема описывает использование условных операторов в плейбуках.

Примечание

Существует множество способов управления потоком выполнения в 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API