Description
Summary
We have several ansible roles which have a task to add a group, something like this:
- name: create group
group: name=jenkins gid=162 system=true local=true
Our machines are joined to an LDAP domain but we specifically want to create local users in these roles. These tasks are very slow to run in our environment - even when the group already exists or in --check
mode. I have tracked it down to this code in https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/group.py
:
def _local_check_gid_exists(self):
if self.gid:
for gr in grp.getgrall():
if self.gid == gr.gr_gid and self.name != gr.gr_name:
self.module.fail_json(msg="GID '{0}' already exists with group '{1}'".format(self.gid, gr.gr_name))
On our machines it takes a couple of minutes to enumerate all groups on a good day
$ /usr/bin/time python3 -c 'import grp; print(len(grp.getgrall()))'
12740
0.06user 0.04system 2:02.21elapsed 0%CPU (0avgtext+0avgdata 22180maxresident)k
0inputs+0outputs (0major+4107minor)pagefaults 0swaps
Maybe this is because we have over 12000 groups, or maybe our LDAP servers are just slow. Both of those things are out of my control.
It seems weird that the group module enumerates all groups in order to find one with a specific gid, as there is an API to do exactly that getgrgid
. Replacing the existing implementation of _local_check_gid_exists
with:
def _local_check_gid_exists(self):
if self.gid:
try:
gr = grp.getgrgid(self.gid)
except KeyError:
gr = None
if gr is not None:
if self.gid == gr.gr_gid and self.name != gr.gr_name:
self.module.fail_json(msg="GID '{0}' already exists with group '{1}'".format(self.gid, gr.gr_name))
Executes quickly and should be equivalent. Is there any reason not to use this instead?
I looked at the history of this code; it was added in 4898b0a
then fixed in
f94772f
otherwise unchanged since 2019 as far as I can see.
Issue Type
Bug Report
Component Name
group
Ansible Version
$ ansible --version
ansible [core 2.16.4]
config file = $HOME/code/ansible/ansible.cfg
configured module search path = ['$HOME/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
ansible collection location = $HOME/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.10.12 (main, Mar 22 2024, 16:50:05) [GCC 11.4.0] (/usr/bin/python3)
jinja version = 3.1.2
libyaml = True
Configuration
# if using a version older than ansible-core 2.12 you should omit the '-t all'
$ ansible-config dump --only-changed -t all
ANSIBLE_NOCOWS($HOME/code/ansible/ansible.cfg) = True
CONFIG_FILE() = $HOME/code/ansible/ansible.cfg
DEFAULT_FORKS($HOME/code/ansible/ansible.cfg) = 30
DEFAULT_GATHERING($HOME/code/ansible/ansible.cfg) = smart
DEFAULT_HOST_LIST(env: ANSIBLE_INVENTORY) = ['$HOME/code/ansible/hosts.yml']
DEFAULT_TIMEOUT($HOME/code/ansible/ansible.cfg) = 60
DEFAULT_VAULT_PASSWORD_FILE($HOME/code/ansible/ansible.cfg) = <omitted>
EDITOR(env: EDITOR) = nvim
INTERPRETER_PYTHON($HOME/code/ansible/ansible.cfg) = auto
CONNECTION:
==========
paramiko_ssh:
____________
ssh_args($HOME/code/ansible/ansible.cfg) = -o ControlMaster=auto -o ControlPersist=600s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
timeout($HOME/code/ansible/ansible.cfg) = 60
ssh:
___
pipelining($HOME/code/ansible/ansible.cfg) = True
ssh_args($HOME/code/ansible/ansible.cfg) = -o ControlMaster=auto -o ControlPersist=600s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
timeout($HOME/code/ansible/ansible.cfg) = 60
SHELL:
=====
sh:
__
remote_tmp($HOME/code/ansible/ansible.cfg) = /tmp/ansible-$USER
OS / Environment
Ubuntu 22.04.4 LTS
Steps to Reproduce
- name: create group
group: name=jenkins gid=162 system=true local=true
Expected Results
Should execute in reasonable time
Actual Results
Is very slow when joined to an LDAP domain with many groups/slow response time.
Code of Conduct
- I agree to follow the Ansible Code of Conduct