I need to generate a single file on the remote host using multiple template files and Jinja's {% block block_name %} in my Ansible role
For example,
main.conf.j2:
value1 = 123
value2 = 456
{% block test %} {% endblock %}
value3 = 789
{% block example %} {% endblock %}
value4 = abcd
test.conf.j2:
{% block test %}
more text here
{% endblock %}
example.conf.j2
{% block example %}
....
example_param = 'example!'
....
{% endblock %}
What's the next step? I must use {% extends 'nginx.conf.j2' %} in test.conf.j2 and example.conf.j2? And if so - how will look my Ansible task? Or even something else?
If I trying something like this:
- name: Copy config
template:
src: "{{ item }}"
dest: "{{ conf_file_path }}"
with_items:
- "main.conf.j2"
- "test.conf.j2"
- "example.conf.j2"
- "abcd.conf.j2"
it works only for main.conf.j2 and test.conf.j2, but ignores example.conf.j2 and other templates
Q: "What's the next step? I must use {% extends 'nginx.conf.j2' %} ... ?"
A: Yes. extends is needed. For example
- template:
src: test.j2
dest: test
with the templates
shell> cat main.j2
value1 = 123
{% block test %}
value = default value in main.j2
{% endblock %}
value3 = 789
shell> cat test.j2
{% extends 'main.j2' %}
{% block test %}
value = custom value in test.j2
{% endblock %}
gives
shell> cat test
value1 = 123
value = custom value in test.j2
value3 = 789
Q: "How will look my Ansible task?"
- name: Copy config
template:
src: "{{ item }}"
dest: "{{ conf_file_path }}"
with_items:
- "main.conf.j2"
- "test.conf.j2"
- "example.conf.j2"
- "abcd.conf.j2"
A: The loop will repeatedly overwrite the dest file in each iteration. See template.
FWIW. It's possible to use blockinfile and loop the lookup of the templates. For example
- template:
src: main2.j2
dest: test
- blockinfile:
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ item }}"
path: test
block: "{{ lookup('template', item) }}"
loop:
- test.conf.j2
- example.conf.j2
with the templates
shell> cat main2.j2
value1 = 123
# BEGIN ANSIBLE MANAGED BLOCK test.conf.j2
value_test = default value in main2.j2
# END ANSIBLE MANAGED BLOCK test.conf.j2
# BEGIN ANSIBLE MANAGED BLOCK example.conf.j2
value_example = default value in main2.j2
# END ANSIBLE MANAGED BLOCK example.conf.j2
value3 = 789
shell> cat test.conf.j2
value_test = custom value in test.conf.j2
shell> cat example.conf.j2
value_example = custom value in example.conf.j2
give
shell> cat test
value1 = 123
# BEGIN ANSIBLE MANAGED BLOCK test.conf.j2
value_test = custom value in test.conf.j2
# END ANSIBLE MANAGED BLOCK test.conf.j2
# BEGIN ANSIBLE MANAGED BLOCK example.conf.j2
value_example = custom value in example.conf.j2
# END ANSIBLE MANAGED BLOCK example.conf.j2
value3 = 789
Related
For lack of a better title, I have this dummy variable, this task and this j2 template:
# variable
my_list:
- filename: something.conf
content: hello
- filename: else.conf
content: asdf
# conf.j2
{% for item in my_list %}
{{ item.content }}
{% endfor %}
# task
- name: set config
template:
src: conf.j2
dest: "/tmp/{{ item.filename }}"
loop: "{{ my_list }}"
This results in having 2 files on /tmp, something.conf and else.conf, having the same content.
Q: How can I configure Ansible so that per filename, it only 'pastes/templates' the content for that filename. Note that the items in my_list will grow.
Expected output on target vm:
# cat /tmp/something.conf
hello
# cat /tmp/else.conf
asdf
Do I have to create an if clause in the template, or edit the loop in a certain way?
Below is the main yml file and further it's pointing to disks_config.yml and volumegroup.j2. when i'm playing this playbook, it's skipping below task which is "Print unique disks and volume group details".
Any leads?
name: Load the disk configuration settings
ansible.builtin.include_vars: disks_config.yml
- name:"Print unique disks and volume group details"
ansible.builtin.debug:
var:
- disktypes
- volume_groups
verbosity: 2
# CREATE VOLUME GROUPS BASED ON parameters.yaml
# -------------------------------------+---------------------------------------8
#
- name: "Volume Group creation"
ansible.builtin.lvg:
vg: "{{ ll }}"
pvs: "{{ item.pvs }}"
pesize: 4M
state: present
loop: "{{ volume_groups }}"
register: vgscreated
when:
- tier == "sapos"
- name: "Filter the vg name from vgscreated results"
ansible.builtin.set_fact:
vgcreatedlist: "{{ vgscreated | json_query('results[*].item.vg') }}"
# Debug for testing
- name: "Print vgcreated details"
ansible.builtin.debug:
var:
- vgcreatedlist
- logical_volumes
- vgscreated.results
verbosity: 2
# Debug testing end of line
======================================================================
disk_config.yml:-
disktypes: >-
{{ disks | selectattr('host', 'defined') |
selectattr('host', 'equalto', inventory_hostname) |
map(attribute='type') | sort | unique |
list }}
# Dynamically determine list of volume groups associated with
# the current node.
volume_groups: "{{ lookup ('template', 'volume_groups.j2') }}"
====================================================================
volume_groups.j2:-
{% set vgs = [] %}
{#
Outer Loop: Loop over disk types
#}
{% for disktype in disktypes %}
{% set pvlist = [] %}
{#
Initialise VG dictionary with VG name derived from disk type
#}
{% set vg = {'vg': 'vg_' ~ (disk_type_to_name_map[disktype] |
default(disktype))} %}
{#
Inner Loop: Loop over list of disks that match the execution host and the disk type
Note: '_' used as dummy variable that can be ignored
#}
{% for disk in disks if (disk.host == inventory_hostname) and (disk.type == disktype) %}
{% set _ = pvlist.append('/dev/disk/azure/scsi1/lun' ~ disk.LUN) %}
{% endfor %}
{#
Add list of pvs and diskcount to VG dictionary
Note: '_' used as dummy variable that can be ignored
#}
{% set _ = vg.update({'pvs': pvlist}) %}
{#
Append VG dictionary to list of VGs
Note: '_' used as dummy variable that can be ignored
#}
{% set _ = vgs.append(vg) %}
{% endfor %}
{#
Output List of Dictionaries
#}
{{ vgs }}
#when i'm running the main.yml file it's skipping the above tasks:-
#ASK [**roles-os/1.5-disk-setup** : Print unique disks and volume group details] #********************************************************************************
#Monday 04 October 2021 07:04:14 +0000 (0:00:00.085) 0:00:25.544 ********
#skipping: [vmsxxxxxxx1n1]
#skipping: [vmsxxxxxxx-n0]
#skipping: [vmsxxxxxxx00]
#skipping: [vmsxxxxxxx01]
#skipping: [vmsxxxxxxx02]
Below is the main yml file and further it's pointing to disks_config.yml and volumegroup.j2. when i'm playing this playbook, it's skipping below task which is "Print unique disks and volume group details".
I need to read a csv file with diferent IPs and make a dictionary with a jinja2 filter for modificate the IP depending the IPNumber value. The yml file is like:
- read_csv:
path: vms.csv
key: Number
fieldnames: Name,IP1,IP2,IP3,IP4,IPNumber
delimiter: ';'
register: vms
- name: vms to dict
debug:
msg:
- {{'Name':{{ item.value.Name }},
{% if item.value.IPNumber == "1" %}
'IP':{{ item.value.IP1 }},
{% endif %}
{% if item.value.IPNumber == "2"%}
'IP':{{ item.value.IP2 }},
{% endif %}
{% if item.value.IPNumber == "3"%}
'IP':{{ item.value.IP3 }},
{% endif %}
{% if item.value.IPNumber == "4"%}
'IP':{{ item.value.IP4 }},
{% endif %}}}
loop: "{{ vms.dict | dict2items }}"
register: vms2
But I get the error:
The error appears to be in '/etc/ansible/roles/vms.yml': line 17, column 16, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
'Name':{{ item.value.Name}},
{% if item.value.IPNumber == "1" %}
^ here
I know is a syntax problem but I dont guess where the problem is.
I need some help.
The following task should create your dictionary as per your requirement inside a var you can reuse elsewhere. Rename my_ip_dict to whatever suits your project better.
- name: Create my IP dictionary
set_fact:
my_ip_dict: >-
{{
my_ip_dict | default({})
| combine({item.value.Name: item.value['IP' + item.value.IPNumber]})
}}
loop: "{{ vms.dict | dict2items }}"
- name: Check the result:
debug:
var: my_ip_dict
Note that I have dropped all the if/else structures by calling directly the correct field depending on IPNumber. I took for granted it always has a value in the valid scope or the other existing IP* fields. If this is not the case, you can always default that value e.g. item.value['IP' + item.value.IPNumber] | default('N/A')
You should put only variables/expressions within {{ or {%. To me 'Name' looks like normal text and should be outside.
Example:
# Notice the quotes `"` symbol at the beginning and end of debug message
- debug:
msg:
- "Name: {{ item.value.Name }},
{% if item.value.IPNumber == "1" %}
IP: {{ item.value.IP1 }}
# and so on...
{% endif %}"
This at least should address the error message.
Let's say I've got a role org.foo with the following files:
defaults/main.yml:
service_conf:
includes:
- "thing.conf"
main:
host: "foo.example.com"
port: 1234
# plus a whole bunch more
templates/service.conf.j2:
{% for value in service_conf.includes %}
include '{{ value }}'
{% endfor %}
{% for key,value in service_conf.main.iteritems() %}
{{ key }} = '{{ value }}'
{% endfor %}
and at some point in a task I'd like to:
Append an item to service_conf.includes
Add or modify an entry in service_conf.main
How would I do this?
I've tried set_fact like below, but it ends up replacing the default values rather than modifying them.
- name: adjust config
set_fact:
service_conf:
main:
host: 'app.domain.com'
port: '5678'
tls: 'yes'
when: condition = 'happened'
i´m trying to rename ethernet-interfaces on Linux. Each interface with the name enp0s* have to be eth*. First, i create udev-rules for renaming. that works fine. Secondly i have to create new configuration-files for each interface ( ' etc/sysconfig/network-scripts/ifcfg-eth* ' ). I don´t know, how to create the loop to place parameters in each interface. Can somneone help me?
Thats an extract of my playbook:
- name: Get new interface-names of the new created udev-rules
become: yes
shell: cat /etc/udev/rules.d/70-persisten-ipoib.rules.cfg | egrep -i 'eth.'
register: car
- name: Create new-ifcfg-eth* files
template:
with_items: "{{ car.stdout_lines }}"
register: cat
template: src= roles/configure_network/templates/create_ifcfg.cfg.j2 dest=/etc/sysconfig/network-scripts/ifcfg-{{ item }}
# Template: roles/configure_network/templates/create_ifcfg.cfg.j2
{% for interface in cat.results %}
NAME="eth{{ item.name }}
TYPE=Ethernet
BOOTPROTO={{item.bootproto|default('dhcp')}}
IPV4_FAILURE_FATAL=no
IPV6INIT=no
{% if item.ipaddress is defined %}
IPADDR={{item.ipaddress}}
{% endif %}
{% if item.netmask is defined %}
NETMASK={{item.netmask}}
{% endif %}
{% if item.gateway is defined %}
GATEWAY={{item.gateway}}
{% endif %}
PEERDNS=no
{% if item.dns is defined %}
DNS1={{item.dns}}
{% endif %}
ONBOOT={{item.onboot|default('yes')}}
{% endfor %}
Just fix your syntax:
- name: Create new-ifcfg-eth* files
template:
src: create_ifcfg.cfg.j2
dest: /etc/sysconfig/network-scripts/ifcfg-{{ item }}
with_items: "{{ car.stdout_lines }}"
register: cat
Remove double template call, use relative path (no need to define full path to role's own templates), use YAML syntax instead of key=value (you had spaces in them, which are not allowed).