Использование фильтров для обработки данных
Фильтры позволяют преобразовывать данные JSON в данные YAML, разбивать URL-адрес для извлечения имени хоста, получать хеш SHA1 строки, складывать или умножать целые числа и выполнять множество других операций. Вы можете использовать описанные здесь фильтры, специфичные для Ansible, чтобы обрабатывать данные, или любые стандартные фильтры, поставляемые с Jinja2 — см. список встроенных фильтров в официальной документации по шаблонам Jinja2. Вы также можете использовать методы Python для преобразования данных. Вы можете создавать пользовательские фильтры Ansible в виде плагинов, хотя мы обычно приветствуем добавление новых фильтров в репозиторий ansible-core, чтобы ими могли пользоваться все.
Поскольку шаблонизация выполняется на управляющем узле Ansible, а не на целевом узле, фильтры выполняются на управляющем узле и преобразуют данные локально.
- Задание разных значений для true/false/null (тернарный оператор)
- Управление переменными-списками
- Выбор из множеств или списков (теория множеств)
- Вычисления с числами (математика)
- Хеширование и шифрование строк и паролей
- Обработка строк
- Управление UUID
- Работа с датами и временем
- Получение имён ресурсов Kubernetes
Обработка неопределённых переменных
Фильтры помогают управлять отсутствующими или неопределёнными переменными: задавать значения по умолчанию или делать некоторые переменные необязательными. Если вы настроили Ansible так, чтобы игнорировать большинство неопределённых переменных, можно пометить некоторые переменные как обязательные с помощью фильтра mandatory.
Задание значений по умолчанию
Вы можете задавать значения переменных по умолчанию непосредственно в шаблонах с помощью фильтра Jinja2 ‘default’. Часто это предпочтительнее, чем завершать работу с ошибкой, если переменная не определена:
{{ some_variable | default(5) }}
В приведённом выше примере, если переменная ‘some_variable’ не определена, Ansible использует значение по умолчанию 5, а не выдаёт ошибку «undefined variable» и завершает работу. Если вы работаете с ролью, можно также добавить значения по умолчанию роли, чтобы задать значения переменных по умолчанию в ней. Подробнее о значениях по умолчанию роли см. в разделе Структура каталога роли.
Начиная с версии 2.8, попытка получить атрибут значения Undefined в Jinja возвращает другое значение Undefined, а не сразу вызывает ошибку. Это означает, что теперь можно просто использовать значение по умолчанию во вложенной структуре данных (другими словами, {{ foo.bar.baz | default('DEFAULT') }}), если неизвестно, определены ли промежуточные значения.
Если вы хотите использовать значение по умолчанию, когда переменные принимают значение false или пустой строки, задайте второй параметр равным true:
{{ lookup('env', 'MY_USER') | default('admin', true) }}
Сделать переменные необязательными
По умолчанию Ansible требует значения для всех переменных в шаблонизированном выражении. Однако отдельные переменные модуля можно сделать необязательными. Например, для некоторых элементов можно использовать системное значение по умолчанию, а для других — управлять значением самостоятельно. Чтобы сделать переменную модуля необязательной, задайте в качестве значения по умолчанию специальную переменную omit:
- name: Touch files with an optional mode
ansible.builtin.file:
dest: "{{ item.path }}"
state: touch
mode: "{{ item.mode | default(omit) }}"
loop:
- path: /tmp/foo
- path: /tmp/bar
- path: /tmp/baz
mode: "0444"
В этом примере режим по умолчанию для файлов /tmp/foo и /tmp/bar определяется маской создания файлов системы (umask). Ansible не передаёт значение для mode. Параметр mode=0444 получает только третий файл, /tmp/baz.
Примечание
Если вы «сцепляете» дополнительные фильтры после фильтра default(omit), вместо этого следует использовать конструкцию наподобие этой: "{{ foo | default(None) | some_filter or omit }}". В этом примере значение по умолчанию None (Python null) приведёт к сбою последующих фильтров, что активирует часть логики or omit. Однако использование omit таким способом зависит от конкретных последующих фильтров, поэтому будьте готовы методом проб и ошибок подобрать нужный вариант.
Задание обязательных значений
Если вы настроили Ansible так, чтобы игнорировать неопределённые переменные, некоторые значения может потребоваться объявить обязательными. По умолчанию Ansible завершает работу с ошибкой, если переменная в плейбуке или команде не определена. Можно настроить Ansible так, чтобы он разрешал неопределённые переменные, задав для DEFAULT_UNDEFINED_VAR_BEHAVIOR значение false. В таком случае можно потребовать, чтобы некоторые переменные были определены. Для этого используйте:
{{ variable | mandatory }}
Значение переменной будет использовано без изменений, но при вычислении шаблона возникнет ошибка, если переменная не определена.
Удобный способ потребовать переопределения переменной — присвоить ей неопределённое значение с помощью функции undef().
galaxy_url: "https://galaxy.ansible.com"
galaxy_api_key: "{{ undef(hint='You must specify your Galaxy API key') }}"
Задание разных значений для true/false/null (тернарный оператор)
Можно выполнить проверку, а затем задать одно значение для случая, когда проверка возвращает true, и другое — когда она возвращает false (доступно начиная с версии 1.9):
{{ (status == 'needs_restart') | ternary('restart', 'continue') }}
Кроме того, можно задать одно значение для true, другое для false и третье для null (доступно начиная с версии 2.8):
{{ enabled | ternary('no shutdown', 'shutdown', omit) }}
Управление типами данных
Вам может потребоваться узнать, изменить или задать тип данных переменной. Например, зарегистрированная переменная может содержать словарь, тогда как для следующей задачи нужен список, или пользовательский запрос может вернуть строку, хотя плейбуку нужно логическое значение. Для управления типами данных используйте фильтры ansible.builtin.type_debug, ansible.builtin.dict2items и ansible.builtin.items2dict. Также можно использовать сам тип данных, чтобы привести значение к определённому типу.
Определение типа данных
Новое в версии 2.3.
Если вы не уверены, каков базовый тип Python у переменной, его можно вывести с помощью фильтра ansible.builtin.type_debug. Это полезно при отладке, когда требуется переменная определённого типа:
{{ myvar | type_debug }}
Следует учитывать, что, хотя этот фильтр может показаться полезным для проверки правильности типа данных переменной, зачастую лучше использовать проверки типов, которые позволяют проверять конкретные типы данных.
Преобразование строк в списки
Используйте фильтр ansible.builtin.split, чтобы преобразовать строку, разделённую символом или строкой-разделителем, в список элементов, подходящий для циклов. Например, если нужно разбить строковую переменную fruits по запятым, можно использовать:
{{ fruits | split(',') }}
Строковые данные (до применения фильтра ansible.builtin.split):
fruits: apple,banana,orange
Данные в виде списка (после применения фильтра ansible.builtin.split):
- apple - banana - orange
Преобразование словарей в списки
Новое в версии 2.6.
Используйте фильтр ansible.builtin.dict2items, чтобы преобразовать словарь в список элементов, подходящий для циклов:
{{ dict | dict2items }}
Данные словаря (до применения фильтра ansible.builtin.dict2items):
tags: Application: payment Environment: dev
Данные в виде списка (после применения фильтра ansible.builtin.dict2items):
- key: Application value: payment - key: Environment value: dev
Новое в версии 2.8.
Фильтр ansible.builtin.dict2items является обратным фильтру ansible.builtin.items2dict.
Если нужно настроить имена ключей, фильтр ansible.builtin.dict2items принимает 2 именованных аргумента. Передайте аргументы key_name и value_name, чтобы задать имена ключей в списке на выходе:
{{ files | dict2items(key_name='file', value_name='path') }}
Данные словаря (до применения фильтра ansible.builtin.dict2items):
files: users: /etc/passwd groups: /etc/group
Данные в виде списка (после применения фильтра ansible.builtin.dict2items):
- file: users path: /etc/passwd - file: groups path: /etc/group
Преобразование списков в словари
Новое в версии 2.7.
Используйте фильтр ansible.builtin.items2dict, чтобы преобразовать список в словарь, сопоставив содержимое парам key: value:
{{ tags | items2dict }}
Данные в виде списка (до применения фильтра ansible.builtin.items2dict):
tags:
- key: Application
value: payment
- key: Environment
value: dev
Данные словаря (после применения фильтра ansible.builtin.items2dict):
Application: payment Environment: dev
Фильтр ansible.builtin.items2dict является обратным фильтру ansible.builtin.dict2items.
В некоторых списках для обозначения ключей используются не key, а для обозначения значений — не value. Например:
fruits:
- fruit: apple
color: red
- fruit: pear
color: yellow
- fruit: grapefruit
color: yellow
В этом примере для настройки преобразования необходимо передать аргументы key_name и value_name. Например:
{{ fruits | items2dict(key_name='fruit', value_name='color') }}
Если не передать эти аргументы или указать неправильные значения для списка, вы увидите KeyError: key или KeyError: my_typo.
Принудительное задание типа данных
Можно приводить значения к определённым типам. Например, если вы ожидаете получить ввод «True» с помощью vars_prompt и хотите, чтобы Ansible распознал его как логическое значение, а не строку:
- ansible.builtin.debug:
msg: test
when: some_string_value | bool
Если нужно выполнить математическое сравнение факта и вы хотите, чтобы Ansible распознал его как целое число, а не строку:
- shell: echo "only on Red Hat 6, derivatives, and later" when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6
Новое в версии 1.6.
Форматирование данных: YAML и JSON
В шаблоне можно преобразовывать структуру данных из JSON или YAML в другой формат и обратно, задавая параметры форматирования, отступов и загрузки данных. Основные фильтры иногда бывают полезны при отладке:
{{ some_variable | to_json }}
{{ some_variable | to_yaml }}
Документацию по этим фильтрам см. в разделах ansible.builtin.to_json и ansible.builtin.to_yaml.
Для вывода, удобного для чтения человеком, можно использовать:
{{ some_variable | to_nice_json }}
{{ some_variable | to_nice_yaml }}
Документацию по этим фильтрам см. в разделах ansible.builtin.to_nice_json и ansible.builtin.to_nice_yaml.
Можно изменить отступы в любом из форматов:
{{ some_variable | to_nice_json(indent=2) }}
{{ some_variable | to_nice_yaml(indent=8) }}
Фильтры ansible.builtin.to_yaml и ansible.builtin.to_nice_yaml используют библиотеку PyYAML, в которой по умолчанию ограничена длина строки 80 символами. Из-за этого после 80-го символа происходит неожиданный перенос строки (если после 80-го символа стоит пробел). Чтобы избежать такого поведения и создавать длинные строки, используйте параметр width. Для задания ширины необходимо указать фиксированное число, а не конструкцию вроде float("inf"), поскольку фильтр не поддерживает передачу функций Python. Например:
{{ some_variable | to_yaml(indent=8, width=1337) }}
{{ some_variable | to_nice_yaml(indent=8, width=1337) }}
Фильтр также поддерживает передачу других параметров YAML. Полный список см. в документации PyYAML для dump().
Если вы читаете уже отформатированные данные:
{{ some_variable | from_json }}
{{ some_variable | from_yaml }}
например:
tasks:
- name: Register JSON output as a variable
ansible.builtin.shell: cat /some/path/to/file.json
register: result
- name: Set a variable
ansible.builtin.set_fact:
myvar: "{{ result.stdout | from_json }}"
Фильтр to_json и поддержка Unicode
По умолчанию фильтры ansible.builtin.to_json и ansible.builtin.to_nice_json преобразуют полученные данные в ASCII, поэтому:
{{ 'München'| to_json }}
вернёт:
'M\u00fcnchen'
Чтобы сохранить символы Unicode, передайте фильтру параметр ensure_ascii=False:
{{ 'München'| to_json(ensure_ascii=False) }}
'München'
Новое в версии 2.7.
Для разбора YAML-строк с несколькими документами предусмотрен фильтр ansible.builtin.from_yaml_all. Фильтр ansible.builtin.from_yaml_all возвращает генератор разобранных документов YAML.
например:
tasks:
- name: Register a file content as a variable
ansible.builtin.shell: cat /some/path/to/multidoc-file.yaml
register: result
- name: Print the transformed variable
ansible.builtin.debug:
msg: '{{ item }}'
loop: '{{ result.stdout | from_yaml_all | list }}'
Объединение и выбор данных
Можно объединять данные из нескольких источников и разных типов, а также выбирать значения из больших структур данных, получая точный контроль над сложными данными.
Объединение элементов из нескольких списков: zip и zip_longest
Добавлено в версии 2.3.
Чтобы получить список, объединяющий элементы других списков, используйте ansible.builtin.zip:
- name: Give me list combo of two lists
ansible.builtin.debug:
msg: "{{ [1,2,3,4,5,6] | zip(['a','b','c','d','e','f']) | list }}"
# => [[1, "a"], [2, "b"], [3, "c"], [4, "d"], [5, "e"], [6, "f"]]
- name: Give me the shortest combo of two lists
ansible.builtin.debug:
msg: "{{ [1,2,3] | zip(['a','b','c','d','e','f']) | list }}"
# => [[1, "a"], [2, "b"], [3, "c"]]
Чтобы всегда обрабатывать все элементы списков, используйте ansible.builtin.zip_longest:
- name: Give me the longest combo of three lists, fill with X
ansible.builtin.debug:
msg: "{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X') | list }}"
# => [[1, "a", 21], [2, "b", 22], [3, "c", 23], ["X", "d", "X"], ["X", "e", "X"], ["X", "f", "X"]]
Как и результат фильтра ansible.builtin.items2dict, упомянутого выше, эти фильтры можно использовать для создания dict:
{{ dict(keys_list | zip(values_list)) }}
Данные списка (до применения фильтра ansible.builtin.zip):
keys_list: - one - two values_list: - apple - orange
Данные словаря (после применения фильтра ansible.builtin.zip):
one: apple two: orange
Объединение объектов и вложенных элементов
Добавлено в версии 2.7.
Фильтр ansible.builtin.subelements создает произведение объекта и значений его вложенных элементов, подобно lookup-плагину ansible.builtin.subelements. Это позволяет указать отдельные вложенные элементы для использования в шаблоне. Например, следующее выражение:
{{ users | subelements('groups', skip_missing=True) }}
Данные до применения фильтра ansible.builtin.subelements:
users: - name: alice authorized: - /tmp/alice/onekey.pub - /tmp/alice/twokey.pub groups: - wheel - docker - name: bob authorized: - /tmp/bob/id_rsa.pub groups: - docker
Данные после применения фильтра ansible.builtin.subelements:
-
- name: alice
groups:
- wheel
- docker
authorized:
- /tmp/alice/onekey.pub
- /tmp/alice/twokey.pub
- wheel
-
- name: alice
groups:
- wheel
- docker
authorized:
- /tmp/alice/onekey.pub
- /tmp/alice/twokey.pub
- docker
-
- name: bob
authorized:
- /tmp/bob/id_rsa.pub
groups:
- docker
- docker
Преобразованные данные можно использовать с loop, чтобы выполнить итерацию по одному и тому же вложенному элементу для нескольких объектов:
- name: Set authorized ssh key, extracting just that data from 'users'
ansible.posix.authorized_key:
user: "{{ item.0.name }}"
key: "{{ lookup('file', item.1) }}"
loop: "{{ users | subelements('authorized') }}"
Объединение хешей/словарей
Добавлено в версии 2.0.
Фильтр ansible.builtin.combine позволяет объединять хеши. Например, следующий код переопределит ключи одного хеша:
{{ {'a':1, 'b':2} | combine({'b':3}) }}
В результате получится хеш:
{'a':1, 'b':3}
Фильтр также может принимать несколько аргументов для объединения:
{{ a | combine(b, c, d) }}
{{ [a, b, c, d] | combine }}
В этом случае ключи в d переопределят ключи в c, которые, в свою очередь, переопределят ключи в b, и так далее.
Фильтр также принимает два необязательных параметра: recursive и list_merge.
- recursive
-
Логическое значение, по умолчанию —
False. Определяет, следует ли фильтру ansible.builtin.combine рекурсивно объединять вложенные хеши. Примечание: значение этого параметра не зависит от значения параметраhash_behaviourвansible.cfg. - list_merge
-
Строка; возможные значения:
replace(по умолчанию),keep,append,prepend,append_rpилиprepend_rp. Изменяет поведение фильтра ansible.builtin.combine, когда объединяемые хеши содержат массивы/списки.
default:
a:
x: default
y: default
b: default
c: default
patch:
a:
y: patch
z: patch
b: patch
Если recursive=False (значение по умолчанию), вложенные хеши не объединяются:
{{ default | combine(patch) }}
В результате получится:
a: y: patch z: patch b: patch c: default
Если recursive=True, рекурсивно обработайте вложенные хеши и объедините их ключи:
{{ default | combine(patch, recursive=True) }}
В результате получится:
a: x: default y: patch z: patch b: patch c: default
Если list_merge='replace' (значение по умолчанию), массивы из правого хеша «заменят» массивы из левого хеша:
default:
a:
- default
patch:
a:
- patch
{{ default | combine(patch) }}
В результате получится:
a: - patch
Если list_merge='keep', массивы из левого хеша сохраняются:
{{ default | combine(patch, list_merge='keep') }}
В результате получится:
a: - default
Если list_merge='append', массивы из правого хеша добавляются в конец массивов из левого хеша:
{{ default | combine(patch, list_merge='append') }}
В результате получится:
a: - default - patch
Если list_merge='prepend', массивы из правого хеша добавляются в начало массивов из левого хеша:
{{ default | combine(patch, list_merge='prepend') }}
В результате получится:
a: - patch - default
Если list_merge='append_rp', массивы из правого хеша добавляются в конец массивов из левого хеша. Элементы массивов в левом хеше, которые также есть в соответствующем массиве правого хеша, удаляются («rp» означает «удалить присутствующие»). Повторяющиеся элементы, отсутствующие в обоих хешах, сохраняются:
default:
a:
- 1
- 1
- 2
- 3
patch:
a:
- 3
- 4
- 5
- 5
{{ default | combine(patch, list_merge='append_rp') }}
В результате получится:
a: - 1 - 1 - 2 - 3 - 4 - 5 - 5
Если list_merge='prepend_rp', поведение аналогично append_rp, но элементы массивов из правого хеша добавляются в начало:
{{ default | combine(patch, list_merge='prepend_rp') }}
В результате получится:
a: - 3 - 4 - 5 - 5 - 1 - 1 - 2
Параметры recursive и list_merge можно использовать вместе:
default:
a:
a':
x: default_value
y: default_value
list:
- default_value
b:
- 1
- 1
- 2
- 3
patch:
a:
a':
y: patch_value
z: patch_value
list:
- patch_value
b:
- 3
- 4
- 4
- key: value
{{ default | combine(patch, recursive=True, list_merge='append_rp') }}
В результате получится:
a:
a':
x: default_value
y: patch_value
z: patch_value
list:
- default_value
- patch_value
b:
- 1
- 1
- 2
- 3
- 4
- 4
- key: value
Выбор значений из массивов или хеш-таблиц
Добавлено в версии 2.1.
Фильтр extract используется для преобразования списка индексов в список значений контейнера (хеша или массива):
{{ [0,2] | map('extract', ['x','y','z']) | list }}
{{ ['x','y'] | map('extract', {'x': 42, 'y': 31}) | list }}
Результаты приведенных выше выражений будут следующими:
['x', 'z'] [42, 31]
Фильтр может принимать еще один аргумент:
{{ groups['x'] | map('extract', hostvars, 'ec2_ip_address') | list }}
Этот фильтр берет список узлов из группы «x», ищет их в hostvars, а затем ищет ec2_ip_address результата. В итоге получается список IP-адресов узлов из группы «x».
Третий аргумент фильтра также может быть списком для рекурсивного поиска внутри контейнера:
{{ ['a'] | map('extract', b, ['x','y']) | list }}
В результате будет возвращен список, содержащий значение b[‘a’][‘x’][‘y’].
Объединение списков
Эта группа фильтров возвращает список объединенных списков.
перестановки
Чтобы получить перестановки списка:
- name: Give me the largest permutations (order matters)
ansible.builtin.debug:
msg: "{{ [1,2,3,4,5] | ansible.builtin.permutations | list }}"
# => [(1, 2, 3, 4, 5), (1, 2, 3, 5, 4), (1, 2, 4, 3, 5), (1, 2, 4, 5, 3), (1, 2, 5, 3, 4), (1, 2, 5, 4, 3), (1, 3, 2, 4, 5), (1, 3, 2, 5, 4), (1, 3, 4, 2, 5), (1, 3, 4, 5, 2), (1, 3, 5, 2, 4), (1, 3, 5, 4, 2), (1, 4, 2, 3, 5), (1, 4, 2, 5, 3), (1, 4, 3, 2, 5), (1, 4, 3, 5, 2), (1, 4, 5, 2, 3), (1, 4, 5, 3, 2), (1, 5, 2, 3, 4), (1, 5, 2, 4, 3), (1, 5, 3, 2, 4), (1, 5, 3, 4, 2), (1, 5, 4, 2, 3), (1, 5, 4, 3, 2), (2, 1, 3, 4, 5), (2, 1, 3, 5, 4), (2, 1, 4, 3, 5), (2, 1, 4, 5, 3), (2, 1, 5, 3, 4), (2, 1, 5, 4, 3), (2, 3, 1, 4, 5), (2, 3, 1, 5, 4), (2, 3, 4, 1, 5), (2, 3, 4, 5, 1), (2, 3, 5, 1, 4), (2, 3, 5, 4, 1), (2, 4, 1, 3, 5), (2, 4, 1, 5, 3), (2, 4, 3, 1, 5), (2, 4, 3, 5, 1), (2, 4, 5, 1, 3), (2, 4, 5, 3, 1), (2, 5, 1, 3, 4), (2, 5, 1, 4, 3), (2, 5, 3, 1, 4), (2, 5, 3, 4, 1), (2, 5, 4, 1, 3), (2, 5, 4, 3, 1), (3, 1, 2, 4, 5), (3, 1, 2, 5, 4), (3, 1, 4, 2, 5), (3, 1, 4, 5, 2), (3, 1, 5, 2, 4), (3, 1, 5, 4, 2), (3, 2, 1, 4, 5), (3, 2, 1, 5, 4), (3, 2, 4, 1, 5), (3, 2, 4, 5, 1), (3, 2, 5, 1, 4), (3, 2, 5, 4, 1), (3, 4, 1, 2, 5), (3, 4, 1, 5, 2), (3, 4, 2, 1, 5), (3, 4, 2, 5, 1), (3, 4, 5, 1, 2), (3, 4, 5, 2, 1), (3, 5, 1, 2, 4), (3, 5, 1, 4, 2), (3, 5, 2, 1, 4), (3, 5, 2, 4, 1), (3, 5, 4, 1, 2), (3, 5, 4, 2, 1), (4, 1, 2, 3, 5), (4, 1, 2, 5, 3), (4, 1, 3, 2, 5), (4, 1, 3, 5, 2), (4, 1, 5, 2, 3), (4, 1, 5, 3, 2), (4, 2, 1, 3, 5), (4, 2, 1, 5, 3), (4, 2, 3, 1, 5), (4, 2, 3, 5, 1), (4, 2, 5, 1, 3), (4, 2, 5, 3, 1), (4, 3, 1, 2, 5), (4, 3, 1, 5, 2), (4, 3, 2, 1, 5), (4, 3, 2, 5, 1), (4, 3, 5, 1, 2), (4, 3, 5, 2, 1), (4, 5, 1, 2, 3), (4, 5, 1, 3, 2), (4, 5, 2, 1, 3), (4, 5, 2, 3, 1), (4, 5, 3, 1, 2), (4, 5, 3, 2, 1), (5, 1, 2, 3, 4), (5, 1, 2, 4, 3), (5, 1, 3, 2, 4), (5, 1, 3, 4, 2), (5, 1, 4, 2, 3), (5, 1, 4, 3, 2), (5, 2, 1, 3, 4), (5, 2, 1, 4, 3), (5, 2, 3, 1, 4), (5, 2, 3, 4, 1), (5, 2, 4, 1, 3), (5, 2, 4, 3, 1), (5, 3, 1, 2, 4), (5, 3, 1, 4, 2), (5, 3, 2, 1, 4), (5, 3, 2, 4, 1), (5, 3, 4, 1, 2), (5, 3, 4, 2, 1), (5, 4, 1, 2, 3), (5, 4, 1, 3, 2), (5, 4, 2, 1, 3), (5, 4, 2, 3, 1), (5, 4, 3, 1, 2), (5, 4, 3, 2, 1)]
- name: Give me permutations of sets of three
ansible.builtin.debug:
msg: "{{ [1,2,3,4,5] | ansible.builtin.permutations(3) | list }}"
# => [(1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 2), (1, 3, 4), (1, 3, 5), (1, 4, 2), (1, 4, 3), (1, 4, 5), (1, 5, 2), (1, 5, 3), (1, 5, 4), (2, 1, 3), (2, 1, 4), (2, 1, 5), (2, 3, 1), (2, 3, 4), (2, 3, 5), (2, 4, 1), (2, 4, 3), (2, 4, 5), (2, 5, 1), (2, 5, 3), (2, 5, 4), (3, 1, 2), (3, 1, 4), (3, 1, 5), (3, 2, 1), (3, 2, 4), (3, 2, 5), (3, 4, 1), (3, 4, 2), (3, 4, 5), (3, 5, 1), (3, 5, 2), (3, 5, 4), (4, 1, 2), (4, 1, 3), (4, 1, 5), (4, 2, 1), (4, 2, 3), (4, 2, 5), (4, 3, 1), (4, 3, 2), (4, 3, 5), (4, 5, 1), (4, 5, 2), (4, 5, 3), (5, 1, 2), (5, 1, 3), (5, 1, 4), (5, 2, 1), (5, 2, 3), (5, 2, 4), (5, 3, 1), (5, 3, 2), (5, 3, 4), (5, 4, 1), (5, 4, 2), (5, 4, 3)]
сочетания
Для сочетаний всегда необходимо указать размер набора:
- name: Give me combinations for sets of two
ansible.builtin.debug:
msg: "{{ [1,2,3,4,5] | ansible.builtin.combinations(2) | list }}"
# => [(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
См. также фильтры zip и zip_longest
декартовы произведения
Фильтр product возвращает декартово произведение входных итерируемых объектов. Это примерно эквивалентно вложенным циклам for в генераторном выражении.
{{ ['foo', 'bar'] | product(['com']) }}
# => [('foo', 'com'), ('bar', 'com')]
Например, сгенерируйте несколько имен узлов:
{{ ['foo', 'bar'] | product(['com']) | map('join', '.') }}
# => ['foo.com', 'bar.com']
Выбор данных JSON: запросы JSON
Чтобы выбрать один элемент или подмножество данных из сложной структуры данных в формате JSON (например, фактов Ansible), используйте фильтр community.general.json_query. Фильтр community.general.json_query позволяет выполнять запросы к сложной структуре JSON и перебирать ее с помощью цикла.
Примечание
Этот фильтр перенесен в коллекцию community.general. Чтобы установить эту коллекцию, следуйте инструкциям по установке.
Примечание
Перед использованием этого фильтра необходимо вручную установить зависимость jmespath на управляющем узле Ansible. Фильтр построен на основе jmespath и использует тот же синтаксис. Примеры см. в разделе примеры jmespath.
Рассмотрим следующую структуру данных:
{
"domain": {
"cluster": [
{
"name": "cluster1"
},
{
"name": "cluster2"
}
],
"server": [
{
"name": "server11",
"cluster": "cluster1",
"port": "8080"
},
{
"name": "server12",
"cluster": "cluster1",
"port": "8090"
},
{
"name": "server21",
"cluster": "cluster2",
"port": "9080"
},
{
"name": "server22",
"cluster": "cluster2",
"port": "9090"
}
],
"library": [
{
"name": "lib1",
"target": "cluster1"
},
{
"name": "lib2",
"target": "cluster2"
}
]
}
}
Чтобы извлечь из этой структуры все кластеры, можно использовать следующий запрос:
- name: Display all cluster names
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query('domain.cluster[*].name') }}"
Чтобы извлечь имена всех серверов:
- name: Display all server names
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query('domain.server[*].name') }}"
Чтобы извлечь порты из cluster1:
- name: Display all ports from cluster1
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
vars:
server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port"
Примечание
Для повышения читаемости запроса можно использовать переменную.
Чтобы вывести порты из cluster1 в виде строки, разделенной запятыми:
- name: Display all ports from cluster1 as a string
ansible.builtin.debug:
msg: "{{ domain_definition | community.general.json_query('domain.server[?cluster==`cluster1`].port') | join(', ') }}"
Примечание
В приведенном выше примере заключение литералов в обратные кавычки позволяет избежать экранирования кавычек и сохранить читаемость.
Можно использовать экранирование одинарных кавычек в YAML:
- name: Display all ports from cluster1
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query('domain.server[?cluster==''cluster1''].port') }}"
Примечание
Чтобы экранировать одинарные кавычки внутри одинарных кавычек в YAML, удвойте одинарную кавычку.
Чтобы получить хеш-таблицу со всеми портами и именами кластера:
- name: Display all server ports and names from cluster1
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
vars:
server_name_cluster1_query: "domain.server[?cluster=='cluster1'].{name: name, port: port}"
Чтобы извлечь порты из всех кластеров, имя которых начинается с «server1»:
- name: Display ports from all clusters with the name starting with 'server1'
ansible.builtin.debug:
msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}"
vars:
server_name_query: "domain.server[?starts_with(name,'server1')].port"
Чтобы извлечь порты из всех кластеров, имя которых содержит «server1»:
- name: Display ports from all clusters with the name containing 'server1'
ansible.builtin.debug:
msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}"
vars:
server_name_query: "domain.server[?contains(name,'server1')].port"
Примечание
При использовании starts_with и contains необходимо применять фильтр `` to_json | from_json `` для корректного разбора структуры данных.
Рандомизация данных
Если вам нужно случайно сгенерированное значение, используйте один из этих фильтров.
Случайные MAC-адреса
Добавлено в версии 2.6.
Этот фильтр можно использовать для генерации случайного MAC-адреса с заданным строковым префиксом.
Примечание
Этот фильтр перенесен в коллекцию community.general. Чтобы установить эту коллекцию, следуйте инструкциям по установке.
Чтобы получить случайный MAC-адрес со строковым префиксом «52:54:00»:
"{{ '52:54:00' | community.general.random_mac }}"
# => '52:54:00:ef:1c:03'
Обратите внимание: если строка префикса содержит ошибку, фильтр выдаст сообщение об ошибке.
Добавлено в версии 2.9.
Начиная с Ansible версии 2.9, можно также инициализировать генератор случайных чисел с помощью начального значения, чтобы создавать случайные, но идемпотентные MAC-адреса:
"{{ '52:54:00' | community.general.random_mac(seed=inventory_hostname) }}"
Случайные элементы или числа
Фильтр ansible.builtin.random в Ansible расширяет стандартный фильтр random в Jinja2. Его можно использовать для выбора случайного элемента из последовательности или для генерации случайного числа в заданном диапазоне.
Чтобы получить случайный элемент из списка:
"{{ ['a','b','c'] | random }}"
# => 'c'
Чтобы получить случайное число от 0 (включительно) до заданного целого числа (не включительно):
"{{ 60 | random }} * * * * root /script/from/cron"
# => '21 * * * * root /script/from/cron'
Чтобы получить случайное число от 0 до 100 с шагом 10:
{{ 101 | random(step=10) }}
# => 70
Чтобы получить случайное число от 1 до 100 с шагом 10:
{{ 101 | random(1, 10) }}
# => 31
{{ 101 | random(start=1, step=10) }}
# => 51
Можно инициализировать генератор случайных чисел с помощью начального значения, чтобы создавать случайные, но идемпотентные числа:
"{{ 60 | random(seed=inventory_hostname) }} * * * * root /script/from/cron"
Перемешивание списка
Фильтр ansible.builtin.shuffle перемешивает существующий список, задавая новый порядок при каждом вызове.
Чтобы получить случайно упорядоченный список из существующего списка:
{{ ['a','b','c'] | shuffle }}
# => ['c','a','b']
{{ ['a','b','c'] | shuffle }}
# => ['b','c','a']
Можно инициализировать генератор перемешивания с помощью начального значения, чтобы получить случайный, но идемпотентный порядок:
{{ ['a','b','c'] | shuffle(seed=inventory_hostname) }}
# => ['b','a','c']
Если возможно, фильтр shuffle возвращает список. При использовании с элементом, который нельзя преобразовать в список, фильтр ничего не делает.
Управление переменными-списками
Можно найти минимальное или максимальное значение в списке либо преобразовать многоуровневый список в плоский.
Чтобы получить минимальное значение из списка чисел:
{{ [3, 4, 2] | min }}
# => 2
Добавлено в версии 2.11.
Чтобы получить минимальное значение в списке объектов:
{{ [{'val': 1}, {'val': 2}] | min(attribute='val') }}
# => {'val': 1}
Чтобы получить максимальное значение из списка чисел:
{{ [3, 4, 2] | max }}
# => 4
Добавлено в версии 2.11.
Чтобы получить максимальное значение в списке объектов:
{{ [{'val': 1}, {'val': 2}] | max(attribute='val') }}
# => {'val': 2}
Добавлено в версии 2.5.
Преобразование списка в плоский (то же, что и lookup-плагин flatten):
{{ [3, [4, 2] ] | flatten }}
# => [3, 4, 2]
Преобразование в плоский список только первого уровня (аналогично lookup-плагину items):
{{ [3, [4, [2]] ] | flatten(levels=1) }}
# => [3, 4, [2]]
Добавлено в версии 2.11.
Сохранение значений null в списке (по умолчанию flatten удаляет их):
{{ [3, None, [4, [2]] ] | flatten(levels=1, skip_nulls=False) }}
# => [3, None, 4, [2]]
Создание списка путем повторения списка N раз:
{{ 3*[1, 2, 3, "foo"] }}
# => [1, 2, 3, 'foo', 1, 2, 3, 'foo', 1, 2, 3, 'foo']
Выбор элементов из множеств или списков (теория множеств)
Можно выбирать или объединять элементы множеств или списков. Обратите внимание: мультимножества пока не поддерживаются, и все перечисленные ниже фильтры предполагают уникальность элементов. Это означает, что повторяющиеся элементы удаляются из результата.
Добавлено в версии 1.4.
Чтобы получить множество уникальных элементов из списка:
# list1: [1, 2, 5, 1, 3, 4, 10]
{{ list1 | unique }}
# => [1, 2, 5, 3, 4, 10]
Чтобы получить объединение двух списков без повторяющихся элементов:
# list1: [1, 2, 5, 1, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | union(list2) }}
# => [1, 2, 3, 4, 5, 99, 10, 11]
Чтобы получить пересечение двух списков (список уникальных элементов, присутствующих в обоих списках):
# list1: [1, 2, 5, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | intersect(list2) }}
# => [1, 2, 3, 4, 5]
Чтобы получить разность двух списков (элементы первого списка, отсутствующие во втором):
# list1: [1, 2, 5, 1, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | difference(list2) }}
# => [10]
Чтобы получить симметрическую разность двух списков (элементы, входящие только в один из списков):
# list1: [1, 2, 5, 1, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | symmetric_difference(list2) }}
# => [99, 10, 11]
Вычисления (математика)
Добавлено в версии 1.9.
С помощью фильтров Ansible можно вычислять логарифмы, степени и корни чисел. Jinja2 предоставляет и другие математические функции, например abs() и round().
Вычисление логарифма (по умолчанию по основанию e):
{{ 8 | log }}
# => 2.0794415416798357
Вычисление логарифма по основанию 10:
{{ 8 | log(10) }}
# => 0.9030899869919435
Возведение в степень 2 (или 5):
{{ 8 | pow(5) }}
# => 32768.0
Квадратный корень или корень пятой степени:
{{ 8 | root }}
# => 2.8284271247461903
{{ 8 | root(5) }}
# => 1.5157165665103982
Управление сетевыми взаимодействиями
Эти фильтры помогают выполнять распространенные сетевые задачи.
Примечание
Эти фильтры перенесены в коллекцию ansible.utils. Чтобы установить эту коллекцию, следуйте инструкциям по установке.
Фильтры IP-адресов
Добавлено в версии 1.9.
Чтобы проверить, является ли строка допустимым IP-адресом:
{{ myvar | ansible.utils.ipaddr }}
Также можно указать конкретную версию IP-протокола:
{{ myvar | ansible.utils.ipv4 }}
{{ myvar | ansible.utils.ipv6 }}
Фильтр IP-адресов также можно использовать для извлечения определенных сведений из IP-адреса. Например, чтобы получить сам IP-адрес из CIDR, можно использовать:
{{ '192.0.2.1/24' | ansible.utils.ipaddr('address') }}
# => 192.0.2.1
Дополнительные сведения о фильтре ansible.utils.ipaddr и полное руководство по его использованию приведены в документации Ansible.Utils.
Фильтры сетевого CLI
Добавлено в версии 2.4.
Чтобы преобразовать вывод команды CLI сетевого устройства в структурированный вывод JSON, используйте фильтр ansible.netcommon.parse_cli:
{{ output | ansible.netcommon.parse_cli('path/to/spec') }}
Фильтр ansible.netcommon.parse_cli загружает файл спецификации и обрабатывает с его помощью вывод команды, возвращая вывод в формате JSON. В файле спецификации YAML задаются правила разбора вывода CLI.
Файл спецификации должен быть корректно отформатированным документом YAML. В нем задается, как разбирать вывод CLI и возвращать данные JSON. Ниже приведен пример корректного файла спецификации для разбора вывода команды show vlan.
---
vars:
vlan:
vlan_id: "{{ item.vlan_id }}"
name: "{{ item.name }}"
enabled: "{{ item.state != 'act/lshut' }}"
state: "{{ item.state }}"
keys:
vlans:
value: "{{ vlan }}"
items: "^(?P<vlan_id>\\d+)\\s+(?P<name>\\w+)\\s+(?P<state>active|act/lshut|suspended)"
state_static:
value: present
Приведенный выше файл спецификации возвращает структуру данных JSON — список хешей с разобранной информацией о VLAN.
Эту же команду можно разобрать в хеш с помощью директив key и values. Ниже показан пример разбора вывода в значение-хеш с использованием той же команды show vlan.
---
vars:
vlan:
key: "{{ item.vlan_id }}"
values:
vlan_id: "{{ item.vlan_id }}"
name: "{{ item.name }}"
enabled: "{{ item.state != 'act/lshut' }}"
state: "{{ item.state }}"
keys:
vlans:
value: "{{ vlan }}"
items: "^(?P<vlan_id>\\d+)\\s+(?P<name>\\w+)\\s+(?P<state>active|act/lshut|suspended)"
state_static:
value: present
Еще один распространенный сценарий использования разбора команд CLI — разделение большой команды на блоки, которые можно обработать отдельно. Это можно сделать с помощью директив start_block и end_block, чтобы разбить команду на блоки для разбора.
---
vars:
interface:
name: "{{ item[0].match[0] }}"
state: "{{ item[1].state }}"
mode: "{{ item[2].match[0] }}"
keys:
interfaces:
value: "{{ interface }}"
start_block: "^Ethernet.*$"
end_block: "^$"
items:
- "^(?P<name>Ethernet\\d\\/\\d*)"
- "admin state is (?P<state>.+),"
- "Port mode is (.+)"
Приведенный выше пример разбирает вывод show interface в список хешей.
Сетевые фильтры также поддерживают разбор вывода команды CLI с помощью библиотеки TextFSM. Чтобы разобрать вывод CLI с помощью TextFSM, используйте следующий фильтр:
{{ output.stdout[0] | ansible.netcommon.parse_cli_textfsm('path/to/fsm') }}
Для использования фильтра TextFSM необходимо установить библиотеку TextFSM.
Фильтры сетевого XML
Добавлено в версии 2.5.
Чтобы преобразовать вывод команды сетевого устройства в формате XML в структурированный вывод JSON, используйте фильтр ansible.netcommon.parse_xml:
{{ output | ansible.netcommon.parse_xml('path/to/spec') }}
Фильтр ansible.netcommon.parse_xml загружает файл спецификации и обрабатывает вывод команды, форматируя его в JSON.
Файл спецификации должен быть корректно отформатированным документом YAML. В нем задается, как разбирать вывод XML и возвращать данные JSON.
Ниже приведен пример корректного файла спецификации для разбора вывода команды show vlan | display xml.
---
vars:
vlan:
vlan_id: "{{ item.vlan_id }}"
name: "{{ item.name }}"
desc: "{{ item.desc }}"
enabled: "{{ item.state.get('inactive') != 'inactive' }}"
state: "{% if item.state.get('inactive') == 'inactive'%} inactive {% else %} active {% endif %}"
keys:
vlans:
value: "{{ vlan }}"
top: configuration/vlans/vlan
items:
vlan_id: vlan-id
name: name
desc: description
state: ".[@inactive='inactive']"
Приведенный выше файл спецификации возвращает структуру данных JSON — список хешей с разобранной информацией о VLAN.
Эту же команду можно разобрать в хеш с помощью директив key и values. Ниже показан пример разбора вывода в значение-хеш с использованием той же команды show vlan | display xml.
---
vars:
vlan:
key: "{{ item.vlan_id }}"
values:
vlan_id: "{{ item.vlan_id }}"
name: "{{ item.name }}"
desc: "{{ item.desc }}"
enabled: "{{ item.state.get('inactive') != 'inactive' }}"
state: "{% if item.state.get('inactive') == 'inactive'%} inactive {% else %} active {% endif %}"
keys:
vlans:
value: "{{ vlan }}"
top: configuration/vlans/vlan
items:
vlan_id: vlan-id
name: name
desc: description
state: ".[@inactive='inactive']"
Значение top — это XPath относительно корневого узла XML. В приведенном ниже примере вывода XML значение top равно configuration/vlans/vlan, то есть выражению XPath относительно корневого узла (<rpc-reply>). configuration в значении top — это самый внешний узел-контейнер, а vlan — самый внутренний узел-контейнер.
items — это словарь пар ключ-значение, которые связывают заданные пользователем имена с выражениями XPath, выбирающими элементы. Выражение XPath задается относительно значения XPath, указанного в top. Например, vlan_id в файле спецификации — это заданное пользователем имя, а его значение vlan-id задано относительно значения XPath в top
Атрибуты тегов XML можно извлекать с помощью выражений XPath. Значение state в спецификации — это выражение XPath, используемое для получения атрибутов тега vlan в выходных данных XML:
<rpc-reply>
<configuration>
<vlans>
<vlan inactive="inactive">
<name>vlan-1</name>
<vlan-id>200</vlan-id>
<description>This is vlan-1</description>
</vlan>
</vlans>
</configuration>
</rpc-reply>
Примечание
Дополнительные сведения о поддерживаемых выражениях XPath см. в разделе Поддержка XPath.
Фильтры сетевых VLAN
Добавлено в версии 2.8.
Используйте фильтр ansible.netcommon.vlan_parser, чтобы преобразовать несортированный список целых чисел VLAN в отсортированный список строк с целыми числами согласно правилам списка VLAN, аналогичным IOS. Этот список обладает следующими свойствами:
- VLAN перечислены в порядке возрастания.
- Три и более последовательных VLAN указываются через дефис.
- Длина первой строки списка может составлять first_line_len символов.
- Длина последующих строк списка может составлять other_line_len символов.
Чтобы отсортировать список VLAN:
{{ [3003, 3004, 3005, 100, 1688, 3002, 3999] | ansible.netcommon.vlan_parser }}
В этом примере формируется следующий отсортированный список:
['100,1688,3002-3005,3999']
Еще один пример шаблона Jinja:
{% set parsed_vlans = vlans | ansible.netcommon.vlan_parser %}
switchport trunk allowed vlan {{ parsed_vlans[0] }}
{% for i in range (1, parsed_vlans | count) %}
switchport trunk allowed vlan add {{ parsed_vlans[i] }}
{% endfor %}
Это позволяет динамически формировать списки VLAN для интерфейса Cisco IOS с тегированием. Можно сохранить полный исходный список всех VLAN, необходимых интерфейсу, а затем сравнить его с разобранным выводом IOS, который будет сформирован для конфигурации.
Хеширование и шифрование строк и паролей
Добавлено в версии 1.9.
Чтобы получить хеш sha1 строки:
{{ 'test1' | hash('sha1') }}
# => "b444ac06613fc8d63795be9ad0beaf55011936ac"
Чтобы получить хеш md5 строки:
{{ 'test1' | hash('md5') }}
# => "5a105e8b9d40e1329780d62ea2265d8a"
Получение контрольной суммы строки:
{{ 'test2' | checksum }}
# => "109f4b3c50d7b0df729d299bc6f8e9ef9066971f"
Другие хеши (зависят от платформы):
{{ 'test2' | hash('blowfish') }}
Чтобы получить хеш пароля sha512 со случайной солью:
{{ 'passwordsaresecret' | password_hash('sha512') }}
# => "$6$UIv3676O/ilZzWEE$ktEfFF19NQPF2zyxqxGkAceTnbEgpEKuGBtk6MlU4v2ZorWaVQUMyurgmHCh2Fr4wpmQ/Y.AlXMJkRnIS4RfH/"
Чтобы получить хеш пароля sha256 с заданной солью:
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt') }}
# => "$5$mysecretsalt$ReKNyDYjkKNqRVwouShhsEqZ3VOE8eoVO4exihOfvG4"
Идемпотентный способ создания уникальных хешей для каждой системы — использовать соль, которая не меняется между запусками:
{{ 'secretpassword' | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }}
# => "$6$43927$lQxPKz2M2X.NWO.gK.t7phLwOKQMcSq72XxDZQ0XzYV6DlL1OD72h417aj16OnHTGxNzhftXJQBcjbunLEepM0"
Доступные типы хешей зависят от управляющей системы, на которой запущен Ansible. Фильтр ansible.builtin.hash использует hashlib, а фильтр ansible.builtin.password_hash использует passlib. Если passlib не установлен, в качестве резервного варианта используется модуль crypt.
Добавлено в версии 2.7.
Для некоторых типов хешей можно указать параметр rounds:
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=10000) }}
# => "$5$rounds=10000$mysecretsalt$Tkm80llAxD4YHll6AgNIztKn0vzAACsuuEfYeGP7tm7"
Фильтр password_hash возвращает разные результаты в зависимости от того, установлен ли passlib.
Чтобы обеспечить идемпотентность, задайте для rounds значение, отличное от значений по умолчанию для crypt и passlib. По умолчанию для crypt используется значение 5000, а для passlib — переменное значение (535000 для sha256, 656000 для sha512):
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=5001) }}
# => "$5$rounds=5001$mysecretsalt$wXcTWWXbfcR8er5IVf7NuquLvnUA6s8/qdtOhAZ.xN."
Тип хеша «blowfish» (BCrypt) позволяет указать версию алгоритма BCrypt.
{{ 'secretpassword' | password_hash('blowfish', '1234567890123456789012', ident='2b') }}
# => "$2b$12$123456789012345678901uuJ4qFdej6xnWjOQT.FStqfdoY8dYUPC"
Примечание
Параметр доступен только для blowfish (BCrypt). Для других типов хешей этот параметр просто игнорируется. Допустимые значения параметра: [‘2’, ‘2a’, ‘2y’, ‘2b’]
Добавлено в версии 2.12.
Для шифрования данных также можно использовать фильтр Ansible ansible.builtin.vault:
# simply encrypt my key in a vault
vars:
myvaultedkey: "{{ keyrawdata|vault(passphrase) }}"
tasks:
- name: save templated vaulted data
template: src=dump_template_data.j2 dest=/some/key/vault.txt
vars:
mysalt: '{{ 2**256|random(seed=inventory_hostname) }}'
template_data: '{{ secretdata|vault(vaultsecret, salt=mysalt) }}'
Затем расшифруйте данные с помощью фильтра unvault:
# simply decrypt my key from a vault
vars:
mykey: "{{ myvaultedkey|unvault(passphrase) }}"
tasks:
- name: save templated unvaulted data
template: src=dump_template_data.j2 dest=/some/key/clear.txt
vars:
template_data: '{{ secretdata|unvault(vaultsecret) }}'
Обработка текста
Несколько фильтров работают с текстом, включая URL-адреса, имена файлов и пути.
Добавление комментариев в файлы
Фильтр ansible.builtin.comment позволяет создавать комментарии в файле на основе текста шаблона, используя различные стили комментариев. По умолчанию Ansible использует # в начале строки комментария и добавляет пустую строку комментария над и под текстом комментария. Например, следующий код:
{{ "Plain style (default)" | comment }}
создаёт такой вывод:
# # Plain style (default) #
Ansible предлагает стили комментариев для C (//...), блока C (/*...*/), Erlang (%...) и XML (<!--...-->):
{{ "C style" | comment('c') }}
{{ "C block style" | comment('cblock') }}
{{ "Erlang style" | comment('erlang') }}
{{ "XML style" | comment('xml') }}
Вы можете указать собственный символ комментария. Этот фильтр:
{{ "My Special Case" | comment(decoration="! ") }}
создаёт:
! ! My Special Case !
Вы можете полностью настроить стиль комментариев:
{{ "Custom style" | comment('plain', prefix='#######\n#', postfix='#\n#######\n ###\n #') }}
Это создаёт следующий вывод:
#######
#
# Custom style
#
#######
###
#
URL-кодирование переменных
Фильтр urlencode выполняет URL-кодирование данных для использования в пути или запросе URL с применением UTF-8:
{{ 'Trollhättan' | urlencode }}
# => 'Trollh%C3%A4ttan'
Разделение URL-адресов
Новое в версии 2.4.
Фильтр ansible.builtin.urlsplit извлекает из URL-адреса фрагмент, имя хоста, сетевой адрес, пароль, путь, порт, запрос, схему и имя пользователя. Если аргументы не указаны, возвращает словарь со всеми полями:
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('hostname') }}
# => 'www.acme.com'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('netloc') }}
# => 'user:password@www.acme.com:9000'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('username') }}
# => 'user'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('password') }}
# => 'password'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('path') }}
# => '/dir/index.html'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('port') }}
# => '9000'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('scheme') }}
# => 'http'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('query') }}
# => 'query=term'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('fragment') }}
# => 'fragment'
{{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit }}
# =>
# {
# "fragment": "fragment",
# "hostname": "www.acme.com",
# "netloc": "user:password@www.acme.com:9000",
# "password": "password",
# "path": "/dir/index.html",
# "port": 9000,
# "query": "query=term",
# "scheme": "http",
# "username": "user"
# }
Поиск в строках с помощью регулярных выражений
Чтобы выполнить поиск в строке или извлечь из неё части с помощью регулярного выражения, используйте фильтр ansible.builtin.regex_search:
# Extracts the database name from a string
{{ 'server1/database42' | regex_search('database[0-9]+') }}
# => 'database42'
# Example for a case insensitive search in multiline mode
{{ 'foo\nBAR' | regex_search('^bar', multiline=True, ignorecase=True) }}
# => 'BAR'
# Example for a case insensitive search in multiline mode using inline regex flags
{{ 'foo\nBAR' | regex_search('(?im)^bar') }}
# => 'BAR'
# Extracts server and database id from a string
{{ 'server1/database42' | regex_search('server([0-9]+)/database([0-9]+)', '\\1', '\\2') }}
# => ['1', '42']
# Extracts dividend and divisor from a division
{{ '21/42' | regex_search('(?P<dividend>[0-9]+)/(?P<divisor>[0-9]+)', '\\g<dividend>', '\\g<divisor>') }}
# => ['21', '42']
Чтобы извлечь все совпадения с регулярным выражением в строке, используйте фильтр ansible.builtin.regex_findall:
# Returns a list of all IPv4 addresses in the string
{{ 'Some DNS servers are 8.8.8.8 and 8.8.4.4' | regex_findall('\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b') }}
# => ['8.8.8.8', '8.8.4.4']
# Returns all lines that end with "ar"
{{ 'CAR\ntar\nfoo\nbar\n' | regex_findall('^.ar$', multiline=True, ignorecase=True) }}
# => ['CAR', 'tar', 'bar']
# Returns all lines that end with "ar" using inline regex flags for multiline and ignorecase
{{ 'CAR\ntar\nfoo\nbar\n' | regex_findall('(?im)^.ar$') }}
# => ['CAR', 'tar', 'bar']
Чтобы заменить текст в строке с помощью регулярного выражения, используйте фильтр ansible.builtin.regex_replace:
# Convert "ansible" to "able"
{{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }}
# => 'able'
# Convert "foobar" to "bar"
{{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}
# => 'bar'
# Convert "localhost:80" to "localhost, 80" using named groups
{{ 'localhost:80' | regex_replace('^(?P<host>.+):(?P<port>\\d+)$', '\\g<host>, \\g<port>') }}
# => 'localhost, 80'
# Convert "localhost:80" to "localhost"
{{ 'localhost:80' | regex_replace(':80') }}
# => 'localhost'
# Comment all lines that end with "ar"
{{ 'CAR\ntar\nfoo\nbar\n' | regex_replace('^(.ar)$', '#\\1', multiline=True, ignorecase=True) }}
# => '#CAR\n#tar\nfoo\n#bar\n'
# Comment all lines that end with "ar" using inline regex flags for multiline and ignorecase
{{ 'CAR\ntar\nfoo\nbar\n' | regex_replace('(?im)^(.ar)$', '#\\1') }}
# => '#CAR\n#tar\nfoo\n#bar\n'
Примечание
Если вы хотите сопоставить всю строку и используете *, обязательно заключайте регулярное выражение в начальный и конечный якоря. Например, ^(.*)$ всегда даст только один результат, тогда как (.*) в некоторых версиях Python сопоставит всю строку и пустую строку в конце, что приведёт к двум заменам:
# add "https://" prefix to each item in a list
GOOD:
{{ hosts | map('regex_replace', '^(.*)$', 'https://\\1') | list }}
{{ hosts | map('regex_replace', '(.+)', 'https://\\1') | list }}
{{ hosts | map('regex_replace', '^', 'https://') | list }}
BAD:
{{ hosts | map('regex_replace', '(.*)', 'https://\\1') | list }}
# append ':80' to each item in a list
GOOD:
{{ hosts | map('regex_replace', '^(.*)$', '\\1:80') | list }}
{{ hosts | map('regex_replace', '(.+)', '\\1:80') | list }}
{{ hosts | map('regex_replace', '$', ':80') | list }}
BAD:
{{ hosts | map('regex_replace', '(.*)', '\\1:80') | list }}
Примечание
До версии ansible 2.0, если фильтр ansible.builtin.regex_replace использовался с переменными в аргументах YAML (в отличие от более простых аргументов «key=value»), обратные ссылки (например, \\1) требовалось экранировать четырьмя обратными косыми чертами (\\\\), а не двумя (\\).
Новое в версии 2.0.
Чтобы экранировать специальные символы в стандартном регулярном выражении Python, используйте фильтр ansible.builtin.regex_escape (по умолчанию используется параметр re_type='python'):
# convert '^f.*o(.*)$' to '\^f\.\*o\(\.\*\)\$'
{{ '^f.*o(.*)$' | regex_escape() }}
Новое в версии 2.8.
Чтобы экранировать специальные символы в базовом регулярном выражении POSIX, используйте фильтр ansible.builtin.regex_escape с параметром re_type='posix_basic':
# convert '^f.*o(.*)$' to '\^f\.\*o(\.\*)\$'
{{ '^f.*o(.*)$' | regex_escape('posix_basic') }}
Работа с именами файлов и путями
Чтобы получить имя файла в конце пути, например «foo.txt» из «/etc/asdf/foo.txt»:
{{ path | basename }}
Чтобы получить имя файла в конце пути Windows (добавлено в версии 2.0):
{{ path | win_basename }}
Чтобы отделить букву диска Windows от остальной части пути к файлу (добавлено в версии 2.0):
{{ path | win_splitdrive }}
Чтобы получить только букву диска Windows:
{{ path | win_splitdrive | first }}
Чтобы получить оставшуюся часть пути без буквы диска:
{{ path | win_splitdrive | last }}
Чтобы получить каталог из пути:
{{ path | dirname }}
Чтобы получить каталог из пути Windows (добавлено в версии 2.0):
{{ path | win_dirname }}
Чтобы развернуть путь, содержащий символ тильды (~) (добавлено в версии 1.5):
{{ path | expanduser }}
Чтобы развернуть путь, содержащий переменные окружения:
{{ path | expandvars }}
Примечание
expandvars раскрывает локальные переменные; его использование в удалённых путях может привести к ошибкам.
Новое в версии 2.6.
Чтобы получить реальный путь ссылки (добавлено в версии 1.8):
{{ path | realpath }}
Чтобы получить относительный путь ссылки от начальной точки (добавлено в версии 1.7):
{{ path | relpath('/etc') }}
Чтобы получить корневую часть и расширение пути или имени файла (добавлено в версии 2.0):
# with path == 'nginx.conf' the return would be ('nginx', '.conf')
{{ path | splitext }}
Фильтр ansible.builtin.splitext всегда возвращает пару строк. К отдельным компонентам можно обратиться с помощью фильтров first и last:
# with path == 'nginx.conf' the return would be 'nginx'
{{ path | splitext | first }}
# with path == 'nginx.conf' the return would be '.conf'
{{ path | splitext | last }}
Чтобы объединить один или несколько компонентов пути:
{{ ('/etc', path, 'subdir', file) | path_join }}
Новое в версии 2.10.
Обработка строк
Чтобы добавить кавычки для использования в оболочке:
- name: Run a shell command
ansible.builtin.shell: echo {{ string_value | quote }}
(Документация: ansible.builtin.quote)
Чтобы объединить список в строку:
{{ list | join(" ") }}
Чтобы разделить строку на список:
{{ csv_string | split(",") }}
Новое в версии 2.11.
Для работы со строками в кодировке Base64:
{{ encoded | b64decode }}
{{ decoded | string | b64encode }}
(Документация: ansible.builtin.b64encode)
Начиная с версии 2.6, можно задать тип кодировки; по умолчанию используется utf-8:
{{ encoded | b64decode(encoding='utf-16-le') }}
{{ decoded | string | b64encode(encoding='utf-16-le') }}
(Документация: ansible.builtin.b64decode)
Примечание
Фильтр string требуется только для Python 2 и гарантирует, что кодируемый текст является строкой Unicode. Без этого фильтра перед b64encode будет закодировано неправильное значение.
Примечание
Фильтр b64decode возвращает строку. Если расшифровать двоичный блок с помощью b64decode, а затем попытаться использовать его (например, записать в файл с помощью copy), скорее всего, окажется, что двоичные данные повреждены. Если нужно взять двоичные данные в кодировке base64 и записать их на диск, лучше использовать системную команду base64 с модулем shell, передав закодированные данные через параметр stdin. Например: shell: cmd="base64 --decode > myfile.bin" stdin="{{ encoded }}"
Новое в версии 2.6.
Работа с UUID
Чтобы создать UUIDv5 с пространством имён:
{{ string | to_uuid(namespace='11111111-2222-3333-4444-555555555555') }}
Новое в версии 2.10.
Чтобы создать UUIDv5 с пространством имён, используя пространство имён Ansible по умолчанию «361E6D51-FAEC-444A-9079-341386DA8E2E»:
{{ string | to_uuid }}
Новое в версии 1.9.
Чтобы использовать один атрибут каждого элемента в списке составных переменных, применяйте фильтр Jinja2 map filter:
# get a comma-separated list of the mount points (for example, "/,/mnt/stuff") on a host
{{ ansible_mounts | map(attribute='mount') | join(',') }}
Работа с датами и временем
Чтобы получить объект даты из строки, используйте фильтр to_datetime:
# Get the total amount of seconds between two dates. Default date format is %Y-%m-%d %H:%M:%S but you can pass your own format
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).total_seconds() }}
# Get remaining seconds after delta has been calculated. NOTE: This does NOT convert years, days, hours, and so on to seconds. For that, use total_seconds()
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2016-08-14 18:00:00" | to_datetime)).seconds }}
# This expression evaluates to "12" and not "132". Delta is 2 hours, 12 seconds
# get amount of days between two dates. This returns only the number of days and discards remaining hours, minutes, and seconds
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).days }}
Примечание
Полный список кодов формата для работы со строками формата даты Python см. в документации Python datetime.
Новое в версии 2.4.
Чтобы отформатировать дату с помощью строки (как в команде оболочки date), используйте фильтр «strftime»:
# Display year-month-day
{{ '%Y-%m-%d' | strftime }}
# => "2021-03-19"
# Display hour:min:sec
{{ '%H:%M:%S' | strftime }}
# => "21:51:04"
# Use ansible_date_time.epoch fact
{{ '%Y-%m-%d %H:%M:%S' | strftime(ansible_date_time.epoch) }}
# => "2021-03-19 21:54:09"
# Use arbitrary epoch value
{{ '%Y-%m-%d' | strftime(0) }} # => 1970-01-01
{{ '%Y-%m-%d' | strftime(1441357287) }} # => 2015-09-04
Новое в версии 2.13.
Для strftime можно указать необязательный аргумент utc. По умолчанию он равен False, то есть время отображается в местном часовом поясе:
{{ '%H:%M:%S' | strftime }} # time now in local timezone
{{ '%H:%M:%S' | strftime(utc=True) }} # time now in UTC
Примечание
Полный список возможных строк см. по адресу https://docs.python.org/3/library/time.html#time.strftime
Получение имён ресурсов Kubernetes
Примечание
Эти фильтры перенесены в коллекцию kubernetes.core. Следуйте инструкциям по установке, чтобы установить эту коллекцию.
Используйте фильтр «k8s_config_resource_name», чтобы получить имя ConfigMap или Secret Kubernetes, включая его хеш:
{{ configmap_resource_definition | kubernetes.core.k8s_config_resource_name }}
Затем его можно использовать для указания хешей в спецификациях Pod:
my_secret:
kind: Secret
metadata:
name: my_secret_name
deployment_resource:
kind: Deployment
spec:
template:
spec:
containers:
- envFrom:
- secretRef:
name: {{ my_secret | kubernetes.core.k8s_config_resource_name }}
Новое в версии 2.8.
См. также
- Плейбуки Ansible
-
Введение в плейбуки
- Условные выражения
-
Условные операторы в плейбуках
- Использование переменных
-
Всё о переменных
- Циклы
-
Циклы в плейбуках
- Роли
-
Организация плейбуков с помощью ролей
- Общие советы
-
Советы и рекомендации по работе с плейбуками
- Общение
-
Есть вопросы? Нужна помощь? Хотите поделиться идеями? Посетите руководство по общению Ansible
- Операции с регулярными выражениями в Python 3
-
Использование встроенных флагов регулярных выражений
© 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_filters.html