Fixing broken bind mount on CentOS 7 (#20703)
* Fixing broken bind mount on CentOS 7 * Fixing remount
This commit is contained in:
parent
65cd21e9a8
commit
d94b2d802e
1 changed files with 79 additions and 103 deletions
|
@ -20,9 +20,12 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'status': ['preview'],
|
|
||||||
|
ANSIBLE_METADATA = {
|
||||||
|
'status': ['preview'],
|
||||||
'supported_by': 'core',
|
'supported_by': 'core',
|
||||||
'version': '1.0'}
|
'version': '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
DOCUMENTATION = '''
|
||||||
---
|
---
|
||||||
|
@ -38,7 +41,8 @@ options:
|
||||||
path:
|
path:
|
||||||
description:
|
description:
|
||||||
- Path to the mount point (e.g. C(/mnt/files)).
|
- Path to the mount point (e.g. C(/mnt/files)).
|
||||||
- Before 2.3 this option was only usable as I(dest), I(destfile) and I(name).
|
- Before 2.3 this option was only usable as I(dest), I(destfile) and
|
||||||
|
I(name).
|
||||||
required: true
|
required: true
|
||||||
aliases: [ name ]
|
aliases: [ name ]
|
||||||
src:
|
src:
|
||||||
|
@ -90,7 +94,8 @@ options:
|
||||||
unless you really know what you are doing. This might be useful if
|
unless you really know what you are doing. This might be useful if
|
||||||
you need to configure mountpoints in a chroot environment. OpenBSD
|
you need to configure mountpoints in a chroot environment. OpenBSD
|
||||||
does not allow specifying alternate fstab files with mount so do not
|
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.
|
||||||
required: false
|
required: false
|
||||||
default: /etc/fstab (/etc/vfstab on Solaris)
|
default: /etc/fstab (/etc/vfstab on Solaris)
|
||||||
boot:
|
boot:
|
||||||
|
@ -102,7 +107,8 @@ options:
|
||||||
default: yes
|
default: yes
|
||||||
choices: ["yes", "no"]
|
choices: ["yes", "no"]
|
||||||
notes:
|
notes:
|
||||||
- As of Ansible 2.3, the I(name) option has been changed to I(path) as default, but I(name) still works as well.
|
- As of Ansible 2.3, the I(name) option has been changed to I(path) as
|
||||||
|
default, but I(name) still works as well.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
|
@ -131,14 +137,15 @@ EXAMPLES = '''
|
||||||
state: present
|
state: present
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# import module snippets
|
|
||||||
from ansible.module_utils._text import to_native
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, get_platform
|
from ansible.module_utils.basic import AnsibleModule, get_platform
|
||||||
from ansible.module_utils.ismount import ismount
|
from ansible.module_utils.ismount import ismount
|
||||||
from ansible.module_utils.pycompat24 import get_exception
|
from ansible.module_utils.pycompat24 import get_exception
|
||||||
from ansible.module_utils.six import iteritems
|
from ansible.module_utils.six import iteritems
|
||||||
|
from ansible.module_utils._text import to_native
|
||||||
|
|
||||||
|
|
||||||
def write_fstab(lines, path):
|
def write_fstab(lines, path):
|
||||||
fs_w = open(path, 'w')
|
fs_w = open(path, 'w')
|
||||||
|
@ -317,16 +324,21 @@ def unset_mount(module, args):
|
||||||
|
|
||||||
return (args['name'], changed)
|
return (args['name'], changed)
|
||||||
|
|
||||||
|
|
||||||
def _set_fstab_args(fstab_file):
|
def _set_fstab_args(fstab_file):
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
if fstab_file and fstab_file != '/etc/fstab':
|
if fstab_file and fstab_file != '/etc/fstab':
|
||||||
if get_platform().lower().endswith('bsd'):
|
if get_platform().lower().endswith('bsd'):
|
||||||
result.append('-F')
|
result.append('-F')
|
||||||
else:
|
else:
|
||||||
result.append('-T')
|
result.append('-T')
|
||||||
|
|
||||||
result.append(fstab_file)
|
result.append(fstab_file)
|
||||||
|
|
||||||
return result
|
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."""
|
||||||
|
|
||||||
|
@ -334,14 +346,14 @@ def mount(module, args):
|
||||||
name = args['name']
|
name = args['name']
|
||||||
cmd = [mount_bin]
|
cmd = [mount_bin]
|
||||||
|
|
||||||
if ismount(name):
|
|
||||||
return remount(module, mount_bin, args)
|
|
||||||
|
|
||||||
if get_platform().lower() == 'openbsd':
|
if get_platform().lower() == 'openbsd':
|
||||||
# Use module.params['fstab'] here as args['fstab'] has been set to the
|
# Use module.params['fstab'] here as args['fstab'] has been set to the
|
||||||
# default value.
|
# default value.
|
||||||
if module.params['fstab'] is not None:
|
if module.params['fstab'] is not None:
|
||||||
module.fail_json(msg='OpenBSD does not support alternate fstab files. Do not specify the fstab parameter for OpenBSD hosts')
|
module.fail_json(
|
||||||
|
msg=(
|
||||||
|
'OpenBSD does not support alternate fstab files. Do not '
|
||||||
|
'specify the fstab parameter for OpenBSD hosts'))
|
||||||
else:
|
else:
|
||||||
cmd += _set_fstab_args(args['fstab'])
|
cmd += _set_fstab_args(args['fstab'])
|
||||||
|
|
||||||
|
@ -368,26 +380,32 @@ def umount(module, path):
|
||||||
else:
|
else:
|
||||||
return rc, out+err
|
return rc, out+err
|
||||||
|
|
||||||
def remount(module, mount_bin, args):
|
|
||||||
''' will try to use -o remount first and fallback to unmount/mount if unsupported'''
|
def remount(module, args):
|
||||||
msg = ''
|
"""Try to use 'remount' first and fallback to (u)mount if unsupported."""
|
||||||
|
mount_bin = module.get_bin_path('mount', required=True)
|
||||||
cmd = [mount_bin]
|
cmd = [mount_bin]
|
||||||
|
|
||||||
# multiplatform remount opts
|
# Multiplatform remount opts
|
||||||
if get_platform().lower().endswith('bsd'):
|
if get_platform().lower().endswith('bsd'):
|
||||||
cmd += ['-u']
|
cmd += ['-u']
|
||||||
else:
|
else:
|
||||||
cmd += ['-o', 'remount' ]
|
cmd += ['-o', 'remount']
|
||||||
|
|
||||||
if get_platform().lower() == 'openbsd':
|
if get_platform().lower() == 'openbsd':
|
||||||
# Use module.params['fstab'] here as args['fstab'] has been set to the
|
# Use module.params['fstab'] here as args['fstab'] has been set to the
|
||||||
# default value.
|
# default value.
|
||||||
if module.params['fstab'] is not None:
|
if module.params['fstab'] is not None:
|
||||||
module.fail_json(msg='OpenBSD does not support alternate fstab files. Do not specify the fstab parameter for OpenBSD hosts')
|
module.fail_json(
|
||||||
|
msg=(
|
||||||
|
'OpenBSD does not support alternate fstab files. Do not '
|
||||||
|
'specify the fstab parameter for OpenBSD hosts'))
|
||||||
else:
|
else:
|
||||||
cmd += _set_fstab_args(args['fstab'])
|
cmd += _set_fstab_args(args['fstab'])
|
||||||
cmd += [ args['name'], ]
|
|
||||||
|
cmd += [args['name']]
|
||||||
out = err = ''
|
out = err = ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if get_platform().lower().endswith('bsd'):
|
if get_platform().lower().endswith('bsd'):
|
||||||
# Note: Forcing BSDs to do umount/mount due to BSD remount not
|
# Note: Forcing BSDs to do umount/mount due to BSD remount not
|
||||||
|
@ -401,14 +419,18 @@ def remount(module, mount_bin, args):
|
||||||
except:
|
except:
|
||||||
rc = 1
|
rc = 1
|
||||||
|
|
||||||
|
msg = ''
|
||||||
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
msg = out + err
|
msg = out + err
|
||||||
if ismount(args['name']):
|
|
||||||
rc, msg = umount(module, args['name'])
|
rc, msg = umount(module, args['name'])
|
||||||
|
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
rc, msg = mount(module, args)
|
rc, msg = mount(module, args)
|
||||||
|
|
||||||
return rc, msg
|
return rc, msg
|
||||||
|
|
||||||
|
|
||||||
# Note if we wanted to put this into module_utils we'd have to get permission
|
# Note if we wanted to put this into module_utils we'd have to get permission
|
||||||
# from @jupeter -- https://github.com/ansible/ansible-modules-core/pull/2923
|
# from @jupeter -- https://github.com/ansible/ansible-modules-core/pull/2923
|
||||||
# @jtyr -- https://github.com/ansible/ansible-modules-core/issues/4439
|
# @jtyr -- https://github.com/ansible/ansible-modules-core/issues/4439
|
||||||
|
@ -485,90 +507,48 @@ def get_linux_mounts(module):
|
||||||
fields = line.split()
|
fields = line.split()
|
||||||
|
|
||||||
record = {
|
record = {
|
||||||
|
'id': int(fields[0]),
|
||||||
|
'parent_id': int(fields[1]),
|
||||||
'root': fields[3],
|
'root': fields[3],
|
||||||
'dst': fields[4],
|
'dst': fields[4],
|
||||||
'opts': fields[5],
|
'opts': fields[5],
|
||||||
'fields': fields[6:-4],
|
|
||||||
'fs': fields[-3],
|
'fs': fields[-3],
|
||||||
'src': fields[-2],
|
'src': fields[-2]
|
||||||
}
|
}
|
||||||
|
|
||||||
mntinfo.append(record)
|
mntinfo.append(record)
|
||||||
|
|
||||||
mounts = {}
|
mounts = {}
|
||||||
|
|
||||||
for i, mnt in enumerate(mntinfo):
|
for mnt in mntinfo:
|
||||||
src = mnt['src']
|
src = mnt['src']
|
||||||
|
|
||||||
if mnt['fs'] == 'tmpfs' and mnt['root'] != '/':
|
if mnt['parent_id'] != 1:
|
||||||
|
# Find parent
|
||||||
|
for m in mntinfo:
|
||||||
|
if mnt['parent_id'] == m['id']:
|
||||||
|
if (
|
||||||
|
len(m['root']) > 1 and
|
||||||
|
mnt['root'].startswith("%s/" % m['root'])):
|
||||||
|
# Ommit the parent's root in the child's root
|
||||||
# == Example:
|
# == Example:
|
||||||
# 65 19 0:35 / /tmp rw shared:25 - tmpfs tmpfs rw
|
|
||||||
# 210 65 0:35 /aaa /tmp/bbb rw shared:25 - tmpfs tmpfs rw
|
|
||||||
# == Expected result:
|
|
||||||
# src=/tmp/aaa
|
|
||||||
# ==
|
|
||||||
|
|
||||||
shared = None
|
|
||||||
|
|
||||||
# Search for the shared field
|
|
||||||
for fld in mnt['fields']:
|
|
||||||
if fld.startswith('shared'):
|
|
||||||
shared = fld
|
|
||||||
|
|
||||||
if shared is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
dest = None
|
|
||||||
|
|
||||||
# Search fo the record with the same field
|
|
||||||
for j, m in enumerate(mntinfo):
|
|
||||||
if j < i:
|
|
||||||
if shared in m['fields']:
|
|
||||||
dest = m['dst']
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
if dest is not None:
|
|
||||||
src = "%s%s" % (dest, mnt['root'])
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif mnt['root'] != '/' and len(mnt['fields']) > 0:
|
|
||||||
# == Example:
|
|
||||||
# 67 19 8:18 / /mnt/disk2 rw shared:26 - ext4 /dev/sdb2 rw
|
|
||||||
# 217 65 8:18 /test /tmp/ccc rw shared:26 - ext4 /dev/sdb2 rw
|
|
||||||
# == Expected result:
|
|
||||||
# src=/mnt/disk2/test
|
|
||||||
# ==
|
|
||||||
|
|
||||||
# Search for parent
|
|
||||||
for j, m in enumerate(mntinfo):
|
|
||||||
if j < i:
|
|
||||||
if m['src'] == mnt['src']:
|
|
||||||
src = "%s%s" % (m['dst'], mnt['root'])
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
elif mnt['root'] != '/' and len(mnt['fields']) == 0:
|
|
||||||
# == Example 1:
|
|
||||||
# 27 20 8:1 /tmp/aaa /tmp/bbb rw - ext4 /dev/sdb2 rw
|
|
||||||
# == Example 2:
|
|
||||||
# 204 136 253:2 /rootfs / rw - ext4 /dev/sdb2 rw
|
# 204 136 253:2 /rootfs / rw - ext4 /dev/sdb2 rw
|
||||||
# 141 140 253:2 /rootfs/tmp/aaa /tmp/bbb rw - ext4 /dev/sdb2 rw
|
# 141 140 253:2 /rootfs/tmp/aaa /tmp/bbb rw - ext4 /dev/sdb2 rw
|
||||||
# == Expected result:
|
# == Expected result:
|
||||||
# src=/tmp/aaa
|
# src=/tmp/aaa
|
||||||
# ==
|
mnt['root'] = mnt['root'][len(m['root']) + 1:]
|
||||||
|
|
||||||
|
# Prepend the parent's dst to the child's root
|
||||||
|
# == Example:
|
||||||
|
# 42 60 0:35 / /tmp rw - tmpfs tmpfs rw
|
||||||
|
# 78 42 0:35 /aaa /tmp/bbb rw - tmpfs tmpfs rw
|
||||||
|
# == Expected result:
|
||||||
|
# src=/tmp/aaa
|
||||||
|
if m['dst'] != '/':
|
||||||
|
mnt['root'] = "%s%s" % (m['dst'], mnt['root'])
|
||||||
|
|
||||||
src = mnt['root']
|
src = mnt['root']
|
||||||
|
|
||||||
# Search for parent
|
|
||||||
for j, m in enumerate(mntinfo):
|
|
||||||
if j < i:
|
|
||||||
if (
|
|
||||||
m['src'] == mnt['src'] and
|
|
||||||
mnt['root'].startswith(m['root'])):
|
|
||||||
src = src.replace("%s/" % m['root'], '/', 1)
|
|
||||||
else:
|
|
||||||
break
|
break
|
||||||
|
|
||||||
mounts[mnt['dst']] = {
|
mounts[mnt['dst']] = {
|
||||||
|
@ -602,7 +582,6 @@ def main():
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
changed = False
|
|
||||||
# solaris args:
|
# solaris args:
|
||||||
# name, src, fstype, opts, boot, passno, state, fstab=/etc/vfstab
|
# name, src, fstype, opts, boot, passno, state, fstab=/etc/vfstab
|
||||||
# linux args:
|
# linux args:
|
||||||
|
@ -671,6 +650,7 @@ def main():
|
||||||
|
|
||||||
state = module.params['state']
|
state = module.params['state']
|
||||||
name = module.params['path']
|
name = module.params['path']
|
||||||
|
changed = False
|
||||||
|
|
||||||
if state == 'absent':
|
if state == 'absent':
|
||||||
name, changed = unset_mount(module, args)
|
name, changed = unset_mount(module, args)
|
||||||
|
@ -711,18 +691,13 @@ def main():
|
||||||
name, changed = set_mount(module, args)
|
name, changed = set_mount(module, args)
|
||||||
res = 0
|
res = 0
|
||||||
|
|
||||||
if ismount(name):
|
if (
|
||||||
|
ismount(name) or
|
||||||
|
is_bind_mounted(
|
||||||
|
module, linux_mounts, name, args['src'], args['fstype'])):
|
||||||
if changed and not module.check_mode:
|
if changed and not module.check_mode:
|
||||||
res, msg = mount(module, args)
|
res, msg = remount(module, args)
|
||||||
changed = True
|
changed = True
|
||||||
elif 'bind' in args.get('opts', []):
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
if is_bind_mounted( module, linux_mounts, name, args['src'], args['fstype']):
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
if changed and not module.check_mode:
|
|
||||||
res, msg = mount(module, args)
|
|
||||||
else:
|
else:
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
|
@ -738,5 +713,6 @@ def main():
|
||||||
|
|
||||||
module.exit_json(changed=changed, **args)
|
module.exit_json(changed=changed, **args)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in a new issue