mirror of
https://github.com/ansible-collections/ansible.posix.git
synced 2026-01-12 15:45:20 +01:00
Compare commits
19 commits
3be32a713b
...
b76243ab59
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b76243ab59 | ||
|
|
5321a9ecb5 | ||
|
|
7194b6bb13 | ||
|
|
a85108e25e | ||
|
|
c353e43a90 | ||
|
|
c401a5d331 | ||
|
|
94059765b6 | ||
|
|
48c2e9310e | ||
|
|
0a58f59906 | ||
|
|
892c045679 | ||
|
|
793518be3c | ||
|
|
b4fe18e6ff | ||
|
|
e72424ae50 | ||
|
|
6afd4cdcf1 | ||
|
|
ee7748732c | ||
|
|
1e0654f8b6 | ||
|
|
fe7dd71bda | ||
|
|
1a4c2051e8 | ||
|
|
8bb61047f8 |
10 changed files with 166 additions and 33 deletions
|
|
@ -57,6 +57,21 @@ stages:
|
|||
test: units
|
||||
- name: Lint
|
||||
test: lint
|
||||
- stage: Sanity_2_18
|
||||
displayName: Ansible 2.18 sanity
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: "{0}"
|
||||
testFormat: 2.18/{0}
|
||||
targets:
|
||||
- name: Sanity
|
||||
test: sanity
|
||||
- name: Units
|
||||
test: units
|
||||
- name: Lint
|
||||
test: lint
|
||||
- stage: Sanity_2_17
|
||||
displayName: Ansible 2.17 sanity
|
||||
dependsOn: []
|
||||
|
|
@ -113,6 +128,20 @@ stages:
|
|||
test: ubuntu2204
|
||||
- name: Ubuntu 24.04
|
||||
test: ubuntu2404
|
||||
- stage: Docker_2_18
|
||||
displayName: Docker devel
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.18/linux/{0}/1
|
||||
targets:
|
||||
- name: Fedora 40
|
||||
test: fedora40
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
- name: Ubuntu 24.04
|
||||
test: ubuntu2404
|
||||
- stage: Docker_2_17
|
||||
displayName: Docker 2.17
|
||||
dependsOn: []
|
||||
|
|
@ -176,6 +205,18 @@ stages:
|
|||
test: rhel/9.4
|
||||
- name: FreeBSD 13.3
|
||||
test: freebsd/13.3
|
||||
- stage: Remote_2_18
|
||||
displayName: Remote devel
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.18/{0}/1
|
||||
targets:
|
||||
- name: RHEL 9.4
|
||||
test: rhel/9.4
|
||||
- name: FreeBSD 13.3
|
||||
test: freebsd/13.3
|
||||
- stage: Remote_2_17
|
||||
displayName: Remote 2.17
|
||||
dependsOn: []
|
||||
|
|
@ -234,8 +275,11 @@ stages:
|
|||
- Sanity_2_17
|
||||
- Remote_2_17
|
||||
- Docker_2_17
|
||||
- Sanity_2_18
|
||||
- Remote_2_18
|
||||
- Docker_2_18
|
||||
- Sanity_devel
|
||||
- Remote_devel
|
||||
- Docker_devel
|
||||
# - Remote_devel # Wait for test environment release
|
||||
# - Docker_devel # Wait for test environment release
|
||||
jobs:
|
||||
- template: templates/coverage.yml
|
||||
|
|
|
|||
|
|
@ -74,11 +74,14 @@ None
|
|||
|
||||
<!-- List the versions of Ansible the collection has been tested with. Must match what is in galaxy.yml. -->
|
||||
|
||||
- ansible-core 2.18 (devel)
|
||||
- ansible-core 2.19 (devel)
|
||||
- ansible-core 2.18 (stable) *
|
||||
- ansible-core 2.17 (stable)
|
||||
- ansible-core 2.16 (stable)
|
||||
- ansible-core 2.15 (stable)
|
||||
|
||||
*Note: For ansible-core 2.18, CI only covers sanity tests and no integration tests will be run until the test environment is released.*
|
||||
|
||||
## Roadmap
|
||||
|
||||
<!-- Optional. Include the roadmap for this collection, and the proposed release/versioning strategy so users can anticipate the upgrade/update cycle. -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- synchronize - add the ``quiet`` option to suppress non-error messages (https://github.com/ansible-collections/ansible.posix/issues/171).
|
||||
3
changelogs/fragments/570_nfs4_acl.yml
Normal file
3
changelogs/fragments/570_nfs4_acl.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
bugfixes:
|
||||
- acl - Fixed to set ACLs on paths mounted with NFS version 4 correctly (https://github.com/ansible-collections/ansible.posix/issues/240).
|
||||
3
changelogs/fragments/571_ci_bump_core_version.yml
Normal file
3
changelogs/fragments/571_ci_bump_core_version.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
trivial:
|
||||
- Bump ansible-core version to 2.19 of devel branch and add 2.18 to CI.
|
||||
|
|
@ -75,6 +75,10 @@ options:
|
|||
use_nfsv4_acls:
|
||||
description:
|
||||
- Use NFSv4 ACLs instead of POSIX ACLs.
|
||||
- This feature uses C(nfs4_setfacl) and C(nfs4_getfacl). The behavior depends on those implementation.
|
||||
And currently it only supports C(A) in ACE, so C(D) must be replaced with the appropriate C(A).
|
||||
- Permission is set as optimised ACLs by the system. You can check the actual ACLs that has been set using the return value.
|
||||
- More info C(man nfs4_setfacl)
|
||||
type: bool
|
||||
default: false
|
||||
recalculate_mask:
|
||||
|
|
@ -179,7 +183,7 @@ def split_entry(entry):
|
|||
def build_entry(etype, entity, permissions=None, use_nfsv4_acls=False):
|
||||
'''Builds and returns an entry string. Does not include the permissions bit if they are not provided.'''
|
||||
if use_nfsv4_acls:
|
||||
return ':'.join([etype, entity, permissions, 'allow'])
|
||||
return ':'.join(['A', 'g' if etype == 'group' else '', entity, permissions + 'tcy'])
|
||||
|
||||
if permissions:
|
||||
return etype + ':' + entity + ':' + permissions
|
||||
|
|
@ -187,22 +191,27 @@ def build_entry(etype, entity, permissions=None, use_nfsv4_acls=False):
|
|||
return etype + ':' + entity
|
||||
|
||||
|
||||
def build_command(module, mode, path, follow, default, recursive, recalculate_mask, entry=''):
|
||||
def build_command(module, mode, path, follow, default, recursive, recalculate_mask, use_nfsv4_acls, entry=''):
|
||||
'''Builds and returns a getfacl/setfacl command.'''
|
||||
if mode == 'set':
|
||||
cmd = [module.get_bin_path('setfacl', True)]
|
||||
cmd.extend(['-m', entry])
|
||||
cmd = [module.get_bin_path('nfs4_setfacl' if use_nfsv4_acls else 'setfacl', True)]
|
||||
cmd.extend(['-a' if use_nfsv4_acls else '-m', entry])
|
||||
elif mode == 'rm':
|
||||
cmd = [module.get_bin_path('setfacl', True)]
|
||||
cmd = [module.get_bin_path('nfs4_setfacl' if use_nfsv4_acls else 'setfacl', True)]
|
||||
cmd.extend(['-x', entry])
|
||||
else: # mode == 'get'
|
||||
cmd = [module.get_bin_path('getfacl', True)]
|
||||
# prevents absolute path warnings and removes headers
|
||||
if platform.system().lower() == 'linux':
|
||||
if use_nfsv4_acls:
|
||||
# use nfs4_getfacl instead of getfacl if use_nfsv4_acls is True
|
||||
cmd = [module.get_bin_path('nfs4_getfacl', True)]
|
||||
else:
|
||||
cmd = [module.get_bin_path('getfacl', True)]
|
||||
cmd.append('--absolute-names')
|
||||
cmd.append('--omit-header')
|
||||
cmd.append('--absolute-names')
|
||||
|
||||
if recursive:
|
||||
if recursive and not use_nfsv4_acls:
|
||||
cmd.append('--recursive')
|
||||
|
||||
if recalculate_mask == 'mask' and mode in ['set', 'rm']:
|
||||
|
|
@ -210,7 +219,7 @@ def build_command(module, mode, path, follow, default, recursive, recalculate_ma
|
|||
elif recalculate_mask == 'no_mask' and mode in ['set', 'rm']:
|
||||
cmd.append('--no-mask')
|
||||
|
||||
if not follow:
|
||||
if not follow and not use_nfsv4_acls:
|
||||
if platform.system().lower() == 'linux':
|
||||
cmd.append('--physical')
|
||||
elif platform.system().lower() == 'freebsd':
|
||||
|
|
@ -223,24 +232,34 @@ def build_command(module, mode, path, follow, default, recursive, recalculate_ma
|
|||
return cmd
|
||||
|
||||
|
||||
def acl_changed(module, cmd):
|
||||
def acl_changed(module, cmd, entry, use_nfsv4_acls=False):
|
||||
'''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"
|
||||
# To check the ACL changes, use the output of setfacl or nfs4_setfacl with '--test'.
|
||||
# FreeBSD do not have a --test flag, so by default, it is safer to always say "true".
|
||||
if platform.system().lower() == 'freebsd':
|
||||
return True
|
||||
|
||||
cmd = cmd[:] # lists are mutables so cmd would be overwritten without this
|
||||
cmd.insert(1, '--test')
|
||||
lines = run_acl(module, cmd)
|
||||
|
||||
counter = 0
|
||||
for line in lines:
|
||||
if not line.endswith('*,*'):
|
||||
return True
|
||||
return False
|
||||
if line.endswith('*,*') and not use_nfsv4_acls:
|
||||
return False
|
||||
# if use_nfsv4_acls and entry is listed
|
||||
if use_nfsv4_acls and entry == line:
|
||||
counter += 1
|
||||
|
||||
# The current 'nfs4_setfacl --test' lists a new entry,
|
||||
# which will be added at the top of list, followed by the existing entries.
|
||||
# So if the entry has already been registered, the entry should be find twice.
|
||||
if counter == 2:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def run_acl(module, cmd, check_rc=True):
|
||||
|
||||
'''Runs the provided command and returns the output as a list of lines.'''
|
||||
try:
|
||||
(rc, out, err) = module.run_command(cmd, check_rc=check_rc)
|
||||
except Exception as e:
|
||||
|
|
@ -313,7 +332,7 @@ def main():
|
|||
module.fail_json(msg="'recalculate_mask' MUST NOT be set to 'mask' or 'no_mask' when 'state=query'.")
|
||||
|
||||
if not entry:
|
||||
if state == 'absent' and permissions:
|
||||
if state == 'absent' and permissions and not use_nfsv4_acls:
|
||||
module.fail_json(msg="'permissions' MUST NOT be set when 'state=absent'.")
|
||||
|
||||
if state == 'absent' and not entity:
|
||||
|
|
@ -350,21 +369,24 @@ def main():
|
|||
entry = build_entry(etype, entity, permissions, use_nfsv4_acls)
|
||||
command = build_command(
|
||||
module, 'set', path, follow,
|
||||
default, recursive, recalculate_mask, entry
|
||||
default, recursive, recalculate_mask, use_nfsv4_acls, entry
|
||||
)
|
||||
changed = acl_changed(module, command)
|
||||
changed = acl_changed(module, command, entry, use_nfsv4_acls)
|
||||
|
||||
if changed and not module.check_mode:
|
||||
run_acl(module, command)
|
||||
msg = "%s is present" % entry
|
||||
|
||||
elif state == 'absent':
|
||||
entry = build_entry(etype, entity, use_nfsv4_acls)
|
||||
if use_nfsv4_acls:
|
||||
entry = build_entry(etype, entity, permissions, use_nfsv4_acls)
|
||||
else:
|
||||
entry = build_entry(etype, entity, use_nfsv4_acls)
|
||||
command = build_command(
|
||||
module, 'rm', path, follow,
|
||||
default, recursive, recalculate_mask, entry
|
||||
default, recursive, recalculate_mask, use_nfsv4_acls, entry
|
||||
)
|
||||
changed = acl_changed(module, command)
|
||||
changed = acl_changed(module, command, entry, use_nfsv4_acls)
|
||||
|
||||
if changed and not module.check_mode:
|
||||
run_acl(module, command, False)
|
||||
|
|
@ -375,7 +397,10 @@ def main():
|
|||
|
||||
acl = run_acl(
|
||||
module,
|
||||
build_command(module, 'get', path, follow, default, recursive, recalculate_mask)
|
||||
build_command(
|
||||
module, 'get', path, follow, default, recursive,
|
||||
recalculate_mask, use_nfsv4_acls
|
||||
)
|
||||
)
|
||||
|
||||
module.exit_json(changed=changed, msg=msg, acl=acl)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: synchronize
|
||||
|
|
@ -204,6 +203,12 @@ options:
|
|||
description: Internal use only. See O(use_ssh_args) for ssh arg settings.
|
||||
type: str
|
||||
required: false
|
||||
quiet:
|
||||
description:
|
||||
- This option specifies quiet option which on true suppresses the output.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: 1.6.0
|
||||
|
||||
notes:
|
||||
- C(rsync) must be installed on both the local and remote host.
|
||||
|
|
@ -361,6 +366,12 @@ EXAMPLES = r'''
|
|||
src: /tmp/localpath/
|
||||
dest: /tmp/remotepath
|
||||
rsync_path: /usr/gnu/bin/rsync
|
||||
|
||||
- name: Synchronization with quiet option enabled
|
||||
ansible.posix.synchronize:
|
||||
src: some/relative/path
|
||||
dest: /some/absolute/path
|
||||
quiet: true
|
||||
'''
|
||||
|
||||
|
||||
|
|
@ -438,6 +449,7 @@ def main():
|
|||
delay_updates=dict(type='bool', default=True),
|
||||
mode=dict(type='str', default='push', choices=['pull', 'push']),
|
||||
link_dest=dict(type='list', elements='path'),
|
||||
quiet=dict(type='bool', default=False)
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
|
@ -478,6 +490,7 @@ def main():
|
|||
verify_host = module.params['verify_host']
|
||||
link_dest = module.params['link_dest']
|
||||
delay_updates = module.params['delay_updates']
|
||||
quiet = module.params['quiet']
|
||||
|
||||
if '/' not in rsync:
|
||||
rsync = module.get_bin_path(rsync, required=True)
|
||||
|
|
@ -602,6 +615,9 @@ def main():
|
|||
|
||||
cmd.append(shlex_quote(source))
|
||||
cmd.append(shlex_quote(dest))
|
||||
if quiet:
|
||||
cmd.append('--quiet')
|
||||
|
||||
cmdstr = ' '.join(cmd)
|
||||
|
||||
# If we are using password authentication, write the password into the pipe
|
||||
|
|
@ -634,14 +650,17 @@ def main():
|
|||
out_lines = out_clean.split('\n')
|
||||
while '' in out_lines:
|
||||
out_lines.remove('')
|
||||
if module._diff:
|
||||
diff = {'prepared': out_clean}
|
||||
return module.exit_json(changed=changed, msg=out_clean,
|
||||
rc=rc, cmd=cmdstr, stdout_lines=out_lines,
|
||||
diff=diff)
|
||||
|
||||
return module.exit_json(changed=changed, msg=out_clean,
|
||||
rc=rc, cmd=cmdstr, stdout_lines=out_lines)
|
||||
result = dict(changed=changed, rc=rc, cmd=cmdstr, stdout_lines=out_lines, msg=out_clean)
|
||||
|
||||
if quiet:
|
||||
changes = out.count(changed_marker) if changed else 0
|
||||
result['msg'] = "%s files/directories have been synchronized" % changes
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': out_clean}
|
||||
|
||||
return module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
2
tests/integration/inventory
Normal file
2
tests/integration/inventory
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[testgroup]
|
||||
testhost ansible_connection="local" ansible_pipelining="yes" ansible_python_interpreter="/Users/mandkulk/venv3.9/bin/python"
|
||||
|
|
@ -339,6 +339,37 @@
|
|||
- stat_result_b.stat.exists == True
|
||||
- stat_result_b.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'
|
||||
|
||||
- name: Synchronize files with quiet option
|
||||
ansible.posix.synchronize:
|
||||
src: '{{ output_dir }}/foo.txt'
|
||||
dest: '{{ output_dir }}/foo.result'
|
||||
quiet: true
|
||||
register: sync_result
|
||||
|
||||
- name: Assertion for synchronize with quiet option
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- '''files/directories have been synchronized'' in sync_result.msg'
|
||||
|
||||
- name: Cleanup
|
||||
ansible.builtin.file:
|
||||
state: absent
|
||||
path: '{{ output_dir }}/{{ item }}'
|
||||
loop:
|
||||
- foo.result
|
||||
- bar.result
|
||||
|
||||
- name: Synchronize files without quiet option
|
||||
ansible.posix.synchronize:
|
||||
src: '{{ output_dir }}/foo.txt'
|
||||
dest: '{{ output_dir }}/foo.result'
|
||||
register: sync_result
|
||||
|
||||
- name: Assertion for synchronize without quiet option
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- '''files/directories have been synchronized'' not in sync_result.msg'
|
||||
|
||||
- name: Cleanup
|
||||
ansible.builtin.file:
|
||||
state: absent
|
||||
|
|
|
|||
1
tests/sanity/ignore-2.19.txt
Normal file
1
tests/sanity/ignore-2.19.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
tests/utils/shippable/timing.py shebang
|
||||
Loading…
Reference in a new issue