首页 > 解决方案 > 合并到嵌套的字典数组中

问题描述

我有这样的事情:

vars:
    process_names:
        - name: myprocess
          exe:
            - /usr/myexe
        - name: myprocess1
          exe:
            - /usr/myexe1

    update1:
        - name: myprocess2
          exe:
            - /usr/myexe2
    
    update2:
        - name: myprocess1
          exe:
            - /opt/myexe1

考虑这process_names是我想在 2 个不同场景中更新的原始数据。

process_names:
    - name: myprocess
        exe:
        - /usr/myexe
    - name: myprocess1
        exe:
        - /usr/myexe1
    - name: myprocess2
        exe:
        - /usr/myexe2
process_names:
    - name: myprocess
        exe:
        - /usr/myexe
    - name: myprocess1
        exe:
        - /opt/myexe1
    - name: myprocess2
        exe:
        - /usr/myexe2

我通过一个简单的数组连接实现了场景 1(+)。但是,在我必须执行更新操作的场景 2中挣扎。

到目前为止,我已经尝试过combineand union,但没有运气。例如:{{ process_names | union(update2) }}。需要指导和帮助。

标签: ansible

解决方案


如果您将初始列表转换为字典以应用更改,这实际上变得更加容易并且对于这两种情况的操作完全相同。该名称成为唯一键,您只需与您的更新结合(也转换为 dict(s))。完成后,您可以转换回列表。

仅使用原始数据

以下示例process_update.yml剧本:

---
- name: update a process list
  hosts: localhost
  gather_facts: false

  vars:
    process_names:
      - name: myprocess
        exe:
          - /usr/myexe
      - name: myprocess1
        exe:
          - /usr/myexe1

    update1:
      - name: myprocess2
        exe:
          - /usr/myexe2

    update2:
      - name: myprocess1
        exe:
          - /opt/myexe1

  tasks:
    - name: Show original list for comparison
      debug:
        var: process_names

    - name: Apply updates to my list
      vars:
        process_dict: "{{ process_names | items2dict(key_name='name', value_name='exe') }}"
        apply_updates:
          - update1
          - update2
        my_dict_updates: "{{ apply_updates | map('extract', vars) | flatten | items2dict(key_name='name', value_name='exe')}}"
        updated_process_names: "{{ process_dict | combine(my_dict_updates) | dict2items(key_name='name', value_name='exe') }}"
      debug:
        var: updated_process_names

给出:

$ ansible-playbook process_update.yml 

PLAY [update a process list] ***********************************************************************************************************************************************************************************************************

TASK [Show original list for comparison] ***********************************************************************************************************************************************************************************************
ok: [localhost] => {
    "process_names": [
        {
            "exe": [
                "/usr/myexe"
            ],
            "name": "myprocess"
        },
        {
            "exe": [
                "/usr/myexe1"
            ],
            "name": "myprocess1"
        }
    ]
}

TASK [Apply updates to my list] ********************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "updated_process_names": [
        {
            "exe": [
                "/usr/myexe"
            ],
            "name": "myprocess"
        },
        {
            "exe": [
                "/opt/myexe1"
            ],
            "name": "myprocess1"
        },
        {
            "exe": [
                "/usr/myexe2"
            ],
            "name": "myprocess2"
        }
    ]
}

PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

走得更远

仅当您的列表元素包含 2 个键 (nameexe) 时,上述内容才有效。如果您向元素添加一个或多个键,它们将在 list => dict 转换期间丢失。

以下是解决此问题的示例。这绝对不是防弹的,最好的解决方案是 IMO 将您的原始结构从源代码转换为字典。

下面的process_update_revised.yml剧本

---
- name: update a process list
  hosts: localhost
  gather_facts: false

  vars:
    process_names:
      - name: myprocess
        exe:
          - /usr/myexe
        a_key: some value
      - name: myprocess1
        exe:
          - /usr/myexe1
        a_key: other value

    update1:
      - name: myprocess2
        exe:
          - /usr/myexe2
        a_key: no value

    update2:
      - name: myprocess1
        exe:
          - /opt/myexe1
        a_key: changed value

  tasks:
    - name: Show original dict for comparison
      debug:
        var: process_names

    - name: Apply updates to my list
      vars:
        process_dict: "{{ process_names | groupby('name') | map('flatten') | items2dict(key_name=0, value_name=1) }}"
        apply_updates:
          - update1
          - update2
        my_dict_updates: "{{ apply_updates | map('extract', vars) | flatten | groupby('name') | map('flatten') | items2dict(key_name=0, value_name=1) }}"
        updated_process_names: "{{ process_dict | combine(my_dict_updates) | dict2items | map(attribute='value') }}"
      debug:
        var: updated_process_names

给出:

$ ansible-playbook process_update_revised.yml 

PLAY [update a process list] ***********************************************************************************************************************************************************************************************************

TASK [Show original dict for comparison] ***********************************************************************************************************************************************************************************************
ok: [localhost] => {
    "process_names": [
        {
            "a_key": "some value",
            "exe": [
                "/usr/myexe"
            ],
            "name": "myprocess"
        },
        {
            "a_key": "other value",
            "exe": [
                "/usr/myexe1"
            ],
            "name": "myprocess1"
        }
    ]
}

TASK [Apply updates to my list] ********************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "updated_process_names": [
        {
            "a_key": "some value",
            "exe": [
                "/usr/myexe"
            ],
            "name": "myprocess"
        },
        {
            "a_key": "changed value",
            "exe": [
                "/opt/myexe1"
            ],
            "name": "myprocess1"
        },
        {
            "a_key": "no value",
            "exe": [
                "/usr/myexe2"
            ],
            "name": "myprocess2"
        }
    ]
}

推荐阅读