Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ansible: generate variables in loops

In my variable file, I need to define a list variable whose items are of similar pattern, and also share some (redundant) info. Instead of manually type all those info, I would like to generate the list variable in a loop.

e.g., I have 100 hosts, with primary ip address 192.168.100.[1:100], and each host has an extra ip addresses 10.0.1.[1:100]. All primary IPs use the same gateway, say 192.168.100.254, and all extra IPs use another gateway, say 10.0.1.254.

In a task, I want to loop through all hosts, and for each host, it's primary ip, extra ip, and gateways are all needed. I want to use "with_items" in my task, so I want to have a list variable "IP_ADDRS", within which each item is a dict like below:

{ primary_ip: 192.168.100.x, primary_gw: 192.168.100.254, extra_ip: 10.0.1.x, extra_gw: 10.0.1.254}

Instead of define IP_ADDRS manually:

IP_ADDRS:
  - { primary_ip: 192.168.100.1, primary_gw: 192.168.100.254, extra_ip: 10.0.1.1, extra_gw: 10.0.1.254}
  - { primary_ip: 192.168.100.2, primary_gw: 192.168.100.254, extra_ip: 10.0.1.2, extra_gw: 10.0.1.254}
  - ...

I want to generate the list variable "IP_ADDRS" somehow...

I tried jinja2 statements, something like below:

IP_ADDRS: >
  "{% for idx in range(1, 101) %}
  - { primary_ip: 192.168.100.{{ idx }}, primary_gw: 192.168.100.254, extra_ip: 10.0.1.{{ idx }}, extra_gw: 10.0.1.254 }
  "{% endfor %}"

When I print IP_ADDRS using debug module, it does literally print all items in the list, BUT it seems Ansible is not treat this variable as a LIST, thus

with_items: {{ IP_ADDRS }}

does not work as I expected.

Is there any thing wrong with the jinja2 statement, or is there a way to achieve the same result?

Thanks a lot,

/bruin

like image 602
bruin Avatar asked Mar 15 '17 13:03

bruin


2 Answers

You may define template of your object and use it in loops:

---
- hosts: localhost
  gather_facts: no
  vars:
    ip_template:
      primary_ip: "192.168.100.{{ item }}"
      primary_gw: "192.168.100.254"
      extra_ip: "10.0.1.{{ item }}"
      extra_gw: "10.0.1.254"
  tasks:
    # execute single task, no need in list
    - debug:
        msg: "{{ ip_template }}"
      with_sequence: "start=1 count=5"

    # construct list
    - set_fact:
        ip_list: "{{ (ip_list | default([])) + [ip_template] }}"
      with_sequence: "start=1 count=5"
    - debug:
        msg: "{{ ip_list }}"

If you still want to have ip_list defined in your variables, you need to construct complex Jinja2 statement to generate list in JSON-format and not YAML, as you tried. Tiny example of such string with two objects inside: '[{"ip":"192.168.0.10","gw":"192.168.0.1"},{"ip":"192.168.0.20","gw":"192.168.0.1"}]'

like image 103
Konstantin Suvorov Avatar answered Jan 04 '23 19:01

Konstantin Suvorov


If there are many complex variables needs to be generated by loops, and if/else. Sometimes, it would become nasty and less understandable when trying to generate them in tasks. Create variable files in jinja template format might help.

For example, create the variable generated by jinja template:

# variable file
{% for idx in range(1, 101) %}
- primary_ip: 192.168.100.{{ idx }}
  primary_gw: 192.168.100.254
  extra_ip: 10.0.1.{{ idx }}
  extra_gw: 10.0.1.254
{% endfor %}

Then read variable in tasks by template lookup plugin:

# playbook tasks
- name: Read variables
  set_fact:
    ip_list: "{{ lookup('template', 'path_to_file') | from_yaml }}"

And the result will be:

TASK [set_fact] *******************************************************************************************************************
Tuesday 06 February 2018  17:16:55 +0800 (0:00:00.110)       0:00:00.110 ******
Tuesday 06 February 2018  17:16:55 +0800 (0:00:00.108)       0:00:00.108 ******
ok: [localhost]

TASK [debug] **********************************************************************************************************************
Tuesday 06 February 2018  17:16:55 +0800 (0:00:00.137)       0:00:00.247 ******
Tuesday 06 February 2018  17:16:55 +0800 (0:00:00.137)       0:00:00.245 ******
ok: [localhost] => {
    "ip_list": [
        {
            "extra_gw": "10.0.1.254",
            "extra_ip": "10.0.1.1",
            "primary_gw": "192.168.100.254",
            "primary_ip": "192.168.100.1"
        },
        {
            "extra_gw": "10.0.1.254",
            "extra_ip": "10.0.1.2",
            "primary_gw": "192.168.100.254",
            "primary_ip": "192.168.100.2"
        },
        ...........
like image 42
William Billingsley Avatar answered Jan 04 '23 20:01

William Billingsley