Creating a dictionary of Hostnames to IPs with Ansible - linux

Been hitting my head against this problem for three or four days. I'm trying to write an Ansible playbook that creates a Docker Swarm cluster comprised of my four Docker hosts. As part of this, I'm gathering the Ansible hostnames and specific ethernet device IPv4 addresses from each node via Ansible facts and creating a dictionary:
- name: "Clear swarm_ips dictionary"
set_fact:
swarm_ips: "{{ swarm_ips | default([]) }}"
- name: "Create dictionary with enp42s0 IP and hostname of manager"
set_fact:
swarm_ips: "{{ swarm_ips | combine ({item.key : item.value}) }}"
with_items:
- { 'key': '{{ ansible_facts.hostname | string }}.ip' , 'value': '{{ ansible_facts.enp42s0.ipv4.address | string }}' }
when: (ansible_facts.hostname == "manager")
- name: "Add eth0 IP and hostname of worker[1,2] to swarm_ips"
set_fact:
swarm_ips: "{{ swarm_ips | combine ({item.key : item.value}) }}"
with_items:
- { 'key': '{{ ansible_facts.hostname | string }}.ip' , 'value': '{{ ansible_facts.eth0.ipv4.address | string }}' }
when: (ansible_facts.hostname == "worker1") or
(ansible_facts.hostname == "worker2")
- name: "Add eth0 IP and hostname of worker0 to swarm_ips dictionary"
set_fact:
swarm_ips: "{{ swarm_ips | combine ({item.key : item.value}) }}"
with_items:
- { 'key': '{{ ansible_facts.hostname | string }}.ip' , 'value': '{{ ansible_facts.br0.ipv4.address | string }}' }
when: (ansible_facts.hostname == "worker0")
- name: "Echo eth0 IP for all nodes"
debug:
var: swarm_ips
I expect the output of the last step in the play to be something like what follows:
ok: {
"swarm_ips": {
"manager.ip": "10.0.1.203"
"worker0.ip": "10.0.1.42"
"worker1.ip": "10.0.1.201"
"worker2.ip": "10.0.1.252"
}
}
Instead I get
ok: [manager.local] => {
"swarm_ips": {
"manager.ip": "10.0.1.203"
}
}
ok: [worker0.local] => {
"swarm_ips": {
"worker0.ip": "10.0.1.42"
}
}
ok: [worker1.local] => {
"swarm_ips": {
"worker1.ip": "10.0.1.201"
}
}
ok: [worker2.local] => {
"swarm_ips": {
"worker2.ip": "10.0.1.252"
}
}
There's likely a way I could run some or all of this on localhost and use hostvars to get information about the cluster hosts, but I think this should also work as I expect. Any idea what am I missing, either specifically or conceptually?

Related

Filter data using JSON query in Ansible to extract data from an ansible_fact

I have created this playbook to extract all mount points starting with any element in the variable whitelist matching the type= ext2, ext3, ext4.
The problem is that I can get all mount_points but I am not able to filter the result with the variables.
- hosts: all
gather_facts: True
become: True
vars:
whitelist:
- /boot
- /home
- /opt
- /var
- /bin
- /usr
tasks:
- name: extract mount_points
set_fact:
mount_point: "{{ansible_facts.mounts | selectattr('fstype', 'in', ['ext2', 'ext3', 'ext4']) | map(attribute='mount') | list }}"
- debug:
var: mount_point
vars:
query: "[?starts_with(mount, whitelist)].mount"
When I execute the playbook I get this
ok: [ansible#controller] => {
"mount_point": [
"/",
"/boot",
"/tmp",
"/home",
"/var",
"/var/opt",
"/var/tmp",
"/var/log",
"/var/log/audit",
"/opt",
]
}
/tmp is included which means that query: "[?starts_with(mount, whitelist)].mount" was skipped and I don't know how to achieve the playbook goal.
You don't really need a json query here IMO. An easy way is to filter the list with match and construct a regex containing all possible prefixes:
- name: show my filtered mountpoints:
vars:
start_regex: "{{ whitelist | map('regex_escape') | join('|') }}"
degug:
msg: "{{ {{ansible_facts.mounts | selectattr('fstype', 'in', ['ext2', 'ext3', 'ext4'])
| map(attribute='mount') | select('match', start_regex) | list }}"
While testing i used a filter expression within the json_query string which achieved the same goal for me Query String with Dynamic Variable
.
In this example the filter expression is used with a built-in function, ?starts_with. This function provides a boolean result, based on a match with a search string, on any element within the fstype and mount_point using a dynamic variable {{whitelist}}.
- name: find mount_points
debug:
msg: "{{ ansible_mounts | to_json | from_json | json_query(mount_point) }}"
vars:
mount_point: " #[?starts_with(fstype, 'ext')] | #[?starts_with(mount, '{{item}}')].mount"
loop: "{{whitelist}}"
this is the output
ok: [ansible#ansible1] => (item=/var) => {
"msg": [
"/var",
"/var/opt",
"/var/tmp",
"/var/log",
"/var/log/audit"
}
ok: [ansible#ansible1] => (item=/usr) => {
"msg": []
}
ok: [ansible#ansible1] => (item=/home) => {
"msg": [
"/home"
>>>

how to traverse through each line of a text file in ansible

I have to traverse through package list file which contains list of packages with their architecture. How can I feed those input to my playbook file? I found a way to get the package names alone but architecture version is not coming. This is my package_list file
nginx | x86_64
telnet| x86_64
openssh | i386
This is my playbook
- name: get contents of package.txt
command: cat "/root/packages.txt"
register: _packages
- name: get contents of architecture from packages.txt
command: cat "/root/packages.txt" | awk '{print $3}'
register: _arch
- name: Filter
theforeman.foreman.content_view_filter:
username: "admin"
password: "mypass"
server_url: "myhost"
name: "myfilter"
organization: "COT"
content_view: "M_view"
filter_type: "rpm"
architecture: "{{ _arch }}"
package_name: "{{ item }}"
inclusion: True
loop: "{{ _packages.stdout_lines }}"
loop: "{{ _arch.stdout_lines }}"
Any help would be appreciated
The required output is package name and architecture should be read from packages.txt file through ansible-playbook
try this playbook:
- name: Reproduce issue
hosts: localhost
gather_facts: no
tasks:
- name: get contents of package.txt
command: cat "/root/packages.txt"
register: _packages
- debug:
msg: "package: {{ line.0 }}, arch: {{ line.1 }}"
loop: "{{ _packages.stdout_lines }}"
vars:
line: "{{ item.split('|')|list }}"
result:
ok: [localhost] => (item=nginx | x86_64) => {
"msg": "package: nginx , arch: x86_64 "
}
ok: [localhost] => (item=telnet| x86_64) => {
"msg": "package: telnet, arch: x86_64 "
}
ok: [localhost] => (item=openssh | i386) => {
"msg": "package: openssh , arch: i386 "
}
for your case:
- name: Filter
theforeman.foreman.content_view_filter:
:
:
architecture: "{{ line.1 }}"
package_name: "{{ line.0 }}"
inclusion: True
loop: "{{ _packages.stdout_lines }}"
vars:
line: "{{ item.split('|')|list }}"
following the version of ansible you could write too line: "{{ item | split('|') | list }}"
You need to split up the line into the necessary values by filtering.
---
- hosts: localhost
become: false
gather_facts: false
tasks:
- name: Gahter package list
shell:
cmd: cat package.txt
register: _packages
- name: Show packages
debug:
msg: "Name: {{ item.split('|')[0] }}, Arch: {{ item.split('|')[1] }}"
loop: "{{ _packages.stdout_lines }}"
Further Documentation
Playbook filters - Manipulating strings
Jinja Template Designer - Filters
Further Q&A
Split string into list in Jinja?

Ansible Undefined variable in vars_files item but the variable was defined using set_fact

I'm trying to select a local file based on what motherboard version exists on the machine on which Ansible will deploy those files.
So my approach was to use dictionary in format {"<board_name>" : "<local_file>.yml"}. And use this dictionary to populate a new variable (pcu_config here) that will finally store the name of the file to be used by ansible. I've gotten so far as shown in my implementation, and I'm get undefined variable error at line
vars_files:
- "{{ playbook_dir }}/pcu_config/{{ pcu_config }}"
But as can be seen in the debug msg in the image of the output just before this play, pcu_config is indeed defined(and is the correct filename too).
- name: Find the pcu_config
hosts: vehicle
gather_facts : no
vars:
pcu_config_dict:
VirtualBox: virtual.yml
n1: n1.yml
n2: n2.yml
n3: n3.yml
n4a: n4a.yml
n4b: n4b.yml
tasks:
- name: Find the motherboard name
shell: cat /sys/devices/virtual/dmi/id/board_name
register: board_name
- name: Find the motherboard version
shell: cat /sys/devices/virtual/dmi/id/chassis_version
register: board_version
- debug:
msg: "board_name : {{ board_name.stdout }}, board_version: {{ board_version.stdout }}, {{pcu_config_dict}} "
- name: Assign the PCU_config file for other than n4's
set_fact:
pcu_config: '{{pcu_config_dict[ board_name.stdout | default("this cpu does not exist in the dict")] | default("") }}'
when: board_name.stdout != "n4"
- name: Assign the PCU_config file for n4
set_fact:
pcu_config: '{{pcu_config_dict[board_version.stdout | default("this cpu_version does not exist in the dict")] | default("") }}'
when: board_name.stdout == "n4"
- name : Check pcu_config is available elsewhere in playbook
hosts: vehicle
tasks:
- debug:
msg: "{{ pcu_config }}, {{ playbook_dir }}"
- name: Deploy software to vehicle
hosts: vehicle
vars_files:
- "{{ playbook_dir }}/pcu_config/{{ pcu_config }}"
- "{{ playbook_dir }}/os_config/deb-files-cache.yml"
- "{{ playbook_dir }}/os_config/python_dep.yml"
roles:
- role: logger_network
tags: base
Here is the output when I run this playbook on my virtual machine.
Suggestions welcome to solve the X or Y problem.(ref #Zeitounator's comment)
Not the best approach but to access the variables set by set_facts elsewhere in the playbook, using cacheable: yes works.
- name: Assign the PCU_config file for other than n4's
set_fact:
pcu_config: '{{pcu_config_dict[ board_name.stdout | default("this cpu does not exist in the dict")] | default("") }}'
cacheable: yes
when: board_name.stdout != "n4"
- name: Assign the PCU_config file for n4
set_fact:
pcu_config: '{{pcu_config_dict[board_version.stdout | default("this cpu_version does not exist in the dict")] | default("") }}'
cacheable: yes
when: board_name.stdout == "n4"

Skip current host and print the rest in a config file ansible

Folks, I'm trying to achieve the below while running my task to edit an XML
Requirement:
Skip current host in which config file is created and print the rest of host from the inventory group to the config file
<ManagerNode ip = "**node IP**" port = "**node port**"/> <haNodes>IP2:port,IP3:port</haNodes> <!-- Comma Seperated Node IPs with port, ,Except the Same Node -->
Can anyone help with achieving this goal?
Q: "Skip current host ... and print the rest of hosts from the inventory group."
A: Create a list of all IPs and use filter difference to remove the current IP. For example the inventory
shell> cat hosts
[ha]
test_01 IP=10.1.0.11
test_02 IP=10.1.0.12
test_03 IP=10.1.0.13
[ha:vars]
port=4567
and the playbook
shell> cat playbook.yml
- hosts: ha
tasks:
- set_fact:
all_IP: "{{ groups.ha|map('extract', hostvars, 'IP')|list }}"
run_once: true
- debug:
msg: "{{ all_IP|difference([IP])|
product([port])|
map('join', ':')|
list }}"
give (abridged)
shell> ansible-playbook -i hosts playbook.yml
ok: [test_01] =>
msg:
- 10.1.0.12:4567
- 10.1.0.13:4567
ok: [test_02] =>
msg:
- 10.1.0.11:4567
- 10.1.0.13:4567
ok: [test_03] =>
msg:
- 10.1.0.11:4567
- 10.1.0.12:4567
Limit the play to test_01 gives abridged
shell> ansible-playbook -i hosts -l test_01 playbook.yml
ok: [test_01] =>
msg:
- 10.1.0.12:4567
- 10.1.0.13:4567
defined a variable
tg_hosts: : "{{ groups['tgzone']|map('extract',hostvars,'ansible_host')|list }}"
used template as:
{{ tg_hosts | difference([ansible_host])| list | join(':port,') + ':port' }}

Print json output in Ansible and store in a list?

Ansible playbook:
---
- task:-
- code goes here
- name: Trying to get instance Private IP from ASG
ec2_instance_facts:
instance_ids:
- "{{ item }}"
with_items: "{{ INSTANCE_IDS_FROM_ASG }}"
register: instance_ids_result
- set_fact:
msg: "{{ instance_ids_result | json_query('results[*].instances[*].network_interfaces[*].private_ip_address') }} "
- debug: var=msg
I have the output as follows:
ok: [localhost] => {
"msg": [
[
"172.31.144.74"
],
[
"172.31.147.69"
]
]
}
But, I would need the output in a list as ["172.31.144.74", "172.31.147.69"] or "172.31.147.69" "172.31.147.69".
What is the best way to print it that way?
You could flatten your list using the filter
- set_fact:
msg: "{{ instance_ids_result | json_query('results[*].instances[*].network_interfaces[*].private_ip_address') | flatten }} "

Resources