Home > Back-end >  Ansible filter for json variable
Ansible filter for json variable

Time:04-01

A little background info. I'm using netbox as a source of truth for our network automation and I am currently in the process of doing some test to automate juniper configuration. Netbox is currently used as my ansible inventory and is supplying host_vars.

I want to achieve a simple task of setting a "value" in my Juniper configuration based on a host var.

Problem

I'm most definetly using the wrong map/filter combination to achieve my goal of looping over the values within the dict "dns" but I've tried countless times without any success so taking my issue to the community for an advice.

I have the following host_vars:

{
  "ansible_host": "192.168.1.1",
  "config_context": [
    {
      "_utility": {
        "dns": [
          "10.10.10.10",
          "10.10.10.20"
        ]
      }
    }
  ],
  "device_roles": [
    "leaf"
  ],
  "device_types": [
    "qfx5120-48y"
  ]
}

And my task currently looks like:

- name: Set DNS server
  juniper.device.config:
    config_mode: 'exclusive'
    load: 'set'
    lines:
      - 'set system name-server "{{ item }}"'
  loop: "{{ config_context | map(attribute='_utility') | flatten | map(attribute='dns') | flatten }}"

Which is giving out a error message

Tried to debug it registering a variable and printing this using debug msg:

DEBUG

- name: Print var
  vars:
    - nameservers: "{{ config_context | map(attribute='_utility') | flatten | map(attribute='dns') | flatten }}"
  debug:
    msg:
      - "{{ nameservers }}"

Result

ok: [poc-spine01-re] => {
    "msg": [
        [
            "10.10.10.10",
            "10.10.10.20"
        ]
    ]
}

But when trying too loop over the values withing the result I get another error

DEBUG

- name: Print var loop
  vars:
    - nameservers: "{{ config_context | map(attribute='_utility') | flatten | map(attribute='dns') | flatten }}"
  debug:
    msg:
      - "{{ item }}"
  loop: nameservers

Result

fatal: [poc-leaf03-re]: FAILED! => {
    "msg": "Invalid data passed to 'loop', it requires a list, got this instead: nameservers. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."
}
fatal: [poc-spine01-re]: FAILED! => {
    "msg": "Invalid data passed to 'loop', it requires a list, got this instead: nameservers. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."
}

UPDATE

Hi, it worked for the loop!. I've adjusted my variables and am now contemplating if i should only get the value of the key dns1 or dns2 ( not in a loop )

host vars

{
  "ansible_host": "192.168.1.1",
  "config_context": [
    {
      "_utility": {
        "dns": {
          "dns1": "10.10.10.10",
          "dns2": "10.10.10.20"
        }
      }
    }
  ],
  "device_roles": [
    "leaf"
  ],
  "device_types": [
    "qfx5120-48y"
  ]
}

but can't get the map filter to properly display one value

task

- name: Set DNS server
  vars:
    ns1: "{{ config_context | map(attribute='_utility.dns.dns1') | flatten }}"
  juniper.device.config:
    config_mode: 'exclusive'
    load: 'set'
    lines:
      - 'set system name-server "{{ ns1 }}"'

result

As you can see the value of 10.10.10.10 is quoted with brackets and a double quote instead of only the value string of 10.10.10.10

fatal: [poc-spine01-re]: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "lines": [
                "set system name-server \"['10.10.10.10']\""
            ],

CodePudding user response:

Your loop should be fixed as follow:

    loop: "{{ config_context | map(attribute='_utility.dns') | flatten }}"

Moreover I would add some extra filters to make sure that:

  • you don't loop over the same dns twice if it is declared in several config_context entries (i.e. unique)
  • you always go other the list in the same order (i.e. sort)
    loop: "{{ config_context | map(attribute='_utility.dns') | flatten | unique | sort }}"

Side notes

- name: Print var loop
  vars:
    - nameservers: "{{ config_context | map(attribute='_utility') | flatten | map(attribute='dns') | flatten }}"
  debug:
    msg:
      - "{{ item }}"
  loop: nameservers

Three problems in this single task.

  1. Although it actually "works" with a list (which ansible automagically converts), your vars declaration should be a mapping:
    vars:
      nameservers: "{{ config_context | map(attribute='_utility') | flatten | map(attribute='dns') | flatten }}"
    
  2. You are looping over the string "nameservers". You want to loop over the content of the variable with that name:
    loop: "{{ nameservers }}"
    
  3. In your debug message, you are asking to print a list which first element content is whatever value is inside the item variable. What you actually want is print the content of that variable directly, which can be acheived with
    debug:
      msg: "{{ item }}"
    
    or since you don't have any processing on the content by addressing the var directly
    debug:
      var: item
    

Putting it all together and integrating my above fix which was the core of your question:

- name: Print var loop
  vars:
    nameservers: "{{ config_context | map(attribute='_utility.dns') | flatten | unique | sort }}"
  debug:
    var: item
  loop: "{{ nameservers }}"
  • Related