Add lines to a local file from a Jinja2 Template with Ansible - ansible

im a newbie to ansible and got problems with this Task.
I want to get some data from various hosts. The Idea is, to use a jinja2 template, get the data from the host and add this data in to a file local on the Ansible machine.
How is it possible to get all the data in one file on the local machine? The way i try to do it brings me the result from just one host. Thanks for your Help!
---
- name: "Server Report"
hosts: all
tasks:
- name: "check packages"
package_facts:
manager: auto
- name: "get PHP info"
shell: "php -v | grep -E ^PHP | awk '{print $2}'"
register: php_version
when: "'php-common' in ansible_facts.packages"
- name: "get MySQL info"
shell: "mysql -V | awk '{print $5}' | sed 's/,//g'"
register: mysql_version
when: "'mysql-common' in ansible_facts.packages"
- name: "Use Template to create File"
template:
src: vrsn.j2
dest: /opt/data.txt
delegate_to: localhost
This is the Jinjer2 Code:
{% if ansible_facts['hostname'] is defined %}{{ ansible_facts['hostname'] }},{% else %}NoINstalled,{% endif %}
{% if ansible_facts['distribution'] is defined %}{{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }},{% else %}NotInstalled,{% endif %}
{% if php_version.stdout is defined %}{{ php_version.stdout }},{% else %}NotInstalled,{% endif %}
{% if mysql_version.stdout is defined %}{{ mysql_version.stdout }},{% else %}NotInstalled,{% endif %}
{% if ansible_facts.packages['apache2'][0].version is defined %}
apache2-version {{ ansible_facts.packages['apache2'][0].version }},
{% elif ansible_facts.packages['apache'][0].version is defined %}
apache-version {{ ansible_facts.packages['apache'][0].version }},
{% elif ansible_facts.packages['nginx-common'][0].version is defined %}
nginx-version {{ ansible_facts.packages['nginx-common'][0].version }},
{% else %}NotInstalled{% endif %}

I'm not sure I fully understand your output format, but the following example should give you a clue to go on:
vrsn.j2
{% for h in groups['all'] %}
Inventory host: {{ h }}
hostname: {{ hostvars[h].ansible_hostname | default('N/A') }}
distribution: {{ hostvars[h].ansible_distribution | default('N/A') }}
php: {{ hostvars[h].php_version.stdout | default('N/A') }}
# add more here now you got the concept
---
{% endfor %}
And you should call your template like so in your play:
- name: "Dump all host info to local machine"
template:
src: vrsn.j2
dest: /opt/data.txt
delegate_to: localhost
run_once: true

Related

Ansible: how to get the calculated value of key "content" displayed into the playbook output

I am a beginner to ansible.
How can I get the content of the csv file printed in my shell?
I tryed to register the calculated value of content key and to display it via
- ansible.builtin.debug:
msg: "{{ csv_content }}"
in another task, but I cannot see it into my playbook output.
vars:
current_date: "{{ '%Y-%m-%d' | strftime }}"
tasks:
- name: Dump results to /tmp/myfile.csv
copy:
dest: /tmp/mycsv_{{ '%Y-%m-%d' | strftime }}.csv
content: |
{% for host in hosts_list %}
{% ---things--- %}
{% set idm=host.inventory_hostname.split('_')[0].split('-')[1] %}
{% set idm_padded = '%03d' % idm|int %}
{% ---things--- %}
{{ [idm_padded, --things-- ] | map('trim') | join(';') }}
{% --things--- %}
{% endfor %}
vars:
hosts_list: "{{ ansible_play_hosts | map('extract', hostvars) | list }}"
register: csv_content
run_once: yes
- ansible.builtin.debug:
msg: "{{ csv_content }}"
Rather than trying to get the content from the copy task, reverse your logic: render the content into a variable first, then use that variable as the argument to the content key in the copy task:
- hosts: localhost
gather_facts: false
tasks:
- set_fact:
csv_content: |
{% for host in hosts_list %}
{% set idm=2 %}
{% set idm_padded = '%03d' % idm|int %}
{{ [idm_padded, "things" ] | map('trim') | join(';') }}
{% endfor %}
vars:
hosts_list:
- foo
- bar
- baz
run_once: true
- debug:
msg: "{{ csv_content }}"
- name: Dump results to /tmp/myfile.csv
copy:
dest: mycsv_{{ '%Y-%m-%d' | strftime }}.csv
content: "{{ csv_content }}"
run_once: true

ansible match exact string from register result

I have below ansible tasks which compares list of packages against register output obtained from yum module.
- hosts: localhost
vars:
pkgs: [ 'git', 'rsync', 'python36' ]
tasks:
- name: stats for installed pkgs
yum:
state: installed
register: installed
- name: Compare against register
set_fact:
unwanted_pkgs: >-
[
{% for value in installed.results %}
{% for pkg in pkgs %}
{% if value.name | regex_search(pkg) %}
" {{ value.name }}",
{% endif %}
{% endfor %}
{% endfor %}
]
- name: print
debug:
msg: "{{ unwanted_pkgs }}"
Upon executing above playbook, debug task print output as :
git
git-core
git-core-doc
python36
python36-devel
While executing the task it prints additional packages (eg: git-core, git-core-doc and python36-devel which I have not defined in pkgs variable). How this can be avoided ?
Is this what you want?
- debug:
msg: |-
{% for value in installed.results %}
{% for pkg in pkgs %}
{% if value.name == pkg %}
{{ value.name }}
{% endif %}
{% endfor %}
{% endfor %}
gives
msg: |-
git
python36
The same can be achieved by intersecting the lists
unwanted_pkgs: "{{ installed.results|map(attribute='name')|intersect(pkgs) }}"
gives
unwanted_pkgs:
- git
- python36

Problems with Jinja2 and ansible making a sub dict

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.

How to create dynamic files?

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).

Vague deprecation error when running ansible playbook

My playbook contains vars that are passed to a role. When I run it, I get [DEPRECATION WARNING]: Skipping task due to undefined Error, in the future this will be a fatal error..
Here's what I have:
---
- hosts: hadoopL0X
become: yes
become_method: sudo
vars:
logrotate_scripts:
- name: "{{ item }}"
with_items:
- zookeeper
- sa
path: "/var/log{{ item }}/{{ item }}.log "
options:
- daily
- rotate 3
- missingok
- compress
- notifempty
roles:
- log-rotation
...
The role is as such:
log-rotation/tasks/main.yml
---
- name: Setup logrotate.d scripts
template:
src: logrotate.d.j2
dest: "{{ logrotate_conf_dir }}{{ item }}"
with_items: "{{ logrotate_scripts }}"
...
log-rotation/defaults/main.yml
---
logrotate_conf_dir: "/etc/logrotate.d/"
logrotate_scripts: []
...
log-rotation/templates/logrotate.d.j2
# {{ ansible_managed }}
"{{ item.path }}" {
{% if item.options is defined -%}
{% for option in item.options -%}
{{ option }}
{% endfor -%}
{% endif %}
{%- if item.scripts is defined -%}
{%- for name, script in item.scripts.iteritems() -%}
{{ name }}
{{ script }}
endscript
{% endfor -%}
{% endif -%}
}
Any help would be much appreciated!
with_items can only be used with tasks, it cannot be used when defining variables, and because of that item isn't defined. It also looks like that the service variable isn't defined as well.

Resources