When I run this playbook, its not finding ansible_uptime_seconds variable. But ansible hostname -m setup gives this variable. I am using ansible 2.9.23 version.
- hosts: all
become: yes
become_method: sudo
gather_facts: yes
tasks:
- name: Print all available facts
ansible.builtin.debug:
var: ansible_facts
Getting this message
'ansible_uptime_seconds' is undefined
How to get this value in the play book?
The fact name is uptime_seconds, when facts are collected without setup module. however its "ansible_uptime_seconds" when collected with setup module.
---
- name: Sample playbook
connection: local
# gather_facts: false
hosts: localhost
tasks:
- name: print uptime sec
debug:
msg: "{{ ansible_facts.uptime_seconds }}"
Output of the above playbook is:
PLAY [Sample playbook] *********************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************
ok: [localhost]
TASK [print uptime sec] **********************************************************************************************************************************************************
ok: [localhost] => {
"msg": "172603"
}
PLAY RECAP *********************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Related
I have the following playbook:
- name: Get-IP
hosts: webservers
tasks:
- debug: var=ansible_all_ipv4_addresses
register: foo
- debug:
var: foo
- local_action: lineinfile line= {{ foo }} path=/root/file2.txt
I added debug to make sure variable foo carrying the IP address, and its working correctly, but when I try to save it to file on local desk, files remains empty. If I delete the file I get an error about the file does not exist.
Whats wrong with my playbook? Thanks.
Write the lines in a loop. For example,
shell> cat pb.yml
- hosts: webservers
tasks:
- debug:
var: ansible_all_ipv4_addresses
- lineinfile:
create: true
dest: /tmp/ansible_all_ipv4_addresses.webservers
line: "{{ item }} {{ hostvars[item].ansible_all_ipv4_addresses|join(',') }}"
loop: "{{ ansible_play_hosts }}"
run_once: true
delegate_to: localhost
gives
shell> ansible-playbook pb.yml
PLAY [webservers] ****************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [test_11]
ok: [test_13]
TASK [debug] *********************************************************************************
ok: [test_11] =>
ansible_all_ipv4_addresses:
- 10.1.0.61
ok: [test_13] =>
ansible_all_ipv4_addresses:
- 10.1.0.63
TASK [lineinfile] ****************************************************************************
changed: [test_11 -> localhost] => (item=test_11)
changed: [test_11 -> localhost] => (item=test_13)
PLAY RECAP ***********************************************************************************
test_11: ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
test_13: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
shell> cat /tmp/ansible_all_ipv4_addresses.webservers
test_11 10.1.0.61
test_13 10.1.0.63
According your description and example I understand your use case that you like to gather Ansible facts about Remote Nodes and Cache facts on the Control Node.
If fact cache plugin is enabled
~/test$ grep fact_ ansible.cfg
fact_caching = yaml # or json
fact_caching_connection = /tmp/ansible/facts_cache
fact_caching_timeout = 129600
a minimal example playbook
---
- hosts: test
become: false
gather_facts: true
gather_subset:
- "!all"
- "!min"
- "network"
tasks:
- name: Show Facts network
debug:
msg: "{{ ansible_facts }}"
will result into local files like
~/test$ grep -A1 _ipv4 /tmp/ansible/facts_cache/test.example.com
ansible_all_ipv4_addresses:
- 192.0.2.1
--
ansible_default_ipv4:
address: 192.0.2.1
--
...
By following this approach there is no need for re-implementing already existing functionality, less and easier to maintain code, less error prone, and so on.
Furthermore,
Fact caching can improve performance. If you manage thousands of hosts, you can configure fact caching to run nightly, then manage configuration on a smaller set of servers periodically throughout the day. With cached facts, you have access to variables and information about all hosts even when you are only managing a small number of servers.
Similar Q&A
How to gather IP addresses of Remote Nodes?
I tried lookup on machine installed on it ansible and it works, but when uploading playbook to awx it does not work.
- name: get file
set_fact:
policer: "{{ lookup('file', 'file.txt') }}"
it gives An unhandled exception occurred while running the lookup plugin 'file'. Error was a <class 'ansible.errors.AnsibleError'>, original message: could not locate file in lookup"
although the file in the same repo of playbook, and have worked on machine but not awx. And if there is something specific to remote hosts how to know the path
Ansible Lookup plugins "execute and are evaluated on the Ansible control machine."
The play works fine when you run it at the localhost (control machine) where the file is located
shell> hostname
test_11
shell> cat /tmp/file.txt
content of file /tmp/file.txt
shell> cat pb1.yml
- hosts: localhost
vars:
policer: "{{ lookup('file', '/tmp/file.txt') }}"
tasks:
- debug:
var: policer
shell> ansible-playbook pb1.yml
PLAY [localhost] *****************************************************************************
TASK [debug] *********************************************************************************
ok: [localhost] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
localhost: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
When you move to another controller (might be AWX) the file remains at the remote host (test_11 in the example) and is not available at the localhost controller. You can test it. See the block below
shell> hostname
awx
shell> cat /tmp/file.txt
cat: /tmp/file.txt: No such file or directory
shell> ssh admin#test_11 cat /tmp/file.txt
content of file /tmp/file.txt
shell> cat pb2.yml
- hosts: test_11
vars:
policer: "{{ lookup('file', '/tmp/file.txt') }}"
tasks:
- block:
- stat:
path: /tmp/file.txt
register: st
- debug:
var: st.stat.exists
delegate_to: localhost
- debug:
var: policer
shell> ansible-playbook pb2.yml
PLAY [test_11] *******************************************************************************
TASK [stat] **********************************************************************************
ok: [test_11 -> localhost]
TASK [debug] *********************************************************************************
ok: [test_11 -> localhost] =>
st.stat.exists: false
TASK [debug] *********************************************************************************
[WARNING]: Unable to find '/tmp/file.txt' in expected paths (use -vvvvv to see paths)
fatal: [test_11]: FAILED! =>
msg: 'An unhandled exception occurred while templating ''{{ lookup(''file'', ''/tmp/file.txt'') }}''. Error was a <class ''ansible.errors.AnsibleError''>, original message: An unhandled exception occurred while running the lookup plugin ''file''. Error was a <class ''ansible.errors.AnsibleError''>, original message: could not locate file in lookup: /tmp/file.txt. could not locate file in lookup: /tmp/file.txt'
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
There are many options for how to fix it. The trivial one is moving also the file to the localhost controller. If the file remains at the remote host you can either read it by command or slurp, or fetch it. The module command always reports changed. The modules slurp and fetch are idempotent.
Read the file by command
shell> cat pb3.yml
- hosts: test_11
vars:
policer: "{{ out.stdout }}"
tasks:
- command: cat /tmp/file.txt
register: out
- debug:
var: policer
shell> ansible-playbook pb3.yml
PLAY [test_11] *******************************************************************************
TASK [command] *******************************************************************************
changed: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Read the file by slurp. This should be used for smaller files only because (quoting from slurp): "This module returns an ‘in memory’ base64 encoded version of the file, take into account that this will require at least twice the RAM as the original file size."
shell> cat pb4.yml
- hosts: test_11
vars:
policer: "{{ out.content|b64decode }}"
tasks:
- slurp:
path: /tmp/file.txt
register: out
- debug:
var: policer
shell> ansible-playbook pb4.yml
PLAY [test_11] *******************************************************************************
TASK [slurp] *********************************************************************************
ok: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: |-
content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The next option is to fetch the file to the local directory dest (will be created). By default, the file(s) is stored in the directory named by the remote host
shell> cat pb5.yml
- hosts: test_11
vars:
file_path: "/tmp/fetched_files/{{ inventory_hostname }}/tmp/file.txt"
policer: "{{ lookup('file', file_path) }}"
tasks:
- fetch:
src: /tmp/file.txt
dest: /tmp/fetched_files
- debug:
var: policer
shell> ansible-playbook pb5.yml
PLAY [test_11] *******************************************************************************
TASK [fetch] *********************************************************************************
changed: [test_11]
TASK [debug] *********************************************************************************
ok: [test_11] =>
policer: content of file /tmp/file.txt
PLAY RECAP ***********************************************************************************
test_11: ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
shell> cat /tmp/fetched_files/test_11/tmp/file.txt
content of file /tmp/file.txt
Notes
An absolute path to the file is used in the examples. See The magic of ‘local’ paths on how to use relative paths locally.
Is it possible during Ansible execution to add another host in the play, without starting a new play?
I am aware of the add_host module, but that requires the start of a new play to add the host, which is undesired.
No. By design, it's not possible to add hosts to 'in-flight play'. Quoting from the Summary of Ansible bug #59401:
By design, the in-flight play will not start running tasks on newly-added hosts, but it will stop running tasks on hosts that have disappeared. Newly-created hosts from an inventory refresh are immediately visible in ansible_play_hosts, even though they're not executing.
Notes
The bug claims refresh_inventory and add_host should have the same effects.
One might expect that the option refresh_inventory of the module meta does the job. The scenario would be:
Start a play
Modify the source of the inventory
Run - meta: refresh_inventory
Unfortunately, the example of the INI file below shows that this doesn't work. The host host03 is added to the inventory and to the list ansible_play_hosts_all as well. But, then, the following task debug doesn't run at this host. Play recap doesn't include this host either.
shell> cat hosts
[test]
host01
host02
The playbook below
shell> cat playbook.yml
- hosts: test
gather_facts: false
tasks:
- debug:
var: ansible_play_hosts_all
run_once: true
- community.general.ini_file:
path: hosts
section: test
option: "{{ item.host }}"
state: "{{ item.state }}"
allow_no_value: true
loop:
- {host: host03, state: present}
run_once: true
delegate_to: localhost
- meta: refresh_inventory
- debug:
var: ansible_play_hosts_all
run_once: true
- debug:
var: inventory_hostname
gives
shell> ansible-playbook -i hosts playbook.yml
PLAY [test] **********************************************************************************
TASK [debug] *********************************************************************************
ok: [host01] =>
ansible_play_hosts_all:
- host01
- host02
TASK [community.general.ini_file] ************************************************************
changed: [host01 -> localhost] => (item={'host': 'host03', 'state': 'present'})
TASK [meta] **********************************************************************************
TASK [debug] *********************************************************************************
ok: [host01] =>
ansible_play_hosts_all:
- host01
- host02
- host03
TASK [debug] *********************************************************************************
ok: [host01] =>
inventory_hostname: host01
ok: [host02] =>
inventory_hostname: host02
PLAY RECAP ***********************************************************************************
host01 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host02 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I need to run task that would check and create if missing folders on Ansible Control Node (where the ansible-playbook command is run) - next tasks will copy some specified files respectively to these local sub-folders:
I have a task:
tasks:
- name: Create local directory
file:
path: "remotes/{{ inventory_hostname }}"
state: directory
recurse: yes
delegate_to: localhost
tags:
localfolders
however when I run with --check it is going to "change" (create folders) on each remote:
TASK [Create local directory] ****************************************************************************************************************
changed: [ansible -> localhost]
changed: [remote1 -> localhost]
changed: [remote2 -> localhost]
Why it not runs task on local only?
Expected result is that on ansible host (only), following folders are created:
remotes/ansible
remotes/remote1
remotes/remote2
To get a better understand of Controlling where tasks run: delegation and local actions and run_once works, I've prepared a small test with an inventory file of
[test]
remote01.example.com
remote02.example.com
and a playbook local.yml
---
- hosts: test
become: false
gather_facts: false
tasks:
- name: Check where I am running on
delegate_to: localhost
shell:
cmd: "hostname && hostname -i"
register: result
run_once: true
- name: Show result
debug:
msg: "{{ result.stdout_lines }}"
run_once: false
executed on control.example.com node via
sshpass -p ${PASSWORD} ansible-playbook --user ${ACCOUNT} --ask-pass local.yml
resulting into an output of
PLAY [test] ********************************
TASK [Check where I am running on] *********
changed: [remote01.example.com -> localhost]
TASK [Show result] *************************
ok: [remote01.example.com] =>
msg:
- control.example.com
- 192.0.2.1
ok: [remote02.example.com] =>
msg:
- control.example.com
- 192.0.2.1
PLAY RECAP *************************************************************************************************
remote01.example.com : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
remote02.example.com : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Interesting Doc
IPv4 Address Blocks Reserved for Documentation
I am working on a playbook (for use in AWX) to handle some backend processing for one of our web apps.
One of the extra variables passed to the playbook via a REST call to AWX is used to pass the hosts
to the playbook
hosts: {{target}}
target can be a single server or a list of servers.
Question: how can I use patterns to skip a host if it is not a member of an inventory group
e.g if I want the playbook to skip a server if it's in the staging group in inventory
I have tried the following:
hosts: "{{target}}:!staging"
this only works if only one server is sent as target var, however it fails if called with a list.
This should work if you do use : as delimiter for your hosts and not ,.
The syntax host1:host2:host3:!staging works, but host1,host2,host3:!staging, on the other hand, does generates a warning
[WARNING]: Could not match supplied host pattern, ignoring: host3:!staging
and this could well be the issue you are facing too.
The two syntaxes are documented here
Given the inventory:
all:
hosts:
host1:
host2:
host3:
children:
staging:
hosts:
host2:
And the playbook:
- hosts: host1:host2:host3:!staging
gather_facts: no
tasks:
- debug:
msg: "{{ inventory_hostname }}"
This yields the recap:
PLAY [host1:host2:host3:!staging] *********************************************************************************
TASK [debug] ******************************************************************************************************
ok: [host1] => {
"msg": "host1"
}
ok: [host3] => {
"msg": "host3"
}
PLAY RECAP ********************************************************************************************************
host1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And it gives the exact same recap when using the playbook:
- hosts: "{{ target }}:!staging"
gather_facts: no
tasks:
- debug:
msg: "{{ inventory_hostname }}"
Run via:
ansible-playbook play.yml -i inventory.yml -e "target=host1:host2:host3"