feature: mount - implement swapon/swapoff (#106)

* Declare functions swapon() & swapoff(), is_swap() & reswap().
* Apply swapon/swapoff for states mounted, unmounted, remounted and
  absent.
* Override default opts and boot when fstype=swap.
* Do not honor 'fstab' when fstype=swap (fail instead).
* Also fail when fstype=swap and 'path' is not 'none' ('-' for Solaris).
* Update module documentation accordingly.
+ Replace all platform.system() calls by a variable.

refactor integration tests

* Improve readability/understanding of what is tested, and what OS is
  targeted.
* Move 'swap' related test cases into dedicated file (swap.yml).
* Add new test cases about swap enabling/disabling.
* Extend tests to FreeBSD when possible.
This commit is contained in:
quidame 2020-11-21 19:17:34 +01:00
parent ecd5ad53e0
commit eafd8f8935
8 changed files with 860 additions and 335 deletions

View file

@ -2,8 +2,9 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2012, Red Hat, inc
# Written by Seth Vidal
# based on the mount modules from salt and puppet
# Copyright: (c) 2021, quidame <quidame@poivron.org>
# Written by Seth Vidal, based on the mount modules from salt and puppet
# Enhanced by quidame (swapon/swapoff support)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
@ -15,15 +16,18 @@ DOCUMENTATION = r'''
module: mount
short_description: Control active and configured mount points
description:
- This module controls active and configured mount points in C(/etc/fstab).
- This module controls active and configured mount points in C(/etc/fstab),
as well as active and configured swap spaces.
author:
- Ansible Core Team
- Seth Vidal (@skvidal)
- quidame (@quidame)
version_added: "1.0.0"
options:
path:
description:
- Path to the mount point (e.g. C(/mnt/files)).
- Path to the mount point (e.g. C(/mnt/files)). Must be C(none) (or C(-)
on Solaris) for swap spaces.
- Before Ansible 2.3 this option was only usable as I(dest), I(destfile) and I(name).
type: path
required: true
@ -31,16 +35,18 @@ options:
src:
description:
- Device (or NFS volume, or something else) to be mounted on I(path).
- Required when I(state) set to C(present) or C(mounted).
- Required when I(state) set to C(present) or C(mounted), or when I(fstype=swap).
type: path
fstype:
description:
- Filesystem type.
- Required when I(state) is C(present) or C(mounted).
- Required when I(state) is C(present) or C(mounted). Also required for
any I(state) to properly handle swap spaces (and then set to c(swap)).
type: str
opts:
description:
- Mount options (see fstab(5), or vfstab(4) on Solaris).
- Mount options (see fstab(5), or vfstab(4) on Solaris) for mountable
filesystems, or swapon(8) options for C(swap) filesystems.
type: str
dump:
description:
@ -64,7 +70,7 @@ options:
description:
- If C(mounted), the device will be actively mounted and appropriately
configured in I(fstab). If the mount point is not present, the mount
point will be created.
point will be created (unless I(fstype=swap)).
- If C(unmounted), the device will be unmounted without changing I(fstab).
- C(present) only specifies that the device is to be configured in
I(fstab) and does not trigger or require a mount.
@ -84,6 +90,9 @@ options:
fstab:
description:
- File to use instead of C(/etc/fstab).
- The filename must not start with a dot, and must end with C(.fstab),
otherwise it is silently ignored. It is also ignored by mount helpers
(for filesystems not natively supported by the C(mount) command).
- You should not use this option unless you really know what you are doing.
- This might be useful if you need to configure mountpoints in a chroot environment.
- OpenBSD does not allow specifying alternate fstab files with mount so do not
@ -93,7 +102,7 @@ options:
boot:
description:
- Determines if the filesystem should be mounted on boot.
- Only applies to Solaris systems.
- Only applies to Solaris systems. Defaults to C(true) unless I(fstype=swap).
type: bool
default: yes
backup:
@ -108,6 +117,11 @@ notes:
- Using C(remounted) with I(opts) set may create unexpected results based on
the existing options already defined on mount, so care should be taken to
ensure that conflicting options are not present before hand.
- Support for swap spaces activation/deactivation as been added in version
1.2.0 of C(ansible.posix).
- Strictly speaking, swap filesystems can't be C(mounted), C(unmounted) or
C(remounted). The module internally calls C(swapon) and C(swapoff) commands
to enable or disable such filesystems and make them usable by the kernel.
'''
EXAMPLES = r'''
@ -169,6 +183,29 @@ EXAMPLES = r'''
opts: rw,sync,hard,intr
state: mounted
fstype: nfs
- name: Enable swap device with priority=1 (Linux)
ansible.posix.mount:
src: /dev/mapper/vg0-swap
fstype: swap
path: none
opts: pri=1
state: mounted
- name: Enable a swapfile (Linux, Solaris)
ansible.posix.mount:
src: /var/swapfile
fstype: swap
path: none
state: mounted
- name: Enable a swapfile (FreeBSD)
ansible.posix.mount:
src: md99
fstype: swap
path: none
opts: file=/var/swapfile
state: mounted
'''
@ -182,6 +219,9 @@ from ansible.module_utils.six import iteritems
from ansible.module_utils._text import to_bytes, to_native
SYSTEM = platform.system().lower()
def write_fstab(module, lines, path):
if module.params['backup']:
module.backup_local(path)
@ -229,7 +269,7 @@ def _set_mount_save_old(module, args):
escaped_args = dict([(k, _escape_fstab(v)) for k, v in iteritems(args)])
new_line = '%(src)s %(name)s %(fstype)s %(opts)s %(dump)s %(passno)s\n'
if platform.system() == 'SunOS':
if SYSTEM == 'sunos':
new_line = (
'%(src)s - %(name)s %(fstype)s %(passno)s %(boot)s %(opts)s\n')
@ -251,16 +291,16 @@ def _set_mount_save_old(module, args):
# Check if we got a valid line for splitting
# (on Linux the 5th and the 6th field is optional)
if (
platform.system() == 'SunOS' and len(fields) != 7 or
platform.system() == 'Linux' and len(fields) not in [4, 5, 6] or
platform.system() not in ['SunOS', 'Linux'] and len(fields) != 6):
SYSTEM == 'sunos' and len(fields) != 7 or
SYSTEM == 'linux' and len(fields) not in [4, 5, 6] or
SYSTEM not in ['sunos', 'linux'] and len(fields) != 6):
to_write.append(line)
continue
ld = {}
if platform.system() == 'SunOS':
if SYSTEM == 'sunos':
(
ld['src'],
dash,
@ -285,8 +325,6 @@ def _set_mount_save_old(module, args):
if (
ld['name'] != escaped_args['name'] or (
# In the case of swap, check the src instead
'src' in args and
ld['name'] == 'none' and
ld['fstype'] == 'swap' and
ld['src'] != args['src'])):
to_write.append(line)
@ -298,7 +336,7 @@ def _set_mount_save_old(module, args):
exists = True
args_to_check = ('src', 'fstype', 'opts', 'dump', 'passno')
if platform.system() == 'SunOS':
if SYSTEM == 'sunos':
args_to_check = ('src', 'fstype', 'passno', 'boot', 'opts')
for t in args_to_check:
@ -341,15 +379,15 @@ def unset_mount(module, args):
# Check if we got a valid line for splitting
if (
platform.system() == 'SunOS' and len(line.split()) != 7 or
platform.system() != 'SunOS' and len(line.split()) != 6):
SYSTEM == 'sunos' and len(line.split()) != 7 or
SYSTEM != 'sunos' and len(line.split()) != 6):
to_write.append(line)
continue
ld = {}
if platform.system() == 'SunOS':
if SYSTEM == 'sunos':
(
ld['src'],
dash,
@ -372,8 +410,6 @@ def unset_mount(module, args):
if (
ld['name'] != escaped_name or (
# In the case of swap, check the src instead
'src' in args and
ld['name'] == 'none' and
ld['fstype'] == 'swap' and
ld['src'] != args['src'])):
to_write.append(line)
@ -395,8 +431,8 @@ def _set_fstab_args(fstab_file):
if (
fstab_file and
fstab_file != '/etc/fstab' and
platform.system().lower() != 'sunos'):
if platform.system().lower().endswith('bsd'):
SYSTEM != 'sunos'):
if SYSTEM.endswith('bsd'):
result.append('-F')
else:
result.append('-T')
@ -413,7 +449,7 @@ def mount(module, args):
name = args['name']
cmd = [mount_bin]
if platform.system().lower() == 'openbsd':
if SYSTEM == 'openbsd':
# Use module.params['fstab'] here as args['fstab'] has been set to the
# default value.
if module.params['fstab'] is not None:
@ -454,7 +490,7 @@ def remount(module, args):
cmd = [mount_bin]
# Multiplatform remount opts
if platform.system().lower().endswith('bsd'):
if SYSTEM.endswith('bsd'):
if module.params['state'] == 'remounted' and args['opts'] != 'defaults':
cmd += ['-u', '-o', args['opts']]
else:
@ -465,7 +501,7 @@ def remount(module, args):
else:
cmd += ['-o', 'remount']
if platform.system().lower() == 'openbsd':
if SYSTEM == 'openbsd':
# Use module.params['fstab'] here as args['fstab'] has been set to the
# default value.
if module.params['fstab'] is not None:
@ -480,7 +516,7 @@ def remount(module, args):
out = err = ''
try:
if platform.system().lower().endswith('bsd'):
if SYSTEM.endswith('bsd'):
# Note: Forcing BSDs to do umount/mount due to BSD remount not
# working as expected (suspect bug in the BSD mount command)
# Interested contributor could rework this to use mount options on
@ -534,7 +570,7 @@ def is_bind_mounted(module, linux_mounts, dest, src=None, fstype=None):
is_mounted = False
if platform.system() == 'Linux' and linux_mounts is not None:
if SYSTEM == 'linux' and linux_mounts is not None:
if src is None:
# That's for unmounted/absent
if dest in linux_mounts:
@ -549,7 +585,7 @@ def is_bind_mounted(module, linux_mounts, dest, src=None, fstype=None):
rc, out, err = module.run_command(cmd)
mounts = []
if len(out):
if len(out) > 0:
mounts = to_native(out).strip().split('\n')
for mnt in mounts:
@ -639,6 +675,123 @@ def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"):
return mounts
def is_swap(module, args):
"""Return True if the device/file is an active swap space, False otherwise."""
if module.params['fstype'] != 'swap':
return False
if SYSTEM == 'sunos':
swap_bin = module.get_bin_path('swap', required=True)
cmd = [swap_bin, '-l']
elif SYSTEM.endswith('bsd'):
swapctl_bin = module.get_bin_path('swapctl', required=True)
cmd = [swapctl_bin, '-l']
else:
# swapon is supposed to be the standard command
swapon_bin = module.get_bin_path('swapon', required=True)
cmd = [swapon_bin]
rc, out, err = module.run_command(cmd)
if rc:
module.fail_json(msg="Error while querying active swaps: %s" % err)
# Get the first field of each line but the header
devices = [x.split()[0] for x in out.splitlines()[1:]]
dev = os.path.realpath(args['src'])
if SYSTEM == 'linux':
if args['src'].startswith('UUID='):
uuid_path = os.path.join('/dev/disk/by-uuid', args['src'].split('=')[1])
dev = os.path.realpath(uuid_path)
elif args['src'].startswith('LABEL='):
label_path = os.path.join('/dev/disk/by-label', args['src'].split('=')[1])
dev = os.path.realpath(label_path)
elif SYSTEM == 'freebsd':
if args['src'].startswith('md'):
dev = os.path.join('/dev', args['src'])
return bool(dev in devices)
def swapon(module, args):
"""Activate a swap device/file with the proper options."""
if SYSTEM == 'sunos':
swap_bin = module.get_bin_path('swap', required=True)
cmd = [swap_bin, '-a']
elif SYSTEM.endswith('bsd'):
swapctl_bin = module.get_bin_path('swapctl', required=True)
cmd = [swapctl_bin, '-a']
else:
# swapon is supposed to be the standard command, isn't it ?
swapon_bin = module.get_bin_path('swapon', required=True)
cmd = [swapon_bin]
# Only 'swapon -a' applies options from fstab, otherwise they are ignored
# unless provided on command line with '-o opts'. But not all versions of
# swapon accept -o or --options. So we don't use it here, but at least we
# keep the 'priority' and 'discard' flags available on Linux.
if SYSTEM == 'linux':
for opt in args['opts'].split(','):
if opt.startswith('pri='):
cmd += ['-p', opt.split('=')[1]]
elif opt.startswith('discard'):
cmd += ['--%s' % opt]
if SYSTEM == 'freebsd' and args['src'].startswith('md'):
cmd += [os.path.join('/dev', args['src'])]
else:
cmd += [args['src']]
rc, out, err = module.run_command(cmd)
if rc:
return rc, out + err
return 0, ''
def swapoff(module, args):
"""Deactivate a swap device/file."""
if SYSTEM == 'sunos':
swap_bin = module.get_bin_path('swap', required=True)
cmd = [swap_bin, '-d']
elif SYSTEM.endswith('bsd'):
swapctl_bin = module.get_bin_path('swapctl', required=True)
cmd = [swapctl_bin, '-d']
else:
# swapoff is supposed to be the standard command, isn't it ?
swapoff_bin = module.get_bin_path('swapoff', required=True)
cmd = [swapoff_bin]
if SYSTEM == 'freebsd' and args['src'].startswith('md'):
cmd += [os.path.join('/dev', args['src'])]
else:
cmd += [args['src']]
rc, out, err = module.run_command(cmd)
if rc:
return rc, out + err
return 0, ''
def reswap(module, args):
"""Deactivate a swap device/file and reactivate it with new options."""
if is_swap(module, args):
rc, msg = swapoff(module, args)
if rc:
return rc, msg
rc, msg = swapon(module, args)
if rc:
return rc, msg
return 0, ''
def main():
module = AnsibleModule(
argument_spec=dict(
@ -651,22 +804,43 @@ def main():
passno=dict(type='str', no_log=False),
src=dict(type='path'),
backup=dict(type='bool', default=False),
state=dict(type='str', required=True, choices=['absent', 'mounted', 'present', 'unmounted', 'remounted']),
state=dict(type='str', required=True,
choices=['absent', 'mounted', 'present', 'unmounted', 'remounted']),
),
supports_check_mode=True,
required_if=(
['state', 'mounted', ['src', 'fstype']],
['state', 'present', ['src', 'fstype']],
['fstype', 'swap', ['src']],
),
)
fstype = module.params['fstype']
# swapon/swapoff (and the likes) don't honor alternative fstab locations
# the same way the mount command does, that could make things very, very
# complicated...
if fstype == 'swap':
if SYSTEM == 'sunos':
swap_fstab_file = '/etc/vfstab'
swap_mountpoint = '-'
else:
swap_fstab_file = '/etc/fstab'
swap_mountpoint = 'none'
if module.params['fstab'] not in (None, swap_fstab_file):
module.fail_json(msg="option 'fstype=swap' does not support alternative fstab locations")
if module.params['path'] != swap_mountpoint:
module.fail_json(msg="swap filesystems can't be mounted, please set path to '%s'" %
swap_mountpoint)
# solaris args:
# name, src, fstype, opts, boot, passno, state, fstab=/etc/vfstab
# linux args:
# name, src, fstype, opts, dump, passno, state, fstab=/etc/fstab
# Note: Do not modify module.params['fstab'] as we need to know if the user
# explicitly specified it in mount() and remount()
if platform.system().lower() == 'sunos':
if SYSTEM == 'sunos':
args = dict(
name=module.params['path'],
opts='-',
@ -676,6 +850,10 @@ def main():
)
if args['fstab'] is None:
args['fstab'] = '/etc/vfstab'
# swap spaces are used internally by kernels and have no mountpoint
if fstype == 'swap':
args['boot'] = 'no'
else:
args = dict(
name=module.params['path'],
@ -687,15 +865,18 @@ def main():
if args['fstab'] is None:
args['fstab'] = '/etc/fstab'
# Override default value of options field for swap filesystems
if fstype == 'swap':
args['opts'] = 'sw'
# FreeBSD doesn't have any 'default' so set 'rw' instead
if platform.system() == 'FreeBSD':
elif SYSTEM == 'freebsd':
args['opts'] = 'rw'
linux_mounts = []
# Cache all mounts here in order we have consistent results if we need to
# call is_bind_mounted() multiple times
if platform.system() == 'Linux':
if SYSTEM == 'linux':
linux_mounts = get_linux_mounts(module)
if linux_mounts is None:
@ -731,7 +912,7 @@ def main():
# changed in fstab then remount it.
state = module.params['state']
name = module.params['path']
name = args['name']
changed = False
if state == 'absent':
@ -744,6 +925,12 @@ def main():
if res:
module.fail_json(
msg="Error unmounting %s: %s" % (name, msg))
elif is_swap(module, args):
res, msg = swapoff(module, args)
if res:
module.fail_json(
msg="Error disabling swap space %s: %s" % (args['src'], msg))
if os.path.exists(name):
try:
@ -759,10 +946,19 @@ def main():
module.fail_json(
msg="Error unmounting %s: %s" % (name, msg))
changed = True
elif is_swap(module, args):
if not module.check_mode:
res, msg = swapoff(module, args)
if res:
module.fail_json(
msg="Error disabling swap space %s: %s" % (args['src'], msg))
changed = True
elif state == 'mounted':
dirs_created = []
if not os.path.exists(name) and not module.check_mode:
if fstype != 'swap' and not os.path.exists(name) and not module.check_mode:
try:
# Something like mkdir -p but with the possibility to undo.
# Based on some copy-paste from the "file" module.
@ -798,10 +994,17 @@ def main():
if changed and not module.check_mode:
res, msg = remount(module, args)
changed = True
elif is_swap(module, args):
if changed and not module.check_mode:
res, msg = reswap(module, args)
changed = True
else:
changed = True
if not module.check_mode:
if fstype == 'swap':
res, msg = swapon(module, args)
else:
res, msg = mount(module, args)
if res:
@ -820,11 +1023,22 @@ def main():
except Exception:
pass
module.fail_json(msg="Error mounting %s: %s" % (name, msg))
if fstype == 'swap':
error_msg = "Error enabling swap space %s: %s" % (args['src'], msg)
else:
error_msg = "Error mounting %s: %s" % (name, msg)
module.fail_json(msg=error_msg)
elif state == 'present':
name, changed = set_mount(module, args)
elif state == 'remounted':
if not module.check_mode:
if fstype == 'swap':
res, msg = reswap(module, args)
if res:
module.fail_json(msg="Error re-enabling swap space %s: %s" % (args['src'], msg))
else:
res, msg = remount(module, args)
if res:

View file

@ -0,0 +1,95 @@
---
# Tasks to validate bind mounts (i.e. mount of a directory to another one).
# Linux and FreeBSD only.
- name: Create the mount point (the target of the mount)
file:
state: directory
path: '{{ output_dir }}/mount_dest'
- name: Create a directory to bind mount (the source of the mount)
file:
state: directory
path: '{{ output_dir }}/mount_source'
- name: Put something in the directory so we see that it worked
copy:
content: 'Testing
'
dest: '{{ output_dir }}/mount_source/test_file'
register: orig_info
- name: Bind mount a directory
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: "{{ 'none' if ansible_system == 'Linux' else 'nullfs' }}"
opts: "{{ 'bind' if ansible_system == 'Linux' else omit }}"
register: bind_result
- name: get checksum for bind mounted file
stat:
path: '{{ output_dir }}/mount_dest/test_file'
when: ansible_system in ('FreeBSD', 'Linux')
register: dest_stat
- name: assert the bind mount was successful
assert:
that:
- bind_result is changed
- dest_stat.stat.exists
- orig_info.checksum == dest_stat.stat.checksum
- name: Bind mount a directory again
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: "{{ 'none' if ansible_system == 'Linux' else 'nullfs' }}"
opts: "{{ 'bind' if ansible_system == 'Linux' else omit }}"
register: bind_result
- name: Make sure we didn't mount a second time
assert:
that:
- bind_result is not changed
- name: Remount directory with different options
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: "{{ 'none' if ansible_system == 'Linux' else 'nullfs' }}"
opts: "{{ 'bind,' if ansible_system == 'Linux' else '' }}ro"
register: bind_result
- name: Get mount options
shell:
cmd: "mount | grep mount_dest | grep -E -w '(ro|read-only)' | wc -l"
register: remount_options
- name: Make sure the filesystem now has the new opts
assert:
that:
- bind_result is changed
- '''1'' in remount_options.stdout'
- 1 == remount_options.stdout_lines | length
- name: Unmount the bind mount
mount:
name: '{{ output_dir }}/mount_dest'
state: absent
register: unmount_result
- name: Check if the file still exists in dest
stat:
path: '{{ output_dir }}/mount_dest/test_file'
register: dest_stat
- name: Assert that we unmounted
assert:
that:
- unmount_result is changed
- not dest_stat.stat.exists

View file

@ -0,0 +1,32 @@
---
# Tasks to validate the two last fields in fstab, that are optional on Linux,
# are added if missing.
- name: Create fstab record with missing last two fields
copy:
dest: /etc/fstab
content: '//nas/photo /home/jik/pictures cifs defaults,credentials=/etc/security/nas.creds,uid=jik,gid=users,forceuid,forcegid,noserverino,_netdev
'
- name: Try to change the fstab record with the missing last two fields
mount:
src: //nas/photo
path: /home/jik/pictures
fstype: cifs
opts: defaults,credentials=/etc/security/nas.creds,uid=jik,gid=users,forceuid,forcegid,noserverino,_netdev,x-systemd.mount-timeout=0
state: present
register: optional_fields_update
- name: Get the content of the fstab file
command:
cmd: cat /etc/fstab
changed_when: false
register: optional_fields_content
- name: Check if the line containing the missing last two fields was changed
assert:
that:
- optional_fields_update is changed
- ''' 0 0'' in optional_fields_content.stdout'
- 1 == optional_fields_content.stdout_lines | length

View file

@ -0,0 +1,93 @@
---
# Tasks to validate that adding a swap record in fstab doesn't replace another
# one, unless the 'src' (not the 'path') is the same. For Linux and FreeBSD.
- name: Create fstab record for the first swap device
mount:
name: none
src: /dev/swap1
opts: sw
fstype: swap
state: present
register: swap1_created
- name: Try to create fstab record for the first swap device again
mount:
name: none
src: /dev/swap1
opts: sw
fstype: swap
state: present
register: swap1_created_again
- name: Check that we created the swap1 record
assert:
that:
- swap1_created is changed
- swap1_created_again is not changed
- name: Create fstab record for the second swap device
mount:
name: none
src: /dev/swap2
opts: sw
fstype: swap
state: present
register: swap2_created
- name: Try to create fstab record for the second swap device again
mount:
name: none
src: /dev/swap1
opts: sw
fstype: swap
state: present
register: swap2_created_again
- name: Check that we created the swap2 record
assert:
that:
- swap2_created is changed
- swap2_created_again is not changed
- name: Remove the fstab record for the first swap device
mount:
name: none
src: /dev/swap1
state: absent
register: swap1_removed
- name: Try to remove the fstab record for the first swap device again
mount:
name: none
src: /dev/swap1
state: absent
register: swap1_removed_again
- name: Check that we removed the swap1 record
assert:
that:
- swap1_removed is changed
- swap1_removed_again is not changed
- name: Remove the fstab record for the second swap device
mount:
name: none
src: /dev/swap2
state: absent
register: swap2_removed
- name: Try to remove the fstab record for the second swap device again
mount:
name: none
src: /dev/swap2
state: absent
register: swap2_removed_again
- name: Check that we removed the swap2 record
assert:
that:
- swap2_removed is changed
- swap2_removed_again is not changed

View file

@ -1,296 +1,29 @@
- name: Create the mount point
file:
state: directory
path: '{{ output_dir }}/mount_dest'
- name: Create a directory to bind mount
file:
state: directory
path: '{{ output_dir }}/mount_source'
- name: Put something in the directory so we see that it worked
copy:
content: 'Testing
---
- name: Include tasks to validate bind mount management (Linux, FreeBSD)
include_tasks: bind_mount.yml
when:
- ansible_system in ['Linux', 'FreeBSD']
'
dest: '{{ output_dir }}/mount_source/test_file'
register: orig_info
- name: Bind mount a filesystem (Linux)
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: None
opts: bind
when: ansible_system == 'Linux'
register: bind_result_linux
- name: Bind mount a filesystem (FreeBSD)
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: nullfs
when: ansible_system == 'FreeBSD'
register: bind_result_freebsd
- name: get checksum for bind mounted file
stat:
path: '{{ output_dir }}/mount_dest/test_file'
when: ansible_system in ('FreeBSD', 'Linux')
register: dest_stat
- name: assert the bind mount was successful
assert:
that:
- (ansible_system == 'Linux' and bind_result_linux['changed']) or (ansible_system == 'FreeBSD' and bind_result_freebsd['changed'])
- dest_stat['stat']['exists']
- orig_info['checksum'] == dest_stat['stat']['checksum']
when: ansible_system in ('FreeBSD', 'Linux')
- name: Bind mount a filesystem (Linux)
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: None
opts: bind
when: ansible_system == 'Linux'
register: bind_result_linux
- name: Bind mount a filesystem (FreeBSD)
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: nullfs
when: ansible_system == 'FreeBSD'
register: bind_result_freebsd
- name: Make sure we didn't mount a second time
assert:
that:
- (ansible_system == 'Linux' and not bind_result_linux['changed']) or (ansible_system == 'FreeBSD' and not bind_result_freebsd['changed'])
when: ansible_system in ('FreeBSD', 'Linux')
- name: Remount filesystem with different opts (Linux)
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: None
opts: bind,ro
when: ansible_system == 'Linux'
register: bind_result_linux
- name: Remount filesystem with different opts (FreeBSD)
mount:
src: '{{ output_dir }}/mount_source'
name: '{{ output_dir }}/mount_dest'
state: mounted
fstype: nullfs
opts: ro
when: ansible_system == 'FreeBSD'
register: bind_result_freebsd
- name: Get mount options
shell: mount | grep mount_dest | grep -E -w '(ro|read-only)' | wc -l
register: remount_options
- name: Make sure the filesystem now has the new opts
assert:
that:
- (ansible_system == 'Linux' and bind_result_linux['changed']) or (ansible_system == 'FreeBSD' and bind_result_freebsd['changed'])
- '''1'' in remount_options.stdout'
- 1 == remount_options.stdout_lines | length
when: ansible_system in ('FreeBSD', 'Linux')
- name: Unmount the bind mount
mount:
name: '{{ output_dir }}/mount_dest'
state: absent
when: ansible_system in ('Linux', 'FreeBSD')
register: unmount_result
- name: Make sure the file no longer exists in dest
stat:
path: '{{ output_dir }}/mount_dest/test_file'
when: ansible_system in ('FreeBSD', 'Linux')
register: dest_stat
- name: Check that we unmounted
assert:
that:
- unmount_result['changed']
- not dest_stat['stat']['exists']
when: ansible_system in ('FreeBSD', 'Linux')
- name: Create fstab record for the first swap file
mount:
name: none
src: /tmp/swap1
opts: sw
fstype: swap
state: present
register: swap1_created
when: ansible_system in ('Linux')
- name: Try to create fstab record for the first swap file again
mount:
name: none
src: /tmp/swap1
opts: sw
fstype: swap
state: present
register: swap1_created_again
when: ansible_system in ('Linux')
- name: Check that we created the swap1 record
assert:
that:
- swap1_created['changed']
- not swap1_created_again['changed']
when: ansible_system in ('Linux')
- name: Create fstab record for the second swap file
mount:
name: none
src: /tmp/swap2
opts: sw
fstype: swap
state: present
register: swap2_created
when: ansible_system in ('Linux')
- name: Try to create fstab record for the second swap file again
mount:
name: none
src: /tmp/swap1
opts: sw
fstype: swap
state: present
register: swap2_created_again
when: ansible_system in ('Linux')
- name: Check that we created the swap2 record
assert:
that:
- swap2_created['changed']
- not swap2_created_again['changed']
when: ansible_system in ('Linux')
- name: Remove the fstab record for the first swap file
mount:
name: none
src: /tmp/swap1
state: absent
register: swap1_removed
when: ansible_system in ('Linux')
- name: Try to remove the fstab record for the first swap file again
mount:
name: none
src: /tmp/swap1
state: absent
register: swap1_removed_again
when: ansible_system in ('Linux')
- name: Check that we removed the swap1 record
assert:
that:
- swap1_removed['changed']
- not swap1_removed_again['changed']
when: ansible_system in ('Linux')
- name: Remove the fstab record for the second swap file
mount:
name: none
src: /tmp/swap2
state: absent
register: swap2_removed
when: ansible_system in ('Linux')
- name: Try to remove the fstab record for the second swap file again
mount:
name: none
src: /tmp/swap2
state: absent
register: swap2_removed_again
when: ansible_system in ('Linux')
- name: Check that we removed the swap2 record
assert:
that:
- swap2_removed['changed']
- not swap2_removed_again['changed']
when: ansible_system in ('Linux')
- name: Create fstab record with missing last two fields
copy:
dest: /etc/fstab
content: '//nas/photo /home/jik/pictures cifs defaults,credentials=/etc/security/nas.creds,uid=jik,gid=users,forceuid,forcegid,noserverino,_netdev
'
when: ansible_system in ('Linux')
- name: Try to change the fstab record with the missing last two fields
mount:
src: //nas/photo
path: /home/jik/pictures
fstype: cifs
opts: defaults,credentials=/etc/security/nas.creds,uid=jik,gid=users,forceuid,forcegid,noserverino,_netdev,x-systemd.mount-timeout=0
state: present
register: optional_fields_update
when: ansible_system in ('Linux')
- name: Get the content of the fstab file
shell: cat /etc/fstab
register: optional_fields_content
when: ansible_system in ('Linux')
- name: Check if the line containing the missing last two fields was changed
assert:
that:
- optional_fields_update['changed']
- ''' 0 0'' in optional_fields_content.stdout'
- 1 == optional_fields_content.stdout_lines | length
when: ansible_system in ('Linux')
- name: Block to test remounted option
block:
- name: Create empty file
command: dd if=/dev/zero of=/tmp/myfs.img bs=1048576 count=20
when: ansible_system in ('Linux')
- name: Format FS
when: ansible_system in ('Linux')
community.general.system.filesystem:
fstype: ext3
dev: /tmp/myfs.img
- name: Mount the FS for the first time
mount:
path: /tmp/myfs
src: /tmp/myfs.img
fstype: ext2
state: mounted
when: ansible_system in ('Linux')
- name: Get the last write time
shell: 'dumpe2fs /tmp/myfs.img 2>/dev/null | grep -i last write time: |cut -d: -f2-'
register: last_write_time
when: ansible_system in ('Linux')
- name: Wait 2 second
pause:
seconds: 2
when: ansible_system in ('Linux')
- name: Test if the FS is remounted
mount:
path: /tmp/myfs
state: remounted
when: ansible_system in ('Linux')
- name: Get again the last write time
shell: 'dumpe2fs /tmp/myfs.img 2>/dev/null | grep -i last write time: |cut -d: -f2-'
register: last_write_time2
when: ansible_system in ('Linux')
- name: Fail if they are the same
fail:
msg: Filesytem was not remounted, testing of the module failed!
when: last_write is defined and last_write_time2 is defined and last_write_time.stdout == last_write_time2.stdout and ansible_system in ('Linux')
- name: Remount filesystem with different opts using remounted option (Linux only)
mount:
path: /tmp/myfs
state: remounted
opts: rw,noexec
when: ansible_system == 'Linux'
- name: Get remounted options (Linux only)
shell: mount | grep myfs | grep -E -w 'noexec' | wc -l
register: remounted_options
when: ansible_system == 'Linux'
- name: Make sure the filesystem now has the new opts after using remounted (Linux only)
assert:
that:
- "'1' in remounted_options.stdout"
- "1 == remounted_options.stdout_lines | length"
when: ansible_system == 'Linux'
always:
- name: Umount the test FS
mount:
path: /tmp/myfs
src: /tmp/myfs.img
opts: loop
state: absent
when: ansible_system in ('Linux')
- name: Remove the test FS
file:
path: '{{ item }}'
state: absent
loop:
- /tmp/myfs.img
- /tmp/myfs
when: ansible_system in ('Linux')
- name: Include tasks to validate optional fields management in fstab (Linux)
include_tasks: fstab_last_fields.yml
when:
- ansible_system in ['Linux']
- name: Include tasks to validate state=remounted behaviour (Linux)
include_tasks: remount.yml
when:
- ansible_system in ['Linux']
- name: Include tasks to validate multi swap management in fstab (Linux)
include_tasks: fstab_multi_swap.yml
when:
- ansible_system in ['Linux', 'FreeBSD']
- name: Include tasks to validate enabling/disabling a swapfile (Linux, FreeBSD)
include_tasks: "swapfile_{{ ansible_system | lower }}.yml"
when:
- ansible_system in ['Linux', 'FreeBSD']

View file

@ -0,0 +1,65 @@
---
# Tasks to validate state=remounted behaves as expected.
# Linux only.
- name: Create empty file
command:
cmd: "dd if=/dev/zero of=/tmp/myfs.img bs=1048576 count=20"
- name: Format FS
community.general.filesystem:
fstype: ext3
dev: /tmp/myfs.img
- name: Mount the FS for the first time
mount:
path: /tmp/myfs
src: /tmp/myfs.img
fstype: ext3
state: mounted
- name: Get the mount counter value
shell:
cmd: "dumpe2fs /tmp/myfs.img 2>/dev/null | grep -i '^mount count:'"
changed_when: false
register: mount_count1
- name: Get the current mount as exposed in /proc/mounts
command:
cmd: "grep /tmp/myfs /proc/mounts"
changed_when: false
register: proc_mounts1
- name: Remount the filesystem
mount:
path: /tmp/myfs
opts: nosuid
state: remounted
- name: Get again the mount counter value
shell:
cmd: "dumpe2fs /tmp/myfs.img 2>/dev/null | grep -i '^mount count:'"
changed_when: false
register: mount_count2
- name: Get again the current mount as exposed in /proc/mounts
command:
cmd: "grep /tmp/myfs /proc/mounts"
changed_when: false
register: proc_mounts2
- name: Assert that mount has changed despite its counter has not
assert:
that:
- mount_count1.stdout == mount_count2.stdout
- proc_mounts1.stdout != proc_mounts2.stdout
- name: Unmount the FS
mount:
path: /tmp/myfs
state: absent
- name: Remove disk image
file:
path: /tmp/myfs.img
state: absent

View file

@ -0,0 +1,139 @@
---
# Tasks to validate swapfile management (enabling/disabling its use) on FreeBSD.
# The swapfile MUST be associated to a memory disk, that becomes the 'src'.
- name: Swap off all (try to cleanup previous erraneous tests)
command:
cmd: swapoff -a
- name: Try to activate swapfile
vars:
swapfile: /var/swapfile0
md_unit: 99
md_name: "md{{ md_unit }}"
md_path: "/dev/{{ md_name }}"
block:
- name: Create a file to be used for memory swapping
command:
cmd: "dd if=/dev/zero of={{ swapfile }} bs=1M count=64"
creates: "{{ swapfile }}"
- name: Setup permissions suitable for swap usage
file:
path: "{{ swapfile }}"
mode: u=rw,go=
state: file
- name: Attach a memory disk to the swap file
command:
cmd: "mdconfig -a -t vnode -u {{ md_unit }} -f {{ swapfile }}"
creates: "{{ md_path }}"
- name: Get swap info (0)
command: swapctl -l
register: swap_info_0
changed_when: false
- name: Assert that memory disk {{ md_path }} is not used by swap
assert:
that:
- swap_info_0.stdout is not search(md_path)
- name: Enable swap file
mount:
path: none
src: "{{ md_name }}"
fstype: swap
opts: "sw,file={{ swapfile }}"
state: mounted
register: swap_enabled_1
- name: Get swap info (1)
command: swapctl -l
register: swap_info_1
changed_when: false
- name: Assert that swap file is enabled
assert:
that:
- swap_enabled_1 is changed
- swap_info_1.stdout is search(md_path)
- name: Enable swap file, again
mount:
path: none
src: "{{ md_name }}"
fstype: swap
opts: "sw,file={{ swapfile }}"
state: mounted
register: swap_enabled_2
- name: Get swap info (2)
command: swapctl -l
register: swap_info_2
changed_when: false
- name: Assert that nothing changed
assert:
that:
- swap_enabled_2 is not changed
- swap_info_2.stdout == swap_info_1.stdout
always:
- name: Disable swap file
mount:
path: none
src: "{{ md_name }}"
fstype: swap
state: absent
register: swap_disabled_1
- name: Get swap info (3)
command: swapctl -l
register: swap_info_3
changed_when: false
- name: Assert that swap file is disabled
assert:
that:
- swap_disabled_1 is changed
- swap_info_3.stdout == swap_info_0.stdout
- name: Disable swap file, again
mount:
path: none
src: "{{ md_name }}"
fstype: swap
state: absent
register: swap_disabled_2
- name: Get swap info (4)
command: swapctl -l
register: swap_info_4
changed_when: false
- name: Assert that swap file is disabled and nothing changed
assert:
that:
- swap_disabled_2 is not changed
- swap_info_4.stdout == swap_info_3.stdout
- name: Detach memory disk
command:
cmd: "mdconfig -d -u {{ md_unit }}"
removes: "{{ md_path }}"
- name: Remove swap file
file:
path: "{{ swapfile }}"
state: absent
- name: Swap on all (try to restore pre-test state)
command:
cmd: swapon -a

View file

@ -0,0 +1,154 @@
---
# Tasks to validate swapfile management (enabling/disabling its use) on Linux.
# The swapfile COULD be associated to a loop device, that would become the 'src'.
- name: Swap off all (try to cleanup previous erraneous tests)
command:
cmd: swapoff -a
- name: Try to activate swapfile
vars:
swapfile: /var/swapfile0
block:
- name: Create a file to be used for memory swapping
command:
cmd: "dd if=/dev/zero of={{ swapfile }} bs=1M count=64"
creates: "{{ swapfile }}"
- name: Setup permissions suitable for swap usage
file:
path: "{{ swapfile }}"
mode: u=rw,go=
state: file
- name: Create a swap filesystem into the dedicated file
community.general.filesystem:
dev: "{{ swapfile }}"
fstype: swap
- name: Get swap info (0)
command: swapon --noheadings --show=name,prio
register: swap_info_0
changed_when: false
- name: Assert that swap file {{ swapfile }} is not used by swap
assert:
that:
- swap_info_0.stdout | length == 0
- name: Enable swap file with priority 1234
mount:
path: none
src: "{{ swapfile }}"
fstype: swap
opts: pri=1234
state: mounted
register: swap_enabled_1
- name: Get swap info (1)
command: swapon --noheadings --show=name,prio
register: swap_info_1
changed_when: false
- name: Assert that swap file is enabled with priority 1234
assert:
that:
- swap_enabled_1 is changed
- swap_info_1.stdout_lines[0].split()[1] | int == 1234
- name: Update swap priority to 10
mount:
path: none
src: "{{ swapfile }}"
fstype: swap
opts: pri=10
state: mounted
register: swap_enabled_2
- name: Get swap info (2)
command: swapon --noheadings --show=name,prio
register: swap_info_2
changed_when: false
- name: Assert that swap file is enabled with priority 10
assert:
that:
- swap_enabled_2 is changed
- swap_info_2.stdout_lines[0].split()[1] | int == 10
- name: Update swap priority to 10, again
mount:
path: none
src: "{{ swapfile }}"
fstype: swap
opts: pri=10
state: mounted
register: swap_enabled_3
- name: Get swap info (3)
command: swapon --noheadings --show=name,prio
register: swap_info_3
changed_when: false
- name: Assert that nothing changed
assert:
that:
- swap_enabled_3 is not changed
- swap_info_3.stdout_lines == swap_info_2.stdout_lines
always:
- name: Disable swap file
mount:
path: none
src: "{{ swapfile }}"
fstype: swap
state: absent
register: swap_disabled_1
- name: Get swap info (4)
command: swapon --noheadings --show=name,prio
register: swap_info_4
changed_when: false
- name: Assert that swap file is disabled
assert:
that:
- swap_disabled_1 is changed
- swap_info_4.stdout_lines == swap_info_0.stdout_lines
- name: Disable swap file, again
mount:
path: none
src: "{{ swapfile }}"
fstype: swap
state: absent
register: swap_disabled_2
- name: Get swap info (5)
command: swapon --noheadings --show=name,prio
register: swap_info_5
changed_when: false
- name: Assert that swap file is disabled and nothing changed
assert:
that:
- swap_disabled_2 is not changed
- swap_info_5.stdout_lines == swap_info_0.stdout_lines
- name: Remove swap file
file:
path: "{{ swapfile }}"
state: absent
- name: Swap on all (try to restore pre-test state)
command:
cmd: swapon -a