Тесты
Тесты в Jinja — это способ оценки выражений шаблонов и возврата значений True или False. Jinja поставляется со многими такими тестами. См. встроенные тесты в официальной документации по шаблонам Jinja.
Основное различие между тестами и фильтрами состоит в том, что тесты Jinja используются для сравнений, а фильтры — для обработки данных, и они имеют разные применения в Jinja. Тесты также могут использоваться в фильтрах обработки списков, таких как map() и select() для выбора элементов в списке.
Как и все шаблоны, тесты всегда выполняются на узле управления Ansible, а не на целевом узле задачи, так как они проверяют локальные данные.
Помимо этих тестов Jinja2, Ansible предоставляет еще несколько, а пользователи могут легко создавать свои собственные.
- Синтаксис тестов
- Проверка строк
- Vault
- Проверка истинности
- Сравнение версий
- Тесты теории множеств
- Проверка, содержит ли список значение
- Проверка, является ли значение в списке True
- Проверка путей
- Проверка результатов задач
- Тесты типов
Синтаксис тестов
Синтаксис тестов отличается от синтаксиса фильтров (variable | filter). Раньше Ansible регистрировал тесты как тесты и фильтры 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 возвращает True, если находит шаблон в начале строки, а search возвращает True, если находит шаблон где-либо в строке. По умолчанию regex работает как search, но regex можно настроить на выполнение других проверок, передав аргумент match_type. В частности, match_type определяет метод re, используемый для поиска. Полный список можно найти в соответствующей документации Python здесь.
Все тесты строк также принимают необязательные аргументы ignorecase и multiline. Они соответствуют re.I и re.M из библиотеки Python re, соответственно.
Vault
Новое в версии 2.10.
Вы можете проверить, является ли переменная встроенным зашифрованным значением Vault, используя тест vault_encrypted.
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 был добавлен параметр pep440 для version_type, и правила этого типа определены в 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, который работает аналогично, но в обратном порядке, по сравнению с тестом Jinja2 in. Тест 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 }}"
Проверка, является ли значение в списке True
Новое в версии 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–2024 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tests.html