Реструктуризация данных с помощью Ansible

Я пытаюсь использовать модуль 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 Roles
Введение в Ansible Roles
Ansible - это отличный инструмент управления конфигурацией, который можно использовать для автоматизации настройки или развертывания на большом...
2
0
69
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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 

Спасибо, Зейтунатор. Похоже, не удалось создать словарь. Возникла ошибка: «Невозможно найти имя или получить доступ к атрибуту».

KWC 02.09.2024 09:28
Ответ принят как подходящий

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

  • Example of a complete playbook for testing
- 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
  • Replace the attribute(s) if you want to
  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 превратился в список списков, содержащих строки.

KWC 02.09.2024 08:32

Похоже, я слишком поторопился с вашим предложением. Это сработало! Сначала я объединил два ваших предложения в одно, и результат стал таким, как я/вы описали.

KWC 03.09.2024 09:57

Другие вопросы по теме