summaryrefslogtreecommitdiffstats
path: root/src/ceph-volume/ceph_volume/devices/raw/prepare.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/ceph-volume/ceph_volume/devices/raw/prepare.py169
1 files changed, 169 insertions, 0 deletions
diff --git a/src/ceph-volume/ceph_volume/devices/raw/prepare.py b/src/ceph-volume/ceph_volume/devices/raw/prepare.py
new file mode 100644
index 000000000..3c96eedac
--- /dev/null
+++ b/src/ceph-volume/ceph_volume/devices/raw/prepare.py
@@ -0,0 +1,169 @@
+from __future__ import print_function
+import json
+import logging
+import os
+from textwrap import dedent
+from ceph_volume.util import prepare as prepare_utils
+from ceph_volume.util import encryption as encryption_utils
+from ceph_volume.util import disk
+from ceph_volume.util import system
+from ceph_volume import decorators, terminal
+from ceph_volume.devices.lvm.common import rollback_osd
+from .common import create_parser
+
+logger = logging.getLogger(__name__)
+
+def prepare_dmcrypt(key, device, device_type, fsid):
+ """
+ Helper for devices that are encrypted. The operations needed for
+ block, db, wal, or data/journal devices are all the same
+ """
+ if not device:
+ return ''
+ kname = disk.lsblk(device)['KNAME']
+ mapping = 'ceph-{}-{}-{}-dmcrypt'.format(fsid, kname, device_type)
+ # format data device
+ encryption_utils.luks_format(
+ key,
+ device
+ )
+ encryption_utils.luks_open(
+ key,
+ device,
+ mapping
+ )
+
+ return '/dev/mapper/{}'.format(mapping)
+
+def prepare_bluestore(block, wal, db, secrets, osd_id, fsid, tmpfs):
+ """
+ :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())
+
+ if secrets.get('dmcrypt_key'):
+ key = secrets['dmcrypt_key']
+ block = prepare_dmcrypt(key, block, 'block', fsid)
+ wal = prepare_dmcrypt(key, wal, 'wal', fsid)
+ db = prepare_dmcrypt(key, db, 'db', fsid)
+
+ # create the directory
+ prepare_utils.create_osd_path(osd_id, tmpfs=tmpfs)
+ # 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 a raw device and associate it with a (BlueStore) OSD'
+
+ def __init__(self, argv):
+ self.argv = argv
+ self.osd_id = None
+
+ def safe_prepare(self, args=None):
+ """
+ An intermediate step between `main()` and `prepare()` so that we can
+ capture the `self.osd_id` in case we need to rollback
+
+ :param args: Injected args, usually from `raw create` which compounds
+ both `prepare` and `create`
+ """
+ if args is not None:
+ self.args = args
+ try:
+ self.prepare()
+ except Exception:
+ logger.exception('raw prepare was unable to complete')
+ logger.info('will rollback OSD ID creation')
+ rollback_osd(self.args, self.osd_id)
+ raise
+ dmcrypt_log = 'dmcrypt' if args.dmcrypt else 'clear'
+ terminal.success("ceph-volume raw {} prepare successful for: {}".format(dmcrypt_log, self.args.data))
+
+
+ @decorators.needs_root
+ def prepare(self):
+ secrets = {'cephx_secret': prepare_utils.create_key()}
+ encrypted = 1 if self.args.dmcrypt else 0
+ cephx_lockbox_secret = '' if not encrypted else prepare_utils.create_key()
+
+ if encrypted:
+ secrets['dmcrypt_key'] = os.getenv('CEPH_VOLUME_DMCRYPT_SECRET')
+ secrets['cephx_lockbox_secret'] = cephx_lockbox_secret # dummy value to make `ceph osd new` not complaining
+
+ osd_fsid = system.generate_uuid()
+ crush_device_class = self.args.crush_device_class
+ if crush_device_class:
+ secrets['crush_device_class'] = crush_device_class
+ tmpfs = not self.args.no_tmpfs
+ wal = ""
+ db = ""
+ if self.args.block_wal:
+ wal = self.args.block_wal
+ if self.args.block_db:
+ db = self.args.block_db
+
+ # reuse a given ID if it exists, otherwise create a new ID
+ self.osd_id = prepare_utils.create_id(
+ osd_fsid, json.dumps(secrets))
+
+ prepare_bluestore(
+ self.args.data,
+ wal,
+ db,
+ secrets,
+ self.osd_id,
+ osd_fsid,
+ tmpfs,
+ )
+
+ 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 the volume.
+
+ 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.
+
+ ceph-volume raw prepare --bluestore --data {device}
+
+ DB and WAL devices are supported.
+
+ ceph-volume raw prepare --bluestore --data {device} --block.db {device} --block.wal {device}
+
+ """)
+ parser = create_parser(
+ prog='ceph-volume raw prepare',
+ description=sub_command_help,
+ )
+ if not self.argv:
+ print(sub_command_help)
+ return
+ self.args = parser.parse_args(self.argv)
+ if not self.args.bluestore:
+ terminal.error('must specify --bluestore (currently the only supported backend)')
+ raise SystemExit(1)
+ if self.args.dmcrypt and not os.getenv('CEPH_VOLUME_DMCRYPT_SECRET'):
+ terminal.error('encryption was requested (--dmcrypt) but environment variable ' \
+ 'CEPH_VOLUME_DMCRYPT_SECRET is not set, you must set ' \
+ 'this variable to provide a dmcrypt secret.')
+ raise SystemExit(1)
+
+ self.safe_prepare(self.args)