From 1f4a4bd88dbaf7bb621ae3ca6989d36656ae1d4e Mon Sep 17 00:00:00 2001
From: Guillaume Abrioux <gabrioux@ibm.com>
Date: Wed, 15 Nov 2023 16:03:10 +0100
Subject: [PATCH] ceph-volume: fix a regression in `raw list`

'ceph-volume raw list' is broken for a specific use case (rook).

rook copies devices from /dev/ to /mnt for specific/internal needs.

when ceph-volume raw list is passed a device from /mnt then
ceph-volume ignores it and return an empty dict.
That prevent rook from creating OSDs properly.

https://tracker.ceph.com/issues/63545

Signed-off-by: Guillaume Abrioux <gabrioux@ibm.com>
(cherry picked from commit e67dce7120954fff9b7b936348bed0b2ac6c1155)
---
 .../ceph_volume/devices/raw/list.py           | 69 ++++++++++---------
 1 file changed, 38 insertions(+), 31 deletions(-)

diff --git a/src/ceph-volume/ceph_volume/devices/raw/list.py b/src/ceph-volume/ceph_volume/devices/raw/list.py
index dacd980fad603..794bb18c103fd 100644
--- a/src/ceph-volume/ceph_volume/devices/raw/list.py
+++ b/src/ceph-volume/ceph_volume/devices/raw/list.py
@@ -5,7 +5,7 @@
 from textwrap import dedent
 from ceph_volume import decorators, process
 from ceph_volume.util import disk
-
+from typing import Any, Dict, List
 
 logger = logging.getLogger(__name__)
 
@@ -66,50 +66,57 @@ class List(object):
     def __init__(self, argv):
         self.argv = argv
 
+    def is_atari_partitions(self, _lsblk: Dict[str, Any]) -> bool:
+        dev = _lsblk['NAME']
+        if _lsblk.get('PKNAME'):
+            parent = _lsblk['PKNAME']
+            try:
+                if disk.has_bluestore_label(parent):
+                    logger.warning(('ignoring child device {} whose parent {} is a BlueStore OSD.'.format(dev, parent),
+                                    'device is likely a phantom Atari partition. device info: {}'.format(_lsblk)))
+                    return True
+            except OSError as e:
+                logger.error(('ignoring child device {} to avoid reporting invalid BlueStore data from phantom Atari partitions.'.format(dev),
+                            'failed to determine if parent device {} is BlueStore. err: {}'.format(parent, e)))
+                return True
+        return False
+
+    def exclude_atari_partitions(self, _lsblk_all: Dict[str, Any]) -> List[Dict[str, Any]]:
+        return [_lsblk for _lsblk in _lsblk_all if not self.is_atari_partitions(_lsblk)]
+
     def generate(self, devs=None):
         logger.debug('Listing block devices via lsblk...')
-        info_devices = disk.lsblk_all(abspath=True)
+        info_devices = []
         if not devs or not any(devs):
             # If no devs are given initially, we want to list ALL devices including children and
             # parents. Parent disks with child partitions may be the appropriate device to return if
             # the parent disk has a bluestore header, but children may be the most appropriate
             # devices to return if the parent disk does not have a bluestore header.
+            info_devices = disk.lsblk_all(abspath=True)
             devs = [device['NAME'] for device in info_devices if device.get('NAME',)]
+        else:
+            for dev in devs:
+                info_devices.append(disk.lsblk(dev, abspath=True))
+
+        # Linux kernels built with CONFIG_ATARI_PARTITION enabled can falsely interpret
+        # bluestore's on-disk format as an Atari partition table. These false Atari partitions
+        # can be interpreted as real OSDs if a bluestore OSD was previously created on the false
+        # partition. See https://tracker.ceph.com/issues/52060 for more info. If a device has a
+        # parent, it is a child. If the parent is a valid bluestore OSD, the child will only
+        # exist if it is a phantom Atari partition, and the child should be ignored. If the
+        # parent isn't bluestore, then the child could be a valid bluestore OSD. If we fail to
+        # determine whether a parent is bluestore, we should err on the side of not reporting
+        # the child so as not to give a false negative.
+        info_devices = self.exclude_atari_partitions(info_devices)
 
         result = {}
         logger.debug('inspecting devices: {}'.format(devs))
-        for dev in devs:
-            # Linux kernels built with CONFIG_ATARI_PARTITION enabled can falsely interpret
-            # bluestore's on-disk format as an Atari partition table. These false Atari partitions
-            # can be interpreted as real OSDs if a bluestore OSD was previously created on the false
-            # partition. See https://tracker.ceph.com/issues/52060 for more info. If a device has a
-            # parent, it is a child. If the parent is a valid bluestore OSD, the child will only
-            # exist if it is a phantom Atari partition, and the child should be ignored. If the
-            # parent isn't bluestore, then the child could be a valid bluestore OSD. If we fail to
-            # determine whether a parent is bluestore, we should err on the side of not reporting
-            # the child so as not to give a false negative.
-            matched_info_devices = [info for info in info_devices if info['NAME'] == dev]
-            if not matched_info_devices:
-                logger.warning('device {} does not exist'.format(dev))
-                continue
-            info_device = matched_info_devices[0]
-            if 'PKNAME' in info_device and info_device['PKNAME'] != "":
-                parent = info_device['PKNAME']
-                try:
-                    if disk.has_bluestore_label(parent):
-                        logger.warning(('ignoring child device {} whose parent {} is a BlueStore OSD.'.format(dev, parent),
-                                        'device is likely a phantom Atari partition. device info: {}'.format(info_device)))
-                        continue
-                except OSError as e:
-                    logger.error(('ignoring child device {} to avoid reporting invalid BlueStore data from phantom Atari partitions.'.format(dev),
-                                'failed to determine if parent device {} is BlueStore. err: {}'.format(parent, e)))
-                    continue
-
-            bs_info = _get_bluestore_info(dev)
+        for info_device in info_devices:
+            bs_info = _get_bluestore_info(info_device['NAME'])
             if bs_info is None:
                 # None is also returned in the rare event that there is an issue reading info from
                 # a BlueStore disk, so be sure to log our assumption that it isn't bluestore
-                logger.info('device {} does not have BlueStore information'.format(dev))
+                logger.info('device {} does not have BlueStore information'.format(info_device['NAME']))
                 continue
             uuid = bs_info['osd_uuid']
             if uuid not in result:
