1 from __future__ import print_function
4 from textwrap import dedent
5 from ceph_volume.util import prepare as prepare_utils
6 from ceph_volume.util import system, disk
7 from ceph_volume import conf, decorators, terminal
8 from ceph_volume.api import lvm as api
9 from .common import prepare_parser
12 def prepare_filestore(device, journal, secrets, id_=None, fsid=None):
14 :param device: The name of the logical volume to work with
15 :param journal: similar to device but can also be a regular/plain disk
16 :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
17 :param id_: The OSD id
18 :param fsid: The OSD fsid, also known as the OSD UUID
20 cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key())
21 json_secrets = json.dumps(secrets)
23 # allow re-using an existing fsid, in case prepare failed
24 fsid = fsid or system.generate_uuid()
25 # allow re-using an id, in case a prepare failed
26 osd_id = id_ or prepare_utils.create_id(fsid, json_secrets)
27 # create the directory
28 prepare_utils.create_osd_path(osd_id)
30 prepare_utils.format_device(device)
31 # mount the data device
32 prepare_utils.mount_osd(device, osd_id)
34 prepare_utils.link_journal(journal, osd_id)
35 # get the latest monmap
36 prepare_utils.get_monmap(osd_id)
37 # prepare the osd filesystem
38 prepare_utils.osd_mkfs_filestore(osd_id, fsid)
39 # write the OSD keyring if it doesn't exist already
40 prepare_utils.write_keyring(osd_id, cephx_secret)
43 def prepare_bluestore(block, wal, db, secrets, id_=None, fsid=None):
45 :param block: The name of the logical volume for the bluestore data
46 :param wal: a regular/plain disk or logical volume, to be used for block.wal
47 :param db: a regular/plain disk or logical volume, to be used for block.db
48 :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
49 :param id_: The OSD id
50 :param fsid: The OSD fsid, also known as the OSD UUID
52 cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key())
53 json_secrets = json.dumps(secrets)
55 # allow re-using an existing fsid, in case prepare failed
56 fsid = fsid or system.generate_uuid()
57 # allow re-using an id, in case a prepare failed
58 osd_id = id_ or prepare_utils.create_id(fsid, json_secrets)
59 # create the directory
60 prepare_utils.create_osd_path(osd_id, tmpfs=True)
62 prepare_utils.link_block(block, osd_id)
63 # get the latest monmap
64 prepare_utils.get_monmap(osd_id)
65 # write the OSD keyring if it doesn't exist already
66 prepare_utils.write_keyring(osd_id, cephx_secret)
67 # prepare the osd filesystem
68 prepare_utils.osd_mkfs_bluestore(
76 class Prepare(object):
78 help = 'Format an LVM device and associate it with an OSD'
80 def __init__(self, argv):
83 def get_ptuuid(self, argument):
84 uuid = disk.get_partuuid(argument)
86 terminal.error('blkid could not detect a PARTUUID for device: %s' % argument)
87 raise RuntimeError('unable to use device')
90 def get_lv(self, argument):
92 Perform some parsing of the command-line value so that the process
93 can determine correctly if it got a device path or an lv.
95 :param argument: The command-line value that will need to be split to
96 retrieve the actual lv
99 vg_name, lv_name = argument.split('/')
100 except (ValueError, AttributeError):
102 return api.get_lv(lv_name=lv_name, vg_name=vg_name)
104 def setup_device(self, device_type, device_name, tags):
106 Check if ``device`` is an lv, if so, set the tags, making sure to
107 update the tags with the lv_uuid and lv_path which the incoming tags
110 If the device is not a logical volume, then retrieve the partition UUID
111 by querying ``blkid``
113 if device_name is None:
115 tags['ceph.type'] = device_type
116 lv = self.get_lv(device_name)
120 tags['ceph.%s_uuid' % device_type] = uuid
121 tags['ceph.%s_device' % device_type] = path
124 # otherwise assume this is a regular disk partition
125 uuid = self.get_ptuuid(device_name)
127 tags['ceph.%s_uuid' % device_type] = uuid
128 tags['ceph.%s_device' % device_type] = path
129 return path, uuid, tags
131 def prepare_device(self, arg, device_type, cluster_fsid, osd_fsid):
133 Check if ``arg`` is a device or partition to create an LV out of it
134 with a distinct volume group name, assigning LV tags on it and
135 ultimately, returning the logical volume object. Failing to detect
136 a device or partition will result in error.
138 :param arg: The value of ``--data`` when parsing args
139 :param device_type: Usually, either ``data`` or ``block`` (filestore vs. bluestore)
140 :param cluster_fsid: The cluster fsid/uuid
141 :param osd_fsid: The OSD fsid/uuid
143 if disk.is_partition(arg) or disk.is_device(arg):
144 # we must create a vg, and then a single lv
145 vg_name = "ceph-%s" % cluster_fsid
146 if api.get_vg(vg_name=vg_name):
147 # means we already have a group for this, make a different one
148 # XXX this could end up being annoying for an operator, maybe?
149 vg_name = "ceph-%s" % str(uuid.uuid4())
150 api.create_vg(vg_name, arg)
151 lv_name = "osd-%s-%s" % (device_type, osd_fsid)
152 return api.create_lv(
154 vg_name, # the volume group
155 tags={'ceph.type': device_type})
158 'Cannot use device (%s).' % arg,
159 'A vg/lv path or an existing device is needed']
160 raise RuntimeError(' '.join(error))
162 raise RuntimeError('no data logical volume found with: %s' % arg)
164 @decorators.needs_root
165 def prepare(self, args):
166 # FIXME we don't allow re-using a keyring, we always generate one for the
167 # OSD, this needs to be fixed. This could either be a file (!) or a string
168 # (!!) or some flags that we would need to compound into a dict so that we
169 # can convert to JSON (!!!)
170 secrets = {'cephx_secret': prepare_utils.create_key()}
172 cluster_fsid = conf.ceph.get('global', 'fsid')
173 osd_fsid = args.osd_fsid or system.generate_uuid()
174 # allow re-using an id, in case a prepare failed
175 osd_id = args.osd_id or prepare_utils.create_id(osd_fsid, json.dumps(secrets))
178 raise RuntimeError('--journal is required when using --filestore')
180 data_lv = self.get_lv(args.data)
182 data_lv = self.prepare_device(args.data, 'data', cluster_fsid, osd_fsid)
185 'ceph.osd_fsid': osd_fsid,
186 'ceph.osd_id': osd_id,
187 'ceph.cluster_fsid': cluster_fsid,
188 'ceph.cluster_name': conf.cluster,
189 'ceph.data_device': data_lv.lv_path,
190 'ceph.data_uuid': data_lv.lv_uuid,
193 journal_device, journal_uuid, tags = self.setup_device('journal', args.journal, tags)
195 tags['ceph.type'] = 'data'
196 data_lv.set_tags(tags)
206 block_lv = self.get_lv(args.data)
208 block_lv = self.prepare_device(args.data, 'block', cluster_fsid, osd_fsid)
211 'ceph.osd_fsid': osd_fsid,
212 'ceph.osd_id': osd_id,
213 'ceph.cluster_fsid': cluster_fsid,
214 'ceph.cluster_name': conf.cluster,
215 'ceph.block_device': block_lv.lv_path,
216 'ceph.block_uuid': block_lv.lv_uuid,
219 wal_device, wal_uuid, tags = self.setup_device('wal', args.block_wal, tags)
220 db_device, db_uuid, tags = self.setup_device('db', args.block_db, tags)
222 tags['ceph.type'] = 'block'
223 block_lv.set_tags(tags)
235 sub_command_help = dedent("""
236 Prepare an OSD by assigning an ID and FSID, registering them with the
237 cluster with an ID and FSID, formatting and mounting the volume, and
238 finally by adding all the metadata to the logical volumes using LVM
239 tags, so that it can later be discovered.
241 Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
242 it can later get activated and the OSD daemon can get started.
244 Most basic Usage looks like (journal will be collocated from the same volume group):
246 ceph-volume lvm prepare --data {volume group name}
249 Example calls for supported scenarios:
251 Dedicated volume group for Journal(s)
252 -------------------------------------
254 Existing logical volume (lv) or device:
256 ceph-volume lvm prepare --filestore --data {vg/lv} --journal /path/to/device
260 ceph-volume lvm prepare --filestore --data {vg/lv} --journal {vg/lv}
262 Existing block device, that will be made a group and logical volume:
264 ceph-volume lvm prepare --filestore --data /path/to/device --journal {vg/lv}
269 Existing logical volume (lv):
271 ceph-volume lvm prepare --bluestore --data {vg/lv}
273 Existing block device, that will be made a group and logical volume:
275 ceph-volume lvm prepare --bluestore --data /path/to/device
277 Optionally, can consume db and wal devices or logical volumes:
279 ceph-volume lvm prepare --bluestore --data {vg/lv} --block.wal {device} --block-db {vg/lv}
281 parser = prepare_parser(
282 prog='ceph-volume lvm prepare',
283 description=sub_command_help,
285 if len(self.argv) == 0:
286 print(sub_command_help)
288 args = parser.parse_args(self.argv)
289 # Default to bluestore here since defaulting it in add_argument may
290 # cause both to be True
291 if args.bluestore is None and args.filestore is None:
292 args.bluestore = True