generate a host list from ansible ini file - ansible

It is not possible to read this file with the ini plugin.
$ cat hosts
[webservers]
www[01:50].example.com
The play
- hosts: localhost
tasks:
- debug:
msg: "{{ item }}"
with_ini:
- '.* section=webservers file=hosts re=True'
gives
ok: [localhost] => (item=11].example.com) => {
"msg": "11].example.com"
}
Is it possible to generate a host list like this?
[webservers]
www01.example.com
www02.example.com
www03.example.com
www04.example.com
www05.example.com
www06.example.com

Q: "Is it possible to generate a host list like ...?"
A: Yes. Use template. For example
$ cat hosts
[webservers]
www[01:50].example.com
$ cat play.yml
- hosts: localhost
vars:
my_group: webservers
tasks:
- template:
src: hosts-template.j2
dest: /etc/ansible/hosts-webservers
$ cat hosts-template.j2
[{{ my_group }}]
{% for my_host in groups[my_group] %}
{{ my_host }}
{% endfor %}
Notes
Ranges in the inventory will be expanded.
If your ansible hosts inventory file is in a non standard location (i.e. not in /etc/ansible/hosts), you will have to load it when launching your playbook: ansible-playbook -i /path/to/inventory/hosts play.yml

Related

Ansible How to store multiple "register" in one file with one playbook

Ansible store only the first output in a file
Example
I have 3 hosts inside the inventory
My playbook ask for memory info.
with
- name: Check memory
hosts: all
tasks:
- name: Check Memory
shell: free
register: memory_output
- name: save
lineinfile:
path: "mypc/test.log"
line: "--{{ memory_output.stdout }}% "
create: yes
delegate_to: localhost
output write in file sometimes all the hosts memory,sometimes only the first,sometimes only the last
How i append every result from every hosts in one file.
Sometimes it export all the results but not every time
For example, given the inventory
shell> cat hosts
test_11
test_12
test_13
declare the below variable and put it into the vars
vmstat: "{{ out.stdout|community.general.jc('vmstat') }}"
Get the free memory
- command: vmstat
register: out
- set_fact:
free_mem: "{{ vmstat.1.free_mem }}"
- debug:
var: free_mem
gives (abridged)
ok: [test_11] =>
free_mem: '3434124'
ok: [test_12] =>
free_mem: '3496908'
ok: [test_13] =>
free_mem: '3434992'
Q: "How to store multiple 'register' in one file with one playbook."
A: Write it to the log
- lineinfile:
create: true
path: /tmp/test.log
line: >-
{{ '%Y-%m-%d %H:%M:%S'|strftime() }}
{{ item }}
{{ hostvars[item].free_mem }}
loop: "{{ ansible_play_hosts }}"
delegate_to: localhost
run_once: true
gives
shell> cat /tmp/test.log
2022-09-12 13:39:48 test_11 3434124
2022-09-12 13:39:49 test_12 3496908
2022-09-12 13:39:49 test_13 3434992
Example of a complete playbook for testing
- hosts: test_11,test_12,test_13
vars:
vmstat: "{{ out.stdout|community.general.jc('vmstat') }}"
tasks:
- command: vmstat
register: out
- set_fact:
free_mem: "{{ vmstat.1.free_mem }}"
- debug:
var: free_mem
- lineinfile:
create: true
path: /tmp/test.log
line: >-
{{ '%Y-%m-%d %H:%M:%S'|strftime() }}
{{ item }}
{{ hostvars[item].free_mem }}
loop: "{{ ansible_play_hosts }}"
delegate_to: localhost
run_once: true
Here's the simplest example. Assuming that you are running ansible in controller machine and you have to append the output of executing tasks in remote machines. The host list will obviously be different for you and will have all the remote machines.
- hosts: localhost
tasks:
## Playbook to copy the file from controller machine to remote machine
- name: Copy the file from controller machine to remote machine
copy:
src: /tmp/tmpdir/output.txt
dest: /tmp/tmpdir/output.txt
## Playbook to store the shell output to a variable
- name: Store the output of the shell command to a variable
shell: "echo '\nHello World'"
register: output
- name: Print the output of the shell command
debug:
msg: "{{ output.stdout }}"
## Playbook to append output to a file
- name: Append output to a file
lineinfile:
path: /tmp/tmpdir/output.txt
line: "{{ output.stdout }}"
create: yes
## Playbook to copy the file from remote machine to controller machine
- name: Copy the file from remote machine to controller machine
fetch:
src: /tmp/tmpdir/output.txt
dest: /tmp/tmpdir/output.txt
flat: yes
After running it the third time
╰─ ansible-playbook test.yaml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *******************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************
ok: [localhost]
TASK [Copy the file from controller machine to remote machine] *********************************************************************************************
ok: [localhost]
TASK [Store the output of the shell command to a variable] *************************************************************************************************
changed: [localhost]
TASK [Print the output of the shell command] ***************************************************************************************************************
ok: [localhost] => {
"msg": "\nHello World"
}
TASK [Append output to a file] *****************************************************************************************************************************
changed: [localhost]
TASK [Copy the file from remote machine to controller machine] *********************************************************************************************
ok: [localhost]
PLAY RECAP *************************************************************************************************************************************************
localhost : ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
╰─ cat output.txt
Hello World
Hello World
Hello World
So on every machine you run the controller machine will get the latest file output. We will copy the file to remote, add contents to the file and then copy it back to controller. Continue the same until all the hosts have been completed.
If you want to take the result from selective servers then the last task can be replaced by following. Replace the hostnames with required values
## Playbook to copy the file from remote machine to controller machine if the hostname maches localhost1 or localhost2
- name: Copy the file from remote machine to controller machine if the hostname maches localhost1 or localhost2
fetch:
src: /tmp/tmpdir/output.txt
dest: /tmp/tmpdir/output.txt
flat: yes
fail_on_missing: yes
when: inventory_hostname == 'localhost1' or inventory_hostname == 'localhost2'

How to use a list of jinja templates store in a YML file with a Playbook?

I need help because i'm stuck since 2 days with a playbook.
First of all, i have a YAML file which contains jinja templates :
---
template lab:
- first_template.j2
- second_template.j2
- third_template.j2
It exists a YAML file which have value for each router, stores in "./yml/{{ inventory_hostname }}.yml"
I have a playbook Ansible which needs to use jinja templates for generate a .conf file.
---
- name: Generate .conf file
hosts: my_routers
gather_facts: no
vars:
- jinja_templates: "{{ (lookup('template', './template_list.yml') | from_yaml).template_lab }}"
vars_files:
- "./yml/{{ inventory_hostname }}.yml"
tasks:
- name: test
debug:
msg: "{{ jinja_templates }}"
- name: Generate configuration files
template:
src: "./templates/{{ jinja_templates }}"
dest: "./tmp/general/{{ inventory_hostname }}.conf"
mode: "0644"
OUTPUT of the playbook when i play it :
Could not find or access './templates/['first_template.j2', 'second_template.j2', 'third_template'.j2']'
1 : The first issue is that { jinja_templates }} output contains "[" "]" and "'" "'", so it is impossible for Ansible to use the Jinja Templates.
2 : How can i do an interation for use all jinja templates and generate configuration in a single file ?
I know that i don't use the correct structure of Ansible' Playbook ! It is for the moment experimental test :)
Thank you
Q: "Use all jinja templates and generate configuration in a single file."
A: Iterate the list of templates and create content. For example, given the tree
shell> tree .
.
├── ansible.cfg
├── hosts
├── pb.yml
├── template_list.yml
└── templates
├── first_template.j2
├── second_template.j2
└── third_template.j2
shell> cat hosts
[my_routers]
test_11
test_12
test_13
shell> cat template_list.yml
template_list:
- first_template.j2
- second_template.j2
- third_template.j2
shell> cat templates/first_template.j2
Content of the 1 template.
shell> cat templates/second_template.j2
Content of the 2 template.
shell> cat templates/third_template.j2
Content of the 3 template.
The playbook
shell> cat pb.yml
- hosts: my_routers
vars_files:
template_list.yml
tasks:
- copy:
dest: "/tmp/{{ inventory_hostname }}.conf"
content: |
{% for file in template_list %}
{{ lookup('template', file) }}
{%- endfor %}
will create the files
shell> ssh admin#test_11 cat /tmp/test_11.conf
Content of the 1 template.
Content of the 2 template.
Content of the 3 template.
shell> ssh admin#test_12 cat /tmp/test_12.conf
Content of the 1 template.
Content of the 2 template.
Content of the 3 template.
shell> ssh admin#test_13 cat /tmp/test_13.conf
Content of the 1 template.
Content of the 2 template.
Content of the 3 template.
You're passing the template files names as one line:
- debug:
msg: "{{ jinja_templates }}"
The output is being an array:
TASK [test] *******************************************************************************
ok: [localhost] => {
"msg": [
"first_template.j2",
"second_template.j2",
"third_template.j2"
]
}
You should loop over it to get them as single values:
- debug:
msg: "{{ item }}"
loop: "{{ jinja_templates }}"
TASK [test] *******************************************************************************
ok: [localhost] => (item=first_template.j2) => {
"msg": "first_template.j2"
}
ok: [localhost] => (item=second_template.j2) => {
"msg": "second_template.j2"
}
ok: [localhost] => (item=third_template.j2) => {
"msg": "third_template.j2"
}
Besides that you defined the same destination file in your template task dest: "./tmp/general/{{ inventory_hostname }}.conf".
That would lead to only writing the last template file.

Ansible lineinfile append

I have a hosts inventory file. That has
url.example.com hostnames='["hostname1.example.com", "hostname2.example.com"]'
Im trying to loop over it to add all the hostnames to /etc/hosts file
I am trying to use lineinfile, but how can I get it to append all of the hostnames to one specific line
line: "{{ ansible_default_ipv4.address }} {{ inventory_hostname }} {{ inventory_hostname_short}}" + append item here
with_items: "{{ hostnames }}"
state: present
How can i append all the items in the end of the line.
The iteration is not needed, I think. Is this what you're looking for?
line: "{{ ansible_default_ipv4.address }}
{{ inventory_hostname }}
{{ inventory_hostname_short}}
{{ hostnames|join(' ') }}"
For example, given the file
shell> cat hosts
10.1.0.27 localhost localhost
The playbook
shell> cat playbook.yml
- hosts: localhost
vars:
hostnames: [hostname1.example.com, hostname2.example.com]
tasks:
- lineinfile:
path: hosts
regex: '^{{ ansible_default_ipv4.address }}\s+(.*)$'
line: "{{ ansible_default_ipv4.address }}
{{ inventory_hostname }}
{{ inventory_hostname_short}}
{{ hostnames|join(' ') }}"
works as expected
shell> ansible-playbook playbook.yml -CD
TASK [lineinfile] ***************************************************************
--- before: hosts (content)
+++ after: hosts (content)
## -1 +1 ##
-10.1.0.27 localhost localhost
+10.1.0.27 localhost localhost hostname1.example.com hostname2.example.com
changed: [localhost]

if else syntax in ansible playbook

I am new to Ansible and trying to understand what is wrong with my syntax.
My goal is that only one of the roles will be selected. I do not want to use 'when'.
Here is what I wrote (I am using Ansible v2.9.5):
- name: Install external DB for Cloudera Manager Server
hosts: db_server
roles:
- {{% if (databases_type == "postgresql") %} role: postgresql {% else %} {% endif %}
{% if (databases_type == "mysql") %} role: mariadb {% else %} {% endif %}
{% if (databases_type == "oracle") %} role: oracledb}
When I run the playbook I get a syntax error but it is not clear enough.
Thanks in advance.
A simple dictionary might be a cleaner option. For example
shell> cat playbook.yml
- name: Install external DB for Cloudera Manager Server
hosts: db_server
vars:
my_roles:
postgresql: postgresql
mysql: mariadb
oracle: oracledb
tasks:
- include_role:
name: "{{ my_roles[databases_type] }}"
Example
Let's create the roles
shell> cat roles/postgresql/tasks/main.yml
- debug:
var: role_name
shell> cat roles/mariadb/tasks/main.yml
- debug:
var: role_name
shell> cat roles/oracledb/tasks/main.yml
- debug:
var: role_name
Next, let's create an inventory with three servers, group_vars with default databases_type and host_vars with the variables for two hosts test_01 and test_02. The third host test_03 will use the variables from group_vars.
shell> cat hosts
[db_server]
test_01
test_02
test_03
shell> cat group_vars/db_server
databases_type: mysql
shell> cat host_vars/test_01
databases_type: postgresql
shell> cat host_vars/test_02
databases_type: oracle
Then the playbook gives (abridged)
shell> ansible-playbook -i hosts playbook.yml
PLAY [Install external DB for Cloudera Manager Server] *****************
TASK [include_role : {{ my_roles[databases_type] }}] *******************
TASK [postgresql : debug] **********************************************
ok: [test_01] =>
role_name: postgresql
TASK [oracledb : debug] ************************************************
ok: [test_02] =>
role_name: oracledb
TASK [mariadb : debug] *************************************************
ok: [test_03] =>
role_name: mariadb
I believe that this is what you are looking for. In the below example, fruit is the variable name. If fruit is equal to Apple then I like it else I do not like it. Let me know if you face any issue or you need more explanation on this.
If else syntax:
- name: "[ If Else Example ]"
command: "echo {{ 'I like it' if fruit == 'Apple' else 'I do not like it'}}"
register: if_reg
- debug:
msg: "{{ if_reg.stdout }}"
It seems you have double curly bracket:
- {{% if ...
Try removing it to make single curly bracket:
e.g.
- {% if ...
also last line could remove ending curly bracket and put endif:
{% if (databases_type == "oracle") %} role: oracledb {% endif %}

Ansible template with list of hosts excluding current

I'm super fresh to ansible and creating a playbook that in one of the tasks should copy templated file and replace values in 2 lines. First line should have current hostname, and in second semicolon separated list of all other hosts (used in the play) - it will be different group
First line is super easy, as it's just:
localnode={{ inventory_hostname }}
but I'm having problem with exclusion in the second line. I'd like something similar to:
{% for host in groups.nodes -%} # but without inventory_hostname
othernodes={{ host }}{% if not loop.last %};{% endif %}
{%- endfor %}
Given the inventory of:
nodes:
hosts:
hosta:
hostb:
hostc:
hostd:
I'd like to get following output (example for hostd):
localnode=hostd
othernodes=hosta,hostb,hostc
I'll be very grateful for all hints on possible solution
Create the list of hosts without inventory_hostname and use it in the template
- set_fact:
list_other_hosts: "{{ groups.nodes|difference([inventory_hostname]) }}"
Simplify the template
othernodes={{ list_other_hosts|join(';') }}
As an example, the inventory
shell> cat hosts
test_jails:
hosts:
test_01:
test_02:
test_03:
and the play
- hosts: test_jails
tasks:
- set_fact:
list_other_hosts: "{{ groups.test_jails|
difference([inventory_hostname]) }}"
- debug:
msg: "{{ msg.split('\n') }}"
vars:
msg: |-
localnode={{ inventory_hostname }}
othernodes={{ list_other_hosts|join(';') }}
give
TASK [debug] ********************************************************
ok: [test_01] => {
"msg": [
"localnode=test_01",
"othernodes=test_02;test_03"
]
}
ok: [test_02] => {
"msg": [
"localnode=test_02",
"othernodes=test_01;test_03"
]
}
ok: [test_03] => {
"msg": [
"localnode=test_03",
"othernodes=test_01;test_02"
]
}

Resources