Ansible to store variable facts - ansible

Using Ansible here, I'm gathering facts about a container:
- name: start my container
lxd_container:
name: vm_srv1
state: started
register: st
- debug: msg="{{ st.addresses }}"
Running the playbook produces the following:
TASK [manager : debug] *********************************************************
ok: [lxc.myvmhost ] => {
"msg": {
"eth0": [
"10.0.3.103"
]
}
}
I would like to store eth0 value into a file
I have added
- debug: msg="{{ st.addresses['eth0'] }}"
output:
TASK [manager : debug] *********************************************************
ok: [lxc.myvmhost ] => {
"msg": [
"10.0.3.103"
]
}
When storing the output to a file
- lineinfile: dest=/tmp/file line="{{ st.addresses.eth0 }}"
I get the following:
Hello world
['10.0.3.103']
How can I store the IP address without any funny bagged that Ansible adds?

In your example eth0 is a list of ip addresses, so to fetch the first element of the given list, use:
st.addresses.eth0[0]

Related

ansible groups.variable in a tasks

I am trying to use a variable I have defined in a group_vars/file.yml and use it in a taks of my role like groups.my_variable (my_variable is the name of the group define in my inventory)
Here is my role tasks:
---
- name: "Create strusted storage pool"
gluster_peer:
state: "{{ CURRENT_NODES.state }}"
nodes: "{{groups.group_name}}"
tags: STORAGE_POOL
my inventory
[gluster]
glusterfs01
glusterfs02
ly-lab-elk
lpic
when I use it like this it works:
---
- name: "Create strusted storage pool"
gluster_peer:
state: "{{ CURRENT_NODES.state }}"
nodes: "{{ groups.gluster }}"
tags: STORAGE_POOL
When I use groups.gluster directly in my tasks/pool_storage.yml as above I have expected result
TASK [debug] ******************************************************************************************************************************************************************************************************
ok: [glusterfs01] => {
"groups.gluster": [
"glusterfs01",
"glusterfs02",
"ly-lab-elk",
"lpic"
]
}
ok: [glusterfs02] => {
"groups.gluster": [
"glusterfs01",
"glusterfs02",
"ly-lab-elk",
"lpic"
]
}
ok: [ly-lab-elk] => {
"groups.gluster": [
"glusterfs01",
"glusterfs02",
"ly-lab-elk",
"lpic"
]
}
ok: [lpic] => {
"groups.gluster": [
"glusterfs01",
"glusterfs02",
"ly-lab-elk",
"lpic"
]
}
But when I use the variable set in group_vars/gluster.yml
group_names: gluster
in my tasks tasks/pool_storage.yml
nodes: "{{groups.group_name}}"
I have this result which is not good
TASK [debug] ******************************************************************************************************************************************************************************************************
ok: [glusterfs01] => {
"group_names": [
"gluster"
]
}
ok: [glusterfs02] => {
"group_names": [
"gluster"
]
}
ok: [ly-lab-elk] => {
"group_names": [
"gluster"
]
}
ok: [lpic] => {
"group_names": [
"gluster"
]
}
As I have different groups in my inventory file, I want to use a variable in which I will define my group name and then append it to groups.group_name or other way but to have the expected result.
Can someone help ?
Thank you in advance.
So, to reference a group_name variable in groups you can try using the groups[group_name] syntax.
An example inventory:
[servers]
host-101.xxx.com
host-202.xxx.com
[group1]
g1-server1
g1-server2
A group_vars/servers.yaml:
group_name: servers
A playbook:
- hosts: servers
connection: local
tasks:
- debug:
var: groups[group_name]
Gives the servers under group1 defined as group_name:
ok: [host-101.xxx.com] => {
"groups[group_name]": [
"host-101.xxx.com",
"host-202.xxx.com"
]
}
ok: [host-202.xxx.com] => {
"groups[group_name]": [
"host-101.xxx.com",
"host-202.xxx.com"
]
}
One more thing to note is that you don't need to establish the peers on each host of GlusterFS cluster. So, either:
target any 1 host of that group
or
add run_once: true to the task
Example:
- name: "Create strusted storage pool"
gluster_peer:
state: "{{ CURRENT_NODES.state }}"
nodes: "{{ groups[group_name] }}"
tags: STORAGE_POOL
run_once: true
Put the variable into the file group_vars/gluster.yml if you want the hosts of the group gluster to use it. The name of the file must be identical to the name of the group. This way Ansible will recognize to which groups the files in the directory group_vars belong.
If you can't change the name of group_vars/file.yml link it to group_vars/gluster.yml.
For example, given the simplified inventory for testing
shell> cat hosts
[gluster]
glusterfs01
glusterfs02
The group_vars
shell> cat group_vars/gluster.yml
my_variable: foo
and the playbook
shell> cat playbook.yml
- hosts: gluster
gather_facts: false
tasks:
- debug:
var: my_variable
give (abridged)
shell> ansible-playbook -i hosts playbook.yml
TASK [debug] *******************************************************
ok: [glusterfs01] =>
my_variable: foo
ok: [glusterfs02] =>
my_variable: foo

Why is ansible creating a new host scoped list variables for each host?

I need to create a variable of ip addresses that are derived from the run state of all hosts. I expect to insert a new string value and have it appended to the list.
When I run the following ansible-playbook, it creates what looks to be a new list instance for each host instead of modifying the playbook level vars.
My understanding is the set_fact below should concatenate the lists and assign them back to the play scoped var ip_list_from_terraform. Why am I getting host scoped results?
---
- name: Bootstrap nodes
hosts: all
become: true
gather_facts: true
vars:
ip_list_from_terraform: ['Verify']
tasks:
- name: assign a list of all the physical network private IPs
set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform + [ item ] }}"
with_items: " {{ hostvars[inventory_hostname]['ansible_' + ansible_default_ipv4['interface']]['ipv4']['address'] }} "
register: ip_list_from_terraform_list
- name: Debug global var
debug:
var: ip_list_from_terraform
- name: Debug register result
debug:
var: ip_list_from_terraform_list
Expected:
ok: [shrimp-master-0] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.41",
"10.0.2.172",
"10.0.2.33",
"10.0.2.215",
"10.0.2.131",
"10.0.2.168",
"10.0.2.118"
]
}
Actual:
TASK [Debug global var] ********************************************************************************************************************************************************************************************************************
ok: [shrimp-master-0] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.12"
]
}
ok: [shrimp-master-1] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.33"
]
}
ok: [shrimp-master-2] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.215"
]
}
ok: [shrimp-worker-0-super-wallaby] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.131"
]
}
ok: [shrimp-gpu-worker-0-settled-wombat] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.151"
]
}
Let's simplify the case. Given the inventory
shell> cat hosts
host1 test_ip=10.0.2.41
host2 test_ip=10.0.2.172
host3 test_ip=10.0.2.33
the playbook
- hosts: host1,host2,host3
vars:
ip_list_from_terraform: ['Verify']
tasks:
- set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform + [ item ] }}"
with_items: "{{ hostvars[inventory_hostname]['test_ip'] }}"
- debug:
var: ip_list_from_terraform
gives
ok: [host2] =>
ip_list_from_terraform:
- Verify
- 10.0.2.172
ok: [host1] =>
ip_list_from_terraform:
- Verify
- 10.0.2.41
ok: [host3] =>
ip_list_from_terraform:
- Verify
- 10.0.2.33
As a side-note, with_items is needed because the argument is a string. loop would crash with the error 'Invalid data passed to ''loop'', it requires a list,.
Q: "set_fact should concatenate the lists"
A: Run once and loop ansible_play_hosts. For example
- set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform +
[hostvars[item]['test_ip']] }}"
loop: "{{ ansible_play_hosts }}"
run_once: true
gives
ip_list_from_terraform:
- Verify
- 10.0.2.41
- 10.0.2.172
- 10.0.2.33

How to run an Ansible task if *any* host has a fact

I'm wrapping my head around something that I'm probably overcomplicating.
I need to check if any of my hosts has ansible_virtualization_type == "openvz"
If this is the case, ALL hosts should execute a specific task.
I'm now trying to set a fact (virt_list) containing a list of hosts with their virtualization_type on localhost:
- name: Set fuse on virtualization OpenVZ
set_fact:
virt_list:
host: "{{item}}"
type: "openvz"
when: hostvars[item].ansible_virtualization_type == "openvz"
with_items: "{{ groups['all'] }}"
delegate_to: localhost
delegate_facts: true
but this doesn't work (both hosts in this play are on openvz):
TASK [roles/testvirt : debug vars ansible_virtualization_type ] ****************************
ok: [host1] => {
"ansible_virtualization_type": "openvz"
}
ok: [host2] => {
"ansible_virtualization_type": "openvz"
}
TASK [roles/testvirt : debug vars virt_list ] **********************************************
ok: [host1] => {
"msg": [
{
"host": "host1",
"type": "openvz"
}
]
}
ok: [host2] => {
"msg": [
{
"host": "host2",
"type": "openvz"
}
]
}
There should be a simpler way, maybe using jinjia2 to combine the lists directly.
Anyone has advices?
Q: "If any of my hosts has ansible_virtualization_type == "openvz" ALL hosts should execute a specific task."
A: For example, given the inventory for testing
shell> cat hosts
host1 ansible_virtualization_type=xen
host2 ansible_virtualization_type=xen
host3 ansible_virtualization_type=openvz
extract the variables
- debug:
msg: "{{ ansible_play_hosts|
map('extract', hostvars, 'ansible_virtualization_type')|
list }}"
run_once: true
gives
msg:
- xen
- xen
- openvz
Test if the type is present
- debug:
msg: OK. ALL hosts should execute a specific task.
when: "'openvz' in vtypes"
vars:
vtypes: "{{ ansible_play_hosts|
map('extract', hostvars, 'ansible_virtualization_type')|
list }}"
run_once: true
gives
msg: OK. ALL hosts should execute a specific task.
If this is working as expected proceed with all hosts
- set_fact:
all_hosts_execute_specific_task: true
when: "'openvz' in vtypes"
vars:
vtypes: "{{ ansible_play_hosts|
map('extract', hostvars, 'ansible_virtualization_type')|
list }}"
run_once: true
- debug:
msg: Execute a specific task.
when: all_hosts_execute_specific_task|default(false)
gives
TASK [set_fact] ************************************************************
ok: [host1]
TASK [debug] ***************************************************************
ok: [host1] =>
msg: Execute a specific task.
ok: [host3] =>
msg: Execute a specific task.
ok: [host2] =>
msg: Execute a specific task.
The task will be skipped if the type is missing.

Use dynamic variable name

I'm trying to get the value of ip_address from the following yaml that I'm including as variables on ansible:
common:
ntp:
- time.google.com
node1:
default_route: 10.128.0.1
dns:
- 10.128.0.2
hostname: ip-10-128-5-17
device_interface: ens5
cluster_interface: ens5
interfaces:
ens5:
ip_address: 10.128.5.17
nat_ip_address: 18.221.63.178
netmask: 255.255.240.0
version: 2
However the network interface (ens5 here) may be named something else, such as eth0. My ansible code is this:
- hosts: all
tasks:
- name: Read configuration from the yaml file
include_vars: "{{ config_yaml }}"
- name: Dump Interface Settings
vars:
msg: node1.interfaces.{{ cvp_device_interface }}.ip_address
debug:
msg: "{{ msg }}"
tags: debug_info
Running the code like this I can get the key's name:
TASK [Dump Interface Settings] *************************************************
│ ok: [18.221.63.178] => {
│ "msg": "node1.interfaces.ens5.ip_address"
│ }
But what I actually need is the value (i.e: something like {{ vars[msg] }}, which should expand into {{ node1.interfaces.ens5.ip_address }}). How can I accomplish this?
Use sqare brackets.
Example: a minimal playbook, which defines a variable called "device". This variable is used to return the active status of the device.
- hosts: localhost
connection: local
vars:
device: enx0050b60c19af
tasks:
- debug: var=device
- debug: var=hostvars.localhost.ansible_facts[device].active
Output:
$ ansible-playbook example.yaml
[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 [debug] ***********************************************************************
ok: [localhost] => {
"device": "enx0050b60c19af"
}
TASK [debug] ***********************************************************************
ok: [localhost] => {
"hostvars.localhost.ansible_facts[device].active": true
}
PLAY RECAP *************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
see comment
- hosts: all
tasks:
- name: Read configuration from the yaml file
include_vars: "{{ config_yaml }}"
- name: Dump Interface Settings
debug:
msg: "{{ node1['interfaces'][cvp_device_interface]['ip_address'] }}"
debug:
msg: "{{ msg }}"
tags: debug_info

Ansible playbook - syntax in set_fact with variables and ip filter - centos7

In centos7, I am trying to define a remote host, where IP is the back-end or front-end IP
Then I ordered the IPADDR filter and there were some obstacles.
For example,
# {{ ansible_all_ipv4.addresses | ipaddr('192.168.0.0/22') }}
['192.168.1.2', '192.168.2.2']
# {{ ansible_all_ipv4.addresses | ipaddr('192.168.1.0/24') }}
['192.168.1.2']
As my case, since in VM have 2 different IPs like 192.168.56.101 and 172.16.1.10, i want to pre-define the network and prefix by variables to get the 192.168.56.101. Then i set:
[defaults/main.yml]
backend:
network: 192.168.56.0
prefix: 24
[tasks/main.yml]
- debug: var="{{item}}"
with_items:
- "ansible_all_ipv4_addresses|ipaddr('''{{backend.network}}/{{backend.prefix}}''')"
result:
TASK [test : debug] ************************************************************
ok: [localhost] => (item=ansible_all_ipv4_addresses|ipaddr('''192.168.56.0/24''')) => {
"ansible_all_ipv4_addresses|ipaddr('''192.168.56.0/24''')": [
"192.168.56.101" <------ THAT IS WHAT I WANT
],
"changed": false,
"item": "ansible_all_ipv4_addresses|ipaddr('''192.168.56.0/24''')"
Then I try to set fact and export the result, its not my want.
- name: define backend ip
set_fact: backendIP="{{ item }}"
with_items:
- "ansible_all_ipv4_addresses|ipaddr('''{{network}}/{{prefix}}''')"
- debug: var=backendIP
result:
TASK [test : define backend ip] ***********************************************************
ok: [localhost] => (item=ansible_all_ipv4_addresses|ipaddr('''192.168.56.0/24'''))
TASK [test : debug] ************************************************************
ok: [localhost] => {
"backendIP": "ansible_all_ipv4_addresses|ipaddr('''192.168.56.0/24''')"
So how can I set the variable through this situation
Since you want a single result, you don’t need any loops.
This is what you want (if you are sure your filter matches a single address):
set_fact:
backend: "{{ ansible_all_ipv4_addresses|ipaddr(backend.network + '/' + backend.prefix|string) }}"

Resources