diff --git a/src/ceph-volume/ceph_volume/tests/util/test_system.py b/src/ceph-volume/ceph_volume/tests/util/test_system.py index 56b88b3f4ae..a742de4804c 100644 --- a/src/ceph-volume/ceph_volume/tests/util/test_system.py +++ b/src/ceph-volume/ceph_volume/tests/util/test_system.py @@ -101,6 +101,18 @@ class TestDeviceIsMounted(object): def test_is_mounted_at_destination(self, fake_proc): assert system.device_is_mounted('/dev/sda1', destination='/far/lib/ceph/osd/ceph-7') is False + def test_is_realpath_dev_mounted_at_destination(self, fake_proc, monkeypatch): + monkeypatch.setattr(system.os.path, 'realpath', lambda x: '/dev/sda1' if 'foo' in x else x) + result = system.device_is_mounted('/dev/maper/foo', destination='/far/lib/ceph/osd/ceph-0') + assert result is True + + def test_is_realpath_path_mounted_at_destination(self, fake_proc, monkeypatch): + monkeypatch.setattr( + system.os.path, 'realpath', + lambda x: '/far/lib/ceph/osd/ceph-0' if 'symlink' in x else x) + result = system.device_is_mounted('/dev/sda1', destination='/symlink/lib/ceph/osd/ceph-0') + assert result is True + class TestGetMounts(object): diff --git a/src/ceph-volume/ceph_volume/util/system.py b/src/ceph-volume/ceph_volume/util/system.py index 952fa56fe58..f04f00e1309 100644 --- a/src/ceph-volume/ceph_volume/util/system.py +++ b/src/ceph-volume/ceph_volume/util/system.py @@ -1,4 +1,5 @@ import errno +import logging import os import pwd import platform @@ -7,6 +8,7 @@ import uuid from ceph_volume import process from . import as_string +logger = logging.getLogger(__name__) # TODO: get these out of here and into a common area for others to consume if platform.system() == 'FreeBSD': @@ -137,16 +139,40 @@ def device_is_mounted(dev, destination=None): Check if the given device is mounted, optionally validating that a destination exists """ - mounts = get_mounts(devices=True) + plain_mounts = get_mounts(devices=True) + realpath_mounts = get_mounts(devices=True, realpath=True) + realpath_dev = os.path.realpath(dev) if dev.startswith('/') else dev destination = os.path.realpath(destination) if destination else None - mounted_locations = mounts.get(dev, []) + # plain mounts + plain_dev_mounts = plain_mounts.get(dev, []) + realpath_dev_mounts = plain_mounts.get(realpath_dev, []) + # realpath mounts + plain_dev_real_mounts = realpath_mounts.get(dev, []) + realpath_dev_real_mounts = realpath_mounts.get(realpath_dev, []) - if destination: - return destination in mounted_locations - return mounted_locations != [] + mount_locations = [ + plain_dev_mounts, + realpath_dev_mounts, + plain_dev_real_mounts, + realpath_dev_real_mounts + ] + + for mounts in mount_locations: + if mounts: # we have a matching mount + if destination: + if destination in mounts: + logger.info( + '%s detected as mounted, exists at destination: %s', dev, destination + ) + return True + else: + logger.info('%s was found as mounted') + return True + logger.info('%s was not found as mounted') + return False -def get_mounts(devices=False, paths=False): +def get_mounts(devices=False, paths=False, realpath=False): """ Create a mapping of all available system mounts so that other helpers can detect nicely what path or device is mounted @@ -160,6 +186,9 @@ def get_mounts(devices=False, paths=False): If ``devices`` is set to ``True`` the mapping will be a device-to-path(s), if ``paths`` is set to ``True`` then the mapping will be a path-to-device(s) + + :param realpath: Resolve devices to use their realpaths. This is useful for + paths like LVM where more than one path can point to the same device """ devices_mounted = {} paths_mounted = {} @@ -173,7 +202,10 @@ def get_mounts(devices=False, paths=False): fields = [as_string(f) for f in line.split()] if len(fields) < 3: continue - device = fields[0] + if realpath: + device = os.path.realpath(fields[0]) if fields[0].startswith('/') else fields[0] + else: + device = fields[0] path = os.path.realpath(fields[1]) # only care about actual existing devices if not os.path.exists(device) or not device.startswith('/'):