I want a playbook to validate that some packages are installed and fail that system if they are not. I thought I could use the package: module as a non-root user and have it validate the packages were installed.
I thought that the idempotency of Ansible would permit (actually encourage) this usage. Is this a bug or enhancement that I should escalate, or am I overlooking something simple to achieve this?
Here is my playbook for testing:
---
- name: Show package module non-root usage
hosts: all
become: false
tasks:
- name: Check that a packages is installed
package:
name:
- vim-common
state: installed
As a normal user I can see that vim-common is installed:
$ rpm -qa vim-common
vim-common-8.0.1763-15.el8.x86_64
$ dnf list vim-common
Not root, Subscription Management repositories not updated
Last metadata expiration check: 0:00:29 ago on Thu 21 Jan 2021 12:50:56 PM CST.
Installed Packages
vim-common.x86_64 2:8.0.1763-15.el8 #rhel-8-for-x86_64-appstream-rpms
$
But when I run the playbook I get this error instead of a "success" result:
$ ansible-playbook package_check.yml -i localhost, --connection=local
PLAY [Show package module non-root usage] *********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************
ok: [localhost]
TASK [Check that a packages is installed] *********************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "This command has to be run under the root user.", "results": []}
PLAY RECAP ****************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
$
As expected, when I run it as root it works properly:
$ sudo ansible-playbook package_check.yml -i localhost, --connection=local
PLAY [Show package module non-root usage] *********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************
ok: [localhost]
TASK [Check that a packages is installed] *********************************************************************************************
ok: [localhost]
PLAY RECAP ****************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
My system is Red Hat Enterprise Linux release 8.3 (Ootpa) with Ansible 2.10:
$ ansible-playbook --version
ansible-playbook 2.10.4
config file = /home/dan/.ansible.cfg
configured module search path = ['/home/dan/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.6/site-packages/ansible
executable location = /usr/local/bin/ansible-playbook
python version = 3.6.8 (default, Aug 18 2020, 08:33:21) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]
Update #1
Searching through the Ansible code, the dnf.py module is where this comes from:
$ egrep -C3 "This command has to be run under the root user." /usr/local/lib/python3.6/site-packages/ansible/modules/dnf.py
# before running it.
if not dnf.util.am_i_root():
self.module.fail_json(
msg="This command has to be run under the root user.",
results=[],
)
self.base = self._base(
$
If I comment out that root check block:
$ egrep -C3 "This command has to be run under the root user." /usr/local/lib/python3.6/site-packages/ansible/modules/dnf.py
# before running it.
# if not dnf.util.am_i_root():
# self.module.fail_json(
# msg="This command has to be run under the root user.",
# results=[],
# )
self.base = self._base(
$
I can now run the playbook and it confirms the package is installed without needing root permissions:
$ ansible-playbook package_check.yml -i localhost, --connection=local
PLAY [Show package module non-root usage] *********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************
ok: [localhost]
TASK [Check that a packages is installed] *********************************************************************************************
ok: [localhost]
PLAY RECAP ****************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
Update #2
Slightly modifying the playbook to check an installed and a missing package:
$ cat package_check.yml
#!/usr/bin/env /usr/local/bin/ansible-playbook
---
- name: Show package module non-root usage
hosts: all
become: false
tasks:
- name: Check that VIM common is installed
package:
name:
- vim-common
state: installed
- name: Check that EMACS is installed
package:
name:
- vim-common
- emacs
state: installed
$
Now we get a success on the first and a failure on the second:
$ ansible-playbook package_check.yml -i localhost, --connection=local
PLAY [Show package module non-root usage] *********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************
ok: [localhost]
TASK [Check that VIM common is installed] *********************************************************************************************
ok: [localhost]
TASK [Check that EMACS is installed] **************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "2021-01-21 13:27:23,224 [ERROR] dnf.py:724879:MainThread #logutil.py:194 - [Errno 13] Permission denied: '/var/log/rhsm/rhsm.log' - Further logging output will be written to stderr\n2021-01-21 13:27:23,233 [ERROR] dnf.py:724879:MainThread #identity.py:156 - Reload of consumer identity cert /etc/pki/consumer/cert.pem raised an exception with msg: [Errno 13] Permission denied: '/etc/pki/consumer/key.pem'\nTraceback (most recent call last):\n File \"/home/dan/.ansible/tmp/ansible-tmp-1611257242.3944752-724867-4205539601634/AnsiballZ_dnf.py\", line 102, in <module>\n _ansiballz_main()\n File \"/home/dan/.ansible/tmp/ansible-tmp-1611257242.3944752-724867-4205539601634/AnsiballZ_dnf.py\", line 94, in _ansiballz_main\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n File \"/home/dan/.ansible/tmp/ansible-tmp-1611257242.3944752-724867-4205539601634/AnsiballZ_dnf.py\", line 40, in invoke_module\n runpy.run_module(mod_name='ansible.modules.dnf', init_globals=None, run_name='__main__', alter_sys=True)\n File \"/usr/lib64/python3.6/runpy.py\", line 205, in run_module\n return _run_module_code(code, init_globals, run_name, mod_spec)\n File \"/usr/lib64/python3.6/runpy.py\", line 96, in _run_module_code\n mod_name, mod_spec, pkg_name, script_name)\n File \"/usr/lib64/python3.6/runpy.py\", line 85, in _run_code\n exec(code, run_globals)\n File \"/tmp/ansible_ansible.legacy.dnf_payload_ewju4h_n/ansible_ansible.legacy.dnf_payload.zip/ansible/modules/dnf.py\", line 1330, in <module>\n File \"/tmp/ansible_ansible.legacy.dnf_payload_ewju4h_n/ansible_ansible.legacy.dnf_payload.zip/ansible/modules/dnf.py\", line 1319, in main\n File \"/tmp/ansible_ansible.legacy.dnf_payload_ewju4h_n/ansible_ansible.legacy.dnf_payload.zip/ansible/modules/dnf.py\", line 1294, in run\n File \"/tmp/ansible_ansible.legacy.dnf_payload_ewju4h_n/ansible_ansible.legacy.dnf_payload.zip/ansible/modules/dnf.py\", line 1213, in ensure\n File \"/usr/lib/python3.6/site-packages/dnf/base.py\", line 882, in do_transaction\n tid = self._run_transaction(cb=cb)\n File \"/usr/lib/python3.6/site-packages/dnf/base.py\", line 955, in _run_transaction\n tid = self.history.beg(rpmdbv, using_pkgs, [], cmdline, comment)\n File \"/usr/lib/python3.6/site-packages/dnf/db/history.py\", line 473, in beg\n comment)\n File \"/usr/lib64/python3.6/site-packages/libdnf/transaction.py\", line 763, in beginTransaction\n return _transaction.Swdb_beginTransaction(self, *args)\nlibdnf._error.Error: SQLite error on \"/var/lib/dnf/history.sqlite\": Reading a row failed: attempt to write a readonly database\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
PLAY RECAP ****************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
$
And running as root works as expected:
$ sudo ansible-playbook package_check.yml -i localhost, --connection=local
PLAY [Show package module non-root usage] *********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************
ok: [localhost]
TASK [Check that VIM common is installed] *********************************************************************************************
ok: [localhost]
TASK [Check that EMACS is installed] **************************************************************************************************
changed: [localhost]
PLAY RECAP ****************************************************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$
This is because state: installed is meant to install the package if it does not exists. So, this is not strictly equal to polling your package manager for a packet.
In order to do this, you will have to use the package_facts gatherer.
Then you can easily use failed_when on that same task if the package(s) you expect are not present.
Here would be an example playbook:
- hosts: all
gather_facts: no
tasks:
- package_facts:
failed_when: "'apache2' not in ansible_facts.packages"
In order to solve the use case you stated in the comment below, you can always use the variable ansible_check_mode:
- hosts: all
gather_facts: no
tasks:
- package_facts:
failed_when: "'apache2' not in ansible_facts.packages"
when: ansible_check_mode
- package:
name: apache2
when: not ansible_check_mode
When i try to install a list of packages('apache2', 'libapache2-mod-wsgi', 'python-pip', 'python-virtualenv') on on a group of hosts (app01 and app02),
it fails randomly either on app01 or app02.
After a first few retries the required packages are already installed on the host. Subsequent execution continue to fail randomly on app01 or app02 instead of returning a simple success.
The ansible controller and hosts are all running ubuntu 16.04
The controller has ansible 2.8.4
I have already set "become: yes" in playbook.
I have already tried running apt-get clean and apt-get update. on the hosts
Inventory file
[webserver]
app01 ansible_python_interpreter=python3
app02 ansible_python_interpreter=python3
Playbook
---
- hosts: webserver
tasks:
- name: install web components
become: yes
apt:
name: ['apache2', 'libapache2-mod-wsgi', 'python-pip', 'python-virtualenv']
state: present
update_cache: yes
In the first run , the execution fails on host app01
In the second run, the execution fails on host app02
1 st Run
shekhar#control:~/ansible$ ansible-playbook -v playbooks/webservers.yml
Using /home/shekhar/ansible/ansible.cfg as config file
PLAY [webserver] ******************************************************************************
TASK [Gathering Facts] ******************************************************************************
ok: [app01]
ok: [app02]
TASK [install web components] ******************************************************************************
fatal: [app01]: FAILED! => {"changed": false, "msg": "Failed to lock apt for exclusive operation"}
ok: [app02] => {"cache_update_time": 1568203558, "cache_updated": false, "changed": false}
PLAY RECAP ******************************************************************************
app01 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
app02 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2nd Run
shekhar#control:~/ansible$ ansible-playbook -v playbooks/webservers.yml
Using /home/shekhar/ansible/ansible.cfg as config file
PLAY [webserver] ******************************************************************************
TASK [Gathering Facts] ******************************************************************************
ok: [app01]
ok: [app02]
TASK [install web components] ******************************************************************************
fatal: [app02]: FAILED! => {"changed": false, "msg": "Failed to lock apt for exclusive operation"}
ok: [app01] => {"cache_update_time": 1568203558, "cache_updated": false, "changed": false}
PLAY RECAP ******************************************************************************
app01 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
app02 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
The behavior is a bit strangeā¦ one succeed, and other failing each time.
My guess is that your app01 and app02 are actually the same host! If so, it sounds normal that the 2 jobs fight against each other.
---
- hosts: all
become_user: ansible
become: yes
become_method: sudo
tasks:
- name: Restart the sshd service
service: name=sshd state=restarted
### sudoers file entry for user on host ####
ansible ALL=(ALL) NOPASSWD:ALL
PLAY [all] ***************************************************************************
TASK [Gathering Facts] ***************************************************************
ok: [host2.domain.local]
ok: [host1.domain.local]
TASK [Restart the ssh service] *******************************************************
fatal: [host2.domain.local]: FAILED! => {"changed": false, "msg": "Unable to restart service sshd: Failed to restart sshd.service: Interactive authentication required.\n"}
fatal: [host1.domain.local]: FAILED! => {"changed": false, "msg": "Unable to restart service sshd: Failed to restart sshd.service: Interactive authentication required.\n"}
to retry, use: --limit #/root/1stplay.retry
PLAY RECAP ***************************************************************************
host1.domain.local : ok=1 changed=0 unreachable=0 failed=1
host2.domain.local : ok=1 changed=0 unreachable=0 failed=1
Lose the line:
become_user: ansible
Presumably you are logging into the target machine as ansible and want to become root, not ansible? If you do not specify the become_user root is used by default.
- hosts: Ebonding
become: yes
become_method: sudo
tasks
- name: Clearing cache of Server4
file: path=/weblogic/bea/user_projects/domains/tmp state=absent
become: yes
become_user: wls10
Ansible version 2.0.0.0 run the above playbook successfully::
PLAY ***************************************************************************
TASK [setup] *******************************************************************
ok: [ggnqinfa2]
TASK [Clearing cache of Server4] ***********************************************
ok: [ggnqinfa2]
PLAY RECAP *********************************************************************
ggnqinfa2 : ok=2 changed=0 unreachable=0 failed=0
But latest version of ansible 2.5.0rc2 encountered below error::
PLAY [Ebonding] *****************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************
ok: [ggnqinfa2]
TASK [Clearing cache of Server4] ************************************************************************************************************************************
fatal: [ggnqinfa2]: FAILED! => {"msg": "Failed to set permissions on the temporary files Ansible needs to create when becoming an unprivileged user (rc: 2, err: chown: /var/tmp/ansible-tmp-1520704924.34-191458796685785/: Not owner\nchown: /var/tmp/ansible-tmp-1520704924.34-191458796685785/file.py: Not owner\n}). For information on working around this, see https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user"}
PLAY RECAP **********************************************************************************************************************************************************
ggnqinfa2 : ok=1 changed=0 unreachable=0 failed=1
How can i run this playbook by latest version of ansible successfully?
Chances are the user you're using (wls10) does not have write access to the remote temporary directory /var/tmp.
This can be overridden using ansible.cfg and set via remote_tmp to a directory you have write-access to -- or, a "normal temp directory" (like /tmp) that has the sticky bit set.
For more info, see
http://docs.ansible.com/ansible/latest/intro_configuration.html#remote-tmp
I'm self-learning ansible and having the files structure below with my customized ansible.cfg and inventory file:
---ansible (Folder) <---Working level
---ansible.cfg (Customized cfg)
---dev (Inventory File)
---playbooks (Folder) <--Issue level
---hostname.yml
ansible.cfg
[defaults]
inventory=./dev
dev
[loadbalancer]
lb01
[webserver]
app01
app02
[database]
db01
[control]
control ansible_connection=localansible
hostname.yml
---
- hosts: all
tasks:
- name: get server hostname
command: hostname
My question is when I run ansible-playbook playbooks/hostname.yml at ansible (Folder) level, everything works fine
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [control]
ok: [app01]
ok: [db01]
ok: [lb01]
ok: [app02]
TASK [get server hostname] *****************************************************
changed: [db01]
changed: [app01]
changed: [app02]
changed: [lb01]
changed: [control]
PLAY RECAP *********************************************************************
app01 : ok=2 changed=1 unreachable=0 failed=0
app02 : ok=2 changed=1 unreachable=0 failed=0
control : ok=2 changed=1 unreachable=0 failed=0
db01 : ok=2 changed=1 unreachable=0 failed=0
lb01 : ok=2 changed=1 unreachable=0 failed=0
Howevery when I go into the playbooks folder and run ansible-playbook hostname.yml it give me warning saying
[WARNING]: provided hosts list is empty, only localhost is available
PLAY RECAP *********************************************************************
Is there anything that prevents playbook from accessing the inventory file?
Ansible does not traverse parent directories in search for the configuration file ansible.cfg. If you want to use a custom configuration file, you need to place it in the current directory and run Ansible executables from there or define an environment variable ANSIBLE_CONFIG pointing to the file.
In your case: changing the directory to ansible and executing is the proper way:
ansible-playbook playbooks/hostname.yml
I don't know where from you got the idea Ansible would check for the configuration file in the parent directory - the documentation is unambiguous:
Changes can be made and used in a configuration file which will be processed in the following order:
ANSIBLE_CONFIG (an environment variable)
ansible.cfg (in the current directory)
.ansible.cfg (in the home directory)
/etc/ansible/ansible.cfg