Daniel Macuare
Daniel Macuare

Reputation: 72

Generate single configuration for multiple hosts from Jinja2 template

Problem: I've got 2 tasks that are executed sequentially as (expected) given the Ansible nature. As I understand, you can only execute one module per each task.

Task 1 - Gather facts information (serials, versions, etc) from network devices.

Task 2 - Render a template with the info gathered in Task1

Ideal Result: Since I'm looping through loads of network devices my ideal result would be to choose one device at a time, gather the information from it and then render a template with that information, next go to the other device on the loop and so on.

Approach: I've thought to keep the same syntax, On task 1 save the facts on a file (.json) and on task 2, read the JSON file and get the variables I'm interested in.

Is there any better way to do this? (Probably there's more than one)

What I've got currently doesn't fit my purpose as when the template renders, it only contains information about the last device:

Tasks: roles/juniper.junos/tasks/main.yaml

- name: 1 - Gathering Facts
    junos_get_facts:
      host: "{{ inventory_hostname}}"
      user: ""
      passwd: ""
      savedir: "~/Ansible/Ouput/Facts"
    ignore_errors: True
    register: junos

- name: 2 - Creating the template
    template:
      src="~/Ansible/roles/juniper.junos/templates/template.j2"
      dest="~/Ansible/Ouput/Facts/Device_facts.yml"

Template: ~/Ansible/roles/juniper.junos/templates/template.j2

{% for host in groups['OOB_AMS'] %}

   ANSIBLE NAME:                   {{ inventory_hostname}}

   HOSTNAME:               {{ junos.facts.hostname }}
   MODEL:                  {{ junos.facts.model }}
   SERIAL:                 {{ junos.facts.serialnumber }}
   VERSION:                {{ junos.facts.model }}
   UP TIME:                {{ junos.facts.RE0.up_time }}

{% endfor %}

IDEAL Output:"~/Ansible/Ouput/Facts/Device_facts.yml"

   ANSIBLE NAME:                   DEVICE 1

   HOSTNAME:               DEVICE 1 HOSTNAME
   MODEL:                  DEVICE 1 MODEL
   SERIAL:                 DEVICE 1 SERIAL
   VERSION:                DEVICE 1 VERSION
   UP TIME:                DEVICE 1 UP TIME

   ANSIBLE NAME:                   DEVICE 2

   HOSTNAME:               DEVICE 2 HOSTNAME
   MODEL:                  DEVICE 2 MODEL
   SERIAL:                 DEVICE 2 SERIAL
   VERSION:                DEVICE 2 VERSION
   UP TIME:                DEVICE 2 UP TIME

   ANSIBLE NAME:                   DEVICE 3

   HOSTNAME:               DEVICE 3 HOSTNAME
   MODEL:                  DEVICE 3 MODEL
   SERIAL:                 DEVICE 3 SERIAL
   VERSION:                DEVICE 3 VERSION
   UP TIME:                DEVICE 3 UP TIME

Upvotes: 0

Views: 2838

Answers (1)

techraf
techraf

Reputation: 68639

You wrote a for-loop with a host variable, but not used it even once.

Change the template to:

{% for host in ansible_play_hosts %}

   ANSIBLE NAME:                   {{ hostvars[host].inventory_hostname}}

   HOSTNAME:               {{ hostvars[host].junos.facts.hostname }}
   MODEL:                  {{ hostvars[host].junos.facts.model }}
   SERIAL:                 {{ hostvars[host].junos.facts.serialnumber }}
   VERSION:                {{ hostvars[host].junos.facts.model }}
   UP TIME:                {{ hostvars[host].junos.facts.RE0.up_time }}

{% endfor %}

Looping over groups['OOB_AMS'] was not bad, but hardcoding group name seems unnecessary for your case. Instead you can use: ansible_play_hosts (play_hosts before version 2.2).

Also for clarity you might add run_once: true to the template task. This is not critical, because the template generates the same output in each iteration, so the subsequent runs will be skipped anyway, but there is no need to do it several times.

Upvotes: 2

Related Questions