How to replace only a specific entry in a nested dictionary

80 views
Skip to first unread message

jean-christophe manciot

unread,
Aug 18, 2023, 9:40:25 AM8/18/23
to Ansible Project
The source nested dictionary is for instance the following:
                vpc:
                        provider:
                                - net_type: 'db'
                                  net_ipv4_subnet: '10.0.0.0/16'
                                  net_ipv6_subnet: 'FD00::/64'
                                  net_id: 0
                                  nic_ids:
                                        - 1111111111
                                        - 2222222222
                                - net_type: 'dns'
                                  net_ipv4_subnet: '10.1.0.0/16'
                                  net_ipv6_subnet: 'FD01::/64'
                                  net_id: 1
                                  nic_ids:
                                        - 3333333333
                                        - 4444444444
                                - net_type: 'ns'
                                  net_ipv4_subnet: '10.2.0.0/16'
                                  net_ipv6_subnet: 'FD02::/64'
                                  net_id: 2
                                  nic_ids:
                                        - 5555555555
                                        - 6666666666

We need to replace only the whole entry indexed by "net_type: 'dns'" with:
                                - net_type: 'dns'
                                  net_ipv4_subnet: '10.3.0.0/16'
                                  net_ipv6_subnet: 'FD03::/64'
                                  net_id: 3
                                  nic_ids: []

so the result would look like:
                vpc:
                        provider:
                                - net_type: 'db'
                                  net_ipv4_subnet: '10.0.0.0/16'
                                  net_ipv6_subnet: 'FD00::/64'
                                  net_id: 0
                                  nic_ids:
                                        - 1111111111
                                        - 2222222222
                                - net_type: 'dns'
                                  net_ipv4_subnet: '10.3.0.0/16'
                                  net_ipv6_subnet: 'FD03::/64'
                                  net_id: 3
                                  nic_ids: []
                                - net_type: 'ns'
                                  net_ipv4_subnet: '10.2.0.0/16'
                                  net_ipv6_subnet: 'FD02::/64'
                                  net_id: 2
                                  nic_ids:
                                        - 5555555555
                                        - 6666666666

I've tried the following task using the combine filter:
        - name: Combining 2 <vpc> dictionaries with combine/list_merge='replace'
          vars:
                vpc:
                        provider:
                                - net_type: 'db'
                                  net_ipv4_subnet: '10.0.0.0/16'
                                  net_ipv6_subnet: 'FD00::/64'
                                  net_id: 0
                                  nic_ids:
                                        - 1111111111
                                        - 2222222222
                                - net_type: 'dns'
                                  net_ipv4_subnet: '10.1.0.0/16'
                                  net_ipv6_subnet: 'FD01::/64'
                                  net_id: 1
                                  nic_ids:
                                        - 3333333333
                                        - 4444444444
                                - net_type: 'ns'
                                  net_ipv4_subnet: '10.2.0.0/16'
                                  net_ipv6_subnet: 'FD02::/64'
                                  net_id: 2
                                  nic_ids:
                                        - 5555555555
                                        - 6666666666
                net_id: 3
                net_ipv4_subnet: '10.3.0.0/16'
                net_ipv6_subnet: 'FD03::/64'
                new_vpc: "{{ vpc | combine({'provider': [{'net_type': 'dns', 'net_id': net_id, 'net_ipv4_subnet': net_ipv4_subnet, 'net_ipv6_subnet': net_ipv6_subnet, 'nic_ids': []}]}, recursive=True, list_merge='replace') }}"
          debug:
                msg: "{{ new_vpc }}"

which unfortunately removes the other 2 entries:
ok: [localhost] =>
  msg:
    provider:
    - net_id: 3
      net_ipv4_subnet: 10.3.0.0/16
      net_ipv6_subnet: FD03::/64
      net_type: dns
      nic_ids: []

Same result with community.general.lists_mergeby filter:
        - name: Combining 2 <vpc> dictionaries with lists_mergeby/list_merge='replace'
          vars:
                vpc:
                        provider:
                                - net_type: 'db'
                                  net_ipv4_subnet: '10.0.0.0/16'
                                  net_ipv6_subnet: 'FD00::/64'
                                  net_id: 0
                                  nic_ids:
                                        - 1111111111
                                        - 2222222222
                                - net_type: 'dns'
                                  net_ipv4_subnet: '10.1.0.0/16'
                                  net_ipv6_subnet: 'FD01::/64'
                                  net_id: 1
                                  nic_ids:
                                        - 3333333333
                                        - 4444444444
                                - net_type: 'ns'
                                  net_ipv4_subnet: '10.2.0.0/16'
                                  net_ipv6_subnet: 'FD02::/64'
                                  net_id: 2
                                  nic_ids:
                                        - 5555555555
                                        - 6666666666
                seed_vpc:
                        provider:
                                - net_type: 'dns'
                                  net_ipv4_subnet: '10.3.0.0/16'
                                  net_ipv6_subnet: 'FD03::/64'
                                  net_id: 3
                                  nic_ids: []
                new_vpc: "{{ [vpc.provider, seed_vpc.provider] | community.general.lists_mergeby('net_type=dns', recursive=True, list_merge='replace') }}"
          debug:
                msg: "{{ new_vpc }}"

leads to:
ok: [localhost] =>
  msg:
    provider:
    - net_id: 3
      net_ipv4_subnet: 10.3.0.0/16
      net_ipv6_subnet: FD03::/64
      net_type: dns
      nic_ids: []

Am I missing something or is it impossible to perform with the currently available ansible filters?

Todd Lewis

unread,
Aug 18, 2023, 12:08:40 PM8/18/23
to ansible...@googlegroups.com, uto...@gmail.com
---
- name: Replace dict in list example
  hosts: localhost
  gather_facts: false
  vars:
    vpc:
      provider:
        - net_type: 'db'
          net_ipv4_subnet: '10.0.0.0/16'
          net_ipv6_subnet: 'FD00::/64'
          net_id: 0
          nic_ids:
            - 1111111111
            - 2222222222
        - net_type: 'dns'
          net_ipv4_subnet: '10.1.0.0/16'
          net_ipv6_subnet: 'FD01::/64'
          net_id: 1
          nic_ids:
            - 3333333333
            - 4444444444
        - net_type: 'ns'
          net_ipv4_subnet: '10.2.0.0/16'
          net_ipv6_subnet: 'FD02::/64'
          net_id: 2
          nic_ids:
            - 5555555555
            - 6666666666
    fix:
      net_type: 'dns'
      net_ipv4_subnet: '10.3.0.0/16'
      net_ipv6_subnet: 'FD03::/64'
      net_id: 3
      nic_ids: []
  tasks:
    - name: Do this if the order doesn't matter
      # N.B. This assumes there is 1 provider to replace.
      ansible.builtin.debug:
        msg: "{{ item }}"
      loop:
        - "{{ [vpc.provider | selectattr('net_type', 'ne', fix.net_type)] | sum(start=[fix]) }}"

    - name: Do this if the order matters
      # N.B. This will replace _all_ "dns" providers with 'fix'.
      ansible.builtin.debug:
        msg: "{{ item }}"
      loop:
        - |
          {% set providers = [] %}
          {% for p in vpc.provider %}
          {%   set _ = providers.append(p if p.net_type != fix.net_type else fix) %}
          {% endfor %}{{ providers }}

--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/f77880a9-c219-41ec-ad7d-d5381594489an%40googlegroups.com.

-- 
Todd

jean-christophe manciot

unread,
Aug 21, 2023, 11:58:56 AM8/21/23
to Ansible Project
Very nice :-)
As it turns out, there is only one entry of type 'dns' , so both solutions work perfectly.


Reply all
Reply to author
Forward
0 new messages