Инструменты пользователя

Инструменты сайта


linux:ansible:filters

Различия

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

Ссылка на это сравнение

Следующая версия
Предыдущая версия
linux:ansible:filters [2023/12/30 16:23]
werwolf создано
linux:ansible:filters [2023/12/30 16:25] (текущий)
werwolf
Строка 3: Строка 3:
 Используя Ansible в качестве инструмента автоматизации,​ часто приходится сталкиваться с задачей обработки и фильтрации структурированных данных. Как правило,​ это набор фактов,​ полученных с управляемых серверов,​ или ответ на запрос к внешним API, которые возвращают данные в виде стандартного json. Многие неопытные инженеры,​ используя Ansible в таких случаях,​ начинают прибегать к помощи привычных консольных команд и начинают городить то, что среди специалистов получило название [[https://​habr.com/​ru/​articles/​494738/​|bashsible]]. В общем,​ вспоминается известный мем: Используя Ansible в качестве инструмента автоматизации,​ часто приходится сталкиваться с задачей обработки и фильтрации структурированных данных. Как правило,​ это набор фактов,​ полученных с управляемых серверов,​ или ответ на запрос к внешним API, которые возвращают данные в виде стандартного json. Многие неопытные инженеры,​ используя Ansible в таких случаях,​ начинают прибегать к помощи привычных консольных команд и начинают городить то, что среди специалистов получило название [[https://​habr.com/​ru/​articles/​494738/​|bashsible]]. В общем,​ вспоминается известный мем:
  
-{{https://​habrastorage.org/​r/​w1560/​getpro/​habr/​upload_files/​9ec/​142/​bd4/​9ec142bd482990322d490613abbb2c56.jpg?​524x476|не надо так!}}не надо так! 
  
-В этой статье мы покажем,​ как легко и просто можно обрабатывать данные прямо в Ansible,​ используя его собственные мощные возможности. Речь идет о фильтрах [[https://​jinja.palletsprojects.com/​en/​3.1.x/​templates/#​list-of-builtin-filters|Jinja2]],​ которые представляют собой мощный,​ но в то же время интуитивно понятный инструмент для трансформации данных. Эти фильтры позволяют эффективно сортировать,​ выбирать и преобразовывать данные,​ устраняя необходимость в сложных внешних командах и скриптах. 
  
 В обычных языках программирования задача обработки данных обычно решается с помощью циклов (for, while, for_each и т. п.) и различных функций преобразования типов объектов (массивы,​ коллекции,​ замыкания и т. п.) Ansible использует упрощенную модель данных,​ используя по сути лишь два варианта объекта с данными,​ список (list) и словарь (dictionary). Если вы не очень хорошо понимаете,​ что это такое и чем они отличаются,​ рекомендую для начала прочесть [[https://​www.redhat.com/​sysadmin/​ansible-lists-dictionaries-yaml|вот эту короткую статью]]. В обычных языках программирования задача обработки данных обычно решается с помощью циклов (for, while, for_each и т. п.) и различных функций преобразования типов объектов (массивы,​ коллекции,​ замыкания и т. п.) Ansible использует упрощенную модель данных,​ используя по сути лишь два варианта объекта с данными,​ список (list) и словарь (dictionary). Если вы не очень хорошо понимаете,​ что это такое и чем они отличаются,​ рекомендую для начала прочесть [[https://​www.redhat.com/​sysadmin/​ansible-lists-dictionaries-yaml|вот эту короткую статью]].
Строка 15: Строка 13:
 Попробуйте запустить у себя следующий плейбук,​ выводящий значения ''​ansible_mounts'':​ Попробуйте запустить у себя следующий плейбук,​ выводящий значения ''​ansible_mounts'':​
  
-<​code>​+<​code ​yaml>
 --- ---
 - name: Test ansible_mounts - name: Test ansible_mounts
Строка 29: Строка 27:
 В выводе получится что-то вроде такого:​ В выводе получится что-то вроде такого:​
  
-<​code>​+<​code ​bash>
 TASK [Show ansible_mounts] ********************************************* TASK [Show ansible_mounts] *********************************************
 ok: [localhost] =>  ok: [localhost] => 
Строка 80: Строка 78:
 Давайте попробуем отфильтровать из этого длинного списка только те значения,​ для которых значение ключа ''​device''​ содержит /dev/sda. Это можно сделать с помощью фильтра selectattr и [[https://​docs.ansible.com/​ansible/​latest/​playbook_guide/​playbooks_tests.html#​testing-strings|теста match]]: Давайте попробуем отфильтровать из этого длинного списка только те значения,​ для которых значение ключа ''​device''​ содержит /dev/sda. Это можно сделать с помощью фильтра selectattr и [[https://​docs.ansible.com/​ansible/​latest/​playbook_guide/​playbooks_tests.html#​testing-strings|теста match]]:
  
-<​code>​+<​code ​yaml>
 --- ---
 - name: Show ansible_mounts filtered - name: Show ansible_mounts filtered
Строка 94: Строка 92:
 Получим примерно такое: Получим примерно такое:
  
-<​code>​+<​code ​bash>
 TASK [Show ansible_mounts] ********************************* TASK [Show ansible_mounts] *********************************
 ok: [localhost] =>  ok: [localhost] => 
Строка 144: Строка 142:
 Теперь давайте предположим,​ что нам из всего набора ключей каждого словаря нужно только значение ключа ''​mount''​. Как выбрать из полученного списка лишь значения определенного ключа? Тут нам на помощь придет фильтр map. Это довольно мощный фильтр,​ суть которого сводится к тому,​ что он применяет фильтр с аргументами,​ которые сами переданы ему в качестве аргументов,​ к каждому элементу списка словарей,​ которые приходят на вход. В простейшем случае,​ если нам нужно просто получить значение конкретного ключа из каждого элемента списка словарей,​ использование данного фильтра будет очень простым. Нужно просто указать значение нужного имени ключа в виде атрибута фильтра с соответствующей командой. В нашем случае это будет ''​map(attribute='​mount'​)''​. В результате получим следующий код: Теперь давайте предположим,​ что нам из всего набора ключей каждого словаря нужно только значение ключа ''​mount''​. Как выбрать из полученного списка лишь значения определенного ключа? Тут нам на помощь придет фильтр map. Это довольно мощный фильтр,​ суть которого сводится к тому,​ что он применяет фильтр с аргументами,​ которые сами переданы ему в качестве аргументов,​ к каждому элементу списка словарей,​ которые приходят на вход. В простейшем случае,​ если нам нужно просто получить значение конкретного ключа из каждого элемента списка словарей,​ использование данного фильтра будет очень простым. Нужно просто указать значение нужного имени ключа в виде атрибута фильтра с соответствующей командой. В нашем случае это будет ''​map(attribute='​mount'​)''​. В результате получим следующий код:
  
-<​code>​+<​code ​yaml>
 --- ---
 - name: Show ansible_mounts filtered - name: Show ansible_mounts filtered
Строка 175: Строка 173:
 Как видим,​ получить нужный нам набор данных оказывается весьма просто даже без использования программирования и циклов. Давайте усложним задачу. Скажем,​ нам нужно получить в выводе значения не только ключа mount, но также значения ключей ''​size_available''​ и ''​size_total''​. Идущие в комплекте фильтры так не умеют. Фильтры Ansible умеют фильтровать списки и списки словарей,​ но не сами ключи словаря. Выход прост: нужно превратить словарь в список и уже его отфильтровать имеющимися инструментами. К счастью,​ в Ansible есть подходящие фильтры для такой задачи. С их помощью можно превращать словари в списки,​ а списки — обратно в словари. Называются эти фильтры,​ соответственно,​ [[https://​docs.ansible.com/​ansible/​latest/​collections/​ansible/​builtin/​dict2items_filter.html|dict2items]] и [[https://​docs.ansible.com/​ansible/​latest/​collections/​ansible/​builtin/​items2dict_filter.html|items2dict]]. Как видим,​ получить нужный нам набор данных оказывается весьма просто даже без использования программирования и циклов. Давайте усложним задачу. Скажем,​ нам нужно получить в выводе значения не только ключа mount, но также значения ключей ''​size_available''​ и ''​size_total''​. Идущие в комплекте фильтры так не умеют. Фильтры Ansible умеют фильтровать списки и списки словарей,​ но не сами ключи словаря. Выход прост: нужно превратить словарь в список и уже его отфильтровать имеющимися инструментами. К счастью,​ в Ansible есть подходящие фильтры для такой задачи. С их помощью можно превращать словари в списки,​ а списки — обратно в словари. Называются эти фильтры,​ соответственно,​ [[https://​docs.ansible.com/​ansible/​latest/​collections/​ansible/​builtin/​dict2items_filter.html|dict2items]] и [[https://​docs.ansible.com/​ansible/​latest/​collections/​ansible/​builtin/​items2dict_filter.html|items2dict]].
  
-Например,​ у нас есть следующий словарь:​+Например,​ у нас есть следующий словарь: ​
  
-<​code>​+<​code ​yaml>
 server_config:​ server_config:​
   apache:   apache:
Строка 195: Строка 193:
 И мы хотим отфильтровать только те элементы словаря,​ где есть ключ ''​version''​. Сделать это мы сможем так. Сначала превращаем словать в список с помощью фильтра dict2items: И мы хотим отфильтровать только те элементы словаря,​ где есть ключ ''​version''​. Сделать это мы сможем так. Сначала превращаем словать в список с помощью фильтра dict2items:
  
-<​code>​+<​code ​yaml>
 server_config | dict2items server_config | dict2items
 </​code>​ </​code>​
Строка 201: Строка 199:
 Получим:​ Получим:​
  
-<​code>​+<​code ​bash>
 ok: [localhost] => ok: [localhost] =>
   server_config | dict2items:   server_config | dict2items:
Строка 232: Строка 230:
 Теперь отфильтруем только те элементы списка,​ которые содержат дочерний ключ ''​version'':​ Теперь отфильтруем только те элементы списка,​ которые содержат дочерний ключ ''​version'':​
  
-<​code>​+<​code ​yaml>
 server_config | dict2items | selectattr('​value.version',​ '​defined'​) server_config | dict2items | selectattr('​value.version',​ '​defined'​)
 </​code>​ </​code>​
Строка 238: Строка 236:
 Получим:​ Получим:​
  
-<​code>​+<​code ​bash>
 ok: [localhost] => ok: [localhost] =>
   server_config | dict2items | selectattr('​value.version',​ '​defined'​):​   server_config | dict2items | selectattr('​value.version',​ '​defined'​):​
Строка 265: Строка 263:
 Теперь превратим обратно список в словарь исходного вида. Для этого добавим в конец конвейера фильтр ''​items2dict''​. Получаем:​ Теперь превратим обратно список в словарь исходного вида. Для этого добавим в конец конвейера фильтр ''​items2dict''​. Получаем:​
  
-<​code>​+<​code ​bash>
 ok: [localhost] => ok: [localhost] =>
   server_config | dict2items | selectattr('​value.version',​ '​defined'​) | items2dict:   server_config | dict2items | selectattr('​value.version',​ '​defined'​) | items2dict:
Строка 289: Строка 287:
 Теперь давайте вспомним про нашу исходную задачу со списком словарей ''​ansible_mounts''​ из которого мы хотим извлечь только некоторые ключи. Сам список мы уже отфильтровали по нужному нам условию,​ теперь нам нужно выбрать только определенные ключи из списка. Получается,​ что к каждому элементу списка словарей нужно применить фильтр ''​dict2items'',​ потом отфильтровать этот список по списку ключей,​ а потом обратно превратить каждый дочерний список обратно в словарь. Сложно?​ На самом деле не очень. Как работать со словарем,​ мы уже видели на примере выше. Теперь нам нужно проделать то же самое со списком словарей. Тут нам как раз поможет упоминавшийся выше фильтр ''​map'',​ только уже в более продвинутом варианте применения. Покажем сразу итоговый результат. Код: Теперь давайте вспомним про нашу исходную задачу со списком словарей ''​ansible_mounts''​ из которого мы хотим извлечь только некоторые ключи. Сам список мы уже отфильтровали по нужному нам условию,​ теперь нам нужно выбрать только определенные ключи из списка. Получается,​ что к каждому элементу списка словарей нужно применить фильтр ''​dict2items'',​ потом отфильтровать этот список по списку ключей,​ а потом обратно превратить каждый дочерний список обратно в словарь. Сложно?​ На самом деле не очень. Как работать со словарем,​ мы уже видели на примере выше. Теперь нам нужно проделать то же самое со списком словарей. Тут нам как раз поможет упоминавшийся выше фильтр ''​map'',​ только уже в более продвинутом варианте применения. Покажем сразу итоговый результат. Код:
  
-<​code>​+<​code ​yaml>
 - name: Show mounts data - name: Show mounts data
   debug:   debug:
Строка 302: Строка 300:
 И результат:​ И результат:​
  
-<​code>​+<​code ​bash>
 ok: [localhost] =>  ok: [localhost] => 
   ? |-   ? |-
Строка 336: Строка 334:
     * Наконец,​ после фильтрации нужных ключей,​ каждый список пар ключ-значение преобразуется обратно в словарь с помощью ''​map''​ и фильтра ''​items2dict''​. ​     * Наконец,​ после фильтрации нужных ключей,​ каждый список пар ключ-значение преобразуется обратно в словарь с помощью ''​map''​ и фильтра ''​items2dict''​. ​
  
-===== Заключение ===== 
- 
-Мы познакомились с наглядными примерами использования фильтров Ansible, и, надеюсь,​ теперь они кажутся вам не такими уж сложными. Ведь часто всё кажется сложным,​ пока не попробуешь. С фильтрами Ansible точно так же:​ попробуйте,​ поэкспериментируйте,​ и вы увидите,​ как они могут упростить вашу жизнь. А еще напишите в комментариях,​ хотели бы вы увидеть продолжение статьи с другими интересными примерами фильтров?​ Если тема будет интересна,​ то в следующих статьях попробую в том числе раскрыть тему написания своих собственных кастомных фильтров. 
- 
-  
- 
-Теги: 
- 
-  * [[:​ru:​search:?​target_type=posts&​amp;​order=relevance&​amp;​q=%5Bansible%5D|ansible]] 
-  * [[:​ru:​search:?​target_type=posts&​amp;​order=relevance&​amp;​q=%5B%D1%84%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B%5D|фильтры]] 
-  * [[:​ru:​search:?​target_type=posts&​amp;​order=relevance&​amp;​q=%5Bmap%5D|map]] 
-  * [[:​ru:​search:?​target_type=posts&​amp;​order=relevance&​amp;​q=%5Bselectattr%5D|selectattr]] 
-  * [[:​ru:​search:?​target_type=posts&​amp;​order=relevance&​amp;​q=%5Bitems2dict%5D|items2dict]] 
-  * [[:​ru:​search:?​target_type=posts&​amp;​order=relevance&​amp;​q=%5Bdict2items%5D|dict2items]] 
-  * [[:​ru:​search:?​target_type=posts&​amp;​order=relevance&​amp;​q=%5Bjinja2%5D|jinja2]] 
- 
-Хабы: 
  
-  * [[:​ru:​hubs:​sys_admin:​|Системное администрирование]] 
-  * [[:​ru:​hubs:​it-infrastructure:​|IT-инфраструктура]] 
-  * [[:​ru:​hubs:​s_admin:​|Серверное администрирование]] 
-  * [[:​ru:​hubs:​devops:​|DevOps]] 
linux/ansible/filters.1703942588.txt.gz · Последние изменения: 2023/12/30 16:23 — werwolf