assert not disk.available
@patch("ceph_volume.util.disk.has_bluestore_label", lambda x: False)
+ @patch('ceph_volume.util.device.disk.path_is_block_device', return_value=True)
@patch('ceph_volume.util.device.os.path.realpath')
@patch('ceph_volume.util.device.os.path.islink')
def test_reject_lv_symlink_to_device(self,
m_os_path_islink,
m_os_path_realpath,
+ m_path_is_block_device,
device_info,
fake_call):
m_os_path_islink.return_value = True
device_info(lv=lv,lsblk=lsblk)
disk = device.Device("/dev/vg/lv")
assert disk.path == '/dev/vg/lv'
+ m_path_is_block_device.assert_called_with('/dev/vg/lv')
+
+ @patch("ceph_volume.util.disk.has_bluestore_label", lambda x: False)
+ @patch('ceph_volume.util.device.disk.path_is_block_device', return_value=False)
+ @patch('ceph_volume.util.device.os.path.realpath')
+ @patch('ceph_volume.util.device.os.path.islink')
+ def test_lv_keeps_mapper_path_when_lv_path_missing(self,
+ m_os_path_islink,
+ m_os_path_realpath,
+ m_path_is_block_device,
+ device_info,
+ fake_call):
+ m_os_path_islink.return_value = False
+ m_os_path_realpath.return_value = '/dev/mapper/vg-var_rmeta_0'
+ lv = {"lv_path": "/dev/vg/var_rmeta_0", "vg_name": "vg", "name": "var_rmeta_0", "tags": {}}
+ lsblk = {"TYPE": "lvm", "NAME": "vg-var_rmeta_0"}
+ device_info(lv=lv, lsblk=lsblk)
+ disk = device.Device("/dev/mapper/vg-var_rmeta_0")
+ assert disk.path == '/dev/mapper/vg-var_rmeta_0'
+ m_path_is_block_device.assert_called_with('/dev/vg/var_rmeta_0')
@patch("ceph_volume.util.disk.has_bluestore_label", lambda x: False)
def test_reject_smaller_than_5gb(self, fake_call, device_info):
fake_filesystem.create_file('/sys/block/dm-0/size', contents='204800')
fake_filesystem.create_file('/sys/block/dm-0/queue/rotational', contents='1')
fake_filesystem.create_file('/sys/block/dm-0/queue/hw_sector_size', contents='512')
+ fake_filesystem.create_file(lv_path, st_mode=(stat.S_IFBLK | 0o600))
with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
mock_instance = MagicMock()
+ mock_instance.is_internal_lv = False
mock_instance.preferred_block_path = lv_path
mock_instance.environment = {}
MockUdevData.return_value = mock_instance
assert result[lv_path]['type'] == 'lvm'
assert result[lv_path]['human_readable_size'] == '100.00 MB'
+ def test_internal_raid_lv_is_excluded(self, patched_get_block_devs_sysfs, fake_filesystem):
+ mapper_path = '/dev/mapper/debian-var_rmeta_0'
+ dm_path = '/dev/dm-5'
+ patched_get_block_devs_sysfs.return_value = [
+ [dm_path, mapper_path, 'lvm', dm_path]
+ ]
+ fake_filesystem.create_dir('/sys/block/dm-5/slaves')
+ fake_filesystem.create_dir('/sys/block/dm-5/queue')
+ fake_filesystem.create_file('/sys/block/dm-5/size', contents='8192')
+ fake_filesystem.create_file('/sys/block/dm-5/queue/rotational', contents='1')
+ fake_filesystem.create_file('/sys/block/dm-5/queue/hw_sector_size', contents='512')
+ with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+ mock_instance = MagicMock()
+ mock_instance.is_internal_lv = True
+ MockUdevData.return_value = mock_instance
+ result = disk.get_devices()
+ assert mapper_path not in result
+ assert not result
+
+ def test_lvm_device_without_accessible_node_is_excluded(
+ self, patched_get_block_devs_sysfs, fake_filesystem
+ ):
+ mapper_path = '/dev/mapper/debian-var_rmeta_0'
+ dm_path = '/dev/dm-5'
+ patched_get_block_devs_sysfs.return_value = [
+ [dm_path, mapper_path, 'lvm', dm_path]
+ ]
+ fake_filesystem.create_dir('/sys/block/dm-5/slaves')
+ fake_filesystem.create_dir('/sys/block/dm-5/queue')
+ fake_filesystem.create_file('/sys/block/dm-5/size', contents='8192')
+ fake_filesystem.create_file('/sys/block/dm-5/queue/rotational', contents='1')
+ fake_filesystem.create_file('/sys/block/dm-5/queue/hw_sector_size', contents='512')
+ with patch("ceph_volume.util.disk.UdevData") as MockUdevData:
+ mock_instance = MagicMock()
+ mock_instance.is_internal_lv = False
+ mock_instance.preferred_block_path = '/dev/debian/var_rmeta_0'
+ MockUdevData.return_value = mock_instance
+ result = disk.get_devices()
+ assert not result
+
class TestGetBlockDevsSysfs(object):
def test_optical_device_is_skipped(self, fake_filesystem):
def test_dashed_path_with_lvm(self) -> None:
assert disk.UdevData(self.fake_device).dashed_path == '/dev/mapper/fake_vg1-fake-lv1'
+ @patch('ceph_volume.util.disk.os.stat', _udev_data_patched_os_stat)
+ @patch('ceph_volume.util.disk.os.minor', Mock(return_value=1))
+ @patch('ceph_volume.util.disk.os.major', Mock(return_value=998))
+ def test_is_internal_lv_true_for_raid_metadata(self) -> None:
+ udev = disk.UdevData(self.fake_device)
+ udev.environment['DM_LV_NAME'] = 'var_rmeta_0'
+ assert udev.is_internal_lv
+
+ @patch('ceph_volume.util.disk.os.stat', _udev_data_patched_os_stat)
+ @patch('ceph_volume.util.disk.os.minor', Mock(return_value=1))
+ @patch('ceph_volume.util.disk.os.major', Mock(return_value=998))
+ def test_is_internal_lv_true_for_raid_image(self) -> None:
+ udev = disk.UdevData(self.fake_device)
+ udev.environment['DM_LV_NAME'] = 'var_rimage_1'
+ assert udev.is_internal_lv
+
+ @patch('ceph_volume.util.disk.os.stat', _udev_data_patched_os_stat)
+ @patch('ceph_volume.util.disk.os.minor', Mock(return_value=1))
+ @patch('ceph_volume.util.disk.os.major', Mock(return_value=998))
+ def test_is_internal_lv_true_for_layered_lv(self) -> None:
+ udev = disk.UdevData(self.fake_device)
+ udev.environment['DM_LV_LAYER'] = 'var'
+ assert udev.is_internal_lv
+
+ @patch('ceph_volume.util.disk.os.stat', _udev_data_patched_os_stat)
+ @patch('ceph_volume.util.disk.os.minor', Mock(return_value=1))
+ @patch('ceph_volume.util.disk.os.major', Mock(return_value=998))
+ def test_is_internal_lv_false_for_public_lv(self) -> None:
+ assert not disk.UdevData(self.fake_device).is_internal_lv
+
@patch('ceph_volume.util.disk.os.stat', _udev_data_patched_os_stat)
@patch('ceph_volume.util.disk.os.minor', Mock(return_value=1))
@patch('ceph_volume.util.disk.os.major', Mock(return_value=998))
return stat.S_ISBLK(stat_obj)
+def path_is_block_device(path: str) -> bool:
+ """True if ``path`` exists and is a block device (follows symlinks)."""
+ try:
+ return _stat_is_device(os.stat(path).st_mode)
+ except OSError:
+ return False
+
+
def _lsblk_parser(line):
"""
Parses lines in lsblk output. Requires output to be in pair mode (``-P`` flag). Lines
for block in block_devs:
metadata: Dict[str, Any] = {}
if block[2] == 'lvm':
- block[1] = UdevData(block[1]).preferred_block_path
+ try:
+ udev_data = UdevData(block[1])
+ except RuntimeError as exc:
+ logger.debug(
+ 'get_devices(): skipping LVM device %s: %s', block[1], exc)
+ continue
+ if udev_data.is_internal_lv:
+ logger.debug(
+ 'get_devices(): skipping internal LVM LV %s', block[1])
+ continue
+ block[1] = udev_data.preferred_block_path
+ if not path_is_block_device(block[1]):
+ logger.debug(
+ 'get_devices(): skipping LVM device without accessible node %s',
+ block[1])
+ continue
devname = os.path.basename(block[0])
diskname = block[1]
if block[2] not in block_types:
"""
return self.environment.get('DM_UUID', '').startswith('LVM')
+ @property
+ def is_internal_lv(self) -> bool:
+ lv_name = self.environment.get('DM_LV_NAME', '')
+ if not lv_name:
+ return False
+ for marker in ('_rmeta_', '_rimage_', '_rtmeta_', '_rtimage_'):
+ if marker in lv_name:
+ return True
+ return bool(self.environment.get('DM_LV_LAYER', ''))
+
@property
def slashed_path(self) -> str:
"""Get the LVM path structured with slashes.
result = f'/dev/mapper/{name}'
return result
- @staticmethod
- def _path_is_block_device(path: str) -> bool:
- """True if ``path`` exists and is a block device (follows symlinks)."""
- try:
- return _stat_is_device(os.stat(path).st_mode)
- except OSError:
- return False
-
@property
def preferred_block_path(self) -> str:
"""Return a device path that exists for typical open(2) / blkid usage.
if not self.is_lvm:
return self.path
slashed: str = self.slashed_path
- if self._path_is_block_device(slashed):
+ if path_is_block_device(slashed):
return slashed
dashed: str = self.dashed_path
- if self._path_is_block_device(dashed):
+ if path_is_block_device(dashed):
return dashed
return self.path