]> git.proxmox.com Git - ceph.git/blame - ceph/src/ceph-volume/ceph_volume/devices/raw/list.py
import ceph quincy 17.2.4
[ceph.git] / ceph / src / ceph-volume / ceph_volume / devices / raw / list.py
CommitLineData
92f5a8d4
TL
1from __future__ import print_function
2import argparse
3import json
4import logging
5from textwrap import dedent
6from ceph_volume import decorators, process
522d829b 7from ceph_volume.util import disk
92f5a8d4
TL
8
9
10logger = logging.getLogger(__name__)
11
522d829b 12
92f5a8d4
TL
13def direct_report(devices):
14 """
15 Other non-cli consumers of listing information will want to consume the
16 report without the need to parse arguments or other flags. This helper
17 bypasses the need to deal with the class interface which is meant for cli
18 handling.
19 """
20 _list = List([])
21 return _list.generate(devices)
22
522d829b
TL
23def _get_bluestore_info(dev):
24 out, err, rc = process.call([
25 'ceph-bluestore-tool', 'show-label',
26 '--dev', dev], verbose_on_failure=False)
27 if rc:
28 # ceph-bluestore-tool returns an error (below) if device is not bluestore OSD
29 # > unable to read label for <device>: (2) No such file or directory
30 # but it's possible the error could be for a different reason (like if the disk fails)
31 logger.debug('assuming device {} is not BlueStore; ceph-bluestore-tool failed to get info from device: {}\n{}'.format(dev, out, err))
32 return None
33 oj = json.loads(''.join(out))
34 if dev not in oj:
35 # should be impossible, so warn
36 logger.warning('skipping device {} because it is not reported in ceph-bluestore-tool output: {}'.format(dev, out))
37 return None
38 try:
20effc67 39 r = {
522d829b 40 'osd_uuid': oj[dev]['osd_uuid'],
522d829b 41 }
20effc67
TL
42 if oj[dev]['description'] == 'main':
43 whoami = oj[dev]['whoami']
44 r.update({
45 'type': 'bluestore',
46 'osd_id': int(whoami),
47 'ceph_fsid': oj[dev]['ceph_fsid'],
48 'device': dev,
49 })
50 elif oj[dev]['description'] == 'bluefs db':
51 r['device_db'] = dev
52 elif oj[dev]['description'] == 'bluefs wal':
53 r['device_wal'] = dev
54 return r
522d829b
TL
55 except KeyError as e:
56 # this will appear for devices that have a bluestore header but aren't valid OSDs
57 # for example, due to incomplete rollback of OSDs: https://tracker.ceph.com/issues/51869
58 logger.error('device {} does not have all BlueStore data needed to be a valid OSD: {}\n{}'.format(dev, out, e))
59 return None
60
92f5a8d4
TL
61
62class List(object):
63
64 help = 'list BlueStore OSDs on raw devices'
65
66 def __init__(self, argv):
67 self.argv = argv
68
69 def generate(self, devs=None):
522d829b 70 logger.debug('Listing block devices via lsblk...')
2a845540 71 info_devices = disk.lsblk_all(abspath=True)
522d829b 72 if devs is None or devs == []:
522d829b
TL
73 # If no devs are given initially, we want to list ALL devices including children and
74 # parents. Parent disks with child partitions may be the appropriate device to return if
75 # the parent disk has a bluestore header, but children may be the most appropriate
76 # devices to return if the parent disk does not have a bluestore header.
2a845540 77 devs = [device['NAME'] for device in info_devices if device.get('NAME',)]
522d829b 78
92f5a8d4 79 result = {}
522d829b 80 logger.debug('inspecting devices: {}'.format(devs))
92f5a8d4 81 for dev in devs:
522d829b
TL
82 # Linux kernels built with CONFIG_ATARI_PARTITION enabled can falsely interpret
83 # bluestore's on-disk format as an Atari partition table. These false Atari partitions
84 # can be interpreted as real OSDs if a bluestore OSD was previously created on the false
85 # partition. See https://tracker.ceph.com/issues/52060 for more info. If a device has a
86 # parent, it is a child. If the parent is a valid bluestore OSD, the child will only
87 # exist if it is a phantom Atari partition, and the child should be ignored. If the
88 # parent isn't bluestore, then the child could be a valid bluestore OSD. If we fail to
89 # determine whether a parent is bluestore, we should err on the side of not reporting
90 # the child so as not to give a false negative.
2a845540
TL
91 for info_device in info_devices:
92 if 'PKNAME' in info_device and info_device['PKNAME'] != "":
93 parent = info_device['PKNAME']
94 try:
95 if disk.has_bluestore_label(parent):
96 logger.warning(('ignoring child device {} whose parent {} is a BlueStore OSD.'.format(dev, parent),
97 'device is likely a phantom Atari partition. device info: {}'.format(info_device)))
98 continue
99 except OSError as e:
100 logger.error(('ignoring child device {} to avoid reporting invalid BlueStore data from phantom Atari partitions.'.format(dev),
101 'failed to determine if parent device {} is BlueStore. err: {}'.format(parent, e)))
522d829b 102 continue
522d829b 103
2a845540
TL
104 bs_info = _get_bluestore_info(dev)
105 if bs_info is None:
106 # None is also returned in the rare event that there is an issue reading info from
107 # a BlueStore disk, so be sure to log our assumption that it isn't bluestore
108 logger.info('device {} does not have BlueStore information'.format(dev))
109 continue
110 uuid = bs_info['osd_uuid']
111 if uuid not in result:
112 result[uuid] = {}
113 result[uuid].update(bs_info)
522d829b 114
92f5a8d4
TL
115 return result
116
117 @decorators.needs_root
118 def list(self, args):
119 report = self.generate(args.device)
120 if args.format == 'json':
121 print(json.dumps(report, indent=4, sort_keys=True))
122 else:
123 if not report:
124 raise SystemExit('No valid Ceph devices found')
125 raise RuntimeError('not implemented yet')
126
127 def main(self):
128 sub_command_help = dedent("""
129 List OSDs on raw devices with raw device labels (usually the first
130 block of the device).
131
132 Full listing of all identifiable (currently, BlueStore) OSDs
133 on raw devices:
134
135 ceph-volume raw list
136
137 List a particular device, reporting all metadata about it::
138
139 ceph-volume raw list /dev/sda1
140
141 """)
142 parser = argparse.ArgumentParser(
143 prog='ceph-volume raw list',
144 formatter_class=argparse.RawDescriptionHelpFormatter,
145 description=sub_command_help,
146 )
147
148 parser.add_argument(
149 'device',
150 metavar='DEVICE',
151 nargs='*',
152 help='Path to a device like /dev/sda1'
153 )
154
155 parser.add_argument(
156 '--format',
157 help='output format, defaults to "pretty"',
158 default='json',
159 choices=['json', 'pretty'],
160 )
161
162 args = parser.parse_args(self.argv)
163 self.list(args)