Mike S
Mike S

Reputation: 1361

Ansible / jinja2 output

I'm running Ansible 2.7.5 on RHEL 7.4. (I know it's old; I'll upgrade soon- promise). The zip feature came out in Ansible 2.3. Anyway, I can't seem to get a couple of lists to create a dict properly through the zip filter.

I had read in the list from a variables file. The list is network_interfaces['computer_type'] in the loop which follows further down; here it is from debug output:

[{u'interface': u'bond0.160', u'notes': u'note160'},
 {u'interface': u'bond0.197', u'notes': u'note197'},
 {u'interface': u'bond1', u'notes': u'bonded, numa1, broadcom device'}]

The problem is, I'm trying to get the interface names that you see above, into a dict along with their values (which for now as an example are "notes"). But when I try to perform the dict (_keys|zip(_vals)) as shown in the first example of _reused_val below, I am getting the corresponding error msg number 1. So for debugging, I reran my task with #2 uncommented, and subsequently with #3 uncommented.

- debug:
    msg: >
         ITEM: {{ item }}
         *        :::network_interfaces::: {{ network_interfaces[computer_type] }}
         *        :::_keys::: {{ _keys }}
         *        :::_vals::: {{ _vals }}
         *        :::_reused_val::: {{ _reused_val }}
  loop: [ " {{ computer_type }} " ]
  vars:
    computer_type: big_computer
    _keys: "{{ network_interfaces[computer_type]|map(attribute='interface')|list }}"
    _vals: "{{ network_interfaces[computer_type] }}" # returns a list
    _reused_val: "{{ dict(_keys|zip(_vals)) }}"    #1
    #_reused_val: "{{ _keys|zip(_vals) | list }}"  #2
    #_reused_val: "{{ _keys|zip(_vals) }}"         #3

output showing reused_val (I adjusted the spacing a little to make it easier to read):

1- "msg": "Unexpected templating type error occurred on ({{ dict(_keys|zip(_vals)) }}): <lambda>() takes exactly 0 arguments (1 given)"
2- [(u'bond0.160', {u'interface': u'bond0.160', u'notes': u'note160'}),
    (u'bond0.197', {u'interface': u'bond0.197', u'notes': u'note197'}),
    (u'bond1', {u'interface': u'bond1', u'notes': u'bonded, numa1, broadcom device'})]
3- <itertools.izip object at 0x7f311c048dd0>

#1 is what I'm trying to get to work- the dict(_keys|zip(_vals)).

I don't know why #3 shows an itertools.izip object. I thought it was supposed to return a list. And for #2, I don't know what those parentheses are for. Is it because there are 3 dictionaries in the list?

Finally, here is the full output of the msg for case #3; I broke the lines a little bit just to make it easier to read:

"ITEM:  big_computer  *        :::network_interfaces:::
[{u'interface': u'bond0.160', u'notes': u'note160'}, {u'interface': u'bond0.197', u'notes': u'note1
97'}, {u'interface': u'bond1', u'notes': u'bonded, numa1, broadcom device'}]
*        :::_keys::: [u'bond0.160', u'bond0.197', u'bond1']
*        :::_vals::: [{u'interface': u'bond0.160', u'notes': u'note160'}, {u'interface': u'bond0.197', u'notes': u'note197'}, {u'interface': u'bond1', u'notes': u'bonded, numa1, broadcom device'}]
*        :::_reused_val::: <itertools.izip object at 0x7fc768049cb0>\n"

This work is a result of the work from my question at Ansible: need to combine information from 2 files for a task later I'm trying to follow the examples but after a days' work, I'm still stuck. Thanks.

Upvotes: 0

Views: 343

Answers (1)

larsks
larsks

Reputation: 311238

I think you should just upgrade your Ansible.

With ansible 2.10.8, the following playbook runs without error and I think it produces the output you want. This is mostly identical to what you have, except I'm using a list in the debug task to produce more legible output.

- hosts: localhost
  gather_facts: false
  vars:
    network_interfaces:
      big_computer:
        - interface: bond0.160
          notes: note160
        - interface: bond0.197
          notes: note197
        - interface: bond1
          notes: 'bonded, numa1, broadcom devices'

  tasks:
    - debug:
        msg:
          - "ITEM: {{ item }}"
          - "network_interfaces: {{ network_interfaces[computer_type] }}"
          - "keys: {{ _keys }}"
          - "vals: {{ _vals }}"
          - "reused_val: {{ _reused_val }}"
      loop: [ " {{ computer_type }} " ]
      vars:
        computer_type: big_computer
        _keys: "{{ network_interfaces[computer_type]|map(attribute='interface')|list }}"
        _vals: "{{ network_interfaces[computer_type] }}" # returns a list
        _reused_val: "{{ dict(_keys|zip(_vals)) }}"    #1

Running this produces:

TASK [debug] *********************************************************************************************************************************************************************************
ok: [localhost] => (item= big_computer ) => {
    "msg": [
        "ITEM:  big_computer ",
        "network_interfaces: [{'interface': 'bond0.160', 'notes': 'note160'}, {'interface': 'bond0.197', 'notes': 'note197'}, {'interface': 'bond1', 'notes': 'bonded, numa1, broadcom devices'}]",
        "keys: ['bond0.160', 'bond0.197', 'bond1']",
        "vals: [{'interface': 'bond0.160', 'notes': 'note160'}, {'interface': 'bond0.197', 'notes': 'note197'}, {'interface': 'bond1', 'notes': 'bonded, numa1, broadcom devices'}]",
        "reused_val: {'bond0.160': {'interface': 'bond0.160', 'notes': 'note160'}, 'bond0.197': {'interface': 'bond0.197', 'notes': 'note197'}, 'bond1': {'interface': 'bond1', 'notes': 'bonded, numa1, broadcom devices'}}"
    ]
}

If I run this with Ansible 2.7.5, I still don't see any of the errors you've reported (that's one reason why it's always a good idea to include a runnable example in your question), but I don't get the same behavior as with 2.10.8, either. I get:

TASK [debug] *********************************************************************************************************************************************************************************
ok: [localhost] => (item= big_computer ) => {
    "msg": [
        "ITEM:  big_computer ",
        "network_interfaces: [{'interface': 'bond0.160', 'notes': 'note160'}, {'interface': 'bond0.197', 'notes': 'note197'}, {'interface': 'bond1', 'notes': 'bonded, numa1, broadcom devices'}]",
        "keys: ['bond0.160', 'bond0.197', 'bond1']",
        "vals: [{'interface': 'bond0.160', 'notes': 'note160'}, {'interface': 'bond0.197', 'notes': 'note197'}, {'interface': 'bond1', 'notes': 'bonded, numa1, broadcom devices'}]",
        "reused_val: {'[': '[', \"'\": 's', 'b': \"'\", 'o': 'n', 'n': 'o', 'd': 't', '0': 'd', '.': '0', '1': 'e', '6': 'a', ',': \"'\", ' ': ',', '9': '1', '7': '6', ']': \"'\"}"
    ]
}

So, it's all good except for the final step. The biggest problem here is that Ansible 2.7.5 doesn't handle anything other than string values very well. When you write this...

_keys: "{{ network_interfaces[computer_type]|map(attribute='interface')|list }}"

The result is a string value, not a list, whereas in more recent versions of Ansible the _keys variable will be a list. We can work around this by explicitly serializing and de-serializing everything to/from JSON:

- debug:
    msg:
      - "ITEM: {{ item }}"
      - "network_interfaces: {{ network_interfaces[computer_type] }}"
      - "keys: {{ _keys }}"
      - "vals: {{ _vals }}"
      - "reused_val: {{ _reused_val }}"
  loop: [ " {{ computer_type }} " ]
  vars:
    computer_type: big_computer
    _keys: "{{ network_interfaces[computer_type]|map(attribute='interface')|list|to_json }}"
    _vals: "{{ network_interfaces[computer_type]|to_json }}"
    _reused_val: "{{ dict(_keys|from_json|zip(_vals|from_json)) }}"

This produces the same output as the original version did with 2.10.8:

TASK [debug] *********************************************************************************************************************************************************************************
ok: [localhost] => (item= big_computer ) => {
    "msg": [
        "ITEM:  big_computer ",
        "network_interfaces: [{'interface': 'bond0.160', 'notes': 'note160'}, {'interface': 'bond0.197', 'notes': 'note197'}, {'interface': 'bond1', 'notes': 'bonded, numa1, broadcom devices'}]",
        "keys: [\"bond0.160\", \"bond0.197\", \"bond1\"]",
        "vals: [{\"interface\": \"bond0.160\", \"notes\": \"note160\"}, {\"interface\": \"bond0.197\", \"notes\": \"note197\"}, {\"interface\": \"bond1\", \"notes\": \"bonded, numa1, broadcom devices\"}]",
        "reused_val: {'bond0.160': {'interface': 'bond0.160', 'notes': 'note160'}, 'bond0.197': {'interface': 'bond0.197', 'notes': 'note197'}, 'bond1': {'interface': 'bond1', 'notes': 'bonded, numa1, broadcom devices'}}"
    ]
}

...but that's an awful solution, and it would be much cleaner just to upgrade.

Upvotes: 2

Related Questions