]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume: Optionally consume loop devices
authorZack Cerza <zack@cerza.org>
Tue, 17 May 2022 17:29:02 +0000 (11:29 -0600)
committerGuillaume Abrioux <gabrioux@redhat.com>
Tue, 2 Aug 2022 09:18:24 +0000 (11:18 +0200)
A similar proposal was rejected in #24765; I understand the logic
behind the rejection, but this will allow us to run Ceph clusters on
machines that lack disk resources for testing purposes. We just need to
make it impossible to accidentally enable, and make it clear it is
unsupported.

Signed-off-by: Zack Cerza <zack@redhat.com>
(cherry picked from commit c7f017b21ade3762ba5b7b9688bed72c6b60dc0e)

src/ceph-volume/ceph_volume/tests/util/test_device.py
src/ceph-volume/ceph_volume/util/device.py
src/ceph-volume/ceph_volume/util/disk.py

index 49a34d7eef1007399ba58aa9b3ecb3d38dd9b825..e3e3b30c4be2acdaf1004ae31b333d5a92e3a18e 100644 (file)
@@ -1,3 +1,4 @@
+import os
 import pytest
 from copy import deepcopy
 from ceph_volume.util import device
@@ -80,6 +81,22 @@ class TestDevice(object):
         disk = device.Device("/dev/sda")
         assert disk.is_device is True
 
+    def test_loop_device_is_not_device(self, fake_call, device_info):
+        data = {"/dev/loop0": {"foo": "bar"}}
+        lsblk = {"TYPE": "loop"}
+        device_info(devices=data, lsblk=lsblk)
+        disk = device.Device("/dev/loop0")
+        assert disk.is_device is False
+
+    def test_loop_device_is_device(self, fake_call, device_info):
+        data = {"/dev/loop0": {"foo": "bar"}}
+        lsblk = {"TYPE": "loop"}
+        os.environ["CEPH_VOLUME_USE_LOOP_DEVICES"] = "1"
+        device_info(devices=data, lsblk=lsblk)
+        disk = device.Device("/dev/loop0")
+        assert disk.is_device is True
+        del os.environ["CEPH_VOLUME_USE_LOOP_DEVICES"]
+
     def test_device_is_rotational(self, fake_call, device_info):
         data = {"/dev/sda": {"rotational": "1"}}
         lsblk = {"TYPE": "device", "NAME": "sda"}
index 276cce9ff6174186e02465acafef8bfa2f2e2891..c556e56768ddd28a16f0af3dddb9e72c09bc26d1 100644 (file)
@@ -213,7 +213,10 @@ class Device(object):
             self.disk_api = dev
             device_type = dev.get('TYPE', '')
             # always check is this is an lvm member
-            if device_type in ['part', 'disk']:
+            valid_types = ['part', 'disk']
+            if os.environ.get("CEPH_VOLUME_USE_LOOP_DEVICES", False):
+                valid_types.append('loop')
+            if device_type in valid_types:
                 self._set_lvm_membership()
             out, err, rc = process.call([
                 'ceph-bluestore-tool', 'show-label',
@@ -428,7 +431,9 @@ class Device(object):
     @property
     def device_type(self):
         self.load_blkid_api()
-        if self.disk_api:
+        if 'type' in self.sys_api:
+            return self.sys_api['type']
+        elif self.disk_api:
             return self.disk_api['TYPE']
         elif self.blkid_api:
             return self.blkid_api['TYPE']
@@ -459,7 +464,10 @@ class Device(object):
         elif self.blkid_api:
             api = self.blkid_api
         if api:
-            return self.device_type in ['disk', 'device', 'mpath']
+            valid_types = ['disk', 'device', 'mpath']
+            if os.environ.get("CEPH_VOLUME_USE_LOOP_DEVICES", False):
+                valid_types.append('loop')
+            return self.device_type in valid_types
         return False
 
     @property
index 31d9edd690d0f93a4aa81664b8154cfc7c1ebb3c..923c12f5246cdd6bc5424272bdc85fffe7093058 100644 (file)
@@ -340,16 +340,18 @@ def is_device(dev):
     """
     if not os.path.exists(dev):
         return False
-    # use lsblk first, fall back to using stat
-    TYPE = lsblk(dev).get('TYPE')
-    if TYPE:
-        return TYPE in ['disk', 'mpath']
+    if not dev.startswith('/dev/'):
+        return False
+    if dev[len('/dev/'):].startswith('loop'):
+        if os.environ.get("CEPH_VOLUME_USE_LOOP_DEVICES", False) is False:
+            return False
+        logger.info(
+            "Allowing the use of loop devices since "
+            "CEPH_VOLUME_USE_LOOP_DEVICES is set."
+        )
 
     # fallback to stat
     return _stat_is_device(os.lstat(dev).st_mode)
-    if stat.S_ISBLK(os.lstat(dev)):
-        return True
-    return False
 
 
 def is_partition(dev):
@@ -756,6 +758,40 @@ def get_block_devs_lsblk(device=''):
         raise OSError('lsblk returned failure, stderr: {}'.format(stderr))
     return [re.split(r'\s+', line) for line in stdout]
 
+
+def get_block_devs_sysfs(_sys_block_path='/sys/block', _sys_dev_block_path='/sys/dev/block'):
+    # First, get devices that are _not_ partitions
+    result = list()
+    dev_names = os.listdir(_sys_block_path)
+    for dev in dev_names:
+        name = kname = os.path.join("/dev", dev)
+        type_ = 'disk'
+        if get_file_contents(os.path.join(_sys_block_path, dev, 'removable')) == "1":
+            continue
+        dm_dir_path = os.path.join(_sys_block_path, dev, 'dm')
+        if os.path.isdir(dm_dir_path):
+            type_ = 'lvm'
+            basename = get_file_contents(os.path.join(dm_dir_path, 'name'))
+            name = os.path.join("/dev/mapper", basename)
+        if dev.startswith('loop'):
+            if os.environ.get("CEPH_VOLUME_USE_LOOP_DEVICES", False) is False:
+                continue
+            # Skip loop devices that are not attached
+            if not os.path.exists(os.path.join(_sys_block_path, dev, 'loop')):
+                continue
+            type_ = 'loop'
+        result.append([kname, name, type_])
+    # Next, look for devices that _are_ partitions
+    for item in os.listdir(_sys_dev_block_path):
+        is_part = get_file_contents(os.path.join(_sys_dev_block_path, item, 'partition')) == "1"
+        dev = os.path.basename(os.readlink(os.path.join(_sys_dev_block_path, item)))
+        if not is_part:
+            continue
+        name = kname = os.path.join("/dev", dev)
+        result.append([name, kname, "part"])
+    return sorted(result, key=lambda x: x[0])
+
+
 def get_devices(_sys_block_path='/sys/block', device=''):
     """
     Captures all available block devices as reported by lsblk.
@@ -769,12 +805,16 @@ def get_devices(_sys_block_path='/sys/block', device=''):
 
     device_facts = {}
 
-    block_devs = get_block_devs_lsblk(device=device)
+    block_devs = get_block_devs_sysfs(_sys_block_path)
+
+    block_types = ['disk', 'mpath']
+    if os.environ.get("CEPH_VOLUME_USE_LOOP_DEVICES", False) is not False:
+        block_types.append('loop')
 
     for block in block_devs:
         devname = os.path.basename(block[0])
         diskname = block[1]
-        if block[2] not in ['disk', 'mpath']:
+        if block[2] not in block_types:
             continue
         sysdir = os.path.join(_sys_block_path, devname)
         metadata = {}
@@ -820,6 +860,7 @@ def get_devices(_sys_block_path='/sys/block', device=''):
         metadata['human_readable_size'] = human_readable_size(metadata['size'])
         metadata['path'] = diskname
         metadata['locked'] = is_locked_raw_device(metadata['path'])
+        metadata['type'] = block[2]
 
         device_facts[diskname] = metadata
     return device_facts