This commit is contained in:
Alfonso Escribano Merino 2023-08-04 23:22:16 +01:00 committed by GitHub
commit cae9b984b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 80 additions and 11 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- acl - Fix module performance (https://github.com/ansible-collections/ansible.posix/pull/197).

View file

@ -141,9 +141,13 @@ acl:
import os import os
import platform import platform
import fcntl
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
from ansible.module_utils.compat import selectors
from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
from ansible.module_utils.six import b
def split_entry(entry): def split_entry(entry):
@ -223,7 +227,7 @@ def build_command(module, mode, path, follow, default, recursive, recalculate_ma
return cmd return cmd
def acl_changed(module, cmd): def acl_changed(module, cmd, check_rc=True):
'''Returns true if the provided command affects the existing ACLs, false otherwise.''' '''Returns true if the provided command affects the existing ACLs, false otherwise.'''
# FreeBSD do not have a --test flag, so by default, it is safer to always say "true" # FreeBSD do not have a --test flag, so by default, it is safer to always say "true"
if platform.system().lower() == 'freebsd': if platform.system().lower() == 'freebsd':
@ -231,10 +235,62 @@ def acl_changed(module, cmd):
cmd = cmd[:] # lists are mutables so cmd would be overwritten without this cmd = cmd[:] # lists are mutables so cmd would be overwritten without this
cmd.insert(1, '--test') cmd.insert(1, '--test')
lines = run_acl(module, cmd) module._acl_changed = False
def _process_stdout_from_pipe(proc, _acl_module=module):
stdout = b''
try:
selector = selectors.DefaultSelector()
except (IOError, OSError):
# Failed to detect default selector for the given platform
# Select PollSelector which is supported by major platforms
selector = selectors.PollSelector()
selector.register(proc.stdout, selectors.EVENT_READ)
if os.name == 'posix':
fcntl.fcntl(proc.stdout.fileno(), fcntl.F_SETFL, fcntl.fcntl(proc.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
while True:
events = selector.select(1)
for key, event in events:
b_chunk = key.fileobj.read()
if b_chunk == b(''):
selector.unregister(key.fileobj)
if key.fileobj == proc.stdout:
stdout = b_chunk
if _acl_module._acl_changed:
continue
lines = []
for l in stdout.splitlines():
lines.append(l.strip())
for line in lines: for line in lines:
if not line.endswith('*,*'): if not line.endswith(b'*,*'):
proc.terminate()
_acl_module._acl_changed = True
proc.returncode = 0
# only break out if no pipes are left to read or
# the pipes are completely read and
# the process is terminated
if (not events or not selector.get_map()) and proc.poll() is not None:
break
# No pipes are left to read but process is not yet terminated
# Only then it is safe to wait for the process to be finished
# NOTE: Actually proc.poll() is always None here if no selectors are left
elif not selector.get_map() and proc.poll() is None:
proc.wait()
# The process is terminated. Since no pipes to read from are
# left, there is no need to call select() again.
break
try:
(rc, out, err) = module.run_command(
cmd, check_rc=check_rc,
before_communicate_callback=_process_stdout_from_pipe)
except Exception as e:
module.fail_json(msg=to_native(e))
if module._acl_changed:
return True return True
return False return False
@ -356,6 +412,9 @@ def main():
if changed and not module.check_mode: if changed and not module.check_mode:
run_acl(module, command) run_acl(module, command)
if recursive:
msg = "%s is present recursively" % entry
else:
msg = "%s is present" % entry msg = "%s is present" % entry
elif state == 'absent': elif state == 'absent':
@ -368,11 +427,19 @@ def main():
if changed and not module.check_mode: if changed and not module.check_mode:
run_acl(module, command, False) run_acl(module, command, False)
if recursive:
msg = "%s is absent recursively" % entry
else:
msg = "%s is absent" % entry msg = "%s is absent" % entry
elif state == 'query': elif state == 'query':
msg = "current acl" msg = "current acl"
if recursive:
acl = []
warn = "Not showing resulting acls in the recursive mode"
module.exit_json(changed=changed, msg=msg, acl=acl, warnings=warn)
else:
acl = run_acl( acl = run_acl(
module, module,
build_command(module, 'get', path, follow, default, recursive, recalculate_mask) build_command(module, 'get', path, follow, default, recursive, recalculate_mask)