Ansible append multiple templates to a single file - linux

How can I append multiple templates to a single file depending on a variable.
I have 3 variables vara, varb, varc. And their true/false determine whether I can use template1, template2, template3.
Their can be multiple permutations of these three files.
So what should be best strategy to append these templates in a single file.
Eg: if vara is true, varb is false, varc is true.
Then output file should contain template1 and template3.

If you are putting all of this in a single output file, you should only use one template, but put your if elsif logic in the template itself.
{% if vara == true %}
text1
{% elif varb == true %}
text2
{% elif varc == true %}
text3
{% endif %}

Q: "Eg: if vara is true, varb is false, varc is true. Then output file should contain template1 and template3."
A: It's possible to use lookup and template plugin. For example
shell> cat test.j2
{% if vara %}{{ lookup('template', 'template1.j2') }}{% endif %}
{% if varb %}{{ lookup('template', 'template2.j2') }}{% endif %}
{% if varc %}{{ lookup('template', 'template3.j2') }}{% endif %}
shell> cat template1.j2
vara: {{ vara }}
shell> cat template2.j2
varb: {{ varb }}
shell> cat template3.j2
varc: {{ varc }}
- hosts: localhost
vars:
vara: true
varb: false
varc: true
tasks:
- template:
src: test.j2
dest: test.txt
give
shell> cat test.txt
vara: True
varc: True

This would cause each template to be copied if its corresponding variable is true without the need of extra Jinja2 templating
- name: copy template
template:
src: "{{ item.name }}"
dest: /my/dest/dir/
when: item.state ## this is a simplified version of "when item.state == True"
loop:
- vara_template: "{ name: 'template1', state: vara }"
- varb_template: "{ name: 'template2', state: varb }"
- varc_template: "{ name: 'template3', state: varc }"

Related

Ansible to print ps on new row of csv file

Newbie ansible user here.
I'm trying to print output of ps into csv file but somehow its printing on next column rather than next row.
This is my playbook xml:
- name: Write running process into a csv file
hosts: servers
gather_facts: yes
vars:
output_path: "./reports/"
filename: "process_{{ date }}.csv"
tasks:
- name: CSV - Generate output filename
set_fact: date="{{lookup('pipe','date +%Y%m%d%H%M%S')}}"
run_once: true
- name: CSV - Create file and set the header
lineinfile:
dest: "{{ output_path }}/{{ filename }}"
line:
PID,Started,CPU,Memory,User,Process
create: yes
state: present
delegate_to: localhost
- name: CSV - Get ps cpu
ansible.builtin.shell:
ps -e -o %p, -o lstart -o ,%C, -o %mem -o user, -o %c --no-header
register: ps
- name: CSV - Write into csv file
lineinfile:
insertafter: EOF
dest: "{{ output_path }}/{{ filename }}"
line: "{inventory_hostname}},{{ps.stdout_lines}}"
loop: "{{ ps.stdout_lines }}"
delegate_to: localhost
- name: CSV - Blank lines removal
lineinfile:
path: "./{{ output_path }}/{{ filename }}"
state: absent
regex: '^\s*$'
delegate_to: localhost
current output is like
Example of desired output :
... but somehow its printing on next column rather than next row ... current output is like ...
This is because within your task "CSV - Write into CSV file" you are printing out all stdout_lines for each iteration steps instead of one, the line for the iteration step only.
To print out line by line one could use an approach like
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: CSV - Get ps cpu
shell:
cmd: ps -e -o %p, -o lstart -o ,%C, -o %mem -o user, -o %c --no-header
register: ps
- name: Show 'stdout_lines' one line per iteration for CSV
debug:
msg: "{{ inventory_hostname }}, {{ ps.stdout_lines[item | int] }}"
loop: "{{ range(0, ps.stdout_lines | length) | list }}"

Using Split and join together in Ansible template file

I want to use hostnames of a group in ansible inventory in a template bash script file.
This is my inventory:
[group_srv]
srv1.co.company
srv2.co.company
srv3.co.company
This is that I wrote in script.sh.j2
for i in '{{ groups['group_srv'][0:3].split('.')[0] | join(' ') }}' ; do
echo $i
done
This is my desired output, but I cannot use split() and join() together.
for i in 'srv1' 'srv2' 'srv3'; do
echo $i
done
Any help is appreciated.
Ansible provides a magic variable containing the short name for each server in your inventory (i.e. the first string before the first dot): inventory_hostname_short
You can access all servers vars in an other magic var: hostvars
You can use the map filter to:
extract values for a list of keys inside a dict (e.g. the relevant hosts for your group in hostvars)
select a single key in a list of dictionaries (e.g. the short inventory name in each extracted server vars).
Those two operations can actually be done at once at extraction time.
Putting it all together:
for i in '{{ groups['group_srv'] | map('extract', hostvars) | map(attribute='inventory_hostname_short') | join("' '") }}' ; do
echo $i
done
Which can be shortened as
for i in '{{ groups['group_srv'] | map('extract', hostvars, 'inventory_hostname_short') | join("' '") }}' ; do
echo $i
done
The declaration below does the job
short_0_3: "{{ groups.group_srv[0:3]|
map('split', '.')|
map('first')|
join(' ') }}"
The next option is the extraction of the variable inventory_hostname_short
short_0_3: "{{ groups.group_srv[0:3]|
map('extract', hostvars, 'inventory_hostname_short')|
join(' ') }}"
Both options give
short_0_3: srv1 srv2 srv3
Example of a complete playbook for testing
- hosts: all
gather_facts: false
vars:
short_0_3: "{{ groups.group_srv[0:3]|
map('split', '.')|
map('first')|
join(' ') }}"
tasks:
- block:
- shell: "for i in {{ short_0_3 }}; do echo $i; done"
register: out
- debug:
var: out.stdout_lines
delegate_to: localhost
run_once: true
shell> cat hosts
[group_srv]
srv1.co.company
srv2.co.company
srv3.co.company
srv4.co.company
srv5.co.company
In Jinja2 loop you can use filters.
{% if not loop.last %} {% endif %} adds a white space after every element except the last one.
for i in {% for host in groups['group_srv'] %}'{{ host.split('.')[0] }}'{% if not loop.last %} {% endif %}{% endfor %} ; do
echo $i
done
Result:
for i in 'test-001' 'test-002' ; do
echo $i
done

Is there a way to use if..else logic in conditional filters in ansible?

I want to hard code 2 different values based on variable stdout in a single play.If a service is running then i want to hard code value as good else bad.How to use this logic in ansible?
I can able to hard code one value based on status result.
---
- hosts: localhost
connection: local
gather_facts: false
vars:
tasks:
- name: load var from file
include_vars:
file: /tmp/var.json
name: imported_var
- name: Checking mysqld status
shell: service mysqld status
register: mysqld_stat
- name: Checking mysqld status
shell: service httpd status
register: httpd_stat
- name: append more key/values
set_fact:
imported_var: "{{ imported_var| default([]) | combine({ 'mysqld_status': 'good' })}}"
when: mysqld_stat.rc == 0
- name: append more key/values
set_fact:
imported_var: "{{ imported_var| default([]) | combine({ 'httpd_status': 'good' })}}"
when: httpd_stat.rc == 0
- name: write var to file
copy:
content: "{{ imported_var | to_nice_json }}"
dest: /tmp/final.json
I want to hard code mysqld_status : Good if mysqld_stat.rc == 0 or mysqld-status: Bad if mysqld_stat.rc != 0.Is it possible to achieve in single play.(i.e) in single command
There are many of ways of approaching this problem. You could just add a second set_fact that runs when mysqld_stat.rc != 0. In the following example, only one of the two set_fact tasks will run:
- name: append more key/values
set_fact:
imported_var: "{{ imported_var| default({}) | combine({ 'mysqld_status': 'good' })}}"
when: mysqld_stat.rc == 0
- name: append more key/values
set_fact:
imported_var: "{{ imported_var| default({}) | combine({ 'mysqld_status': 'bad' })}}"
when: mysqld_stat.rc != 0
You could instead use Ansible's ternary filter:
- name: append more key/values
set_fact:
imported_var: "{{ imported_var| default({}) | combine({ 'mysqld_status': (mysqld_stat.rc == 0)|ternary('good', 'bad') })}}"

Saltstack passing character Escaping backslash and doublequotes for cronjobs Variables

I am writing a sls template in saltstack and tried to correctly write a cronjob entry but i am not able to do so cause compiler always cries about syntax problem cause it seems the escaping is not working as i thought.
i need the escaping backslashes inside the cron command too cause otherwise cronjob does not use the format charackters correctly.
this is the code in my template
{{instance}} cron-entry-for-backup-daily:
cron.present:
- user: root
- name: "{{ online_basedir }}/online_tools/db-tools.py -b {{ settings.port }} -s {{ settings.online_master }} -x {{ instance + '.domain.name' }} backup -d {{ instance }} -f {{ '/opt/onlinebackupdir/' + instance + '/' + instance + '-odoo_db-' + '`date' + '\"' + '+\%Y_\%m_\%d-\%H_\%M' + '\"' + '`' + '_daily.zip'}}"
- minute: '*'
- hour: '22'
- daymonth: '*'
- month: '*'
- dayweek: '1-6'
- identifier: {{ instance + '_dailybackup' }}
- comment: 'Installed via Salt'
the cronjob entry should look like this
* 22 * * 1-6 /opt/online/online_tools/db-tools.py -b 63000 -s xyz123 -x sub.domain.name backup -d dbname -f /opt/onlinebackupdir/dbname/dbname-odoo_db-`date "+\%Y_\%m_\%d-\%H_\%M"`_daily.zip
If i execute this on command line it work if i execute it via cron it works ....
Error message:
i cannot compile my code from the sls file expected , but
found '' in "", line 124, column 154:
... ckupdir/dbname/dbname-odoo_db-date"+\%Y_\%m_\%d-\%H_\%M"_daily.zip"
i already tried some ways but i think i am unable to solve it at the moment to much brain dead already :-( maybe someone knows a solution i also checked the jinja docs for escaping aso ... but i am helpless at the moment
tries:
'`date +\%Y_\%m_\%d-\%H_\%M`' --> error
'`date "+\%Y_\%m_\%d-\%H_\%M"`' --> error
'`date \"+\%Y_\%m_\%d-\%H_\%M\"`' --> error
'`date '+'\%Y_\%m_\%d-\%H_\%M`' --> error
'`date '+\%Y_\%m_\%d-\%H_\%M'`' --> error
'`date \"+"\%Y_\%m_\%d-\%H_\%M"\"'`' --> error
'`date' + '"' + '+\%Y_\%m_\%d-\%H_\%M' + '"' + '`' --> error
'`date' + '"' + '+\%Y_\%m_\%d-\%H_\%M' + '"' + '`' --> error
'`date +%Y_%m_%d-%H%M`' --> compiled and worked in command line but not inside crontab % needs to be escaped
in the meantime i tried this too
"{{ online_basedir }}/online_tools/db-tools.py -b {{ settings.port }} -s {{ settings.online_master_pw }} -x {{ instance + '.domainname.net' }} backup -d {{ instance }} -f {{ '/opt/onlinebackupdir/' + instance + '/' + instance + '-odoo_db-' }} + {% raw %} `date "+\%Y_\%m_\%d-\%H_\%M"` {% endraw %} + {{'_daily.zip'}}"
"{{ online_basedir }}/online_tools/db-tools.py -b {{ settings.port }} -s {{ settings.online_master_pw }} -x {{ instance + '.domainname.net' }} backup -d {{ instance }} -f {{ '/opt/onlinebackupdir/' + instance + '/' + instance + '-odoo_db-' + {% raw %} `date "+\%Y_\%m_\%d-\%H_\%M"` {% endraw %} + '_daily.zip'}}"
This does not work either
OK i solved the Problem with this solution, maybe this is not the best way but it solves my problem....
If someone reads this i would be interested in why {% raw %} .... {% endraw % }
does not work
SOLUTION:
"{{ online_basedir }}/online_tools/db-tools.py -b {{ settings.port }} -s {{ settings.online_master_pw }} -x {{ instance + '.domainname.net' }} backup -d {{ instance }} -f {{ '/opt/onlinebackupdir/' + instance + '/' + instance + '-odoo_db-' + 'date \\\"+\\\\%Y_\\\\%m_\\\\%d-\\\\%H_\\\\%M\\\"' + '_weekly.zip'}}"
now the line in the crontab looks exactly like i need it:
.......`date "+\%Y_\%m_\%d-\%H_\%M"`..... .zip
RESULT in my BACKUP Folder: instance-odoo_db-2016_10_19-22_00_daily.zip

Run an Ansible task only when the variable contains a specific string

I have multiple tasks depend from the value of variable1. I want to check if the value is in {{ variable1 }} but I get an error:
- name: do something when the value in variable1
command: <command>
when: "'value' in {{ variable1 }}"
I'm using ansible 2.0.2
If variable1 is a string, and you are searching for a substring in it, this should work:
when: '"value" in variable1'
if variable1 is an array or dict instead, in will search for the exact string as one of its items.
None of the above answers worked for me in ansible 2.3.0.0, but the following does:
when: variable1 | search("value")
In ansible 2.9 this is deprecated in favor of using ~ concatenation for variable replacement:
when: "variable1.find('v=' ~ value) == -1"
http://jinja.pocoo.org/docs/dev/templates/#other-operators
Other options:
when: "inventory_hostname in groups[sync_source]"
From Ansible 2.5
when: variable1 is search("value")
For negative scenarios
when: not variable1 is search("value")
Some of the answers no longer work as explained.
Currently here is something that works for me in ansible 2.6.x
when: register_var.stdout is search('some_string')
This works for me in Ansible 2.9:
variable1 = www.example.com.
variable2 = www.example.org.
when: ".com" in variable1
and for not:
when: not ".com" in variable2
This example uses regex_search to perform a substring search.
- name: make conditional variable
command: "file -s /dev/xvdf"
register: fsm_out
- name: makefs
command: touch "/tmp/condition_satisfied"
when: fsm_out.stdout | regex_search(' data')
ansible version: 2.4.3.0
In Ansible version 2.9.2:
If your variable variable1 is declared:
when: "'value' in variable1"
If you registered variable1 then:
when: "'value' in variable1.stdout"
use this
when: "{{ 'value' in variable1}}"
instead of
when: "'value' in {{variable1}}"
Also for string comparison you can use
when: "{{ variable1 == 'value' }}"
I used
failed_when: not(promtool_version.stdout.find('1.5.2') != -1)
means - failed only when the previously registered variable "promtool_version" doesn't contains string '1.5.2'.

Resources