Skip to content

Commit 3b667ed

Browse files
committed
feature: mount - implement swapon/swapoff (ansible-collections#106)
* Declare functions swapon() & swapoff(), is_swap() & reswap(). * Apply swapon/swapoff for states mounted, unmounted, remounted and absent. * Move 'swap' related test cases into dedicated file (swap.yml) * Add new test cases about swap enabling/disabling * Update documentation
1 parent ecd5ad5 commit 3b667ed

File tree

3 files changed

+408
-109
lines changed

3 files changed

+408
-109
lines changed

plugins/modules/mount.py

Lines changed: 154 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
# -*- coding: utf-8 -*-
33

44
# Copyright: (c) 2012, Red Hat, inc
5-
# Written by Seth Vidal
6-
# based on the mount modules from salt and puppet
5+
# Copyright: (c) 2021, quidame <[email protected]>
6+
# Written by Seth Vidal, based on the mount modules from salt and puppet
7+
# Enhanced by quidame (swapon/swapoff support)
78
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
89

910
from __future__ import absolute_import, division, print_function
@@ -15,32 +16,36 @@
1516
module: mount
1617
short_description: Control active and configured mount points
1718
description:
18-
- This module controls active and configured mount points in C(/etc/fstab).
19+
- This module controls active and configured mount points in C(/etc/fstab),
20+
as well as active and configured swap spaces.
1921
author:
2022
- Ansible Core Team
2123
- Seth Vidal (@skvidal)
24+
- quidame (@quidame)
2225
version_added: "1.0.0"
2326
options:
2427
path:
2528
description:
26-
- Path to the mount point (e.g. C(/mnt/files)).
29+
- Path to the mount point (e.g. C(/mnt/files)). Must be C(none) for swap spaces.
2730
- Before Ansible 2.3 this option was only usable as I(dest), I(destfile) and I(name).
2831
type: path
2932
required: true
3033
aliases: [ name ]
3134
src:
3235
description:
3336
- Device (or NFS volume, or something else) to be mounted on I(path).
34-
- Required when I(state) set to C(present) or C(mounted).
37+
- Required when I(state) set to C(present) or C(mounted), or when I(fstype=swap).
3538
type: path
3639
fstype:
3740
description:
3841
- Filesystem type.
39-
- Required when I(state) is C(present) or C(mounted).
42+
- Required when I(state) is C(present) or C(mounted). Also required for
43+
any I(state) to properly handle swap spaces (and then set to c(swap)).
4044
type: str
4145
opts:
4246
description:
43-
- Mount options (see fstab(5), or vfstab(4) on Solaris).
47+
- Mount options (see fstab(5), or vfstab(4) on Solaris) for mountable
48+
filesystems, or swapon(8) options for C(swap) filesystems.
4449
type: str
4550
dump:
4651
description:
@@ -64,7 +69,8 @@
6469
description:
6570
- If C(mounted), the device will be actively mounted and appropriately
6671
configured in I(fstab). If the mount point is not present, the mount
67-
point will be created.
72+
point will be created (unless I(path=none), as expected for C(swap)
73+
filesystems).
6874
- If C(unmounted), the device will be unmounted without changing I(fstab).
6975
- C(present) only specifies that the device is to be configured in
7076
I(fstab) and does not trigger or require a mount.
@@ -84,6 +90,9 @@
8490
fstab:
8591
description:
8692
- File to use instead of C(/etc/fstab).
93+
- The filename must not start with a dot, and must end with C(.fstab),
94+
otherwise it is silently ignored. It is also ignored by mount helpers
95+
(for filesystems not natively supported by the C(mount) command).
8796
- You should not use this option unless you really know what you are doing.
8897
- This might be useful if you need to configure mountpoints in a chroot environment.
8998
- OpenBSD does not allow specifying alternate fstab files with mount so do not
@@ -108,6 +117,11 @@
108117
- Using C(remounted) with I(opts) set may create unexpected results based on
109118
the existing options already defined on mount, so care should be taken to
110119
ensure that conflicting options are not present before hand.
120+
- Support for swap spaces activation/deactivation as been added in version
121+
1.2.0 of C(ansible.posix).
122+
- Strictly speaking, swap filesystems can't be C(mounted), C(unmounted) or
123+
C(remounted). The module internally calls C(swapon) and C(swapoff) commands
124+
to enable or disable such filesystems and make them usable by the kernel.
111125
'''
112126

113127
EXAMPLES = r'''
@@ -169,11 +183,27 @@
169183
opts: rw,sync,hard,intr
170184
state: mounted
171185
fstype: nfs
186+
187+
- name: Enable swap device with priority=1
188+
ansible.posix.mount:
189+
src: /dev/mapper/vg0-swap
190+
fstype: swap
191+
path: none
192+
opts: pri=1
193+
state: mounted
194+
195+
- name: Disable swap file and keep its record in fstab
196+
ansible.posix.mount:
197+
src: /var/swapfile
198+
fstype: swap
199+
path: none
200+
state: unmounted
172201
'''
173202

174203

175204
import errno
176205
import os
206+
import re
177207
import platform
178208

179209
from ansible.module_utils.basic import AnsibleModule
@@ -546,10 +576,10 @@ def is_bind_mounted(module, linux_mounts, dest, src=None, fstype=None):
546576
else:
547577
bin_path = module.get_bin_path('mount', required=True)
548578
cmd = '%s -l' % bin_path
549-
rc, out, err = module.run_command(cmd)
579+
_, out, _ = module.run_command(cmd)
550580
mounts = []
551581

552-
if len(out):
582+
if len(out) > 0:
553583
mounts = to_native(out).strip().split('\n')
554584

555585
for mnt in mounts:
@@ -639,6 +669,78 @@ def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"):
639669
return mounts
640670

641671

672+
def is_swap(module, args):
673+
"""Return True if the device/file is an active swap space, False otherwise."""
674+
675+
if module.params['fstype'] != 'swap' or args['name'] != 'none':
676+
return False
677+
678+
swapon_bin = module.get_bin_path('swapon', required=True)
679+
cmd = [swapon_bin, '--noheadings', '--show=name']
680+
dev = os.path.realpath(args['src'])
681+
682+
rc, out, err = module.run_command(cmd)
683+
684+
if rc != 0:
685+
module.fail_json(msg="Error while querying active swaps: %s" % err)
686+
687+
return bool(dev in out.splitlines())
688+
689+
690+
def swapon(module, args):
691+
"""Activate a swap device/file with the proper options."""
692+
693+
swapon_bin = module.get_bin_path('swapon', required=True)
694+
cmd = [swapon_bin]
695+
696+
# Only 'swapon -a' applies options from fstab, otherwise they are ignored
697+
# unless provided on command line with '-o opts'. But not all versions of
698+
# swapon accept -o or --options. So we don't use it here, but at least we
699+
# keep the 'priority' and 'discard' options.
700+
if args['opts'] is not None:
701+
for opt in args['opts'].split(','):
702+
if re.match('pri=[0-9]', opt):
703+
cmd += ['-p', opt.split('=')[1]]
704+
if re.match('discard=', opt):
705+
cmd += ['-d', opt.split('=')[1]]
706+
707+
# src such as UUID=some_uuid and LABEL=some_label work as is.
708+
cmd += [args['src']]
709+
710+
rc, out, err = module.run_command(cmd)
711+
712+
if rc:
713+
return rc, out + err
714+
return 0, ''
715+
716+
717+
def swapoff(module, args):
718+
"""Deactivate a swap device/file."""
719+
720+
swapoff_bin = module.get_bin_path('swapoff', required=True)
721+
cmd = [swapoff_bin, args['src']]
722+
723+
rc, out, err = module.run_command(cmd)
724+
725+
if rc:
726+
return rc, out + err
727+
return 0, ''
728+
729+
730+
def reswap(module, args):
731+
"""Deactivate a swap device/file and reactivate it with new options."""
732+
733+
if is_swap(module, args):
734+
rc, msg = swapoff(module, args)
735+
if rc:
736+
return rc, msg
737+
738+
rc, msg = swapon(module, args)
739+
if rc:
740+
return rc, msg
741+
return 0, ''
742+
743+
642744
def main():
643745
module = AnsibleModule(
644746
argument_spec=dict(
@@ -651,12 +753,14 @@ def main():
651753
passno=dict(type='str', no_log=False),
652754
src=dict(type='path'),
653755
backup=dict(type='bool', default=False),
654-
state=dict(type='str', required=True, choices=['absent', 'mounted', 'present', 'unmounted', 'remounted']),
756+
state=dict(type='str', required=True,
757+
choices=['absent', 'mounted', 'present', 'unmounted', 'remounted']),
655758
),
656759
supports_check_mode=True,
657760
required_if=(
658761
['state', 'mounted', ['src', 'fstype']],
659762
['state', 'present', ['src', 'fstype']],
763+
['fstype', 'swap', ['src']],
660764
),
661765
)
662766

@@ -744,6 +848,12 @@ def main():
744848
if res:
745849
module.fail_json(
746850
msg="Error unmounting %s: %s" % (name, msg))
851+
elif is_swap(module, args):
852+
res, msg = swapoff(module, args)
853+
854+
if res:
855+
module.fail_json(
856+
msg="Error disabling swap space %s: %s" % (args['src'], msg))
747857

748858
if os.path.exists(name):
749859
try:
@@ -759,10 +869,19 @@ def main():
759869
module.fail_json(
760870
msg="Error unmounting %s: %s" % (name, msg))
761871

872+
changed = True
873+
elif is_swap(module, args):
874+
if not module.check_mode:
875+
res, msg = swapoff(module, args)
876+
877+
if res:
878+
module.fail_json(
879+
msg="Error disabling swap space %s: %s" % (args['src'], msg))
880+
762881
changed = True
763882
elif state == 'mounted':
764883
dirs_created = []
765-
if not os.path.exists(name) and not module.check_mode:
884+
if name != 'none' and not os.path.exists(name) and not module.check_mode:
766885
try:
767886
# Something like mkdir -p but with the possibility to undo.
768887
# Based on some copy-paste from the "file" module.
@@ -798,11 +917,18 @@ def main():
798917
if changed and not module.check_mode:
799918
res, msg = remount(module, args)
800919
changed = True
920+
elif is_swap(module, args):
921+
if changed and not module.check_mode:
922+
res, msg = reswap(module, args)
923+
changed = True
801924
else:
802925
changed = True
803926

804927
if not module.check_mode:
805-
res, msg = mount(module, args)
928+
if args['fstype'] == 'swap' and name == 'none':
929+
res, msg = swapon(module, args)
930+
else:
931+
res, msg = mount(module, args)
806932

807933
if res:
808934
# Not restoring fstab after a failed mount was reported as a bug,
@@ -820,15 +946,26 @@ def main():
820946
except Exception:
821947
pass
822948

823-
module.fail_json(msg="Error mounting %s: %s" % (name, msg))
949+
if args['fstype'] == 'swap' and name == 'none':
950+
error_msg = "Error enabling swap space %s: %s" % (args['src'], msg)
951+
else:
952+
error_msg = "Error mounting %s: %s" % (name, msg)
953+
954+
module.fail_json(msg=error_msg)
824955
elif state == 'present':
825956
name, changed = set_mount(module, args)
826957
elif state == 'remounted':
827958
if not module.check_mode:
828-
res, msg = remount(module, args)
959+
if module.params['fstype'] == 'swap' and name == 'none':
960+
res, msg = reswap(module, args)
961+
962+
if res:
963+
module.fail_json(msg="Error re-enabling swap space %s: %s" % (args['src'], msg))
964+
else:
965+
res, msg = remount(module, args)
829966

830-
if res:
831-
module.fail_json(msg="Error remounting %s: %s" % (name, msg))
967+
if res:
968+
module.fail_json(msg="Error remounting %s: %s" % (name, msg))
832969

833970
changed = True
834971
else:

0 commit comments

Comments
 (0)