]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | from __future__ import print_function |
2 | import argparse | |
3 | import json | |
4 | import logging | |
5 | from textwrap import dedent | |
6 | from ceph_volume import decorators, process | |
522d829b | 7 | from ceph_volume.util import disk |
92f5a8d4 TL |
8 | |
9 | ||
10 | logger = logging.getLogger(__name__) | |
11 | ||
522d829b | 12 | |
92f5a8d4 TL |
13 | def 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 |
23 | def _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: | |
39 | if oj[dev]['description'] != 'main': | |
40 | # ignore non-main devices, for now | |
41 | logger.info('ignoring non-main device {}'.format(dev)) | |
42 | return None | |
43 | whoami = oj[dev]['whoami'] | |
44 | return { | |
45 | 'type': 'bluestore', | |
46 | 'osd_id': int(whoami), | |
47 | 'osd_uuid': oj[dev]['osd_uuid'], | |
48 | 'ceph_fsid': oj[dev]['ceph_fsid'], | |
49 | 'device': dev | |
50 | } | |
51 | except KeyError as e: | |
52 | # this will appear for devices that have a bluestore header but aren't valid OSDs | |
53 | # for example, due to incomplete rollback of OSDs: https://tracker.ceph.com/issues/51869 | |
54 | logger.error('device {} does not have all BlueStore data needed to be a valid OSD: {}\n{}'.format(dev, out, e)) | |
55 | return None | |
56 | ||
92f5a8d4 TL |
57 | |
58 | class List(object): | |
59 | ||
60 | help = 'list BlueStore OSDs on raw devices' | |
61 | ||
62 | def __init__(self, argv): | |
63 | self.argv = argv | |
64 | ||
65 | def generate(self, devs=None): | |
522d829b TL |
66 | logger.debug('Listing block devices via lsblk...') |
67 | ||
68 | if devs is None or devs == []: | |
92f5a8d4 | 69 | devs = [] |
522d829b TL |
70 | # If no devs are given initially, we want to list ALL devices including children and |
71 | # parents. Parent disks with child partitions may be the appropriate device to return if | |
72 | # the parent disk has a bluestore header, but children may be the most appropriate | |
73 | # devices to return if the parent disk does not have a bluestore header. | |
92f5a8d4 | 74 | out, err, ret = process.call([ |
522d829b | 75 | 'lsblk', '--paths', '--output=NAME', '--noheadings', '--list' |
92f5a8d4 TL |
76 | ]) |
77 | assert not ret | |
78 | devs = out | |
522d829b | 79 | |
92f5a8d4 | 80 | result = {} |
522d829b | 81 | logger.debug('inspecting devices: {}'.format(devs)) |
92f5a8d4 | 82 | for dev in devs: |
522d829b TL |
83 | info = disk.lsblk(dev, abspath=True) |
84 | # Linux kernels built with CONFIG_ATARI_PARTITION enabled can falsely interpret | |
85 | # bluestore's on-disk format as an Atari partition table. These false Atari partitions | |
86 | # can be interpreted as real OSDs if a bluestore OSD was previously created on the false | |
87 | # partition. See https://tracker.ceph.com/issues/52060 for more info. If a device has a | |
88 | # parent, it is a child. If the parent is a valid bluestore OSD, the child will only | |
89 | # exist if it is a phantom Atari partition, and the child should be ignored. If the | |
90 | # parent isn't bluestore, then the child could be a valid bluestore OSD. If we fail to | |
91 | # determine whether a parent is bluestore, we should err on the side of not reporting | |
92 | # the child so as not to give a false negative. | |
93 | if 'PKNAME' in info and info['PKNAME'] != "": | |
94 | parent = info['PKNAME'] | |
95 | try: | |
96 | if disk.has_bluestore_label(parent): | |
97 | logger.warning(('ignoring child device {} whose parent {} is a BlueStore OSD.'.format(dev, parent), | |
98 | 'device is likely a phantom Atari partition. device info: {}'.format(info))) | |
99 | continue | |
100 | except OSError as e: | |
101 | logger.error(('ignoring child device {} to avoid reporting invalid BlueStore data from phantom Atari partitions.'.format(dev), | |
102 | 'failed to determine if parent device {} is BlueStore. err: {}'.format(parent, e))) | |
103 | continue | |
104 | ||
105 | bs_info = _get_bluestore_info(dev) | |
106 | if bs_info is None: | |
107 | # None is also returned in the rare event that there is an issue reading info from | |
108 | # a BlueStore disk, so be sure to log our assumption that it isn't bluestore | |
109 | logger.info('device {} does not have BlueStore information'.format(dev)) | |
92f5a8d4 | 110 | continue |
522d829b TL |
111 | result[bs_info['osd_uuid']] = bs_info |
112 | ||
92f5a8d4 TL |
113 | return result |
114 | ||
115 | @decorators.needs_root | |
116 | def list(self, args): | |
117 | report = self.generate(args.device) | |
118 | if args.format == 'json': | |
119 | print(json.dumps(report, indent=4, sort_keys=True)) | |
120 | else: | |
121 | if not report: | |
122 | raise SystemExit('No valid Ceph devices found') | |
123 | raise RuntimeError('not implemented yet') | |
124 | ||
125 | def main(self): | |
126 | sub_command_help = dedent(""" | |
127 | List OSDs on raw devices with raw device labels (usually the first | |
128 | block of the device). | |
129 | ||
130 | Full listing of all identifiable (currently, BlueStore) OSDs | |
131 | on raw devices: | |
132 | ||
133 | ceph-volume raw list | |
134 | ||
135 | List a particular device, reporting all metadata about it:: | |
136 | ||
137 | ceph-volume raw list /dev/sda1 | |
138 | ||
139 | """) | |
140 | parser = argparse.ArgumentParser( | |
141 | prog='ceph-volume raw list', | |
142 | formatter_class=argparse.RawDescriptionHelpFormatter, | |
143 | description=sub_command_help, | |
144 | ) | |
145 | ||
146 | parser.add_argument( | |
147 | 'device', | |
148 | metavar='DEVICE', | |
149 | nargs='*', | |
150 | help='Path to a device like /dev/sda1' | |
151 | ) | |
152 | ||
153 | parser.add_argument( | |
154 | '--format', | |
155 | help='output format, defaults to "pretty"', | |
156 | default='json', | |
157 | choices=['json', 'pretty'], | |
158 | ) | |
159 | ||
160 | args = parser.parse_args(self.argv) | |
161 | self.list(args) |