Skip to content
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

state = absent does not unmount path if it is not present in fstab #322

Open
NeodymiumFerBore opened this issue Feb 2, 2022 · 4 comments
Labels
docs The issue/pr is related to Documentation verified This issue has been verified/reproduced by maintainer

Comments

@NeodymiumFerBore
Copy link
Contributor

NeodymiumFerBore commented Feb 2, 2022

SUMMARY

When using state=absent, the mountpoint is not unmounted if it isn't present in the fstab file.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

mount module

ANSIBLE VERSION
ansible [core 2.12.2]
  config file = None
  configured module search path = ['/home/ndfeb/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /DATA/venv_ansible_tmp/lib/python3.8/site-packages/ansible
  ansible collection location = /home/ndfeb/.ansible/collections:/usr/share/ansible/collections
  executable location = /DATA/venv_ansible_tmp/bin/ansible
  python version = 3.8.10 (default, Jun  2 2021, 10:49:15) [GCC 9.4.0]
  jinja version = 3.0.3
  libyaml = True
COLLECTION VERSION
# /DATA/venv_ansible_tmp/lib/python3.8/site-packages/ansible_collections
Collection    Version
------------- -------
ansible.posix 1.3.0  

Note: this bug is also present in upstream in preparation of 1.4.0

CONFIGURATION
(default configuration)
OS / ENVIRONMENT
Distributor ID:	Linuxmint
Description:	Linux Mint 20
Release:	20
Codename:	ulyana
STEPS TO REPRODUCE
$ # Setup test environment
$ python3 -m venv /tmp/venv_ansible
$ source /tmp/venv_ansible/bin/activate
$ python -m pip install -U pip
$ python -m pip install ansible
$ mkdir /tmp/my_mountpoint
$ dd if=/dev/zero of=/tmp/test.img bs=10M count=10
$ mkfs.ext4 /tmp/test.img

$ # Mount the file without adding it to fstab
$ sudo mount -t ext4 /tmp/test.img /tmp/my_mountpoint

$ # Take note of the current state
$ df -HT | grep my_mountpoint
/dev/loop1                                  ext4       98M   74k   90M   1% /tmp/my_mountpoint
$ grep my_mountpoint /etc/fstab
(no output)
$ sha1sum /etc/fstab
74528a180d8b9be29585862713736156ebdebbe0  /etc/fstab

$ # Play the mount module with state = absent
$ ansible --become -K -m mount -a 'path=/tmp/my_mountpoint state=absent' localhost
BECOME password: 
localhost | SUCCESS => {
    "backup_file": "",
    "boot": "yes",
    "changed": false,
    "dump": "0",
    "fstab": "/etc/fstab",
    "name": "/tmp/my_mountpoint",
    "opts": "defaults",
    "passno": "0"
}

$ # The system state did not change, the FS is still mounted
$ df -HT | grep my_mountpoint
/dev/loop1                                  ext4       98M   74k   90M   1% /tmp/my_mountpoint
$ grep my_mountpoint /etc/fstab
(no output)
$ sha1sum /etc/fstab # no change occurred
74528a180d8b9be29585862713736156ebdebbe0  /etc/fstab

$ # Nuke the test environment
$ sudo umount /tmp/my_mountpoint
$ rm -rf /tmp/my_mountpoint
$ rm -f /tmp/test.img
$ deactivate
$ rm -rf /tmp/venv_ansible
EXPECTED RESULTS

/tmp/my_mountpoint should have been unmounted.

ACTUAL RESULTS

/tmp/my_mountpoint is still mounted.

MORE INFO

In this code, the condition if changed is True only if changes occurred in the fstab file. The function unset_mount removes the given path from the fstab, and returns changed=True if it did remove something. If the given path is not in the fstab, then this change is not triggered, and the unmount is not executed.

By looking at the documentation, I'm not sure this behavior is intended

FIX SUGGESTIONS
  • if the mountpoint was present in the fstab, keep the current behavior:
    • remove entry in fstab
    • unmount the mountpoint
    • delete the mountpoint
  • else if the mountpoint was not present in the fstab:
    • unmount the mountpoint if necessary (and trigger a change)
    • do NOT delete the mountpoint

If this behavior is intended, then add a note in the absent documentation. Like "A filesystem mounted on a mountpoint that is not registered in the fstab will not be unmounted with this option. Use unmount instead".

@saito-hideki
Copy link
Collaborator

@NeodymiumFerBore Thank you for reporting this :)
For the purpose of umounting, I think it is better to use state=unmounted and it is more reasonable to describe the actual behavior of state=absent and state=unmounted correctly than to extend the behavior of state=absent.

So, I will vote +1 to the following your opinion:

If this behavior is intended, then add a note in the absent documentation. Like "A filesystem mounted on a mountpoint that is not registered in the fstab will not be unmounted with this option. Use unmount instead".

@saito-hideki saito-hideki added docs The issue/pr is related to Documentation verified This issue has been verified/reproduced by maintainer labels Mar 9, 2022
@NeodymiumFerBore
Copy link
Contributor Author

Hello. Thank you for your answer. I can do a PR during this week, it will not take long.

@NeodymiumFerBore
Copy link
Contributor Author

NeodymiumFerBore commented Mar 12, 2022

Just adding a note so the state=absent behavior is clear.

I made different tests to be the most accurate possible in the new doc:

  • if the mountpoint is registered in fstab, and the source FS in the fstab is the same than the one really mounted (ideal situation), then state=absent does what it is supposed to do (remove fstab entry, unmount then remove mountpoint)
  • if the mountpoint is registered in fstab, and the source FS in the fstab is different than the one actually mounted, then state=absent removes the fstab entry, unmounts the mountpoint (regardless of what is mounted on it), and removes the mountpoint. src is ignored

At this point, it is clear that state=absent only considers the mountpoint, and NOT what is actually mounted on it, even if src is specified. Reading the code is pretty obvious about that, but i preferred to try.

  • if the mountpoint is registered in fstab, and multiple FS are mounted on the same mountpoint, then state=absent unmounts it once, then the module fails because it cannot remove the mountpoint ([Errno 16] Device or resource busy)

  • if the mountpoint is not registered in fstab, then the behavior is the one described in this issue (no change happens)

The new doc should include those corrections:

  • state=absent does not specify that (quote) a device mount's entry will be removed from fstab. It specifies that a mountpoint entry will be removed from fstab, because the device is ignored
  • state=absent does not recursively unmounts all devices mounted on the mountpoint, and will fail if there is more than one.
  • state=absent will have no effect for a mountpoint that is not registered in the fstab, and state=unmounted should be used instead.
  • src is ignored with state=absent or state=unmounted

I will link a PR to this issue.

@a-rhodes
Copy link

I see this is still open. I'd like to advocate for changing the functionality so that "absent" means removed from fstab and unmounted. That is the true meaning of absent.

The recent situation we ran into to illustrate the issue is as follows:

We have an ansible task to create a mount with the mounted state, and a cleanup task to remove a mount with the absent state. Most of this time this works fine and as intended. In a rare case though, someone will have an open file on that mount, causing the task to fail because the umount cannot complete. After the file handle is released and the task is re-run, the task will return "ok" because the entry is no longer in fstab, but leave the mount in place, which is NOT expected.

The only workaround is to add two separate tasks, one with the state absent_from_fstab and the other with unmounted to force both actions to happen reliably.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs The issue/pr is related to Documentation verified This issue has been verified/reproduced by maintainer
Projects
None yet
Development

No branches or pull requests

3 participants