Я пытаюсь использовать модуль set_fact
для создания новой переменной на основе данных другой переменной. Я пробовал фильтры groupby
и lists_mergeby
, но безуспешно.
Как мне из этого списка:
"myVms": [
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS625"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS626"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS812"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS817"
}
]
Превратить его в новый список со структурой, подобной следующей?
"myVmsModified": [
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"MyBeautifulVms": [
"K606WS817",
"K606WS812",
"K606WS626",
"K606WS625"
]
}
]
Ansible не очень хорош в манипулировании данными. Хотя с помощью комбинации фильтров Ansible можно делать то, что вы хотите, почти во всех случаях проще просто написать собственный фильтр на Python. Добавьте следующее в filter_plugins/myfilters.py
:
import itertools
def pivot_on_subscriptionid(v):
return [
{
"SubscriptionId": key,
"MyBeautifulVms": [vm["VirtualMachine"] for vm in grp],
}
for key, grp in itertools.groupby(
sorted(v, key=lambda x: x["SubscriptionId"]),
key=lambda x: x["SubscriptionId"],
)
]
class FilterModule:
def filters(self):
return {
"pivot_on_subscriptionid": pivot_on_subscriptionid,
}
Теперь вы можете написать такую книгу:
- hosts: localhost
gather_facts: false
vars:
maxuid: 500
myVms: [
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS625"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS626"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS812"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS817"
}
]
tasks:
- set_fact:
new_vm_list: "{{ myVms | pivot_on_subscriptionid }}"
- debug:
var: new_vm_list
И получите этот вывод:
PLAY [localhost] ***************************************************************
TASK [set_fact] ****************************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"new_vm_list": [
{
"MyBeautifulVms": [
"K606WS625",
"K606WS626",
"K606WS812",
"K606WS817"
],
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c"
}
]
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Вот один из возможных способов. Обратите внимание, что в этом решении используется groupby
, но, как видите, оно требует небольшой доработки. Но вы, вероятно, были на правильном пути. Вы можете отладить промежуточные переменные, чтобы изучить решение.
Примечание: я использовал соглашение об именах Ansible для своих переменных, но вы можете переключиться на верблюжий регистр для своих данных, если это необходимо для вашего проекта.
Следующая пьеса:
---
- name: Transform data
hosts: localhost
gather_facts: false
vars:
# Original variable
"my_vms": [
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS625"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS626"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS812"
},
{
"SubscriptionId": "564aba40-77683-4426-84ac-d195769c826c",
"VirtualMachine": "K606WS817"
}
]
# Group original var by subscription id
_raw_grouped_vms: "{{ my_vms | groupby('SubscriptionId') }}"
# List of subscription ids
_subscription_ids: "{{ _raw_grouped_vms | map(attribute=0) }}"
# List of lists of vms for each subscription id (in the same order)
_vms_by_id: "{{ _raw_grouped_vms | map(attribute=1) | map('map', attribute='VirtualMachine') }}"
# Create a dict with key=<subscription id> and value=<list of vms>
my_vms_modified_dict: "{{ dict(_subscription_ids | zip(_vms_by_id)) }}"
# Transform that dict to a list of dicts as expected
my_vms_modified_list: "{{ my_vms_modified_dict | dict2items(key_name='subscription_id', value_name='my_beautiful_vms') }}"
tasks:
- name: Show expected result
ansible.builtin.debug:
var: my_vms_modified_list
дает:
PLAY [Transform data] ****************************************************************************************************************************************************************************************************************
TASK [Show expected result] **********************************************************************************************************************************************************************************************************
ok: [localhost] => {
"my_vms_modified_list": [
{
"my_beautiful_vms": [
"K606WS625",
"K606WS626",
"K606WS812",
"K606WS817"
],
"subscription_id": "564aba40-77683-4426-84ac-d195769c826c"
}
]
}
PLAY RECAP ***************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Фильтр lists_mergeby может добавлять списки. Преобразуйте атрибуты VirtualMachine в списки.
myVmsModified: "{{ myVms | json_query('[].{SubscriptionId: SubscriptionId,
VirtualMachine: [VirtualMachine]}') }}"
дает
myVmsModified:
- SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c
VirtualMachine: [K606WS625]
- SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c
VirtualMachine: [K606WS626]
- SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c
VirtualMachine: [K606WS812]
- SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c
VirtualMachine: [K606WS817]
Фильтр предназначен для объединения двух и более списков. Если вы хотите объединить элементы в один список, объедините его в пустой список.
myVmsModified: "{{ [myVms | json_query('[].{SubscriptionId: SubscriptionId,
VirtualMachine: [VirtualMachine]}'), []] |
community.general.lists_mergeby('SubscriptionId', list_merge='append') }}"
дает то, что ты хочешь
myVmsModified:
- SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c
VirtualMachine: [K606WS625, K606WS626, K606WS812, K606WS817]
- hosts: localhost
vars:
myVms:
- {SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c, VirtualMachine: K606WS625}
- {SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c, VirtualMachine: K606WS626}
- {SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c, VirtualMachine: K606WS812}
- {SubscriptionId: 564aba40-77683-4426-84ac-d195769c826c, VirtualMachine: K606WS817}
myVmsModified: "{{ myVms | json_query('[].{SubscriptionId: SubscriptionId,
VirtualMachine: [VirtualMachine]}') }}"
myVmsModifie2: "{{ [myVms | json_query('[].{SubscriptionId: SubscriptionId,
VirtualMachine: [VirtualMachine]}'), []] |
community.general.lists_mergeby('SubscriptionId', list_merge='append') }}"
tasks:
- debug:
var: myVmsModified | to_yaml
- debug:
var: myVmsModifie2 | to_yaml
target:
- before: 'VirtualMachine'
after: 'MyBeautifulVms'
myVmsModified: "{{ [myVms | json_query('[].{SubscriptionId: SubscriptionId,
VirtualMachine: [VirtualMachine]}'), []] |
community.general.lists_mergeby('SubscriptionId', list_merge='append') |
community.general.replace_keys(target=target) }}"
Спасибо за ваш ответ. Я попробовал это предложение, но ключ VirtualMachine превратился в список списков, содержащих строки.
Похоже, я слишком поторопился с вашим предложением. Это сработало! Сначала я объединил два ваших предложения в одно, и результат стал таким, как я/вы описали.
Спасибо, Зейтунатор. Похоже, не удалось создать словарь. Возникла ошибка: «Невозможно найти имя или получить доступ к атрибуту».