skip to Main Content

am trying to create my 1st jinja2 template that extracts a field from a json string as am trying to create DHCP reserveration file.

here is an example inventory feed.

I would like to select the value from the mac address to use as a value as jinja2 value for a field for the interface that matches the ansible host. which could be in any postion on the interfaces.

how would I go about it


 "TEST": {
                "ansible_host": "192.171.130.8",
                "interfaces": [
                    {
                        "ip_addresses": [
                            {
                                "address": "192.171.130.8/32",
                            }
                        ],
                        "mac_address": "C8-F1-13-78-9E-E7"
                        
                    },
                    {
                        "ip_addresses": [
                            {
                                "address": "192.171.130.80/32",
                            }
                        ],
                        "mac_address": "85-9F-76-AE-57-10"
                        
                    }
                ],
                
               
         }

so I would like to doe something like

{{ hostvars[host].interfaces.[ip_addresses.address = ansible_host].mac_address }}

but am not sure how to go about it.

2

Answers


  1. This looks like a good candidate for the json_query filter. My first thought was that this should work:

    hostvars[host].interfaces | json_query("[?ip_addresses[].address == '192.171.130.8/32']")
    

    But that returns an empty list. The following is a bit more verbose, but produces the expected result:

    hostvars[host].interfaces|json_query("[?ip_addresses[].address | contains(@, '192.171.130.8/32')]")
    

    In a playbook:

    - hosts: localhost
      gather_facts: false
      vars:
        TEST: {
          "ansible_host": "192.171.130.8",
          "interfaces": [
            {
              "ip_addresses": [
                {
                  "address": "192.171.130.8/32",
                  }
                  ],
              "mac_address": "C8-F1-13-78-9E-E7"
    
              },
              {
                "ip_addresses": [
                  {
                    "address": "192.171.130.80/32",
                    }
                    ],
                "mac_address": "85-9F-76-AE-57-10"
    
                }
                ],
    
    
              }
      tasks:
        - debug:
            msg: >-
              {{ TEST.interfaces | json_query("[?ip_addresses[].address | contains(@, '192.171.130.8/32')]") }}
    

    NB: Watch your quoting; you’ll note that here I’ve used the YAML folding quote operating (>) because the expression contains both double quotes and single quotes, so putting either of those around the outside would mean escaping quotes inside the expression.

    Running the above playbook produces:

    ok: [localhost] => {
        "msg": [
            {
                "ip_addresses": [
                    {
                        "address": "192.171.130.8/32"
                    }
                ],
                "mac_address": "C8-F1-13-78-9E-E7"
            }
        ]
    }
    

    Since you’ve got the desired address in a variable (ansible_host), you would write instead:

      tasks:
        - debug:
            msg: >-
              {{
                hostvars[host].interfaces | json_query("[?ip_addresses[].address | contains(@, '%s/32')]" % ansible_host)
              }}
    

    Our expression returns a list containing a single dictionary. If you want the mac_address field, treat the result just like any other Ansible array variable:

      tasks:
        - debug:
            msg: >-
              {{
              (
                hostvars[host].interfaces |
                json_query("[?ip_addresses[].address | contains(@, '%s/32')]" % ansible_host)
              ).0.mac_address
              }}
    
    Login or Signup to reply.
  2. Create the dictionary from the MAC addresses and lists of IPs

      mac_ip_raw: "{{ dict(interfaces|json_query(mac_ip_query)) }}"
      mac_ip_query: '[].[mac_address, ip_addresses[].address]'
    

    gives

      mac_ip_raw:
        85-9F-76-AE-57-10:
        - 192.171.130.80/32
        C8-F1-13-78-9E-E7:
        - 192.171.130.8/32
    

    Get rid of the masks

      mac_ip: "{{ dict(mac_ip_raw.keys()|
                       zip(mac_ip_raw.values()|
                           map('map', 'split', '/')|
                           map('map', 'first'))) }}"
    

    gives

      mac_ip:
        85-9F-76-AE-57-10:
        - 192.171.130.80
        C8-F1-13-78-9E-E7:
        - 192.171.130.8
    

    Now, test which list contains the IP

      my_mac: "{{ mac_ip|dict2items|
                  selectattr('value', 'contains', ansible_host)|
                  map(attribute='key')|first }}"
    

    gives the expected result

      my_mac: C8-F1-13-78-9E-E7
    

    Example of a complete project for testing

    shell> tree .
    .
    ├── ansible.cfg
    ├── hosts
    ├── host_vars
    │   └── srv1
    │       └── interfaces.json
    └── pb.yml
    
    2 directories, 4 files
    
    shell> cat ansible.cfg 
    [defaults]
    gathering = explicit
    collections_path = $HOME/.local/lib/python3.9/site-packages/
    inventory = $PWD/hosts
    roles_path = $PWD/roles
    retry_files_enabled = false
    stdout_callback = yaml
    
    shell> cat hosts
    srv1 ansible_host=192.171.130.8
    
    shell> cat host_vars/srv1/interfaces.json 
    {
      "interfaces":
        [
            {
                "ip_addresses": [
                    {
                        "address": "192.171.130.8/32"
                    }
                ],
                "mac_address": "C8-F1-13-78-9E-E7"
            },
            {
                "ip_addresses": [
                    {
                        "address": "192.171.130.80/32"
                    }
                ],
                "mac_address": "85-9F-76-AE-57-10"
            }
        ]
    }
    
    shell> cat pb.yml 
    - hosts: all
    
      vars:
    
        mac_ip_raw: "{{ dict(interfaces|json_query(mac_ip_query)) }}"
        mac_ip_query: '[].[mac_address, ip_addresses[].address]'
        mac_ip: "{{ dict(mac_ip_raw.keys()|
                         zip(mac_ip_raw.values()|
                             map('map', 'split', '/')|
                             map('map', 'first'))) }}"
        my_mac: "{{ mac_ip|dict2items|
                    selectattr('value', 'contains', ansible_host)|
                    map(attribute='key')|first }}"
        
      tasks:
    
        - debug:
            var: ansible_host
        - debug:
            var: interfaces
        - debug:
            var: mac_ip_raw
        - debug:
            var: mac_ip
        - debug:
            var: my_mac
    

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search