mirror of
https://github.com/ansible-collections/ansible.posix.git
synced 2026-01-12 07:35:31 +01:00
Compare commits
16 commits
2244bcbd12
...
43a0758078
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43a0758078 | ||
|
|
1994b2cf1c | ||
|
|
2f224e6a6a | ||
|
|
96ec2097cc | ||
|
|
7b9b1f4957 | ||
|
|
d05a5c70b5 | ||
|
|
572167b9c1 | ||
|
|
2c8aad9f39 | ||
|
|
70b838f031 | ||
|
|
5a16ef3bea | ||
|
|
841a0f3314 | ||
|
|
5bc6f636f7 | ||
|
|
284025660c | ||
|
|
f7f54f242d | ||
|
|
966df79767 | ||
|
|
5ee818ec86 |
9 changed files with 163 additions and 22 deletions
3
changelogs/fragments/569_keep_mountpoint.yml
Normal file
3
changelogs/fragments/569_keep_mountpoint.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
minor_changes:
|
||||||
|
- keep_mountpoint - added keep_mountpoint option with default value false. If set to true keep_mountpoint changes the behaviour of state\: absent by keeping the mountpoint.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
minor_changes:
|
||||||
|
- profile_tasks - Add option to provide a different date/time format (https://github.com/ansible-collections/ansible.posix/issues/279).
|
||||||
6
changelogs/fragments/631_fixes_module_path.yml
Normal file
6
changelogs/fragments/631_fixes_module_path.yml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
bugfixes:
|
||||||
|
- ansible.posix.cgroup_perf_recap - fixes json module load path (https://github.com/ansible-collections/ansible.posix/issues/630).
|
||||||
|
trivial:
|
||||||
|
- ansible.posix.seboolean - remove unnecessary condition from seboolean integration tests (https://github.com/ansible-collections/ansible.posix/issues/630).
|
||||||
|
- ansible.posix.selinux - optimize conditions for selinux integration tests (https://github.com/ansible-collections/ansible.posix/issues/630).
|
||||||
|
|
@ -132,6 +132,7 @@ DOCUMENTATION = '''
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
|
@ -142,7 +143,7 @@ from functools import partial
|
||||||
|
|
||||||
from ansible.module_utils._text import to_bytes, to_text
|
from ansible.module_utils._text import to_bytes, to_text
|
||||||
from ansible.module_utils.six import with_metaclass
|
from ansible.module_utils.six import with_metaclass
|
||||||
from ansible.parsing.ajson import AnsibleJSONEncoder, json
|
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||||
from ansible.plugins.callback import CallbackBase
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,17 @@ DOCUMENTATION = '''
|
||||||
- section: callback_profile_tasks
|
- section: callback_profile_tasks
|
||||||
key: summary_only
|
key: summary_only
|
||||||
version_added: 1.5.0
|
version_added: 1.5.0
|
||||||
|
datetime_format:
|
||||||
|
description:
|
||||||
|
- Datetime format, as expected by the C(strftime) and C(strptime) methods.
|
||||||
|
An C(iso8601) alias will be translated to C('%Y-%m-%dT%H:%M:%S.%f') if that datetime standard wants to be used.
|
||||||
|
default: '%A %d %B %Y %H:%M:%S %z'
|
||||||
|
env:
|
||||||
|
- name: PROFILE_TASKS_DATETIME_FORMAT
|
||||||
|
ini:
|
||||||
|
- section: callback_profile_tasks
|
||||||
|
key: datetime_format
|
||||||
|
version_added: 3.0.0
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
|
|
@ -72,14 +83,15 @@ sample output: >
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import time
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from ansible.module_utils.six.moves import reduce
|
from ansible.module_utils.six.moves import reduce
|
||||||
from ansible.plugins.callback import CallbackBase
|
from ansible.plugins.callback import CallbackBase
|
||||||
|
|
||||||
|
|
||||||
# define start time
|
# define start time
|
||||||
t0 = tn = time.time()
|
dt0 = dtn = datetime.now().astimezone()
|
||||||
|
|
||||||
|
|
||||||
def secondsToStr(t):
|
def secondsToStr(t):
|
||||||
|
|
@ -104,17 +116,18 @@ def filled(msg, fchar="*"):
|
||||||
|
|
||||||
def timestamp(self):
|
def timestamp(self):
|
||||||
if self.current is not None:
|
if self.current is not None:
|
||||||
elapsed = time.time() - self.stats[self.current]['started']
|
elapsed = (datetime.now().astimezone() - self.stats[self.current]['started']).total_seconds()
|
||||||
self.stats[self.current]['elapsed'] += elapsed
|
self.stats[self.current]['elapsed'] += elapsed
|
||||||
|
|
||||||
|
|
||||||
def tasktime():
|
def tasktime(self):
|
||||||
global tn
|
global dtn
|
||||||
time_current = time.strftime('%A %d %B %Y %H:%M:%S %z')
|
cdtn = datetime.now().astimezone()
|
||||||
time_elapsed = secondsToStr(time.time() - tn)
|
datetime_current = cdtn.strftime(self.datetime_format)
|
||||||
time_total_elapsed = secondsToStr(time.time() - t0)
|
time_elapsed = secondsToStr((cdtn - dtn).total_seconds())
|
||||||
tn = time.time()
|
time_total_elapsed = secondsToStr((cdtn - dt0).total_seconds())
|
||||||
return filled('%s (%s)%s%s' % (time_current, time_elapsed, ' ' * 7, time_total_elapsed))
|
dtn = cdtn
|
||||||
|
return filled('%s (%s)%s%s' % (datetime_current, time_elapsed, ' ' * 7, time_total_elapsed))
|
||||||
|
|
||||||
|
|
||||||
class CallbackModule(CallbackBase):
|
class CallbackModule(CallbackBase):
|
||||||
|
|
@ -134,6 +147,7 @@ class CallbackModule(CallbackBase):
|
||||||
self.sort_order = None
|
self.sort_order = None
|
||||||
self.summary_only = None
|
self.summary_only = None
|
||||||
self.task_output_limit = None
|
self.task_output_limit = None
|
||||||
|
self.datetime_format = None
|
||||||
|
|
||||||
super(CallbackModule, self).__init__()
|
super(CallbackModule, self).__init__()
|
||||||
|
|
||||||
|
|
@ -159,9 +173,14 @@ class CallbackModule(CallbackBase):
|
||||||
else:
|
else:
|
||||||
self.task_output_limit = int(self.task_output_limit)
|
self.task_output_limit = int(self.task_output_limit)
|
||||||
|
|
||||||
|
self.datetime_format = self.get_option('datetime_format')
|
||||||
|
if self.datetime_format is not None:
|
||||||
|
if self.datetime_format == 'iso8601':
|
||||||
|
self.datetime_format = '%Y-%m-%dT%H:%M:%S.%f'
|
||||||
|
|
||||||
def _display_tasktime(self):
|
def _display_tasktime(self):
|
||||||
if not self.summary_only:
|
if not self.summary_only:
|
||||||
self._display.display(tasktime())
|
self._display.display(tasktime(self))
|
||||||
|
|
||||||
def _record_task(self, task):
|
def _record_task(self, task):
|
||||||
"""
|
"""
|
||||||
|
|
@ -176,10 +195,11 @@ class CallbackModule(CallbackBase):
|
||||||
# with the same UUID is executed when `serial` is specified in a playbook.
|
# with the same UUID is executed when `serial` is specified in a playbook.
|
||||||
# elapsed: Elapsed time since the first serialized task was started
|
# elapsed: Elapsed time since the first serialized task was started
|
||||||
self.current = task._uuid
|
self.current = task._uuid
|
||||||
|
dtn = datetime.now().astimezone()
|
||||||
if self.current not in self.stats:
|
if self.current not in self.stats:
|
||||||
self.stats[self.current] = {'started': time.time(), 'elapsed': 0.0, 'name': task.get_name()}
|
self.stats[self.current] = {'started': dtn, 'elapsed': 0.0, 'name': task.get_name()}
|
||||||
else:
|
else:
|
||||||
self.stats[self.current]['started'] = time.time()
|
self.stats[self.current]['started'] = dtn
|
||||||
if self._display.verbosity >= 2:
|
if self._display.verbosity >= 2:
|
||||||
self.stats[self.current]['path'] = task.get_path()
|
self.stats[self.current]['path'] = task.get_path()
|
||||||
|
|
||||||
|
|
@ -196,7 +216,7 @@ class CallbackModule(CallbackBase):
|
||||||
# Align summary report header with other callback plugin summary
|
# Align summary report header with other callback plugin summary
|
||||||
self._display.banner("TASKS RECAP")
|
self._display.banner("TASKS RECAP")
|
||||||
|
|
||||||
self._display.display(tasktime())
|
self._display.display(tasktime(self))
|
||||||
self._display.display(filled("", fchar="="))
|
self._display.display(filled("", fchar="="))
|
||||||
|
|
||||||
timestamp(self)
|
timestamp(self)
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,8 @@ options:
|
||||||
real source. V(absent) does not unmount recursively, and the module will
|
real source. V(absent) does not unmount recursively, and the module will
|
||||||
fail if multiple devices are mounted on the same mount point. Using
|
fail if multiple devices are mounted on the same mount point. Using
|
||||||
V(absent) with a mount point that is not registered in the I(fstab) has
|
V(absent) with a mount point that is not registered in the I(fstab) has
|
||||||
no effect, use V(unmounted) instead.
|
no effect, use V(unmounted) instead. You can set O(keep_mountpoint) to
|
||||||
|
True to keep the mountpoint.
|
||||||
- V(remounted) specifies that the device will be remounted for when you
|
- V(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 RV(ignore:changed=true). If O(opts) is set, the options will be
|
always return RV(ignore:changed=true). If O(opts) is set, the options will be
|
||||||
|
|
@ -132,6 +133,16 @@ options:
|
||||||
the original file back if you somehow clobbered it incorrectly.
|
the original file back if you somehow clobbered it incorrectly.
|
||||||
type: bool
|
type: bool
|
||||||
default: false
|
default: false
|
||||||
|
keep_mountpoint:
|
||||||
|
description:
|
||||||
|
- Change the default behaviour of state=absent by keeping the mountpoint
|
||||||
|
- With keep_mountpoint=true, state=absent is like unmounted plus the
|
||||||
|
fstab update.
|
||||||
|
- Use it if you care about finding original mountpoint content without failing
|
||||||
|
and want to remove the entry in fstab. If you have no entry to clean in
|
||||||
|
fstab you can use state=unmounted
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
notes:
|
notes:
|
||||||
- As of Ansible 2.3, the O(name) option has been changed to O(path) as
|
- As of Ansible 2.3, the O(name) option has been changed to O(path) as
|
||||||
default, but O(name) still works as well.
|
default, but O(name) still works as well.
|
||||||
|
|
@ -175,6 +186,23 @@ EXAMPLES = r'''
|
||||||
path: /tmp/mnt-pnt
|
path: /tmp/mnt-pnt
|
||||||
state: remounted
|
state: remounted
|
||||||
|
|
||||||
|
# The following will fail on first run
|
||||||
|
# if /home/mydir is not empty after unmounting,
|
||||||
|
# though unmount and remove from fstab are successfull.
|
||||||
|
# It will be successfull on subsequent runs (already unmounted).
|
||||||
|
- name: Unmount and remove from fstab, then if unmount was necessary try to remove mountpoint /home/mydir
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: /home/mydir
|
||||||
|
state: absent
|
||||||
|
# The following will not fail on first run
|
||||||
|
# if /home/mydir is not empty after unmounting.
|
||||||
|
# It will leave /home/mydir and its content (if any) after unmounting.
|
||||||
|
- name: Unmount and remove from fstab, but keep /home/mydir
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: /home/mydir
|
||||||
|
state: absent
|
||||||
|
keep_mountpoint: true
|
||||||
|
|
||||||
# The following will not save changes to fstab, and only be temporary until
|
# The following will not save changes to fstab, and only be temporary until
|
||||||
# a reboot, or until calling "state: unmounted" followed by "state: mounted"
|
# a reboot, or until calling "state: unmounted" followed by "state: mounted"
|
||||||
# on the same "path"
|
# on the same "path"
|
||||||
|
|
@ -779,6 +807,7 @@ def main():
|
||||||
src=dict(type='path'),
|
src=dict(type='path'),
|
||||||
backup=dict(type='bool', default=False),
|
backup=dict(type='bool', default=False),
|
||||||
state=dict(type='str', required=True, choices=['absent', 'absent_from_fstab', 'mounted', 'present', 'unmounted', 'remounted', 'ephemeral']),
|
state=dict(type='str', required=True, choices=['absent', 'absent_from_fstab', 'mounted', 'present', 'unmounted', 'remounted', 'ephemeral']),
|
||||||
|
keep_mountpoint=dict(type='bool', default=False),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
required_if=(
|
required_if=(
|
||||||
|
|
@ -896,7 +925,7 @@ def main():
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
msg="Error unmounting %s: %s" % (name, msg))
|
msg="Error unmounting %s: %s" % (name, msg))
|
||||||
|
|
||||||
if os.path.exists(name):
|
if os.path.exists(name) and module.params['keep_mountpoint'] is False:
|
||||||
try:
|
try:
|
||||||
os.rmdir(name)
|
os.rmdir(name)
|
||||||
except (OSError, IOError) as e:
|
except (OSError, IOError) as e:
|
||||||
|
|
|
||||||
|
|
@ -1132,3 +1132,85 @@
|
||||||
loop:
|
loop:
|
||||||
- /tmp/myfs.img
|
- /tmp/myfs.img
|
||||||
- /tmp/myfs
|
- /tmp/myfs
|
||||||
|
|
||||||
|
- name: Block to test keep_mountpoint option
|
||||||
|
block:
|
||||||
|
- name: Create the mount point
|
||||||
|
ansible.builtin.file:
|
||||||
|
state: directory
|
||||||
|
path: '/tmp/myfs'
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Create empty file for FS aaa
|
||||||
|
community.general.filesize:
|
||||||
|
path: /tmp/myfs.img
|
||||||
|
size: 20M
|
||||||
|
|
||||||
|
- name: Format FS bbb
|
||||||
|
community.general.filesystem:
|
||||||
|
fstype: xfs
|
||||||
|
dev: /tmp/myfs.img
|
||||||
|
|
||||||
|
- name: Put data in the mount point before mounting
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: 'Testing
|
||||||
|
This is the data before mounting
|
||||||
|
'
|
||||||
|
dest: '/tmp/myfs/test_file'
|
||||||
|
mode: '0644'
|
||||||
|
register: file_before_info
|
||||||
|
|
||||||
|
- name: Mount with fstab
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: '/tmp/myfs'
|
||||||
|
fstype: xfs
|
||||||
|
state: mounted
|
||||||
|
src: '/tmp/myfs.img'
|
||||||
|
|
||||||
|
- name: Check data disappears - stat data
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: '/tmp/myfs/test_file'
|
||||||
|
register: file_stat_after_mount
|
||||||
|
- name: Check data disappears - file does not exist
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- file_stat_after_mount['stat']['exists'] == false
|
||||||
|
- name: Put data in the mount point after mounting
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: 'Testing
|
||||||
|
This is the data updated after mounting
|
||||||
|
'
|
||||||
|
dest: '/tmp/myfs/test_file'
|
||||||
|
mode: '0644'
|
||||||
|
register: file_after_info
|
||||||
|
- name: Umount with keep_mountpoint
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: '/tmp/myfs'
|
||||||
|
fstype: xfs
|
||||||
|
state: absent
|
||||||
|
keep_mountpoint: true
|
||||||
|
- name: Check original data reappears - stat data
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: '/tmp/myfs/test_file'
|
||||||
|
register: file_stat_after_umount
|
||||||
|
- name: Check original data reappears - compare checksums
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- file_stat_after_umount['stat']['checksum'] == file_before_info['checksum']
|
||||||
|
always:
|
||||||
|
- name: Remove the first test file
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /tmp/myfs/test_file
|
||||||
|
state: absent
|
||||||
|
- name: Unmount FS
|
||||||
|
ansible.posix.mount:
|
||||||
|
path: /tmp/myfs
|
||||||
|
state: absent
|
||||||
|
- name: Remove the test FS and the second test file
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: '{{ item }}'
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- /tmp/myfs/test_file
|
||||||
|
- /tmp/myfs.img
|
||||||
|
- /tmp/myfs
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,4 @@
|
||||||
ansible.builtin.include_tasks: seboolean.yml
|
ansible.builtin.include_tasks: seboolean.yml
|
||||||
when:
|
when:
|
||||||
- ansible_selinux is defined
|
- ansible_selinux is defined
|
||||||
- ansible_selinux
|
|
||||||
- ansible_selinux.status == 'enabled'
|
- ansible_selinux.status == 'enabled'
|
||||||
|
|
|
||||||
|
|
@ -19,23 +19,21 @@
|
||||||
- name: Debug message for when SELinux is disabled
|
- name: Debug message for when SELinux is disabled
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg: SELinux is disabled
|
msg: SELinux is disabled
|
||||||
when: ansible_selinux is defined and not ansible_selinux
|
when: ansible_selinux is defined and ansible_selinux.status == 'disabled'
|
||||||
|
|
||||||
- name: Debug message for when SELinux is enabled and not disabled
|
- name: Debug message for when SELinux is enabled and not disabled
|
||||||
ansible.builtin.debug:
|
ansible.builtin.debug:
|
||||||
msg: SELinux is {{ ansible_selinux.status }}
|
msg: SELinux is {{ ansible_selinux.status }}
|
||||||
when: ansible_selinux is defined and ansible_selinux
|
when: ansible_selinux is defined
|
||||||
|
|
||||||
- name: Include_tasks for when SELinux is enabled
|
- name: Include_tasks for when SELinux is enabled
|
||||||
ansible.builtin.include_tasks: selinux.yml
|
ansible.builtin.include_tasks: selinux.yml
|
||||||
when:
|
when:
|
||||||
- ansible_selinux is defined
|
- ansible_selinux is defined
|
||||||
- ansible_selinux
|
|
||||||
- ansible_selinux.status == 'enabled'
|
- ansible_selinux.status == 'enabled'
|
||||||
|
|
||||||
- name: Include tasks for selogin when SELinux is enabled
|
- name: Include tasks for selogin when SELinux is enabled
|
||||||
ansible.builtin.include_tasks: selogin.yml
|
ansible.builtin.include_tasks: selogin.yml
|
||||||
when:
|
when:
|
||||||
- ansible_selinux is defined
|
- ansible_selinux is defined
|
||||||
- ansible_selinux
|
|
||||||
- ansible_selinux.status == 'enabled'
|
- ansible_selinux.status == 'enabled'
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue