Search code examples
variablesansibleruntime-errorjinja2ansible-inventory

Unable to get variable in Ansible Jinja template of a different / dynamic host


I have a variable final_delegate that determines the hosts depending upon user passed parameter -e perform_action=nslookup

- name: "Play 1"
  hosts: localhost
  tasks:

   - set_fact:
       final_delegate: "{{ 'localhost' if perform_action in 'telnetcurlnslookuptracerouteget_ip_address' else 'dest_nodes' }}"


- name: "Play 2"
  hosts: "{{ hostvars.localhost.final_delegate }}"
  tasks:

   - name: Run command
     ignore_errors: yes
     command:  ls -ltr /tmp/all.pdf
     register: cmdoutput

   - name: Creating body template
     template:
       src: "{{ playbook_dir }}/body.html.j2"
       dest: "{{ playbook_dir }}/body.html.tmp"
     delegate_to: localhost

Here is my body.html.j2

 <tr>
    <td>{{ inventory_hostname }}</td>
    <td><pre>{{ hostvars.[inventory_hostname].cmdoutput.stdout_lines | join('\n') }}</pre></td>
  </tr>

I get the below error:

An exception occurred during task execution. To see the full traceback, use -vvv. The error was:   </tr>
fatal: [localhost]: FAILED! => {"changed": false, "msg": "AnsibleError: template error while templating string: expected name or number. String:  <tr>\n    <td>{{ inventory_hostname }}</td>\n    <td><pre>{{ hostvars.[inventory_hostname].cmdoutput.stdout_lines | join('\\n') }}</pre></td>\n    </tr>\n\n"}

I also tried the following:

1.
    <td><pre>{{ hostvars.[hostvars.localhost.final_delegate].cmdoutput.stdout_lines | join('\n') }}</pre></td>
2.
    <td><pre>{{ hostvars.['hostvars.localhost.final_delegate'].cmdoutput.stdout_lines | join('\n') }}</pre></td>

Maybe I'm not getting the syntax right. Can you please suggest?


Solution

  • This...

    hostvars.[inventory_hostname]
    

    ...is not valid Jinja syntax. When referencing a variable attribute, you have two options:

    1. Dot notation: item.attribute

    2. Bracket notation: item["attribute"]

    When you want to reference an attribute whose name is contained in a variable, you need the second (bracket) syntax, so:

    hostvars[inventory_hostname].cmdoutput.stdout_lines
    

    If it helps, this runs without errors when I run it locally (whether or not /tmp/all.pdf exists):

    - name: "Play 1"
      hosts: localhost
      vars:
        perform_action: something
      tasks:
       - set_fact:
           final_delegate: "{{ 'localhost' if perform_action in 'telnetcurlnslookuptracerouteget_ip_address' else 'dest_nodes' }}"
    
    
    - name: "Play 2"
      hosts: "{{ hostvars.localhost.final_delegate }}"
      tasks:
    
       - name: Run command
         ignore_errors: true
         command:  ls -ltr /tmp/all.pdf
         register: cmdoutput
    
       - name: Creating body template
         template:
           src: "body.html.j2"
           dest: "body.html.tmp"
         delegate_to: localhost
    

    Using the fixed template:

     <tr>
        <td>{{ inventory_hostname }}</td>
        <td><pre>{{ hostvars[inventory_hostname].cmdoutput.stdout_lines | join('\n') }}</pre></td>
      </tr>
    

    Note that you don't need to use hostvars in the above template; you could write instead:

     <tr>
        <td>{{ inventory_hostname }}</td>
        <td><pre>{{ cmdoutput.stdout_lines | join('\n') }}</pre></td>
      </tr>