1 from __future__ import print_function
5 from textwrap import dedent
6 from ceph_volume import decorators
7 from ceph_volume.util import disk
8 from ceph_volume.api import lvm as api
10 logger = logging.getLogger(__name__)
13 osd_list_header_template = """\n
17 osd_device_header_template = """
22 device_metadata_item_template = """
23 {tag_name: <25} {value}"""
26 def readable_tag(tag):
27 actual_name = tag.split('.')[-1]
28 return actual_name.replace('_', ' ')
31 def pretty_report(report):
33 for _id, devices in report.items():
35 osd_list_header_template.format(osd_id=" osd.%s " % _id)
37 for device in devices:
39 osd_device_header_template.format(
44 for tag_name, value in device.get('tags', {}).items():
46 device_metadata_item_template.format(
47 tag_name=readable_tag(tag_name),
51 print(''.join(output))
56 help = 'list logical volumes and devices associated with Ceph'
58 def __init__(self, argv):
61 @decorators.needs_root
63 # ensure everything is up to date before calling out
66 report = self.generate(args)
67 if args.format == 'json':
68 # If the report is empty, we don't return a non-zero exit status
69 # because it is assumed this is going to be consumed by automated
70 # systems like ceph-ansible which would be forced to ignore the
71 # non-zero exit status if all they need is the information in the
73 print(json.dumps(report, indent=4, sort_keys=True))
76 raise SystemExit('No valid Ceph devices found')
81 Ensure all journal devices are up to date if they aren't a logical
87 lv.tags['ceph.osd_id']
89 # only consider ceph-based logical volumes, everything else
93 for device_type in ['journal', 'block', 'wal', 'db']:
94 device_name = 'ceph.%s_device' % device_type
95 device_uuid = lv.tags.get('ceph.%s_uuid' % device_type)
97 # bluestore will not have a journal, filestore will not have
98 # a block/wal/db, so we must skip if not present
100 disk_device = disk.get_device_from_partuuid(device_uuid)
102 if lv.tags[device_name] != disk_device:
103 # this means that the device has changed, so it must be updated
104 # on the API to reflect this
105 lv.set_tags({device_name: disk_device})
107 def generate(self, args):
109 Generate reports for an individual device or for all Ceph-related
110 devices, logical or physical, as long as they have been prepared by
111 this tool before and contain enough metadata.
114 return self.single_report(args.device)
116 return self.full_report()
118 def single_report(self, device):
120 Generate a report for a single device. This can be either a logical
121 volume in the form of vg/lv or a device with an absolute path like
126 lv = api.get_lv_from_argument(device)
129 _id = lv.tags['ceph.osd_id']
131 logger.warning('device is not part of ceph: %s', device)
134 report.setdefault(_id, [])
140 # this has to be a journal/wal/db device (not a logical volume) so try
141 # to find the PARTUUID that should be stored in the OSD logical
143 for device_type in ['journal', 'block', 'wal', 'db']:
144 device_tag_name = 'ceph.%s_device' % device_type
145 device_tag_uuid = 'ceph.%s_uuid' % device_type
146 associated_lv = lvs.get(lv_tags={device_tag_name: device})
148 _id = associated_lv.tags['ceph.osd_id']
149 uuid = associated_lv.tags[device_tag_uuid]
151 report.setdefault(_id, [])
154 'tags': {'PARTUUID': uuid},
161 def full_report(self):
163 Generate a report for all the logical volumes and associated devices
164 that have been previously prepared by Ceph
170 _id = lv.tags['ceph.osd_id']
172 # only consider ceph-based logical volumes, everything else
176 report.setdefault(_id, [])
181 for device_type in ['journal', 'block', 'wal', 'db']:
182 device_uuid = lv.tags.get('ceph.%s_uuid' % device_type)
184 # bluestore will not have a journal, filestore will not have
185 # a block/wal/db, so we must skip if not present
187 if not api.get_lv(lv_uuid=device_uuid):
188 # means we have a regular device, so query blkid
189 disk_device = disk.get_device_from_partuuid(device_uuid)
193 'tags': {'PARTUUID': device_uuid},
202 sub_command_help = dedent("""
203 List devices or logical volumes associated with Ceph. An association is
204 determined if a device has information relating to an OSD. This is
205 verified by querying LVM's metadata and correlating it with devices.
207 The lvs associated with the OSD need to have been prepared previously,
208 so that all needed tags and metadata exist.
210 Full listing of all system devices associated with a cluster::
214 List a particular device, reporting all metadata about it::
216 ceph-volume lvm list /dev/sda1
218 List a logical volume, along with all its metadata (vg is a volume
219 group, and lv the logical volume name)::
221 ceph-volume lvm list {vg/lv}
223 parser = argparse.ArgumentParser(
224 prog='ceph-volume lvm list',
225 formatter_class=argparse.RawDescriptionHelpFormatter,
226 description=sub_command_help,
233 help='Path to an lv (as vg/lv) or to a device like /dev/sda1'
238 help='output format, defaults to "pretty"',
240 choices=['json', 'pretty'],
243 args = parser.parse_args(self.argv)