ansible.posix
ansible.posix copied to clipboard
state = absent does not unmount path if it is not present in fstab
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".
@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".
Hello. Thank you for your answer. I can do a PR during this week, it will not take long.
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 thata 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, andstate=unmounted
should be used instead. -
src
is ignored withstate=absent
orstate=unmounted
I will link a PR to this issue.
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.