Ansible playbook loop from site yaml or template?

Viewed 61

I'm trying to use my Ansible playbook to call upon a site YAML reference to create a filename that increment for multiple switches. What am I doing wrong? I believe the playbook is pulling from the host YAML?

Format: <switch>-<site>-<floor><stackid>.txt

e.g.: with two switches:

  • swi-lon-101.txt
  • swi-lon-202.txt

host_vars/host.yaml

project_name: test
device_name: swi
site_abbrev: lon
device_type: switch
switch_stacks:
- id: 01
  installation_floor: 1
- id: 02
  installation_floor: 2

templates/switch-template.j2

{% for stack in switch_stacks %}
set system host-name {{ device_name }}-{{ site_abbrev }}-{{ stack.installation_floor }}{{ stack.id }}
{% endfor %}

The playbook, in which the problem lies, how do I get the hostname to create correctly for each of the 2 switches?

My playbook:

- name: Create Folder Structure
  hosts: junos
  gather_facts: false

  tasks:
    - name: Create Site Specific Folder
      file:
        path: /home/usr/complete_config/{{ project_name }}
        state: directory
        mode: 0755

    - name: Set Destination Directory & Filename for Switch Configurations
      set_fact:
        dest_dir: /home/usr/complete_config/{{ project_name }}
        full_device_name: "{{ device_name|lower }}-{{ site_abbrev|lower }}-{{ switch_stacks.installation_floor }}{{ switch_stacks.id }}.txt"
      when: device_type == 'switch'

Ansible error, running:

ansible-playbook playbooks/site-playbook.yaml
TASK [Set Destination Directory & Filename for Switch Configurations] **************************************************
fatal: [site-switch]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'installation_floor'\n\nThe error appears to be in '/home/usr/playbooks/switch-playbook.yaml': line 19, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: Set Destination Directory & Filename for Switch Configurations\n      ^ here\n"}
3 Answers

So, you do need a loop in order to set this fact, otherwise, you are trying to access a installation_floor on a list, which cannot be.

You will also face an issue with the id of your items in switch_stacks, as 01 is an int and will end up displayed as 1, simply. So you either need to declare those as string, or to pad them with a format filter.

So, you end up with this task:

- set_fact:
    full_device_name: >-
      {{ 
        full_device_name 
        | default([]) 
        + [
          device_name | lower ~ '-' ~ 
          site_abbrev | lower ~ '-' ~ 
          item.installation_floor  ~  
          "%02d" | format(item.id) ~ '.txt'
        ]
      }}
  loop: "{{ switch_stacks }}"
  when: device_type == 'switch'

Which will create a list:

full_device_name:
  - swi-lon-101.txt
  - swi-lon-202.txt

Given the playbook:

- hosts: localhost
  gather_facts: false

  tasks:
    - set_fact:
        full_device_name: >-
          {{ 
            full_device_name 
            | default([]) 
            + [
              device_name | lower ~ '-' ~ 
              site_abbrev | lower ~ '-' ~ 
              item.installation_floor  ~  
              "%02d" | format(item.id) ~ '.txt'
            ]
          }}
      loop: "{{ switch_stacks }}"
      when: device_type == 'switch'
      vars:
        device_name: swi
        site_abbrev: lon
        device_type: switch
        switch_stacks:
          - id: 01
            installation_floor: 1
          - id: 02
            installation_floor: 2

    - debug: 
        var: full_device_name

This yields:

TASK [set_fact] ************************************************************
ok: [localhost] => (item={'id': 1, 'installation_floor': 1})
ok: [localhost] => (item={'id': 2, 'installation_floor': 2})

TASK [debug] ***************************************************************
ok: [localhost] => 
  full_device_name:
  - swi-lon-101.txt
  - swi-lon-202.txt

Let's keep the attribute id as a string

switch_stacks:
  - id: '01'
    installation_floor: 1
  - id: '02'
    installation_floor: 2

The task

    - set_fact:
        full_device_name: "{{ [prefix]|product(sw_fl_id)|map('join')|
                                       product([postfix])|map('join')|list }}"
      vars:
        prefix: "{{ device_name }}-{{ site_abbrev }}-"
        postfix: ".txt"
        sw_id: "{{ switch_stacks|map(attribute='id')|list }}"
        sw_fl: "{{ switch_stacks|map(attribute='installation_floor')|list }}"
        sw_fl_id: "{{ sw_fl|zip(sw_id)|map('join')|list }}"

gives you the list

full_device_name:
  - swi-lon-101.txt
  - swi-lon-202.txt

@β.εηοιτ.βε

Thanks for your reply, it worked.

I have however slightly tweeked it as you will see below. Once again thanks for showing the way

    name: Set Destination Directory & Filename for Switch Configurations
  set_fact:
    dest_dir: /home/usr/complete_config/{{ project_name }}
    full_device_name: >-
      {{
          device_name | lower ~ '-' ~
          site_abbrev | lower ~ '-' ~
          item.installation_floor  ~
          "%02d" | format(item.id)

      }}
  loop: "{{ switch_stacks }}"
  when: device_type == 'switch'
Related