Spec-Zone.ru › Ansible 2.9

Фильтры

  • Фильтры для форматирования данных
  • Принудительное определение переменных
  • Установка значений по умолчанию для неопределённых переменных
  • Исключение параметров
  • Фильтры для списков
  • Фильтры теории множеств
  • Фильтр словарей
  • Фильтр items2dict
  • Фильтры zip и zip_longest
  • Фильтр subelements
  • Фильтр случайного MAC-адреса
  • Фильтр случайного числа
  • Фильтр перемешивания
  • Математические
  • Фильтр JSON-запроса
  • Фильтр IP-адреса
  • Фильтры командной строки сети
  • Фильтры XML сети
  • Фильтры VLAN сети
  • Фильтры хеширования
  • Объединение хешей/словарей
  • Извлечение значений из контейнеров
  • Фильтр комментариев
  • Фильтр разделения URL
  • Фильтры регулярных выражений
  • Фильтры Kubernetes
  • Другие полезные фильтры
  • Комбинированные фильтры
  • Фильтры продуктов
  • Фильтры отладки
  • Утверждения компьютерной теории
  • Человекочитаемый вид
  • Перевод из человекочитаемого вида в байты

Фильтры в Ansible взяты из Jinja2 и используются для преобразования данных внутри выражения шаблона. Jinja2 поставляется со многими фильтрами. См. встроенные фильтры в официальной документации шаблонов Jinja2.

Учтите, что шаблонизация происходит на контроллере Ansible, а не на целевом узле задачи, поэтому фильтры также выполняются на контроллере, так как они обрабатывают локальные данные.

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

Фильтры для форматирования данных

Следующие фильтры примут структуру данных в шаблоне и отобразят её в немного другом формате. Они иногда полезны для отладки:

{{ some_variable | to_json }}
{{ some_variable | to_yaml }}

Для отображения в удобочитаемом виде можно использовать:

{{ some_variable | to_nice_json }}
{{ some_variable | to_nice_yaml }}

Также можно изменить отступы обоих (новая функция в версии 2.2):

{{ some_variable | to_nice_json(indent=2) }}
{{ some_variable | to_nice_yaml(indent=8) }}

to_yaml и to_nice_yaml фильтры используют библиотеку PyYAML, которая имеет ограничение по длине строки в 80 символов. Это приводит к неожиданному переносу строки после 80-го символа (если после 80-го символа есть пробел). Чтобы избежать такого поведения и сгенерировать длинные строки, можно использовать width опцию:

{{ some_variable | to_yaml(indent=8, width=1337) }}
{{ some_variable | to_nice_yaml(indent=8, width=1337) }}

Хотя было бы удобнее использовать конструкцию вроде float("inf"), а не жёстко закодированное число, к сожалению, фильтр не поддерживает проксирование функций Python. Обратите внимание, что он также поддерживает передачу других параметров YAML. Полный список можно найти в документации PyYAML.

В качестве альтернативы, вы можете читать уже отформатированные данные:

{{ some_variable | from_json }}
{{ some_variable | from_yaml }}

например:

tasks:
  - shell: cat /some/path/to/file.json
    register: result

  - set_fact:
      myvar: "{{ result.stdout | from_json }}"

Новая функция в версии 2.7.

Для разбора yaml-строк с несколькими документами предоставляется фильтр from_yaml_all. Фильтр from_yaml_all вернёт генератор обработанных yaml-документов.

например:

tasks:
  - shell: cat /some/path/to/multidoc-file.yaml
    register: result
  - debug:
      msg: '{{ item }}'
    loop: '{{ result.stdout | from_yaml_all | list }}'

Принудительное определение переменных

По умолчанию Ansible и ansible.cfg вызывают ошибку, если переменные не определены, но вы можете отключить эту функцию.

Это позволяет явно проверять, отключив эту функцию:

{{ variable | mandatory }}

Значение переменной будет использоваться как есть, но оценка шаблона вызовет ошибку, если она не определена.

Установка значений по умолчанию для неопределённых переменных

Jinja2 предоставляет полезный фильтр ‘default’, который часто является лучшим подходом к обработке случаев, когда переменная не определена:

{{ some_variable | default(5) }}

В приведенном выше примере, если переменная ‘some_variable’ не определена, будет использовано значение 5, а не возникнет ошибка.

Если вы хотите использовать значение по умолчанию, когда переменные оцениваются как false или пустая строка, вы должны установить второй параметр в true:

{{ lookup('env', 'MY_USER') | default('admin', true) }}

Исключение параметров

Начиная с Ansible 1.8, можно использовать фильтр default для исключения параметров модуля, используя специальную переменную omit:

- name: touch files with an optional mode
  file:
    dest: "{{ item.path }}"
    state: touch
    mode: "{{ item.mode | default(omit) }}"
  loop:
    - path: /tmp/foo
    - path: /tmp/bar
    - path: /tmp/baz
      mode: "0444"

Для первых двух файлов в списке режим по умолчанию будет определяться по umask системы, так как параметр mode= не будет отправлен в модуль файла, в то время как последний файл получит опцию mode=0444.

Примечание

Если вы «цепляете» дополнительные фильтры после фильтра default(omit) , вам следует сделать что-то вроде этого: "{{ foo | default(None) | some_filter or omit }}". В этом примере значение по умолчанию None (Python null) приведёт к ошибке в последующих фильтрах, что вызовет выполнение части логики or omit. Использование omit таким образом очень специфично для последующих фильтров, поэтому будьте готовы к некоторым экспериментам, если вы сделаете это.

Фильтры для списков

Все эти фильтры работают со списовыми переменными.

Новая функция в версии 1.8.

Чтобы получить минимальное значение из списка чисел:

{{ list1 | min }}

Чтобы получить максимальное значение из списка чисел:

{{ [3, 4, 2] | max }}

Новая функция в версии 2.5.

Разворачивать список (то же самое, что делает lookup flatten):

{{ [3, [4, 2] ] | flatten }}

Разворачивать только первый уровень списка (аналогично lookup items):

{{ [3, [4, [2]] ] | flatten(levels=1) }}

Фильтры теории множеств

Все эти функции возвращают уникальное множество из множеств или списков.

Новая функция в версии 1.4.

Чтобы получить уникальное множество из списка:

{{ list1 | unique }}

Чтобы получить объединение двух списков:

{{ list1 | union(list2) }}

Чтобы получить пересечение 2 списков (уникальный список всех элементов в обоих):

{{ list1 | intersect(list2) }}

Чтобы получить разность 2 списков (элементы из 1, которых нет во 2):

{{ list1 | difference(list2) }}

Чтобы получить симметрическую разность 2 списков (элементы, уникальные для каждого списка):

{{ list1 | symmetric_difference(list2) }}

Фильтр словарей

Новая функция в версии 2.6.

Чтобы преобразовать словарь в список элементов, подходящий для циклов, используйте dict2items:

{{ dict | dict2items }}

Что преобразует:

tags:
  Application: payment
  Environment: dev

в:

- key: Application
  value: payment
- key: Environment
  value: dev

Новая функция в версии 2.8.

dict2items принимает 2 ключевых аргумента, key_name и value_name , которые позволяют настроить имена ключей для преобразования:

{{ files | dict2items(key_name='file', value_name='path') }}

Что преобразует:

files:
  users: /etc/passwd
  groups: /etc/group

в:

- file: users
  path: /etc/passwd
- file: groups
  path: /etc/group

Фильтр items2dict

Новая функция в версии 2.7.

Этот фильтр преобразует список словарей с двумя ключами в словарь, сопоставляющий значения этих ключей в key: value пары:

{{ tags | items2dict }}

Что преобразует:

tags:
  - key: Application
    value: payment
  - key: Environment
    value: dev

в:

Application: payment
Environment: dev

Это обратное преобразование фильтра dict2items.

items2dict принимает 2 ключевых аргумента, key_name и value_name , которые позволяют настроить имена ключей для преобразования:

{{ tags | items2dict(key_name='key', value_name='value') }}

Фильтры zip и zip_longest

Новая функция в версии 2.3.

Чтобы получить список, объединяющий элементы других списков, используйте zip:

- name: give me list combo of two lists
  debug:
   msg: "{{ [1,2,3,4,5] | zip(['a','b','c','d','e','f']) | list }}"

- name: give me shortest combo of two lists
  debug:
    msg: "{{ [1,2,3] | zip(['a','b','c','d','e','f']) | list }}"

Чтобы всегда использовать все списки, используйте zip_longest:

- name: give me longest combo of three lists , fill with X
  debug:
    msg: "{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X') | list }}"

Аналогично результату фильтра items2dict , упомянутого выше, эти фильтры могут использоваться для создания dict:

{{ dict(keys_list | zip(values_list)) }}

Что преобразует:

keys_list:
  - one
  - two
values_list:
  - apple
  - orange

в:

one: apple
two: orange

Фильтр subelements

Новая функция в версии 2.7.

Создаёт произведение объекта и значений подэлементов этого объекта, аналогично lookup subelements:

{{ users | subelements('groups', skip_missing=True) }}

Что преобразует:

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

в:

-
  - 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'
  authorized_key:
    user: "{{ item.0.name }}"
    key: "{{ lookup('file', item.1) }}"
  loop: "{{ users | subelements('authorized') }}"

Фильтр случайного MAC-адреса

Новая функция в версии 2.6.

Этот фильтр можно использовать для генерации случайного MAC-адреса из строчного префикса.

Чтобы получить случайный MAC-адрес из строчного префикса, начинающегося с ‘52:54:00’:

"{{ '52:54:00' | random_mac }}"
# => '52:54:00:ef:1c:03'

Обратите внимание, что если в строке префикса есть ошибки, фильтр выдаст ошибку.

Начиная с версии Ansible 2.9, также можно инициализировать генератор случайных чисел из начального значения. Таким образом, вы можете создавать случайные, но идемпотентные MAC-адреса:

"{{ '52:54:00' | random_mac(seed=inventory_hostname) }}"

Фильтр случайных чисел

Новый в версии 1.6.

Этот фильтр можно использовать аналогично стандартному фильтру jinja2 random (возвращающему случайный элемент из последовательности элементов), но также можно сгенерировать случайное число в заданном диапазоне.

Чтобы получить случайный элемент из списка:

"{{ ['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

Начиная с версии Ansible 2.3, также можно инициализировать генератор случайных чисел из начального значения. Таким образом, вы можете создавать случайные, но идемпотентные числа:

"{{ 60 | random(seed=inventory_hostname) }} * * * * root /script/from/cron"

Фильтр перемешивания

Новый в версии 1.8.

Этот фильтр случайным образом перемешает существующий список, предоставляя различный порядок при каждом вызове.

Чтобы получить случайный список из существующего списка:

{{ ['a','b','c'] | shuffle }}
# => ['c','a','b']
{{ ['a','b','c'] | shuffle }}
# => ['b','c','a']

Начиная с версии Ansible 2.3, также можно перемешать список идемпотентно. Всё, что вам нужно, это начальное значение.

{{ ['a','b','c'] | shuffle(seed=inventory_hostname) }}
# => ['b','a','c']

Обратите внимание, что при использовании с элементом, не являющимся «списком», это будет операция без эффекта, в противном случае всегда возвращается список.

Математические операции

Новый в версии 1.9.

Получить логарифм (по умолчанию по основанию е):

{{ myvar | log }}

Получить логарифм по основанию 10:

{{ myvar | log(10) }}

Возвести 2 (или 5) в степень:

{{ myvar | pow(2) }}
{{ myvar | pow(5) }}

Квадратный корень, или пятая степень:

{{ myvar | root }}
{{ myvar | root(5) }}

Обратите внимание, что Jinja2 уже предоставляет некоторые функции, такие как abs() и round().

Фильтр запросов JSON

Новый в версии 2.2.

Иногда у вас есть сложная структура данных в формате JSON, и вам нужно извлечь только небольшой набор данных. Фильтр json_query позволяет вам запросить сложную структуру JSON и итерироваться по ней с помощью цикла.

Примечание

Этот фильтр построен на основе jmespath, и вы можете использовать тот же синтаксис. Примеры см. на странице примеров jmespath.

Теперь давайте рассмотрим следующую структуру данных:

{
    "domain_definition": {
        "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"
  debug:
    var: item
  loop: "{{ domain_definition | json_query('domain.cluster[*].name') }}"

То же самое для всех имён серверов:

- name: "Display all server names"
  debug:
    var: item
  loop: "{{ domain_definition | json_query('domain.server[*].name') }}"

Этот пример показывает порты из кластера cluster1:

- name: "Display all ports from cluster1"
  debug:
    var: item
  loop: "{{ domain_definition | json_query(server_name_cluster1_query) }}"
  vars:
    server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port"

Примечание

Вы можете использовать переменную, чтобы сделать запрос более удобочитаемым.

Или, как альтернативу, вывести порты в виде строки, разделённой запятыми:

- name: "Display all ports from cluster1 as a string"
  debug:
    msg: "{{ domain_definition | json_query('domain.server[?cluster==`cluster1`].port') | join(', ') }}"

Примечание

Здесь использование обратных кавычек для литералов предотвращает экранирование кавычек и сохраняет удобочитаемость.

Или, используя YAML экранирование одинарными кавычками:

- name: "Display all ports from cluster1"
  debug:
    var: item
  loop: "{{ domain_definition | json_query('domain.server[?cluster==''cluster1''].port') }}"

Примечание

Экранирование одинарных кавычек внутри одинарных кавычек в YAML выполняется путём удвоения одинарной кавычки.

В этом примере мы получаем хеш-таблицу со всеми портами и именами кластера:

- name: "Display all server ports and names from cluster1"
  debug:
    var: item
  loop: "{{ domain_definition | json_query(server_name_cluster1_query) }}"
  vars:
    server_name_cluster1_query: "domain.server[?cluster=='cluster2'].{name: name, port: port}"

Фильтр IP-адресов

Новый в версии 1.9.

Для проверки, является ли строка валидным IP-адресом:

{{ myvar | ipaddr }}

Вы также можете потребовать определённую версию протокола IP:

{{ myvar | ipv4 }}
{{ myvar | ipv6 }}

Фильтр IP-адресов также можно использовать для извлечения определённой информации из IP-адреса. Например, чтобы получить сам IP-адрес из CIDR, вы можете использовать:

{{ '192.0.2.1/24' | ipaddr('address') }}

Дополнительную информацию о фильтре ipaddr и полное руководство по использованию можно найти в фильтре ipaddr.

Сетевые фильтры командной строки

Новый в версии 2.4.

Для преобразования вывода команды CLI сетевого устройства в структурированный JSON-вывод используйте фильтр parse_cli:

{{ output | parse_cli('path/to/spec') }}

Фильтр 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.

Ту же самую команду можно проанализировать в хеш-таблицу, используя директивы ключа и значения. Вот пример того, как проанализировать вывод в хеш-значение, используя ту же самую команду 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] | parse_cli_textfsm('path/to/fsm') }}

Для использования фильтра TextFSM необходимо установить библиотеку TextFSM.

Сетевые XML-фильтры

Новый в версии 2.5.

Для преобразования XML-вывода команды сетевого устройства в структурированный JSON-вывод используйте фильтр parse_xml:

{{ output | parse_xml('path/to/spec') }}

Фильтр 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.

Ту же самую команду можно проанализировать в хеш-таблицу, используя директивы ключа и значения. Вот пример того, как проанализировать вывод в хеш-значение, используя ту же самую команду 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-выражениях см. https://docs.python.org/2/library/xml.etree.elementtree.html#xpath-support.

Сетевые фильтры VLAN

Новый в версии 2.8.

Используйте фильтр vlan_parser для преобразования несортированного списка целых чисел VLAN в отсортированный строковый список целых чисел в соответствии с правилами списков VLAN в стиле IOS. Этот список имеет следующие свойства:

  • VLANы перечислены в порядке возрастания.
  • Три или более последовательных VLANы перечислены с дефисом.
  • Первая строка списка может иметь длину first_line_len символов.
  • Следующие строки списка могут иметь другую длину other_line_len символов.

Для сортировки списка VLAN:

{{ [3003, 3004, 3005, 100, 1688, 3002, 3999] | vlan_parser }}

Этот пример отображает следующий отсортированный список:

['100,1688,3002-3005,3999']

Другой пример шаблона Jinja:

{% set parsed_vlans = vlans | 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] }}

Это позволяет динамически генерировать списки VLAN на интерфейсе Cisco IOS с тегами. Вы можете хранить исчерпывающий исходный список точных VLAN, необходимых для интерфейса, а затем сравнивать его с проанализированным выводом IOS, который фактически будет сгенерирован для конфигурации.

Фильтры хеширования

Новый в версии 1.9.

Чтобы получить хеш sha1 строки:

{{ 'test1' | hash('sha1') }}

Чтобы получить хеш md5 строки:

{{ 'test1' | hash('md5') }}

Получить контрольную сумму строки:

{{ 'test2' | checksum }}

Другие хеши (зависит от платформы):

{{ 'test2' | hash('blowfish') }}

Чтобы получить хеш пароля sha512 (случайная соль):

{{ 'passwordsaresecret' | password_hash('sha512') }}

Чтобы получить хеш пароля sha256 со специфической солью:

{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt') }}

Идемпотентный способ генерации уникальных хешей на каждом системе – использование соли, которая согласована между запусками:

{{ 'secretpassword' | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }}

Доступные типы хешей зависят от основного системы, на которой работает Ansible, «hash» зависит от hashlib, «password_hash» зависит от passlib (https://passlib.readthedocs.io/en/stable/lib/passlib.hash.html).

Новый в версии 2.7.

Некоторые типы хешей позволяют указывать параметр rounds:

{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=10000) }}

Объединение хешей/словарей

Новый в версии 2.0.

Фильтр combine позволяет объединять хэши. Например, следующее переопределит ключи в одном хэше:

{{ {'a':1, 'b':2} | combine({'b':3}) }}

Результат объединения будет:

{'a':1, 'b':3}

Фильтр также принимает необязательный параметр recursive=True, чтобы не только переопределять ключи в первом хэше, но и рекурсивно объединять ключи вложенных хэшей.

{{ {'a':{'foo':1, 'bar':2}, 'b':2} | combine({'a':{'bar':3, 'baz':4}}, recursive=True) }}

Это приведет к:

{'a':{'foo':1, 'bar':3, 'baz':4}, 'b':2}

Фильтр также может принимать несколько аргументов для объединения:

{{ a | combine(b, c, d) }}

В этом случае ключи в d переопределят ключи в c, которые, в свою очередь, переопределят ключи в b и так далее.

Это поведение не зависит от значения параметра hash_behaviour в ansible.cfg.

Извлечение значений из контейнеров

Новое в версии 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’].

Фильтр комментариев

Новое в версии 2.0.

Фильтр comment позволяет форматировать текст с выбранным стилем комментариев. Например, следующее:

{{ "Plain style (default)" | comment }}

выведет такой результат:

#
# Plain style (default)
#

Аналогичным образом можно применить стили для 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
#
#######
   ###
    #

Фильтр также может быть применён к любой переменной Ansible. Например, чтобы сделать вывод переменной ansible_managed более читабельным, можно изменить её определение в файле ansible.cfg на:

[defaults]

ansible_managed = This file is managed by Ansible.%n
  template: {file}
  date: %Y-%m-%d %H:%M:%S
  user: {uid}
  host: {host}

а затем использовать переменную с фильтром comment:

{{ ansible_managed | comment }}

что даст следующий вывод:

#
# This file is managed by Ansible.
#
# template: /home/ansible/env/dev/ansible_managed/roles/role1/templates/test.j2
# date: 2015-09-10 11:02:58
# user: ansible
# host: myhost
#

Фильтр разделения URL

Новое в версии 2.4.

Фильтр 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"
#   }

Фильтры регулярных выражений

Для поиска строки с использованием регулярного выражения используйте фильтр “regex_search”:

# search for "foo" in "foobar"
{{ 'foobar' | regex_search('(foo)') }}

# will return empty if it cannot find a match
{{ 'ansible' | regex_search('(foobar)') }}

# case insensitive search in multiline mode
{{ 'foo\nBAR' | regex_search("^bar", multiline=True, ignorecase=True) }}

Для поиска всех совпадений с регулярным выражением используйте фильтр “regex_findall”:

# Return 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') }}

Для замены текста в строке с использованием регулярного выражения используйте фильтр “regex_replace”:

# convert "ansible" to "able"
{{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }}

# convert "foobar" to "bar"
{{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}

# convert "localhost:80" to "localhost, 80" using named groups
{{ 'localhost:80' | regex_replace('^(?P<host>.+):(?P<port>\\d+)$', '\\g<host>, \\g<port>') }}

# convert "localhost:80" to "localhost"
{{ 'localhost:80' | regex_replace(':80') }}

Примечание

Если вы хотите сопоставить всю строку и используете *, убедитесь, что всегда оборачиваете регулярное выражение якорями начала/конца. Например, ^(.*)$ всегда будет соответствовать только одному результату, в то время как (.*) на некоторых версиях Python будет соответствовать всей строке и пустой строке в конце, что означает, что это произведёт две замены.

# Добавление префикса “https://” к каждому элементу в списке ХОРОШО: {{ hosts | map(‘regex_replace’, ‘^(.*)$’, ‘https://\1’) | list }} {{ hosts | map(‘regex_replace’, ‘(.+)’, ‘https://\1’) | list }} {{ hosts | map(‘regex_replace’, ‘^’, ‘https://’) | list }}

ПЛОХО: {{ hosts | map(‘regex_replace’, ‘(.*)’, ‘https://\1’) | list }}

# Добавление ':80' к каждому элементу в списке ХОРОШО: {{ hosts | map(‘regex_replace’, ‘^(.*)$’, ‘\1:80’) | list }} {{ hosts | map(‘regex_replace’, ‘(.+)’, ‘\1:80’) | list }} {{ hosts | map(‘regex_replace’, ‘$’, ‘:80’) | list }}

ПЛОХО: {{ hosts | map(‘regex_replace’, ‘(.*)’, ‘\1:80’) | list }}

Примечание

До Ansible 2.0, если фильтр “regex_replace” использовался с переменными внутри YAML-аргументов (в отличие от более простых аргументов ‘ключ=значение’), вам нужно было экранировать обратные ссылки (например, \\1) четырьмя обратными слешами (\\\\) вместо двух (\\).

Новое в версии 2.0.

Для экранирования специальных символов в стандартном Python регулярном выражении используйте фильтр “regex_escape” (используя параметр re_type=’python’ по умолчанию):

# convert '^f.*o(.*)$' to '\^f\.\*o\(\.\*\)\$'
{{ '^f.*o(.*)$' | regex_escape() }}

Новое в версии 2.8.

Для экранирования специальных символов в POSIX базовом регулярном выражении используйте фильтр “regex_escape” с параметром re_type=’posix_basic’:

# convert '^f.*o(.*)$' to '\^f\.\*o(\.\*)\$'
{{ '^f.*o(.*)$' | regex_escape('posix_basic') }}

Фильтры Kubernetes

Используйте фильтр “k8s_config_resource_name”, чтобы получить имя Kubernetes ConfigMap или Secret, включая его хэш:

{{ configmap_resource_definition | k8s_config_resource_name }}

Это может быть использовано для ссылки на хэши в спецификациях Pod:

my_secret:
  kind: Secret
  name: my_secret_name

deployment_resource:
  kind: Deployment
  spec:
    template:
      spec:
        containers:
        - envFrom:
            - secretRef:
                name: {{ my_secret | k8s_config_resource_name }}

Новое в версии 2.8.

Другие полезные фильтры

Для добавления кавычек для использования в оболочке:

- shell: echo {{ string_value | quote }}

Для использования одного значения при true и другого при false (новое в версии 1.9):

{{ (name == "John") | ternary('Mr','Ms') }}

Для использования одного значения при true, одного при false и третьего при null (новое в версии 2.8):

{{ enabled | ternary('no shutdown', 'shutdown', omit) }}

Для объединения списка в строку:

{{ list | join(" ") }}

Для получения имени последней части пути файла, например, ‘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 }}

Для работы со строками, закодированными в Base64:

{{ encoded | b64decode }}
{{ decoded | string | b64encode }}

Начиная с версии 2.6, вы можете указать тип кодировки, по умолчанию используется utf-8:

{{ encoded | b64decode(encoding='utf-16-le') }}
{{ decoded | string | b64encode(encoding='utf-16-le') }}

Примечание

Фильтр string необходим только для Python 2 и гарантирует, что текст для кодирования — это строка Unicode. Без этого фильтра некорректное значение будет закодировано с помощью b64encode.

Новое в версии 2.6.

Для создания UUID из строки (новое в версии 1.9):

{{ hostname | to_uuid }}

Для преобразования значений в определённые типы, например, когда вы вводите строку как “True” из vars_prompt, и система не понимает, что это булево значение:

- debug:
    msg: test
  when: some_string_value | bool

Новое в версии 1.6.

Для использования одного атрибута из каждого элемента списка сложных переменных, используйте фильтр “map” (см. документацию Jinja2 map() для получения более подробной информации):

# get a comma-separated list of the mount points (e.g. "/,/mnt/stuff") on a host
{{ ansible_mounts | map(attribute='mount') | join(',') }}

Для получения объекта даты из строки используйте фильтр to_datetime, (новое в версии 2.2):

# Get 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, etc 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 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  }}

Новое в версии 2.4.

Для форматирования даты с использованием строки (как с командой shell date), используйте фильтр “strftime”:

# Display year-month-day
{{ '%Y-%m-%d' | strftime }}

# Display hour:min:sec
{{ '%H:%M:%S' | strftime }}

# Use ansible_date_time.epoch fact
{{ '%Y-%m-%d %H:%M:%S' | strftime(ansible_date_time.epoch) }}

# Use arbitrary epoch value
{{ '%Y-%m-%d' | strftime(0) }}          # => 1970-01-01
{{ '%Y-%m-%d' | strftime(1441357287) }} # => 2015-09-04

Примечание

Чтобы получить все возможные строки, обратитесь к https://docs.python.org/2/library/time.html#time.strftime

Фильтры комбинаций

Новое в версии 2.3.

Этот набор фильтров возвращает список объединённых списков. Для получения перестановок списка:

- name: give me largest permutations (order matters)
  debug:
    msg: "{{ [1,2,3,4,5] | permutations | list }}"

- name: give me permutations of sets of three
  debug:
    msg: "{{ [1,2,3,4,5] | permutations(3) | list }}"

Для комбинаций всегда требуется заданный размер:

- name: give me combinations for sets of two
  debug:
    msg: "{{ [1,2,3,4,5] | combinations(2) | list }}"

Также см. фильтры zip и zip_longest

Фильтры произведения

Фильтр product возвращает декартово произведение входных итерируемых объектов.

Это примерно эквивалентно вложенным циклам for в выражении генератора.

Например:

- name: generate multiple hostnames
  debug:
    msg: "{{ ['foo', 'bar'] | product(['com']) | map('join', '.') | join(',') }}"

Это даст результат:

{ "msg": "foo.com,bar.com" }

Фильтры отладки

Новое в версии 2.3.

Используйте фильтр type_debug для отображения базового Python типа переменной. Это может быть полезно при отладке в ситуациях, когда вам нужно знать точный тип переменной:

{{ myvar | type_debug }}

Утверждения теории вычислений

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

Чловекочитаемый формат

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

Например:

- 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" }

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

См. также

О Playbooks
Введение в playbooks
Условные операторы
Условные операторы в playbooks
Использование переменных
Все о переменных
Циклы
Циклы в playbooks
Роли
Организация Playbook по ролям
Рекомендации по разработке
Рекомендации по разработке playbooks
Список рассылки пользователей
Есть вопрос? Загляните в группу 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.9/user_guide/playbooks_filters.html

Spec-Zone.ru

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