Search code examples
listdictionaryansiblenested

Ansible; how to add list of dicts into nested dict?


i have empty dict 'ixes' which should be filled with data from external source collected to 'peeringdb_response_net'.

vars:
  ixes:
    ix_id_001:
      ix_short_name: 'ix1'
      ixlan_id: '331'
      ix_neighbors:
    ix_id_002:
      ix_short_name: 'ix2'
      ixlan_id: '222'
      ix_neighbors:


{
    "peeringdb_response_net": {
        "data": [
            {
                "aka": "BIX.BG",
                "netixlan_set": [
                    {
                        "asn": 15669,
                        "ipaddr4": "193.169.199.10",
                        "ipaddr6": "2001:7f8:58::3d35:0:2",
                        "ixlan_id": 331,
                        "operational": true,
                        "status": "ok",
                    },
                    {
                        "asn": 15669,
                        "ipaddr4": "193.169.198.10",
                        "ipaddr6": "2001:7f8:58::3d35:0:1",
                        "ixlan_id": 331,
                        "operational": true,
                        "status": "ok",
                    }
                ],
            }
        ],
    }
}

please help me find the way to get the following result. the neighbors entries should be filled only if "status": "ok", "operational": true and "ixlan_id" equals. 'ix_neighbor_short_name' should be indexed.

vars:
  ixes:
    ix_id_001:
      ix_short_name: 'ix1'
      ixlan_id: '331'
      ix_neighbors:
        - { ix_neighbor_ipv4_addr: '193.169.199.10', ix_neighbor_short_name: 'rs1' }
        - { ix_neighbor_ipv4_addr: '193.169.198.10', ix_neighbor_short_name: 'rs2' }
    ix_id_002:
      ix_short_name: 'ix2'
      ixlan_id: '222'
      ix_neighbors:

no idea how to code it


Solution

  • Given the data

      peeringdb_response_net:
        data:
          - aka: BIX.BG
            netixlan_set:
            - asn: 15669
              ipaddr4: 193.169.199.10
              ipaddr6: 2001:7f8:58::3d35:0:2
              ixlan_id: 331
              operational: true
              status: ok
            - asn: 15669
              ipaddr4: 193.169.198.10
              ipaddr6: 2001:7f8:58::3d35:0:1
              ixlan_id: 331
              operational: true
              status: ok
    

    Select the sets

      sets: "{{ peeringdb_response_net.data|
                map(attribute='netixlan_set')|
                map('groupby', 'ixlan_id')|
                list }}"
    

    gives

      sets:
      - - - 331
          - - asn: 15669
              ipaddr4: 193.169.199.10
              ipaddr6: 2001:7f8:58::3d35:0:2
              ixlan_id: 331
              operational: true
              status: ok
            - asn: 15669
              ipaddr4: 193.169.198.10
              ipaddr6: 2001:7f8:58::3d35:0:1
              ixlan_id: 331
              operational: true
              status: ok
    

    Use Jinja to create the structure and convert it from YAML

      ixes_str: |
        {% for set in sets %}
        ix_id_{{ "%03d"|format(loop.index) }}:
          ix_short_name: 'ix{{ loop.index }}'
          ixlan_id: '{{ set.0.0 }}'
          ix_neighbors: [
        {% for neighbor in set.0.1 %}
        {% if neighbor.status == 'ok' and neighbor.operational %}
          {ix_neighbor_ipv4_addr: '{{ neighbor.ipaddr4 }}',
           ix_neighbor_short_name: 'rs{{ loop.index }}'},
        {% endif %}
        {% endfor %}
          ]
        {% endfor %}
      ixes: "{{ ixes_str|from_yaml }}"
    

    gives

      ixes:
        ix_id_001:
          ix_neighbors:
          - ix_neighbor_ipv4_addr: 193.169.199.10
            ix_neighbor_short_name: rs1
          - ix_neighbor_ipv4_addr: 193.169.198.10
            ix_neighbor_short_name: rs2
          ix_short_name: ix1
          ixlan_id: '331'
    

    You can take the default dictionary

      ixes_default:
        ix_id_001:
          ix_short_name: 'ix1'
          ixlan_id: '331'
          ix_neighbors:
        ix_id_002:
          ix_short_name: 'ix2'
          ixlan_id: '222'
          ix_neighbors:
    

    and combine the dictionaries

      ixes: "{{ ixes_default|combine(ixes_str|from_yaml) }}"
    

    gives

      ixes:
        ix_id_001:
          ix_neighbors:
          - ix_neighbor_ipv4_addr: 193.169.199.10
            ix_neighbor_short_name: rs1
          - ix_neighbor_ipv4_addr: 193.169.198.10
            ix_neighbor_short_name: rs2
          ix_short_name: ix1
          ixlan_id: '331'
        ix_id_002:
          ix_neighbors: null
          ix_short_name: ix2
          ixlan_id: '222'
    

    Example of a complete playbook for testing

    - hosts: localhost
    
      vars:
    
        peeringdb_response_net:
          data:
          - aka: BIX.BG
            netixlan_set:
            - asn: 15669
              ipaddr4: 193.169.199.10
              ipaddr6: 2001:7f8:58::3d35:0:2
              ixlan_id: 331
              operational: true
              status: ok
            - asn: 15669
              ipaddr4: 193.169.198.10
              ipaddr6: 2001:7f8:58::3d35:0:1
              ixlan_id: 331
              operational: true
              status: ok
    
        sets: "{{ peeringdb_response_net.data|
                  map(attribute='netixlan_set')|
                  map('groupby', 'ixlan_id')|
                  list }}"
        ixes_str: |
          {% for set in sets %}
          ix_id_{{ "%03d"|format(loop.index) }}:
            ix_short_name: 'ix{{ loop.index }}'
            ixlan_id: '{{ set.0.0 }}'
            ix_neighbors: [
          {% for neighbor in set.0.1 %}
          {% if neighbor.status == 'ok' and neighbor.operational %}
            {ix_neighbor_ipv4_addr: '{{ neighbor.ipaddr4 }}',
             ix_neighbor_short_name: 'rs{{ loop.index }}'},
          {% endif %}
          {% endfor %}
            ]
          {% endfor %}
        ixes1: "{{ ixes_str|from_yaml }}"
        ixes2: "{{ ixes_default|combine(ixes_str|from_yaml) }}"
    
        ixes_default:
          ix_id_001:
            ix_short_name: 'ix1'
            ixlan_id: '331'
            ix_neighbors:
          ix_id_002:
            ix_short_name: 'ix2'
            ixlan_id: '222'
            ix_neighbors:
    
      tasks:
    
        - debug:
            var: peeringdb_response_net
        - debug:
            var: sets
        - debug:
            var: ixes_str
        - debug:
            var: ixes1
        - debug:
            var: ixes2