-
Notifications
You must be signed in to change notification settings - Fork 154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ephemeral state to mount fs without altering fstab #264
Changes from 3 commits
5c70124
c458204
d218c37
c4b3678
53a10bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
--- | ||
minor_changes: | ||
- mount - Add ``ephemeral`` value for the ``state`` parameter, that allows to mount a filesystem | ||
without altering the ``fstab`` file (https://github.com/ansible-collections/ansible.posix/pull/264). |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,12 +31,12 @@ | |
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), C(mounted) or C(ephemeral). | ||
type: path | ||
fstype: | ||
description: | ||
- 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 | ||
opts: | ||
description: | ||
|
@@ -48,7 +48,7 @@ | |
- 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 | ||
with subsequent runs. | ||
- Has no effect on Solaris systems. | ||
- Has no effect on Solaris systems or when used with C(ephemeral). | ||
type: str | ||
default: 0 | ||
passno: | ||
|
@@ -57,7 +57,7 @@ | |
- 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 | ||
with subsequent runs. | ||
- Deprecated on Solaris systems. | ||
- Deprecated on Solaris systems. Has no effect when used with C(ephemeral). | ||
type: str | ||
default: 0 | ||
state: | ||
|
@@ -68,6 +68,13 @@ | |
- 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. | ||
- 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 | ||
I(fstab) and will also unmount the device and remove the mount | ||
point. | ||
|
@@ -80,7 +87,7 @@ | |
instead to work around this issue. | ||
type: str | ||
required: true | ||
choices: [ absent, mounted, present, unmounted, remounted ] | ||
choices: [ absent, mounted, present, unmounted, remounted, ephemeral ] | ||
fstab: | ||
description: | ||
- File to use instead of C(/etc/fstab). | ||
|
@@ -89,6 +96,7 @@ | |
- 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. | ||
- 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 | ||
boot: | ||
description: | ||
|
@@ -100,6 +108,7 @@ | |
to mount options in I(/etc/fstab). | ||
- To avoid mount option conflicts, if C(noauto) specified in C(opts), | ||
mount module will ignore C(boot). | ||
- This parameter is ignored when I(state) is set to C(ephemeral). | ||
type: bool | ||
default: yes | ||
backup: | ||
|
@@ -184,6 +193,14 @@ | |
boot: no | ||
state: mounted | ||
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 | ||
|
@@ -426,6 +443,23 @@ def _set_fstab_args(fstab_file): | |
return result | ||
|
||
|
||
def _set_ephemeral_args(args): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
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): | ||
"""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 ' | ||
'specify the fstab parameter for OpenBSD hosts')) | ||
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] | ||
|
||
|
@@ -494,7 +531,10 @@ def remount(module, args): | |
'OpenBSD does not support alternate fstab files. Do not ' | ||
'specify the fstab parameter for OpenBSD hosts')) | ||
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']] | ||
out = err = '' | ||
|
@@ -587,9 +627,8 @@ def is_bind_mounted(module, linux_mounts, dest, src=None, fstype=None): | |
return is_mounted | ||
|
||
|
||
def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"): | ||
"""Gather mount information""" | ||
|
||
def _get_mount_info(module, mntinfo_file="/proc/self/mountinfo"): | ||
"""Return raw mount information""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New function This function does exactly what |
||
try: | ||
f = open(mntinfo_file) | ||
except IOError: | ||
|
@@ -602,6 +641,17 @@ def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"): | |
except IOError: | ||
module.fail_json(msg="Cannot close file %s" % mntinfo_file) | ||
|
||
return lines | ||
|
||
|
||
def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As previously noticed, this function has been edited to use the new function |
||
"""Gather mount information""" | ||
|
||
lines = _get_mount_info(module) | ||
# Keep same behavior than before | ||
if lines is None: | ||
return | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The condition |
||
mntinfo = {} | ||
|
||
for line in lines: | ||
|
@@ -659,6 +709,24 @@ def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"): | |
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""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New function |
||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return True | ||
|
||
# (dst == mountpoint and src == name) was never reached | ||
return False | ||
|
||
def main(): | ||
module = AnsibleModule( | ||
argument_spec=dict( | ||
|
@@ -671,12 +739,13 @@ 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', 'ephemeral']), | ||
), | ||
supports_check_mode=True, | ||
required_if=( | ||
['state', 'mounted', ['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 | ||
# happens when fstab option is passed to the module. | ||
if not os.path.exists(args['fstab']): | ||
if not os.path.exists(os.path.dirname(args['fstab'])): | ||
os.makedirs(os.path.dirname(args['fstab'])) | ||
try: | ||
open(args['fstab'], 'a').close() | ||
except PermissionError as 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))) | ||
# If state is 'ephemeral', we do not need fstab file | ||
if module.params['state'] != 'ephemeral': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only this line changed (add |
||
if not os.path.exists(args['fstab']): | ||
if not os.path.exists(os.path.dirname(args['fstab'])): | ||
os.makedirs(os.path.dirname(args['fstab'])) | ||
try: | ||
open(args['fstab'], 'a').close() | ||
except PermissionError as 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: | ||
# Remove from fstab and unmounted. | ||
|
@@ -766,6 +837,8 @@ def main(): | |
# mounted: | ||
# Add to fstab if not there and make sure it is mounted. If it has | ||
# changed in fstab then remount it. | ||
# ephemeral: | ||
# Do not change fstab state, but mount. | ||
|
||
state = module.params['state'] | ||
name = module.params['path'] | ||
|
@@ -797,7 +870,7 @@ def main(): | |
msg="Error unmounting %s: %s" % (name, msg)) | ||
|
||
changed = True | ||
elif state == 'mounted': | ||
elif state == 'mounted' or state == 'ephemeral': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
dirs_created = [] | ||
if not os.path.exists(name) and not module.check_mode: | ||
try: | ||
|
@@ -825,7 +898,11 @@ def main(): | |
module.fail_json( | ||
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 | ||
|
||
if ( | ||
|
@@ -835,7 +912,25 @@ def main(): | |
if changed and not module.check_mode: | ||
res, msg = remount(module, args) | ||
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.')) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When state is In the case of |
||
else: | ||
# If not already mounted, mount it | ||
changed = True | ||
|
||
if not module.check_mode: | ||
|
@@ -847,7 +942,8 @@ def main(): | |
# A non-working fstab entry may break the system at the reboot, | ||
# so undo all the changes if possible. | ||
try: | ||
write_fstab(module, backup_lines, args['fstab']) | ||
if state != 'ephemeral': | ||
write_fstab(module, backup_lines, args['fstab']) | ||
except Exception: | ||
pass | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Examples: add an example for the use of
state=ephemeral
. This example has been tested on my side.