Spec-Zone.ru › Ansible 2.6

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

  • Оператор 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.6/user_guide/playbooks_conditionals.html

Spec-Zone.ru

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