From ea7aa27452f47f0808871abc366daa3bd7b6d2ab Mon Sep 17 00:00:00 2001 From: Guillaume Abrioux Date: Wed, 1 Oct 2025 16:04:20 +0200 Subject: [PATCH] ceph-volume: use udev data instead of LVM subprocess in get_devices() 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 --- .../ceph_volume/tests/util/test_disk.py | 42 +++++++++++++++---- src/ceph-volume/ceph_volume/util/disk.py | 8 ++-- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/src/ceph-volume/ceph_volume/tests/util/test_disk.py b/src/ceph-volume/ceph_volume/tests/util/test_disk.py index 1f359ba8b0c13..09c4f5d7360af 100644 --- a/src/ceph-volume/ceph_volume/tests/util/test_disk.py +++ b/src/ceph-volume/ceph_volume/tests/util/test_disk.py @@ -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 diff --git a/src/ceph-volume/ceph_volume/util/disk.py b/src/ceph-volume/ceph_volume/util/disk.py index 51712f54be8a6..3b2d2df359e10 100644 --- a/src/ceph-volume/ceph_volume/util/disk.py +++ b/src/ceph-volume/ceph_volume/util/disk.py @@ -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 # (, ) -- 2.39.5