]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
ceph-volume: use udev data instead of LVM subprocess in get_devices()
authorGuillaume Abrioux <gabrioux@ibm.com>
Wed, 1 Oct 2025 14:04:20 +0000 (16:04 +0200)
committerGuillaume Abrioux <gabrioux@ibm.com>
Wed, 1 Oct 2025 14:28:24 +0000 (14:28 +0000)
Replace the  check using `lvm.get_device_lvs(diskname)`, which
spawned a `pvs` subprocess, with a direct check on `/run/udev/data`
via `UdevData(diskname).is_lvm`.

This avoids spawning subprocesses while scanning devices. It improves
performance on systems with many disks, and keeps the device filtering
logic intact.

Fixes: https://tracker.ceph.com/issues/73334
Signed-off-by: Guillaume Abrioux <gabrioux@ibm.com>
src/ceph-volume/ceph_volume/tests/util/test_disk.py
src/ceph-volume/ceph_volume/util/disk.py

index 1f359ba8b0c1352f64d57b27aa8e3264cd52c6f0..09c4f5d7360af3854ccd7075a9ac100bbe85ddcf 100644 (file)
@@ -256,7 +256,11 @@ class TestGetDevices(object):
     def test_sda_block_is_found(self, patched_get_block_devs_sysfs, fake_filesystem):
         sda_path = '/dev/sda'
         patched_get_block_devs_sysfs.return_value = [[sda_path, sda_path, 'disk', sda_path]]
-        result = disk.get_devices()
+        with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+            mock_instance = MagicMock()
+            mock_instance.is_lvm = False
+            MockUdevData.return_value = mock_instance
+            result = disk.get_devices()
         assert len(result.keys()) == 1
         assert result[sda_path]['human_readable_size'] == '0.00 B'
         assert result[sda_path]['model'] == ''
@@ -266,7 +270,11 @@ class TestGetDevices(object):
         sda_path = '/dev/sda'
         patched_get_block_devs_sysfs.return_value = [[sda_path, sda_path, 'disk', sda_path]]
         fake_filesystem.create_file('/sys/block/sda/size', contents = '1024')
-        result = disk.get_devices()
+        with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+            mock_instance = MagicMock()
+            mock_instance.is_lvm = False
+            MockUdevData.return_value = mock_instance
+            result = disk.get_devices()
         assert list(result.keys()) == [sda_path]
         assert result[sda_path]['human_readable_size'] == '512.00 KB'
 
@@ -275,7 +283,11 @@ class TestGetDevices(object):
         sda_path = '/dev/sda'
         patched_get_block_devs_sysfs.return_value = [[sda_path, sda_path, 'disk', sda_path]]
         fake_filesystem.create_file('/sys/block/sda/queue/hw_sector_size', contents = '1024')
-        result = disk.get_devices()
+        with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+            mock_instance = MagicMock()
+            mock_instance.is_lvm = False
+            MockUdevData.return_value = mock_instance
+            result = disk.get_devices()
         assert list(result.keys()) == [sda_path]
         assert result[sda_path]['sectorsize'] == '1024'
 
@@ -283,7 +295,11 @@ class TestGetDevices(object):
         sda_path = '/dev/sda'
         patched_get_block_devs_sysfs.return_value = [[sda_path, sda_path, 'disk', sda_path]]
         fake_filesystem.create_file('/sys/block/sda/queue/logical_block_size', contents = '99')
-        result = disk.get_devices()
+        with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+            mock_instance = MagicMock()
+            mock_instance.is_lvm = False
+            MockUdevData.return_value = mock_instance
+            result = disk.get_devices()
         assert result[sda_path]['sectorsize'] == '99'
 
     def test_sda_sectorsize_does_not_fallback(self, patched_get_block_devs_sysfs, fake_filesystem):
@@ -291,14 +307,22 @@ class TestGetDevices(object):
         patched_get_block_devs_sysfs.return_value = [[sda_path, sda_path, 'disk', sda_path]]
         fake_filesystem.create_file('/sys/block/sda/queue/logical_block_size', contents = '99')
         fake_filesystem.create_file('/sys/block/sda/queue/hw_sector_size', contents = '1024')
-        result = disk.get_devices()
+        with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+            mock_instance = MagicMock()
+            mock_instance.is_lvm = False
+            MockUdevData.return_value = mock_instance
+            result = disk.get_devices()
         assert result[sda_path]['sectorsize'] == '99'
 
     def test_is_rotational(self, patched_get_block_devs_sysfs, fake_filesystem):
         sda_path = '/dev/sda'
         patched_get_block_devs_sysfs.return_value = [[sda_path, sda_path, 'disk', sda_path]]
         fake_filesystem.create_file('/sys/block/sda/queue/rotational', contents = '1')
-        result = disk.get_devices()
+        with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+            mock_instance = MagicMock()
+            mock_instance.is_lvm = False
+            MockUdevData.return_value = mock_instance
+            result = disk.get_devices()
         assert result[sda_path]['rotational'] == '1'
 
     def test_is_ceph_rbd(self, patched_get_block_devs_sysfs, fake_filesystem):
@@ -313,7 +337,11 @@ class TestGetDevices(object):
         patched_get_block_devs_sysfs.return_value = [[sda_path, sda_path, 'disk', sda_path]]
         for actuator in range(0, fake_actuator_nb):
             fake_filesystem.create_dir(f'/sys/block/sda/queue/independent_access_ranges/{actuator}')
-        result = disk.get_devices()
+        with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+            mock_instance = MagicMock()
+            mock_instance.is_lvm = False
+            MockUdevData.return_value = mock_instance
+            result = disk.get_devices()
         assert result[sda_path]['actuators'] == fake_actuator_nb
 
 
index 51712f54be8a67a1c31680313577bbcfe261d810..3b2d2df359e10788f1b4fef9437b12a90be647b6 100644 (file)
@@ -5,7 +5,6 @@ import stat
 import time
 import json
 from ceph_volume import process, allow_loop_devices
-from ceph_volume.api import lvm
 from ceph_volume.util.system import get_file_contents
 from typing import Dict, List, Any, Union, Optional
 
@@ -802,9 +801,12 @@ def get_devices(_sys_block_path='/sys/block', device=''):
             continue
 
         # If the mapper device is a logical volume it gets excluded
-        if is_mapper_device(diskname):
-            if lvm.get_device_lvs(diskname):
+        try:
+            if UdevData(diskname).is_lvm:
                 continue
+        except RuntimeError:
+            logger.debug("get_devices(): device {} couldn't be found.".format(diskname))
+            continue
 
         # all facts that have no defaults
         # (<name>, <path relative to _sys_block_path>)