mirror of
https://github.com/ansible-collections/ansible.posix.git
synced 2026-01-11 23:25:28 +01:00
Add ephemeral state to mount fs without altering fstab
- Edit DOCUMENTATION section to add ephemeral state - Edit EXAMPLES section to add ephemeral state example - Add new function `_set_ephemeral_args` to replace `_set_fstab_args` when using ephemeral state - Add new function `_is_same_mount_src` to determine if the mounted volume on the destination path has the same source than the one supplied to the module - Add new function `_get_mount_info` to avoid redundant code between functions `get_linux_mounts` and `_is_same_mount_src` - Modify `get_linux_mount` to use the new function `_get_mount_info`. Behavior is preserved. - Integrate `ephemeral` parameter treatment into `mounted` treatment, and add `if` statements to avoid IO from/to fstab - Add `ephemeral` as a possible value for the `state` parameter in `main()` - Add `required_if` dependencies for `ephemeral` state
This commit is contained in:
parent
610717ca76
commit
5c70124433
1 changed files with 119 additions and 23 deletions
|
|
@ -31,12 +31,12 @@ options:
|
||||||
src:
|
src:
|
||||||
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) or C(mounted).
|
- Required when I(state) set to C(present), C(mounted) or C(ephemeral).
|
||||||
type: path
|
type: path
|
||||||
fstype:
|
fstype:
|
||||||
description:
|
description:
|
||||||
- Filesystem type.
|
- Filesystem type.
|
||||||
- Required when I(state) is C(present) or C(mounted).
|
- Required when I(state) is C(present), C(mounted) or C(ephemeral).
|
||||||
type: str
|
type: str
|
||||||
opts:
|
opts:
|
||||||
description:
|
description:
|
||||||
|
|
@ -48,7 +48,7 @@ options:
|
||||||
- Note that if set to C(null) and I(state) set to C(present),
|
- Note that if set to C(null) and I(state) set to C(present),
|
||||||
it will cease to work and duplicate entries will be made
|
it will cease to work and duplicate entries will be made
|
||||||
with subsequent runs.
|
with subsequent runs.
|
||||||
- Has no effect on Solaris systems.
|
- Has no effect on Solaris systems or when used with C(ephemeral).
|
||||||
type: str
|
type: str
|
||||||
default: 0
|
default: 0
|
||||||
passno:
|
passno:
|
||||||
|
|
@ -57,7 +57,7 @@ options:
|
||||||
- Note that if set to C(null) and I(state) set to C(present),
|
- Note that if set to C(null) and I(state) set to C(present),
|
||||||
it will cease to work and duplicate entries will be made
|
it will cease to work and duplicate entries will be made
|
||||||
with subsequent runs.
|
with subsequent runs.
|
||||||
- Deprecated on Solaris systems.
|
- Deprecated on Solaris systems. Has no effect when used with C(ephemeral).
|
||||||
type: str
|
type: str
|
||||||
default: 0
|
default: 0
|
||||||
state:
|
state:
|
||||||
|
|
@ -68,6 +68,13 @@ options:
|
||||||
- If C(unmounted), the device will be unmounted without changing I(fstab).
|
- If C(unmounted), the device will be unmounted without changing I(fstab).
|
||||||
- C(present) only specifies that the device is to be configured in
|
- C(present) only specifies that the device is to be configured in
|
||||||
I(fstab) and does not trigger or require a mount.
|
I(fstab) and does not trigger or require a mount.
|
||||||
|
- C(ephemeral) only specifies that the device is to be mounted, without changing
|
||||||
|
I(fstab). If it is already mounted, a remount will be triggered.
|
||||||
|
This will always return changed=True. If the mount point C(path)
|
||||||
|
has already a device mounted on, and its I(src) is different than C(src),
|
||||||
|
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.
|
||||||
|
The value of C(fstab) is ignored.
|
||||||
- C(absent) specifies that the device mount's entry will be removed from
|
- C(absent) specifies that the device mount's entry will be removed from
|
||||||
I(fstab) and will also unmount the device and remove the mount
|
I(fstab) and will also unmount the device and remove the mount
|
||||||
point.
|
point.
|
||||||
|
|
@ -80,7 +87,7 @@ options:
|
||||||
instead to work around this issue.
|
instead to work around this issue.
|
||||||
type: str
|
type: str
|
||||||
required: true
|
required: true
|
||||||
choices: [ absent, mounted, present, unmounted, remounted ]
|
choices: [ absent, mounted, present, unmounted, remounted, ephemeral ]
|
||||||
fstab:
|
fstab:
|
||||||
description:
|
description:
|
||||||
- File to use instead of C(/etc/fstab).
|
- File to use instead of C(/etc/fstab).
|
||||||
|
|
@ -89,6 +96,7 @@ options:
|
||||||
- OpenBSD does not allow specifying alternate fstab files with mount so do not
|
- OpenBSD does not allow specifying alternate fstab files with mount so do not
|
||||||
use this on OpenBSD with any state that operates on the live filesystem.
|
use this on OpenBSD with any state that operates on the live filesystem.
|
||||||
- This parameter defaults to /etc/fstab or /etc/vfstab on Solaris.
|
- This parameter defaults to /etc/fstab or /etc/vfstab on Solaris.
|
||||||
|
- This parameter is ignored when I(state) is set to C(ephemeral).
|
||||||
type: str
|
type: str
|
||||||
boot:
|
boot:
|
||||||
description:
|
description:
|
||||||
|
|
@ -100,6 +108,7 @@ options:
|
||||||
to mount options in I(/etc/fstab).
|
to mount options in I(/etc/fstab).
|
||||||
- To avoid mount option conflicts, if C(noauto) specified in C(opts),
|
- To avoid mount option conflicts, if C(noauto) specified in C(opts),
|
||||||
mount module will ignore C(boot).
|
mount module will ignore C(boot).
|
||||||
|
- This parameter is ignored when I(state) is set to C(ephemeral).
|
||||||
type: bool
|
type: bool
|
||||||
default: yes
|
default: yes
|
||||||
backup:
|
backup:
|
||||||
|
|
@ -184,6 +193,14 @@ EXAMPLES = r'''
|
||||||
boot: no
|
boot: no
|
||||||
state: mounted
|
state: mounted
|
||||||
fstype: nfs
|
fstype: nfs
|
||||||
|
|
||||||
|
- name: Mount ephemeral SMB volume
|
||||||
|
ansible.posix.mount:
|
||||||
|
src: //192.168.1.200/share
|
||||||
|
path: /mnt/smb_share
|
||||||
|
opts: "rw,vers=3,file_mode=0600,dir_mode=0700,dom={{ ad_domain }},username={{ ad_username }},password={{ ad_password }}"
|
||||||
|
fstype: cifs
|
||||||
|
state: ephemeral
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
|
|
@ -426,6 +443,23 @@ def _set_fstab_args(fstab_file):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _set_ephemeral_args(args):
|
||||||
|
result = []
|
||||||
|
# Set fstype switch according to platform. SunOS/Solaris use -F
|
||||||
|
if platform.system().lower() == 'sunos':
|
||||||
|
result.append('-F')
|
||||||
|
else:
|
||||||
|
result.append('-t')
|
||||||
|
result.append(args['fstype'])
|
||||||
|
|
||||||
|
# Even if '-o remount' is already set, specifying multiple -o is valid
|
||||||
|
if args['opts'] != 'defaults':
|
||||||
|
result += ['-o', args['opts']]
|
||||||
|
|
||||||
|
result.append(args['src'])
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def mount(module, args):
|
def mount(module, args):
|
||||||
"""Mount up a path or remount if needed."""
|
"""Mount up a path or remount if needed."""
|
||||||
|
|
||||||
|
|
@ -442,7 +476,10 @@ def mount(module, args):
|
||||||
'OpenBSD does not support alternate fstab files. Do not '
|
'OpenBSD does not support alternate fstab files. Do not '
|
||||||
'specify the fstab parameter for OpenBSD hosts'))
|
'specify the fstab parameter for OpenBSD hosts'))
|
||||||
else:
|
else:
|
||||||
cmd += _set_fstab_args(args['fstab'])
|
if module.params['state'] != 'ephemeral':
|
||||||
|
cmd += _set_fstab_args(args['fstab'])
|
||||||
|
else:
|
||||||
|
cmd += _set_ephemeral_args(args)
|
||||||
|
|
||||||
cmd += [name]
|
cmd += [name]
|
||||||
|
|
||||||
|
|
@ -494,7 +531,10 @@ def remount(module, args):
|
||||||
'OpenBSD does not support alternate fstab files. Do not '
|
'OpenBSD does not support alternate fstab files. Do not '
|
||||||
'specify the fstab parameter for OpenBSD hosts'))
|
'specify the fstab parameter for OpenBSD hosts'))
|
||||||
else:
|
else:
|
||||||
cmd += _set_fstab_args(args['fstab'])
|
if module.params['state'] != 'ephemeral':
|
||||||
|
cmd += _set_fstab_args(args['fstab'])
|
||||||
|
else:
|
||||||
|
cmd += _set_ephemeral_args(args)
|
||||||
|
|
||||||
cmd += [args['name']]
|
cmd += [args['name']]
|
||||||
out = err = ''
|
out = err = ''
|
||||||
|
|
@ -587,9 +627,8 @@ def is_bind_mounted(module, linux_mounts, dest, src=None, fstype=None):
|
||||||
return is_mounted
|
return is_mounted
|
||||||
|
|
||||||
|
|
||||||
def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"):
|
def _get_mount_info(module, mntinfo_file="/proc/self/mountinfo"):
|
||||||
"""Gather mount information"""
|
"""Return raw mount information"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f = open(mntinfo_file)
|
f = open(mntinfo_file)
|
||||||
except IOError:
|
except IOError:
|
||||||
|
|
@ -602,6 +641,17 @@ def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"):
|
||||||
except IOError:
|
except IOError:
|
||||||
module.fail_json(msg="Cannot close file %s" % mntinfo_file)
|
module.fail_json(msg="Cannot close file %s" % mntinfo_file)
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"):
|
||||||
|
"""Gather mount information"""
|
||||||
|
|
||||||
|
lines = _get_mount_info(module)
|
||||||
|
# Keep same behavior than before
|
||||||
|
if lines is None:
|
||||||
|
return
|
||||||
|
|
||||||
mntinfo = {}
|
mntinfo = {}
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
|
@ -659,6 +709,24 @@ def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"):
|
||||||
return mounts
|
return mounts
|
||||||
|
|
||||||
|
|
||||||
|
def _is_same_mount_src(module, args, mntinfo_file="/proc/self/mountinfo"):
|
||||||
|
"""Return True if the mounted fs on mountpoint is the same source than src"""
|
||||||
|
mountpoint = args['name']
|
||||||
|
src = args['src']
|
||||||
|
lines = _get_mount_info(module)
|
||||||
|
|
||||||
|
# If this function is used and we cannot retrieve mount info, we must fail to avoid unexpected behavior
|
||||||
|
if lines is None:
|
||||||
|
module.fail_json(msg="Unable to retrieve mount info from '%s'" % mntinfo_file)
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
fields = line.split()
|
||||||
|
if fields[4] == mountpoint and fields[-2] == src:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# (dst == mountpoint and src == name) was never reached
|
||||||
|
return False
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
|
|
@ -671,12 +739,13 @@ def main():
|
||||||
passno=dict(type='str', no_log=False),
|
passno=dict(type='str', no_log=False),
|
||||||
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', 'mounted', 'present', 'unmounted', 'remounted']),
|
state=dict(type='str', required=True, choices=['absent', 'mounted', 'present', 'unmounted', 'remounted', 'ephemeral']),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
required_if=(
|
required_if=(
|
||||||
['state', 'mounted', ['src', 'fstype']],
|
['state', 'mounted', ['src', 'fstype']],
|
||||||
['state', 'present', ['src', 'fstype']],
|
['state', 'present', ['src', 'fstype']],
|
||||||
|
['state', 'ephemeral', ['src', 'fstype']]
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -747,15 +816,17 @@ def main():
|
||||||
|
|
||||||
# If fstab file does not exist, we first need to create it. This mainly
|
# If fstab file does not exist, we first need to create it. This mainly
|
||||||
# happens when fstab option is passed to the module.
|
# happens when fstab option is passed to the module.
|
||||||
if not os.path.exists(args['fstab']):
|
# If state is 'ephemeral', we do not need fstab file
|
||||||
if not os.path.exists(os.path.dirname(args['fstab'])):
|
if module.params['state'] != 'ephemeral':
|
||||||
os.makedirs(os.path.dirname(args['fstab']))
|
if not os.path.exists(args['fstab']):
|
||||||
try:
|
if not os.path.exists(os.path.dirname(args['fstab'])):
|
||||||
open(args['fstab'], 'a').close()
|
os.makedirs(os.path.dirname(args['fstab']))
|
||||||
except PermissionError as e:
|
try:
|
||||||
module.fail_json(msg="Failed to open %s due to permission issue" % args['fstab'])
|
open(args['fstab'], 'a').close()
|
||||||
except Exception as e:
|
except PermissionError as e:
|
||||||
module.fail_json(msg="Failed to open %s due to %s" % (args['fstab'], to_native(e)))
|
module.fail_json(msg="Failed to open %s due to permission issue" % args['fstab'])
|
||||||
|
except Exception as e:
|
||||||
|
module.fail_json(msg="Failed to open %s due to %s" % (args['fstab'], to_native(e)))
|
||||||
|
|
||||||
# absent:
|
# absent:
|
||||||
# Remove from fstab and unmounted.
|
# Remove from fstab and unmounted.
|
||||||
|
|
@ -766,6 +837,8 @@ def main():
|
||||||
# mounted:
|
# mounted:
|
||||||
# Add to fstab if not there and make sure it is mounted. If it has
|
# Add to fstab if not there and make sure it is mounted. If it has
|
||||||
# changed in fstab then remount it.
|
# changed in fstab then remount it.
|
||||||
|
# ephemeral:
|
||||||
|
# Do not change fstab state, but mount.
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
name = module.params['path']
|
name = module.params['path']
|
||||||
|
|
@ -797,7 +870,7 @@ def main():
|
||||||
msg="Error unmounting %s: %s" % (name, msg))
|
msg="Error unmounting %s: %s" % (name, msg))
|
||||||
|
|
||||||
changed = True
|
changed = True
|
||||||
elif state == 'mounted':
|
elif state == 'mounted' or state == 'ephemeral':
|
||||||
dirs_created = []
|
dirs_created = []
|
||||||
if not os.path.exists(name) and not module.check_mode:
|
if not os.path.exists(name) and not module.check_mode:
|
||||||
try:
|
try:
|
||||||
|
|
@ -825,7 +898,11 @@ def main():
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
msg="Error making dir %s: %s" % (name, to_native(e)))
|
msg="Error making dir %s: %s" % (name, to_native(e)))
|
||||||
|
|
||||||
name, backup_lines, changed = _set_mount_save_old(module, args)
|
# ephemeral: completely ignore fstab
|
||||||
|
if state != 'ephemeral':
|
||||||
|
name, backup_lines, changed = _set_mount_save_old(module, args)
|
||||||
|
else:
|
||||||
|
name, backup_lines, changed = args['name'], [], False
|
||||||
res = 0
|
res = 0
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
@ -835,7 +912,25 @@ def main():
|
||||||
if changed and not module.check_mode:
|
if changed and not module.check_mode:
|
||||||
res, msg = remount(module, args)
|
res, msg = remount(module, args)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
|
# When 'state' == 'ephemeral', we don't know what is in fstab, and 'changed' is always False
|
||||||
|
if state == 'ephemeral':
|
||||||
|
# If state == 'ephemeral', check if the mountpoint src == module.params['src']
|
||||||
|
# If it doesn't, fail to prevent unwanted unmount or unwanted mountpoint override
|
||||||
|
if _is_same_mount_src(module, args):
|
||||||
|
res, msg = remount(module, args)
|
||||||
|
changed = True
|
||||||
|
else:
|
||||||
|
module.fail_json(
|
||||||
|
msg=(
|
||||||
|
'Ephemeral mount point is already mounted with a different '
|
||||||
|
'source than the specified one. Failing in order to prevent an '
|
||||||
|
'unwanted unmount or override operation. Try replacing this command with '
|
||||||
|
'a "state: unmounted" followed by a "state: ephemeral", or use '
|
||||||
|
'a different destination path.'))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# If not already mounted, mount it
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
if not module.check_mode:
|
if not module.check_mode:
|
||||||
|
|
@ -847,7 +942,8 @@ def main():
|
||||||
# A non-working fstab entry may break the system at the reboot,
|
# A non-working fstab entry may break the system at the reboot,
|
||||||
# so undo all the changes if possible.
|
# so undo all the changes if possible.
|
||||||
try:
|
try:
|
||||||
write_fstab(module, backup_lines, args['fstab'])
|
if state != 'ephemeral':
|
||||||
|
write_fstab(module, backup_lines, args['fstab'])
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue