+++ /dev/null
-from __future__ import print_function
-import json
-import uuid
-from textwrap import dedent
-from ceph_volume.util import prepare as prepare_utils
-from ceph_volume.util import system, disk
-from ceph_volume import conf, decorators, terminal
-from ceph_volume.api import lvm as api
-from .common import prepare_parser
-
-
-def prepare_filestore(device, journal, secrets, id_=None, fsid=None):
- """
- :param device: The name of the logical volume to work with
- :param journal: similar to device but can also be a regular/plain disk
- :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
- :param id_: The OSD id
- :param fsid: The OSD fsid, also known as the OSD UUID
- """
- cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key())
- json_secrets = json.dumps(secrets)
-
- # allow re-using an existing fsid, in case prepare failed
- fsid = fsid or system.generate_uuid()
- # allow re-using an id, in case a prepare failed
- osd_id = id_ or prepare_utils.create_id(fsid, json_secrets)
- # create the directory
- prepare_utils.create_osd_path(osd_id)
- # format the device
- prepare_utils.format_device(device)
- # mount the data device
- prepare_utils.mount_osd(device, osd_id)
- # symlink the journal
- prepare_utils.link_journal(journal, osd_id)
- # get the latest monmap
- prepare_utils.get_monmap(osd_id)
- # prepare the osd filesystem
- prepare_utils.osd_mkfs_filestore(osd_id, fsid)
- # write the OSD keyring if it doesn't exist already
- prepare_utils.write_keyring(osd_id, cephx_secret)
-
-
-def prepare_bluestore(block, wal, db, secrets, id_=None, fsid=None):
- """
- :param block: The name of the logical volume for the bluestore data
- :param wal: a regular/plain disk or logical volume, to be used for block.wal
- :param db: a regular/plain disk or logical volume, to be used for block.db
- :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
- :param id_: The OSD id
- :param fsid: The OSD fsid, also known as the OSD UUID
- """
- cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key())
- json_secrets = json.dumps(secrets)
-
- # allow re-using an existing fsid, in case prepare failed
- fsid = fsid or system.generate_uuid()
- # allow re-using an id, in case a prepare failed
- osd_id = id_ or prepare_utils.create_id(fsid, json_secrets)
- # create the directory
- prepare_utils.create_osd_path(osd_id, tmpfs=True)
- # symlink the block
- prepare_utils.link_block(block, osd_id)
- # get the latest monmap
- prepare_utils.get_monmap(osd_id)
- # write the OSD keyring if it doesn't exist already
- prepare_utils.write_keyring(osd_id, cephx_secret)
- # prepare the osd filesystem
- prepare_utils.osd_mkfs_bluestore(
- osd_id, fsid,
- keyring=cephx_secret,
- wal=wal,
- db=db
- )
-
-
-class Prepare(object):
-
- help = 'Format an LVM device and associate it with an OSD'
-
- def __init__(self, argv):
- self.argv = argv
-
- def get_ptuuid(self, argument):
- uuid = disk.get_partuuid(argument)
- if not uuid:
- terminal.error('blkid could not detect a PARTUUID for device: %s' % argument)
- raise RuntimeError('unable to use device')
- return uuid
-
- def get_lv(self, argument):
- """
- Perform some parsing of the command-line value so that the process
- can determine correctly if it got a device path or an lv.
-
- :param argument: The command-line value that will need to be split to
- retrieve the actual lv
- """
- try:
- vg_name, lv_name = argument.split('/')
- except (ValueError, AttributeError):
- return None
- return api.get_lv(lv_name=lv_name, vg_name=vg_name)
-
- def setup_device(self, device_type, device_name, tags):
- """
- Check if ``device`` is an lv, if so, set the tags, making sure to
- update the tags with the lv_uuid and lv_path which the incoming tags
- will not have.
-
- If the device is not a logical volume, then retrieve the partition UUID
- by querying ``blkid``
- """
- if device_name is None:
- return '', '', tags
- tags['ceph.type'] = device_type
- lv = self.get_lv(device_name)
- if lv:
- uuid = lv.lv_uuid
- path = lv.lv_path
- tags['ceph.%s_uuid' % device_type] = uuid
- tags['ceph.%s_device' % device_type] = path
- lv.set_tags(tags)
- else:
- # otherwise assume this is a regular disk partition
- uuid = self.get_ptuuid(device_name)
- path = device_name
- tags['ceph.%s_uuid' % device_type] = uuid
- tags['ceph.%s_device' % device_type] = path
- return path, uuid, tags
-
- def prepare_device(self, arg, device_type, cluster_fsid, osd_fsid):
- """
- Check if ``arg`` is a device or partition to create an LV out of it
- with a distinct volume group name, assigning LV tags on it and
- ultimately, returning the logical volume object. Failing to detect
- a device or partition will result in error.
-
- :param arg: The value of ``--data`` when parsing args
- :param device_type: Usually, either ``data`` or ``block`` (filestore vs. bluestore)
- :param cluster_fsid: The cluster fsid/uuid
- :param osd_fsid: The OSD fsid/uuid
- """
- if disk.is_partition(arg) or disk.is_device(arg):
- # we must create a vg, and then a single lv
- vg_name = "ceph-%s" % cluster_fsid
- if api.get_vg(vg_name=vg_name):
- # means we already have a group for this, make a different one
- # XXX this could end up being annoying for an operator, maybe?
- vg_name = "ceph-%s" % str(uuid.uuid4())
- api.create_vg(vg_name, arg)
- lv_name = "osd-%s-%s" % (device_type, osd_fsid)
- return api.create_lv(
- lv_name,
- vg_name, # the volume group
- tags={'ceph.type': device_type})
- else:
- error = [
- 'Cannot use device (%s).' % arg,
- 'A vg/lv path or an existing device is needed']
- raise RuntimeError(' '.join(error))
-
- raise RuntimeError('no data logical volume found with: %s' % arg)
-
- @decorators.needs_root
- def prepare(self, args):
- # FIXME we don't allow re-using a keyring, we always generate one for the
- # OSD, this needs to be fixed. This could either be a file (!) or a string
- # (!!) or some flags that we would need to compound into a dict so that we
- # can convert to JSON (!!!)
- secrets = {'cephx_secret': prepare_utils.create_key()}
-
- cluster_fsid = conf.ceph.get('global', 'fsid')
- osd_fsid = args.osd_fsid or system.generate_uuid()
- # allow re-using an id, in case a prepare failed
- osd_id = args.osd_id or prepare_utils.create_id(osd_fsid, json.dumps(secrets))
- if args.filestore:
- if not args.journal:
- raise RuntimeError('--journal is required when using --filestore')
-
- data_lv = self.get_lv(args.data)
- if not data_lv:
- data_lv = self.prepare_device(args.data, 'data', cluster_fsid, osd_fsid)
-
- tags = {
- 'ceph.osd_fsid': osd_fsid,
- 'ceph.osd_id': osd_id,
- 'ceph.cluster_fsid': cluster_fsid,
- 'ceph.cluster_name': conf.cluster,
- 'ceph.data_device': data_lv.lv_path,
- 'ceph.data_uuid': data_lv.lv_uuid,
- }
-
- journal_device, journal_uuid, tags = self.setup_device('journal', args.journal, tags)
-
- tags['ceph.type'] = 'data'
- data_lv.set_tags(tags)
-
- prepare_filestore(
- data_lv.lv_path,
- journal_device,
- secrets,
- id_=osd_id,
- fsid=osd_fsid,
- )
- elif args.bluestore:
- block_lv = self.get_lv(args.data)
- if not block_lv:
- block_lv = self.prepare_device(args.data, 'block', cluster_fsid, osd_fsid)
-
- tags = {
- 'ceph.osd_fsid': osd_fsid,
- 'ceph.osd_id': osd_id,
- 'ceph.cluster_fsid': cluster_fsid,
- 'ceph.cluster_name': conf.cluster,
- 'ceph.block_device': block_lv.lv_path,
- 'ceph.block_uuid': block_lv.lv_uuid,
- }
-
- wal_device, wal_uuid, tags = self.setup_device('wal', args.block_wal, tags)
- db_device, db_uuid, tags = self.setup_device('db', args.block_db, tags)
-
- tags['ceph.type'] = 'block'
- block_lv.set_tags(tags)
-
- prepare_bluestore(
- block_lv.lv_path,
- wal_device,
- db_device,
- secrets,
- id_=osd_id,
- fsid=osd_fsid,
- )
-
- def main(self):
- sub_command_help = dedent("""
- Prepare an OSD by assigning an ID and FSID, registering them with the
- cluster with an ID and FSID, formatting and mounting the volume, and
- finally by adding all the metadata to the logical volumes using LVM
- tags, so that it can later be discovered.
-
- Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
- it can later get activated and the OSD daemon can get started.
-
- Most basic Usage looks like (journal will be collocated from the same volume group):
-
- ceph-volume lvm prepare --data {volume group name}
-
-
- Example calls for supported scenarios:
-
- Dedicated volume group for Journal(s)
- -------------------------------------
-
- Existing logical volume (lv) or device:
-
- ceph-volume lvm prepare --filestore --data {vg/lv} --journal /path/to/device
-
- Or:
-
- ceph-volume lvm prepare --filestore --data {vg/lv} --journal {vg/lv}
-
- Existing block device, that will be made a group and logical volume:
-
- ceph-volume lvm prepare --filestore --data /path/to/device --journal {vg/lv}
-
- Bluestore
- ---------
-
- Existing logical volume (lv):
-
- ceph-volume lvm prepare --bluestore --data {vg/lv}
-
- Existing block device, that will be made a group and logical volume:
-
- ceph-volume lvm prepare --bluestore --data /path/to/device
-
- Optionally, can consume db and wal devices or logical volumes:
-
- ceph-volume lvm prepare --bluestore --data {vg/lv} --block.wal {device} --block-db {vg/lv}
- """)
- parser = prepare_parser(
- prog='ceph-volume lvm prepare',
- description=sub_command_help,
- )
- if len(self.argv) == 0:
- print(sub_command_help)
- return
- args = parser.parse_args(self.argv)
- # Default to bluestore here since defaulting it in add_argument may
- # cause both to be True
- if args.bluestore is None and args.filestore is None:
- args.bluestore = True
- self.prepare(args)