Как в Ansible сравнить 2 json по ключу?
Пишу роль, которая монтирует общие папки для пользователя, если он входит в ту или иную группу. Всё получается за исключением сравнения двух объектов по ключу. После всех операций у меня есть вот что:
Список того, что в принципе можно смонтировать (но если прав нет, то конечно же внутрь не пустит):
"united_info_about_shares_fact": [
{
"PathToShare": "//depit.my.domain/depit",
"ShareAccessGroups": "it_group",
"ShareName": "Общая папка управления IT"
},
{
"PathToShare": "//share.edu.my.domain/share",
"ShareAccessGroups": "edu_ecc_share",
"ShareName": "Общая папка для студентов и преподавателей"
},
{
"PathToShare": "//storage.my.domain/kaf_raket-dvig",
"ShareAccessGroups": "kaf_raket-dvig",
"ShareName": "Общая папка кафедры ракетных двигателей"
},
{
"PathToShare": "//storage.my.domain/dek_fisis",
"ShareAccessGroups": "dek_fisis_share",
"ShareName": "Общая папка деканата ФИСИС"
},
{
"PathToShare": "//storage.my.domain/kaf_fiziki-tverd-tela",
"ShareAccessGroups": "kaf_fiziki-tverd-tela_share",
"ShareName": "Общая папка кафедры физики твёрдого тела"
}
]
Список того в каких группах пользователь состоит:
"user_groups_list_fact": [
{
"ShareAccessGroups": "it_group"
},
{
"ShareAccessGroups": "vpn_users"
},
{
"ShareAccessGroups": "wifi_users"
},
{
"ShareAccessGroups": "edu_ecc_share"
},
{
"ShareAccessGroups": "site_users"
}
]
Т.е. я хочу, чтобы мне выдало:
{
"PathToShare": "//depit.my.domain/depit",
"ShareAccessGroups": "it_group",
"ShareName": "Общая папка управления IT"
},
{
"PathToShare": "//share.edu.my.domain/share",
"ShareAccessGroups": "edu_ecc_share",
"ShareName": "Общая папка для студентов и преподавателей"
},
Гугление привело меня на https://stackoverflow.com/a/58487241 и https://stackoverflow.com/a/56396809, но адаптировать под себя я не смог. Получилось как-то так, но код не работает:
- debug:
msg: "{{ item.PathToShare }}"
loop: "{{ united_info_about_shares_fact }}"
when: "united_info_about_shares_fact | intersect(user_groups_list_fact | json_query('[*].ShareAccessGroups')) | length == 0"
UPD. Наковырял ещё, но смог добиться получения только одного элемента:
- set_fact:
matching_by_groups: "{{ united_info_about_shares_fact | json_query('[*].ShareAccessGroups') | intersect(user_groups_list_fact | json_query('[*].ShareAccessGroups')) }}"
- set_fact:
needed_shares: "{{ united_info_about_shares_fact | json_query(query_for_get_needed_shares) }}"
loop: "{{ united_info_about_shares_fact }}"
with_items: "{{ united_info_about_shares_fact }}"
vars:
query_for_get_needed_shares: "[?ShareAccessGroups == `{{ matching_by_groups[0] }}`]"
Ответы (1 шт):
Всё оказалось просто как и думал изначально, но всё равно со своими заморочками. Итак:
Надо перестать думать, что если мне нужно это и это и это значения - значит нужно искать в гугле слово and. Нужно искать or.
Получается вот так:
- set_fact:
needed_shares: "{{ united_info_about_shares_fact | json_query(query_for_get_needed_shares) }}"
vars:
query_for_get_needed_shares: "[? ShareAccessGroups == 'it_group' || ShareAccessGroups == 'vpn_users' || ShareAccessGroups == 'edu_ecc_share']"
Т.к. к группе vpn_users никаких общих папок не привязано, то мне выдаётся ровно то, что я хотел получить (см. вопрос).
Но возникла проблема: как переделать и передать этот список, чтобы по дороге ничего не поломалось? Преобразовать {"ShareAccessGroups": "it_group"}, в ShareAccessGroups == 'it_group', запихнуть в set_facts и передать - не получается, хотя везде всё вроде ок. Т.е. конструкция
query_for_get_needed_shares: "[? `{{ user_groups_list_for_json_query_fact }}` ]"
не сработала. Зато сработало формирование файла и include_vars. Получилось монструозно, зато работает и идемпотентность:
Преобразовываем json из вопроса в строку, которую сможет прожевать jmespath:
- set_fact:
user_groups_list_for_json_query_fact:
"{{ user_groups_list_fact | join(' || ')
| replace(var_vanilla_json_to_jmespath_query_string_part_01, ' == ')
| replace(var_vanilla_json_to_jmespath_query_string_part_02, '')
| replace(var_vanilla_json_to_jmespath_query_string_part_03, '')
| regex_replace('^', var_vanilla_json_to_jmespath_query_string_start)
| regex_replace('$', var_vanilla_json_to_jmespath_query_string_end)
| string }}"
vars:
var_vanilla_json_to_jmespath_query_string_part_01: "': "
var_vanilla_json_to_jmespath_query_string_part_02: "{'"
var_vanilla_json_to_jmespath_query_string_part_03: "}"
var_vanilla_json_to_jmespath_query_string_start: '"[? '
var_vanilla_json_to_jmespath_query_string_end: ' ]"'
Пишем в файл:
- copy:
content: |
---
query_for_get_needed_shares: {{ user_groups_list_for_json_query_fact }}
dest: '{{ main_user_temp_directory }}/vars_query_for_get_needed_shares.yml'
Инклудим и получаем новый факт:
- name: Include vars with all joined groups for "json_query"
include_vars: '{{ main_user_temp_directory }}/vars_query_for_get_needed_shares.yml'
- set_fact:
needed_shares: "{{ united_info_about_shares_fact | json_query(query_for_get_needed_shares) }}"
P.S. В принципе, если в дальнейшем понадобится дёргать список групп, в которых на данный момент состоит пользователь, то формирование файла vars_query_for_get_needed_shares.yml лучше вытащить в отдельную таску\плейбук\роль и сделать более общее название. Тогда можно будет инклудить из других мест экономя, на времени преобразования (я использовал выхлоп /usr/bin/id).