]> git-server-git.apps.pok.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>
Fri, 5 Aug 2022 06:58:17 +0000 (08:58 +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 18698e5bd955ce29bba7aa827351193a14cf2573..ab7b8c54e56768cfbad5da11703dd2220501f015 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 c388d9497114097910395421e363690ab4ce13e9..866e5dc2741a480ff090510dfe189492f781743b 100644 (file)
@@ -211,7 +211,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',
@@ -424,7 +427,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']
@@ -455,7 +460,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 b2e4b84192cd422d4675fc9977b2f390c22a41e2..645104010c560cde487bad8a80eac0b2b98538d7 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):
@@ -763,6 +765,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.
@@ -776,12 +812,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 = {}
@@ -831,6 +871,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