Spec-Zone.ru › Ansible

Тесты

Тесты в Jinja позволяют оценивать выражения шаблона и возвращать True или False. Jinja включает множество таких тестов. См. встроенные тесты в официальной документации по шаблонам Jinja.

Главное различие между тестами и фильтрами состоит в том, что тесты Jinja используются для сравнений, а фильтры — для обработки данных, поэтому в Jinja они применяются по-разному. Тесты также можно использовать в фильтрах обработки списков, таких как map() и select(), чтобы выбирать элементы списка.

Как и все шаблоны, тесты всегда выполняются на управляющем узле Ansible, а не на целевом узле задачи, поскольку они проверяют локальные данные.

Помимо тестов Jinja2, Ansible предоставляет ещё несколько тестов, а пользователи могут легко создавать собственные.

  • Синтаксис тестов
  • Проверка строк
  • Vault
  • Проверка истинности
  • Сравнение версий
  • Тесты теории множеств
  • Проверка наличия значения в списке
  • Проверка истинности значения в списке
  • Проверка путей
  • Проверка форматов размера

    • Удобочитаемый формат
    • Преобразование удобочитаемого формата в байты
  • Проверка результатов задач
  • Проверки типов

Синтаксис тестов

Синтаксис тестов отличается от синтаксиса фильтров (variable | filter). Раньше Ansible регистрировал тесты и как тесты Jinja, и как фильтры Jinja, что позволяло обращаться к ним с помощью синтаксиса фильтров.

Начиная с Ansible 2.5 использование теста Jinja в качестве фильтра приводит к появлению предупреждения об устаревании. Начиная с Ansible 2.9 необходимо использовать синтаксис тестов Jinja.

Синтаксис использования теста Jinja выглядит следующим образом

variable is test_name

Например

result is failed

Проверка строк

Чтобы проверить соответствие строк подстроке или регулярному выражению, используйте тесты match, search или regex

vars:
  url: "https://example.com/users/foo/resources/bar"

tasks:
    - debug:
        msg: "matched pattern 1"
      when: url is match("https://example.com/users/.*/resources")

    - debug:
        msg: "matched pattern 2"
      when: url is search("users/.*/resources/.*")

    - debug:
        msg: "matched pattern 3"
      when: url is search("users")

    - debug:
        msg: "matched pattern 4"
      when: url is regex("example\.com/\w+/foo")

Тест match завершается успешно, если находит шаблон в начале строки, а search — если находит шаблон в любом месте строки. По умолчанию regex работает так же, как search, но regex можно настроить для выполнения и других проверок, передав аргумент с ключевым словом match_type. В частности, match_type определяет метод re, используемый для поиска. Полный список приведён в соответствующей документации Python здесь.

Все строковые тесты также принимают необязательные аргументы ignorecase и multiline. Они соответствуют re.I и re.M из библиотеки re Python соответственно.

Vault

Добавлено в версии 2.10.

С помощью теста vault_encrypted можно проверить, является ли переменная встроенным зашифрованным значением vault.

vars:
  variable: !vault |
    $ANSIBLE_VAULT;1.2;AES256;dev
    61323931353866666336306139373937316366366138656131323863373866376666353364373761
    3539633234313836346435323766306164626134376564330a373530313635343535343133316133
    36643666306434616266376434363239346433643238336464643566386135356334303736353136
    6565633133366366360a326566323363363936613664616364623437336130623133343530333739
    3039

tasks:
  - debug:
      msg: '{{ (variable is vault_encrypted) | ternary("Vault encrypted", "Not vault encrypted") }}'

Проверка истинности

Добавлено в версии 2.10.

Начиная с Ansible 2.10, можно выполнять проверки истинных и ложных значений, аналогичные проверкам в Python.

- debug:
    msg: "Truthy"
  when: value is truthy
  vars:
    value: "some string"

- debug:
    msg: "Falsy"
  when: value is falsy
  vars:
    value: ""

Кроме того, тесты truthy и falsy принимают необязательный параметр convert_bool, который пытается преобразовать индикаторы логических значений в фактические логические значения.

- debug:
    msg: "Truthy"
  when: value is truthy(convert_bool=True)
  vars:
    value: "yes"

- debug:
    msg: "Falsy"
  when: value is falsy(convert_bool=True)
  vars:
    value: "off"

Сравнение версий

Добавлено в версии 1.6.

Примечание

В версии 2.5 version_compare переименован в version

Чтобы сравнить номер версии, например проверить, больше ли версия ansible_facts['distribution_version'] или равна ли она «12.04», можно использовать тест version.

Тест version также можно использовать для проверки ansible_facts['distribution_version']

"{{ ansible_facts['distribution_version'] is version('12.04', '>=') }}"

Если ansible_facts['distribution_version'] больше или равно 12.04, этот тест возвращает True, в противном случае — False.

Тест version принимает следующие операторы

<, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne

Этот тест также принимает третий параметр strict, который определяет, следует ли использовать строгий разбор версий, как определено в ansible.module_utils.compat.version.StrictVersion. По умолчанию используется False (с использованием ansible.module_utils.compat.version.LooseVersion); значение True включает строгий разбор версий

{{ sample_version_var is version('1.0', operator='lt', strict=True) }}

Начиная с Ansible 2.11 тест version принимает параметр version_type, который нельзя использовать одновременно с strict и который принимает следующие значения

loose, strict, semver, semantic, pep440
loose

Этот тип соответствует классу Python distutils.version.LooseVersion. Для этого типа допустимы все форматы версий. Правила сравнения просты и предсказуемы, но не всегда могут дать ожидаемый результат.

strict

Этот тип соответствует классу Python distutils.version.StrictVersion. Номер версии состоит из двух или трёх числовых компонентов, разделённых точками, и может заканчиваться необязательной меткой предварительного выпуска. Метка предварительного выпуска состоит из одной буквы «a» или «b», за которой следует число. Если числовые компоненты двух номеров версий совпадают, версия с меткой предварительного выпуска всегда считается более ранней (меньшей), чем версия без такой метки.

semver/semantic

Этот тип реализует схему сравнения версий Semantic Version.

pep440

Этот тип реализует правила сравнения версий Python PEP-440. Добавлено в версии 2.14.

Для сравнения семантической версии с помощью version_type можно использовать следующий код

"{{ sample_semver_var is version('2.0.0-rc.1+build.123', 'lt', version_type='semver') }}"

В Ansible 2.14 для version_type добавлен параметр pep440, а правила этого типа определены в PEP-440. В следующем примере показано, как этот тип позволяет отличить предварительные выпуски, которые считаются более ранними, от обычных выпусков.

"{{ '2.14.0rc1' is version('2.14.0', 'lt', version_type='pep440') }}"

При использовании version в плейбуке или роли не используйте {{ }}, как описано в разделе FAQ:

vars:
    my_version: 1.2.3

tasks:
    - debug:
        msg: "my_version is higher than 1.0.0"
      when: my_version is version('1.0.0', '>')

Тесты теории множеств

Добавлено в версии 2.1.

Примечание

В версии 2.5 issubset и issuperset были переименованы в subset и superset

Чтобы проверить, содержит ли список другой список или входит ли он в другой список, можно использовать «subset» и «superset»

vars:
    a: [1,2,3,4,5]
    b: [2,3]
tasks:
    - debug:
        msg: "A includes B"
      when: a is superset(b)

    - debug:
        msg: "B is included in A"
      when: b is subset(a)

Проверка наличия значения в списке

Добавлено в версии 2.8.

Ansible включает тест contains, который работает аналогично тесту in, предоставляемому Jinja2, но в обратном направлении. Тест contains предназначен для использования с фильтрами select, reject, selectattr и rejectattr

vars:
  lacp_groups:
    - master: lacp0
      network: 10.65.100.0/24
      gateway: 10.65.100.1
      dns4:
        - 10.65.100.10
        - 10.65.100.11
      interfaces:
        - em1
        - em2

    - master: lacp1
      network: 10.65.120.0/24
      gateway: 10.65.120.1
      dns4:
        - 10.65.100.10
        - 10.65.100.11
      interfaces:
          - em3
          - em4

tasks:
  - debug:
      msg: "{{ (lacp_groups|selectattr('interfaces', 'contains', 'em1')|first).master }}"

Проверка истинности значения в списке

Добавлено в версии 2.4.

С помощью any и all можно проверить, являются ли истинными некоторые или все элементы списка

vars:
  mylist:
      - 1
      - "{{ 3 == 3 }}"
      - True
  myotherlist:
      - False
      - True
tasks:

  - debug:
      msg: "all are true!"
    when: mylist is all

  - debug:
      msg: "at least one is true"
    when: myotherlist is any

Проверка путей

Примечание

В версии 2.5 следующие тесты были переименованы: из их названий убрали префикс is_

Следующие тесты позволяют получить сведения о пути на управляющем узле

- debug:
    msg: "path is a directory"
  when: mypath is directory

- debug:
    msg: "path is a file"
  when: mypath is file

- debug:
    msg: "path is a symlink"
  when: mypath is link

- debug:
    msg: "path already exists"
  when: mypath is exists

- debug:
    msg: "path is {{ (mypath is abs)|ternary('absolute','relative')}}"

- debug:
    msg: "path is the same file as path2"
  when: mypath is same_file(path2)

- debug:
    msg: "path is a mount"
  when: mypath is mount

- debug:
    msg: "path is a directory"
  when: mypath is directory
  vars:
     mypath: /my/path

- debug:
    msg: "path is a file"
  when: "'/my/path' is file"

Проверка форматов размера

Функции human_readable и human_to_bytes позволяют проверять плейбуки и убеждаться, что в задачах используется правильный формат размера, а компьютерам передаются значения в байтах, а людям — в удобочитаемом формате.

Удобочитаемый формат

Проверяет, является ли указанная строка удобочитаемой.

Например

- name: "Human Readable"
  assert:
    that:
      - '"1.00 Bytes" == 1|human_readable'
      - '"1.00 bits" == 1|human_readable(isbits=True)'
      - '"10.00 KB" == 10240|human_readable'
      - '"97.66 MB" == 102400000|human_readable'
      - '"0.10 GB" == 102400000|human_readable(unit="G")'
      - '"0.10 Gb" == 102400000|human_readable(isbits=True, unit="G")'

Результат будет следующим

{ "changed": false, "msg": "All assertions passed" }

Преобразование удобочитаемого формата в байты

Возвращает указанную строку в формате байтов.

Например

- name: "Human to Bytes"
  assert:
    that:
      - "{{'0'|human_to_bytes}}        == 0"
      - "{{'0.1'|human_to_bytes}}      == 0"
      - "{{'0.9'|human_to_bytes}}      == 1"
      - "{{'1'|human_to_bytes}}        == 1"
      - "{{'10.00 KB'|human_to_bytes}} == 10240"
      - "{{   '11 MB'|human_to_bytes}} == 11534336"
      - "{{  '1.1 GB'|human_to_bytes}} == 1181116006"
      - "{{'10.00 Kb'|human_to_bytes(isbits=True)}} == 10240"

Результат будет следующим

{ "changed": false, "msg": "All assertions passed" }

Проверка результатов задач

Следующие задачи иллюстрируют использование тестов для проверки состояния задач

tasks:

  - shell: /usr/bin/foo
    register: result
    ignore_errors: True

  - debug:
      msg: "it failed"
    when: result is failed

  # in most cases you'll want a handler, but if you want to do something right now, this is nice
  - debug:
      msg: "it changed"
    when: result is changed

  - debug:
      msg: "it succeeded in Ansible >= 2.1"
    when: result is succeeded

  - debug:
      msg: "it succeeded"
    when: result is success

  - debug:
      msg: "it was skipped"
    when: result is skipped

Примечание

Начиная с версии 2.1 можно также использовать success, failure, change и skip, чтобы синтаксис соответствовал грамматике, если требуется строгое соответствие.

Проверки типов

Чтобы определить тип, может показаться удобным использовать фильтр type_debug и сравнить результат со строковым названием этого типа. Однако вместо этого следует использовать сравнения с проверками типов, например:

tasks:
  - name: "String interpretation"
    vars:
      a_string: "A string"
      a_dictionary: {"a": "dictionary"}
      a_list: ["a", "list"]
    assert:
      that:
      # Note that a string is classed as also being "iterable" and "sequence", but not "mapping"
      - a_string is string and a_string is iterable and a_string is sequence and a_string is not mapping

      # Note that a dictionary is classed as not being a "string", but is "iterable", "sequence" and "mapping"
      - a_dictionary is not string and a_dictionary is iterable and a_dictionary is mapping

      # Note that a list is classed as not being a "string" or "mapping" but is "iterable" and "sequence"
      - a_list is not string and a_list is not mapping and a_list is iterable

  - name: "Number interpretation"
    vars:
      a_float: 1.01
      a_float_as_string: "1.01"
      an_integer: 1
      an_integer_as_string: "1"
    assert:
      that:
      # Both a_float and an_integer are "number", but each has their own type as well
      - a_float is number and a_float is float
      - an_integer is number and an_integer is integer

      # Both a_float_as_string and an_integer_as_string are not numbers
      - a_float_as_string is not number and a_float_as_string is string
      - an_integer_as_string is not number and a_float_as_string is string

      # a_float or a_float_as_string when cast to a float and then to a string should match the same value cast only to a string
      - a_float | float | string == a_float | string
      - a_float_as_string | float | string == a_float_as_string | string

      # Likewise an_integer and an_integer_as_string when cast to an integer and then to a string should match the same value cast only to an integer
      - an_integer | int | string == an_integer | string
      - an_integer_as_string | int | string == an_integer_as_string | string

      # However, a_float or a_float_as_string cast as an integer and then a string does not match the same value cast to a string
      - a_float | int | string != a_float | string
      - a_float_as_string | int | string != a_float_as_string | string

      # Again, Likewise an_integer and an_integer_as_string cast as a float and then a string does not match the same value cast to a string
      - an_integer | float | string != an_integer | string
      - an_integer_as_string | float | string != an_integer_as_string | string

  - name: "Native Boolean interpretation"
    loop:
    - yes
    - true
    - True
    - TRUE
    - no
    - No
    - NO
    - false
    - False
    - FALSE
    assert:
      that:
      # Note that while other values may be cast to boolean values, these are the only ones that are natively considered boolean
      # Note also that `yes` is the only case-sensitive variant of these values.
      - item is boolean

См. также

Плейбуки Ansible

Введение в плейбуки

Условные выражения

Условные операторы в плейбуках

Использование переменных

Всё о переменных

Циклы

Циклы в плейбуках

Роли

Организация плейбуков с помощью ролей

Общие рекомендации

Советы и рекомендации по работе с плейбуками

Общение

Есть вопросы? Нужна помощь? Хотите поделиться идеями? Посетите руководство по общению сообщества Ansible

© 2012–2018 Michael DeHaan
© 2018–2025 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_tests.html

Spec-Zone.ru

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