mirror of
https://github.com/ansible-collections/ansible.posix.git
synced 2026-03-10 11:35:20 +01:00
Compare commits
8 commits
7540f7c4c2
...
d390e7cf20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d390e7cf20 | ||
|
|
9bfed58f7f | ||
|
|
a18d180246 | ||
|
|
51b94f536c | ||
|
|
0a07bdb358 | ||
|
|
230e5f2a98 | ||
|
|
fa4dd35d66 | ||
|
|
211e6c74b5 |
11 changed files with 106 additions and 38 deletions
4
changelogs/fragments/333_doc_absent_precision.yml
Normal file
4
changelogs/fragments/333_doc_absent_precision.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
trivial:
|
||||||
|
- mount - fix description in the documentation of the state ``absent`` to match its actual behavior
|
||||||
|
and point out that ``src`` is ignored with state ``absent`` and ``unmounted`` (https://github.com/ansible-collections/ansible.posix/issues/322)
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
minor_changes:
|
||||||
|
- synchronize - elevating privileges now works even when `sudo` requires entering the `become_pass`
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
bugfixes:
|
||||||
|
- seboolean - make it work with disabled SELinux
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
minor_changes:
|
||||||
|
- "Add summary_only parameter to profile_roles and profile_tasks callbacks."
|
||||||
|
|
@ -580,7 +580,7 @@ Notes
|
||||||
|
|
||||||
- The user and permissions for the synchronize `dest` are those of the `remote_user` on the destination host or the `become_user` if `become=yes` is active.
|
- The user and permissions for the synchronize `dest` are those of the `remote_user` on the destination host or the `become_user` if `become=yes` is active.
|
||||||
- In Ansible 2.0 a bug in the synchronize module made become occur on the "local host". This was fixed in Ansible 2.0.1.
|
- In Ansible 2.0 a bug in the synchronize module made become occur on the "local host". This was fixed in Ansible 2.0.1.
|
||||||
- Currently, synchronize is limited to elevating permissions via passwordless sudo. This is because rsync itself is connecting to the remote machine and rsync doesn't give us a way to pass sudo credentials in.
|
- Currently, synchronize is limited to elevating permissions via sudo. This now even works when password entry is required.
|
||||||
- Currently there are only a few connection types which support synchronize (ssh, paramiko, local, and docker) because a sync strategy has been determined for those connection types. Note that the connection for these must not need a password as rsync itself is making the connection and rsync does not provide us a way to pass a password to the connection.
|
- Currently there are only a few connection types which support synchronize (ssh, paramiko, local, and docker) because a sync strategy has been determined for those connection types. Note that the connection for these must not need a password as rsync itself is making the connection and rsync does not provide us a way to pass a password to the connection.
|
||||||
- Expect that dest=~/x will be ~<remote_user>/x even if using sudo.
|
- Expect that dest=~/x will be ~<remote_user>/x even if using sudo.
|
||||||
- Inspect the verbose output to validate the destination user/host/path are what was expected.
|
- Inspect the verbose output to validate the destination user/host/path are what was expected.
|
||||||
|
|
|
||||||
|
|
@ -391,10 +391,24 @@ class ActionModule(ActionBase):
|
||||||
# If no rsync_path is set, become was originally set, and dest is
|
# If no rsync_path is set, become was originally set, and dest is
|
||||||
# remote then add privilege escalation here.
|
# remote then add privilege escalation here.
|
||||||
if self._play_context.become_method == 'sudo':
|
if self._play_context.become_method == 'sudo':
|
||||||
if self._play_context.become_user:
|
|
||||||
rsync_path = 'sudo -u %s rsync' % self._play_context.become_user
|
# if become is set, we can either rely on passwordless sudo or pass the password
|
||||||
|
if self._play_context.become_pass is None:
|
||||||
|
rsync_path = 'sudo '
|
||||||
else:
|
else:
|
||||||
rsync_path = 'sudo rsync'
|
# pass the become password using the environment so that the synchronize module
|
||||||
|
# can wrap ssh on the host with a shell script that injects the password into
|
||||||
|
# stdin, allowing for `sudo -S` on the target machine to retrieve the password
|
||||||
|
if hasattr(self._task, 'environment'):
|
||||||
|
self._task.environment = []
|
||||||
|
self._task.environment.append({'BECOME_PASS': self._play_context.become_pass})
|
||||||
|
_tmp_args['_ssh_wrapper'] = True
|
||||||
|
rsync_path = 'sudo -S '
|
||||||
|
|
||||||
|
if self._play_context.become_user:
|
||||||
|
rsync_path += '-u %s rsync' % self._play_context.become_user
|
||||||
|
else:
|
||||||
|
rsync_path += 'rsync'
|
||||||
# TODO: have to add in the rest of the become methods here
|
# TODO: have to add in the rest of the become methods here
|
||||||
|
|
||||||
# We cannot use privilege escalation on the machine running the
|
# We cannot use privilege escalation on the machine running the
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,19 @@ DOCUMENTATION = '''
|
||||||
- This callback module provides profiling for ansible roles.
|
- This callback module provides profiling for ansible roles.
|
||||||
requirements:
|
requirements:
|
||||||
- whitelisting in configuration
|
- whitelisting in configuration
|
||||||
|
options:
|
||||||
|
summary_only:
|
||||||
|
description:
|
||||||
|
- Only show summary, not individual task profiles.
|
||||||
|
Especially usefull in combination with C(DISPLAY_SKIPPED_HOSTS=false) and/or C(ANSIBLE_DISPLAY_OK_HOSTS=false).
|
||||||
|
type: bool
|
||||||
|
default: False
|
||||||
|
env:
|
||||||
|
- name: PROFILE_ROLES_SUMMARY_ONLY
|
||||||
|
ini:
|
||||||
|
- section: callback_profile_roles
|
||||||
|
key: summary_only
|
||||||
|
version_added: 1.5.0
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
|
@ -76,13 +89,26 @@ class CallbackModule(CallbackBase):
|
||||||
self.stats = collections.Counter()
|
self.stats = collections.Counter()
|
||||||
self.totals = collections.Counter()
|
self.totals = collections.Counter()
|
||||||
self.current = None
|
self.current = None
|
||||||
|
|
||||||
|
self.summary_only = None
|
||||||
|
|
||||||
super(CallbackModule, self).__init__()
|
super(CallbackModule, self).__init__()
|
||||||
|
|
||||||
|
def set_options(self, task_keys=None, var_options=None, direct=None):
|
||||||
|
|
||||||
|
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
|
||||||
|
|
||||||
|
self.summary_only = self.get_option('summary_only')
|
||||||
|
|
||||||
|
def _display_tasktime(self):
|
||||||
|
if not self.summary_only:
|
||||||
|
self._display.display(tasktime())
|
||||||
|
|
||||||
def _record_task(self, task):
|
def _record_task(self, task):
|
||||||
"""
|
"""
|
||||||
Logs the start of each task
|
Logs the start of each task
|
||||||
"""
|
"""
|
||||||
self._display.display(tasktime())
|
self._display_tasktime()
|
||||||
timestamp(self)
|
timestamp(self)
|
||||||
|
|
||||||
if task._role:
|
if task._role:
|
||||||
|
|
@ -99,10 +125,10 @@ class CallbackModule(CallbackBase):
|
||||||
self._record_task(task)
|
self._record_task(task)
|
||||||
|
|
||||||
def playbook_on_setup(self):
|
def playbook_on_setup(self):
|
||||||
self._display.display(tasktime())
|
self._display_tasktime()
|
||||||
|
|
||||||
def playbook_on_stats(self, stats):
|
def playbook_on_stats(self, stats):
|
||||||
self._display.display(tasktime())
|
self._display_tasktime()
|
||||||
self._display.display(filled("", fchar="="))
|
self._display.display(filled("", fchar="="))
|
||||||
|
|
||||||
timestamp(self)
|
timestamp(self)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,18 @@ DOCUMENTATION = '''
|
||||||
ini:
|
ini:
|
||||||
- section: callback_profile_tasks
|
- section: callback_profile_tasks
|
||||||
key: sort_order
|
key: sort_order
|
||||||
|
summary_only:
|
||||||
|
description:
|
||||||
|
- Only show summary, not individual task profiles.
|
||||||
|
Especially usefull in combination with C(DISPLAY_SKIPPED_HOSTS=false) and/or C(ANSIBLE_DISPLAY_OK_HOSTS=false).
|
||||||
|
type: bool
|
||||||
|
default: False
|
||||||
|
env:
|
||||||
|
- name: PROFILE_TASKS_SUMMARY_ONLY
|
||||||
|
ini:
|
||||||
|
- section: callback_profile_tasks
|
||||||
|
key: summary_only
|
||||||
|
version_added: 1.5.0
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
|
|
@ -120,6 +132,7 @@ class CallbackModule(CallbackBase):
|
||||||
self.current = None
|
self.current = None
|
||||||
|
|
||||||
self.sort_order = None
|
self.sort_order = None
|
||||||
|
self.summary_only = None
|
||||||
self.task_output_limit = None
|
self.task_output_limit = None
|
||||||
|
|
||||||
super(CallbackModule, self).__init__()
|
super(CallbackModule, self).__init__()
|
||||||
|
|
@ -137,6 +150,8 @@ class CallbackModule(CallbackBase):
|
||||||
elif self.sort_order == 'none':
|
elif self.sort_order == 'none':
|
||||||
self.sort_order = None
|
self.sort_order = None
|
||||||
|
|
||||||
|
self.summary_only = self.get_option('summary_only')
|
||||||
|
|
||||||
self.task_output_limit = self.get_option('output_limit')
|
self.task_output_limit = self.get_option('output_limit')
|
||||||
if self.task_output_limit is not None:
|
if self.task_output_limit is not None:
|
||||||
if self.task_output_limit == 'all':
|
if self.task_output_limit == 'all':
|
||||||
|
|
@ -144,11 +159,15 @@ class CallbackModule(CallbackBase):
|
||||||
else:
|
else:
|
||||||
self.task_output_limit = int(self.task_output_limit)
|
self.task_output_limit = int(self.task_output_limit)
|
||||||
|
|
||||||
|
def _display_tasktime(self):
|
||||||
|
if not self.summary_only:
|
||||||
|
self._display.display(tasktime())
|
||||||
|
|
||||||
def _record_task(self, task):
|
def _record_task(self, task):
|
||||||
"""
|
"""
|
||||||
Logs the start of each task
|
Logs the start of each task
|
||||||
"""
|
"""
|
||||||
self._display.display(tasktime())
|
self._display_tasktime()
|
||||||
timestamp(self)
|
timestamp(self)
|
||||||
|
|
||||||
# Record the start time of the current task
|
# Record the start time of the current task
|
||||||
|
|
@ -171,10 +190,10 @@ class CallbackModule(CallbackBase):
|
||||||
self._record_task(task)
|
self._record_task(task)
|
||||||
|
|
||||||
def playbook_on_setup(self):
|
def playbook_on_setup(self):
|
||||||
self._display.display(tasktime())
|
self._display_tasktime()
|
||||||
|
|
||||||
def playbook_on_stats(self, stats):
|
def playbook_on_stats(self, stats):
|
||||||
self._display.display(tasktime())
|
self._display_tasktime()
|
||||||
self._display.display(filled("", fchar="="))
|
self._display.display(filled("", fchar="="))
|
||||||
|
|
||||||
timestamp(self)
|
timestamp(self)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ options:
|
||||||
description:
|
description:
|
||||||
- Device (or NFS volume, or something else) to be mounted on I(path).
|
- Device (or NFS volume, or something else) to be mounted on I(path).
|
||||||
- Required when I(state) set to C(present), C(mounted) or C(ephemeral).
|
- Required when I(state) set to C(present), C(mounted) or C(ephemeral).
|
||||||
|
- Ignored when I(state) set to C(absent) or C(unmounted).
|
||||||
type: path
|
type: path
|
||||||
fstype:
|
fstype:
|
||||||
description:
|
description:
|
||||||
|
|
@ -75,9 +76,13 @@ options:
|
||||||
the module will fail to avoid unexpected unmount or mount point override.
|
the module will fail to avoid unexpected unmount or mount point override.
|
||||||
If the mount point is not present, the mount point will be created.
|
If the mount point is not present, the mount point will be created.
|
||||||
The I(fstab) is completely ignored. This option is added in version 1.5.0.
|
The I(fstab) is completely ignored. This option is added in version 1.5.0.
|
||||||
- C(absent) specifies that the device mount's entry will be removed from
|
- C(absent) specifies that the mount point entry I(path) will be removed
|
||||||
I(fstab) and will also unmount the device and remove the mount
|
from I(fstab) and will also unmount the mounted device and remove the
|
||||||
point.
|
mount point. A mounted device will be unmounted regardless of I(src) or its
|
||||||
|
real source. C(absent) does not unmount recursively, and the module will
|
||||||
|
fail if multiple devices are mounted on the same mount point. Using
|
||||||
|
C(absent) with a mount point that is not registered in the I(fstab) has
|
||||||
|
no effect. Use C(unmounted) instead..
|
||||||
- C(remounted) specifies that the device will be remounted for when you
|
- C(remounted) specifies that the device will be remounted for when you
|
||||||
want to force a refresh on the mount itself (added in 2.9). This will
|
want to force a refresh on the mount itself (added in 2.9). This will
|
||||||
always return changed=true. If I(opts) is set, the options will be
|
always return changed=true. If I(opts) is set, the options will be
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,7 @@ except ImportError:
|
||||||
HAVE_SEMANAGE = False
|
HAVE_SEMANAGE = False
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||||
from ansible.module_utils.six import binary_type
|
from ansible.module_utils._text import to_text
|
||||||
from ansible.module_utils._text import to_bytes, to_text
|
|
||||||
from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL
|
from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -82,23 +81,6 @@ def get_runtime_status(ignore_selinux_state=False):
|
||||||
return True if ignore_selinux_state is True else selinux.is_selinux_enabled()
|
return True if ignore_selinux_state is True else selinux.is_selinux_enabled()
|
||||||
|
|
||||||
|
|
||||||
def has_boolean_value(module, name):
|
|
||||||
bools = []
|
|
||||||
try:
|
|
||||||
rc, bools = selinux.security_get_boolean_names()
|
|
||||||
except OSError:
|
|
||||||
module.fail_json(msg="Failed to get list of boolean names")
|
|
||||||
# work around for selinux who changed its API, see
|
|
||||||
# https://github.com/ansible/ansible/issues/25651
|
|
||||||
if len(bools) > 0:
|
|
||||||
if isinstance(bools[0], binary_type):
|
|
||||||
name = to_bytes(name)
|
|
||||||
if name in bools:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def get_boolean_value(module, name):
|
def get_boolean_value(module, name):
|
||||||
state = 0
|
state = 0
|
||||||
try:
|
try:
|
||||||
|
|
@ -174,7 +156,10 @@ def semanage_set_boolean_value(module, handle, name, value):
|
||||||
semanage.semanage_handle_destroy(handle)
|
semanage.semanage_handle_destroy(handle)
|
||||||
module.fail_json(msg="Failed to modify boolean key with semanage")
|
module.fail_json(msg="Failed to modify boolean key with semanage")
|
||||||
|
|
||||||
if semanage.semanage_bool_set_active(handle, boolkey, sebool) < 0:
|
if (
|
||||||
|
selinux.is_selinux_enabled()
|
||||||
|
and semanage.semanage_bool_set_active(handle, boolkey, sebool) < 0
|
||||||
|
):
|
||||||
semanage.semanage_handle_destroy(handle)
|
semanage.semanage_handle_destroy(handle)
|
||||||
module.fail_json(msg="Failed to set boolean key active with semanage")
|
module.fail_json(msg="Failed to set boolean key active with semanage")
|
||||||
|
|
||||||
|
|
@ -315,12 +300,9 @@ def main():
|
||||||
# Feature only available in selinux library since 2012.
|
# Feature only available in selinux library since 2012.
|
||||||
name = selinux.selinux_boolean_sub(name)
|
name = selinux.selinux_boolean_sub(name)
|
||||||
|
|
||||||
if not has_boolean_value(module, name):
|
|
||||||
module.fail_json(msg="SELinux boolean %s does not exist." % name)
|
|
||||||
|
|
||||||
if persistent:
|
if persistent:
|
||||||
changed = semanage_boolean_value(module, name, state)
|
changed = semanage_boolean_value(module, name, state)
|
||||||
else:
|
elif selinux.is_selinux_enabled():
|
||||||
cur_value = get_boolean_value(module, name)
|
cur_value = get_boolean_value(module, name)
|
||||||
if cur_value != state:
|
if cur_value != state:
|
||||||
changed = True
|
changed = True
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ notes:
|
||||||
delegate_to host when delegate_to is used).
|
delegate_to host when delegate_to is used).
|
||||||
- The user and permissions for the synchronize `dest` are those of the `remote_user` on the destination host or the `become_user` if `become=yes` is active.
|
- The user and permissions for the synchronize `dest` are those of the `remote_user` on the destination host or the `become_user` if `become=yes` is active.
|
||||||
- In Ansible 2.0 a bug in the synchronize module made become occur on the "local host". This was fixed in Ansible 2.0.1.
|
- In Ansible 2.0 a bug in the synchronize module made become occur on the "local host". This was fixed in Ansible 2.0.1.
|
||||||
- Currently, synchronize is limited to elevating permissions via passwordless sudo. This is because rsync itself is connecting to the remote machine
|
- Currently, synchronize is limited to elevating permissions via sudo. This now even works when password entry is required.
|
||||||
and rsync doesn't give us a way to pass sudo credentials in.
|
and rsync doesn't give us a way to pass sudo credentials in.
|
||||||
- Currently there are only a few connection types which support synchronize (ssh, paramiko, local, and docker) because a sync strategy has been
|
- Currently there are only a few connection types which support synchronize (ssh, paramiko, local, and docker) because a sync strategy has been
|
||||||
determined for those connection types. Note that the connection for these must not need a password as rsync itself is making the connection and
|
determined for those connection types. Note that the connection for these must not need a password as rsync itself is making the connection and
|
||||||
|
|
@ -432,6 +432,7 @@ def main():
|
||||||
_ssh_args=dict(type='str'),
|
_ssh_args=dict(type='str'),
|
||||||
use_ssh_args=dict(type='bool', default=False),
|
use_ssh_args=dict(type='bool', default=False),
|
||||||
ssh_connection_multiplexing=dict(type='bool', default=False),
|
ssh_connection_multiplexing=dict(type='bool', default=False),
|
||||||
|
_ssh_wrapper=dict(type='bool', default=False),
|
||||||
partial=dict(type='bool', default=False),
|
partial=dict(type='bool', default=False),
|
||||||
verify_host=dict(type='bool', default=False),
|
verify_host=dict(type='bool', default=False),
|
||||||
delay_updates=dict(type='bool', default=True),
|
delay_updates=dict(type='bool', default=True),
|
||||||
|
|
@ -474,6 +475,7 @@ def main():
|
||||||
rsync_opts = module.params['rsync_opts']
|
rsync_opts = module.params['rsync_opts']
|
||||||
ssh_args = module.params['_ssh_args']
|
ssh_args = module.params['_ssh_args']
|
||||||
ssh_connection_multiplexing = module.params['ssh_connection_multiplexing']
|
ssh_connection_multiplexing = module.params['ssh_connection_multiplexing']
|
||||||
|
ssh_wrapper = module.params['_ssh_wrapper']
|
||||||
verify_host = module.params['verify_host']
|
verify_host = module.params['verify_host']
|
||||||
link_dest = module.params['link_dest']
|
link_dest = module.params['link_dest']
|
||||||
delay_updates = module.params['delay_updates']
|
delay_updates = module.params['delay_updates']
|
||||||
|
|
@ -568,6 +570,13 @@ def main():
|
||||||
ssh_cmd_str = ' '.join(shlex_quote(arg) for arg in ssh_cmd)
|
ssh_cmd_str = ' '.join(shlex_quote(arg) for arg in ssh_cmd)
|
||||||
if ssh_args:
|
if ssh_args:
|
||||||
ssh_cmd_str += ' %s' % ssh_args
|
ssh_cmd_str += ' %s' % ssh_args
|
||||||
|
# When `become: yes` is set but the account on the target requires a password for sudo, we have to supply
|
||||||
|
# it from the host side by wrapping the remote shell and inserting the password into stdin.
|
||||||
|
# In the ActionPlugin, the password is assigned to the BECOME_PASS environment variable, so we will not have
|
||||||
|
# to make it visible if anyone logs the command issued by ansible.
|
||||||
|
# Adapted from https://askubuntu.com/a/1263657
|
||||||
|
if ssh_wrapper:
|
||||||
|
ssh_cmd_str = '/bin/sh -c "{ echo $BECOME_PASS; cat - ; } | ' + ssh_cmd_str + ' $0 $* &"'
|
||||||
cmd.append('--rsh=%s' % shlex_quote(ssh_cmd_str))
|
cmd.append('--rsh=%s' % shlex_quote(ssh_cmd_str))
|
||||||
|
|
||||||
if rsync_path:
|
if rsync_path:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue