Using Split and join together in Ansible template file - python-3.x

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

Related

Ansible - Find files via a list with their name and delete them

im currently running into an issue with deleting certain files from our thumbor cache with ansible. After alot of snipping I receive a list with the file names and im running following ansible task to find and delete them:
shell: find . -name {{ item }} -exec rm "{}" \;
args:
chdir: "{{ thumbor_data_path }}"
ignore_errors: true
loop:
- deletion_hash
when: file_url is defined and deletion_hash | length > 0
the list is definitly filled with the correct names of files I know exist and the task itself marks himself as changed, but the files are not getting deleted. The names of the files are sha1 hashes, and are two directories deep.
Is there something wrong with the shell script?
Example of the deletion_hash list:
"msg": [
"115b744b9f6b23bbad3b6181c858cb953136",
"f52f17b2cca937e5586751ff2e938979890b",
"1c39661a0925b3cdb3b524983aaf6cccd6ee",
"1afc79a9e0e3c07ff0e95e1af3b5cb7ae54c",
"424e9159fe652f47c8e01d0aa85a86fbefed",
"11e4994789f24537d6feea085d2bf39c355b",
"a1d2fe0e122d37555df4062d4c0a5d10b651",
"aef976fc897a87091be5a8d5a11698e19591",
"e79f3ee1e6ccb3caff288b0028e031d75d77",
"9448e5e49679c908263922debdffff68eecb",
"a3933be52277a341906751c3da2dfb07ccd8",
"bef3370862a7504f7857be396d5a3139f5c0",
"8cc0cbe847234af96c0463d49c258c85d50f",
"1e7bf6110dcf994d1270682939e14416fc6e",
"d21dae2c047895129e7c462f6ddc4e512a58",
"c107b29b3185171ec46b479352fab6c97ad2"
]
You can try using the file module; this comes with an assumption that the thumbor_data_path variable does not end with a /; if it does, you need to modify this a bit.
- name: Remove file (delete file)
ansible.builtin.file:
path: "{{ thumbor_data_path }}/{{ item }}"
state: absent
loop: deletion_hash
when: file_url is defined and deletion_hash | length > 0

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 }}"

saltstack how to verify variable from jinja, result wrong from if statement

I was trying to write a state to set patching status, the if statement based on a result from a cmd.run, I checked the result that it should be 'No', meaning no available patches, but my state return said otherwise.
I checked:-
# salt 'minion1' cmd.run "yum --security check-update | grep 'for security'| awk '{print \$1}'"
minion1:
No
#
My state file:-
{% set minion = salt['cmd.run']("yum --security check-update | grep 'for security'| awk '{print \$1}'") %}
{% if minion|float == 'No' %}
no_patch:
cmd.run:
- name: echo "no update"
{% else %}
patch_pls:
cmd.run:
- name: echo "Patch please"
{% endif %}
my run result:-
minion1:
----------
ID: patch_pls
Function: cmd.run
Name: echo "Patch please"
Result: True
Comment: Command "echo "Patch please"" run
Started: 12:22:10.894779
Duration: 10.047 ms
Changes:
----------
pid:
19397
retcode:
0
stderr:
stdout:
Patch please
Summary for minion1
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 10.047 ms
Been spending hours trying to figure it out but just feeling defeated and frustrated right now.
You're my last hope. Thanks in advance.

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') })}}"

Ansible conditional based on stdout of result?

How do I use the when statement based on the standard output of register: result? If standard output exists I want somecommand to run if no standard output exists I want someothercommand to run.
- hosts: myhosts
tasks:
- name: echo hello
command: echo hello
register: result
- command: somecommand {{ result.stdout }}
when: result|success
- command: someothercommand
when: result|failed
Try checking to see it if equals a blank string or not?
- hosts: myhosts
tasks:
- name: echo hello
command: echo hello
register: result
- command: somecommand {{ result.stdout }}
when: result.stdout != ""
- command: someothercommand
when: result.stdout == ""
As of 2018, the recommended way to test if output is empty is just:
when: result.stdout | length > 0
That is the pythonic way of evaluating truth, null, empty strings, empty lists all evaluate as false.
Other older alternatives not recommended or even not working:
result.stdout != "" would not pass ansible-lint check!
result.stdout | bool will NOT work as most strings will evaluate as False, only cases where it would return true is if stdout happens to be one of the true, yes,... kind of strings.
result.stdout used to work but now triggers:
[DEPRECATION WARNING]: evaluating as a bare variable, this
behaviour will go away and you might need to add |bool to the
expression in the future. Also see CONDITIONAL_BARE_VARS configuration
toggle.. This feature will be removed in version 2.12. Deprecation
warnings can be disabled by setting deprecation_warnings=False in
ansible.cfg.`
To expand on this answer and address the comment regarding potential problems if stdout isn't defined, the following when statement can be used to ensure stdout is defined before trying to check its length:
when: result.stdout is defined and result.stdout | length > 0

Resources