]>
git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/devices/lvm/prepare.py
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 encryption
as encryption_utils
7 from ceph_volume
.util
import system
, disk
8 from ceph_volume
.util
.arg_validators
import exclude_group_options
9 from ceph_volume
import conf
, decorators
, terminal
10 from ceph_volume
.api
import lvm
as api
11 from .common
import prepare_parser
, rollback_osd
14 logger
= logging
.getLogger(__name__
)
17 def prepare_dmcrypt(key
, device
, device_type
, tags
):
19 Helper for devices that are encrypted. The operations needed for
20 block, db, wal devices are all the same
24 tag_name
= 'ceph.%s_uuid' % device_type
27 encryption_utils
.luks_format(
31 encryption_utils
.luks_open(
37 return '/dev/mapper/%s' % uuid
40 def prepare_bluestore(block
, wal
, db
, secrets
, tags
, osd_id
, fsid
):
42 :param block: The name of the logical volume for the bluestore data
43 :param wal: a regular/plain disk or logical volume, to be used for block.wal
44 :param db: a regular/plain disk or logical volume, to be used for block.db
45 :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
46 :param id_: The OSD id
47 :param fsid: The OSD fsid, also known as the OSD UUID
49 cephx_secret
= secrets
.get('cephx_secret', prepare_utils
.create_key())
50 # encryption-only operations
51 if secrets
.get('dmcrypt_key'):
52 # If encrypted, there is no need to create the lockbox keyring file because
53 # bluestore re-creates the files and does not have support for other files
54 # like the custom lockbox one. This will need to be done on activation.
55 # format and open ('decrypt' devices) and re-assign the device and journal
56 # variables so that the rest of the process can use the mapper paths
57 key
= secrets
['dmcrypt_key']
58 block
= prepare_dmcrypt(key
, block
, 'block', tags
)
59 wal
= prepare_dmcrypt(key
, wal
, 'wal', tags
)
60 db
= prepare_dmcrypt(key
, db
, 'db', tags
)
62 # create the directory
63 prepare_utils
.create_osd_path(osd_id
, tmpfs
=True)
65 prepare_utils
.link_block(block
, osd_id
)
66 # get the latest monmap
67 prepare_utils
.get_monmap(osd_id
)
68 # write the OSD keyring if it doesn't exist already
69 prepare_utils
.write_keyring(osd_id
, cephx_secret
)
70 # prepare the osd filesystem
71 prepare_utils
.osd_mkfs_bluestore(
79 class Prepare(object):
81 help = 'Format an LVM device and associate it with an OSD'
83 def __init__(self
, argv
):
87 def get_ptuuid(self
, argument
):
88 uuid
= disk
.get_partuuid(argument
)
90 terminal
.error('blkid could not detect a PARTUUID for device: %s' % argument
)
91 raise RuntimeError('unable to use device')
94 def setup_device(self
, device_type
, device_name
, tags
, size
, slots
):
96 Check if ``device`` is an lv, if so, set the tags, making sure to
97 update the tags with the lv_uuid and lv_path which the incoming tags
100 If the device is not a logical volume, then retrieve the partition UUID
101 by querying ``blkid``
103 if device_name
is None:
105 tags
['ceph.type'] = device_type
106 tags
['ceph.vdo'] = api
.is_vdo(device_name
)
109 vg_name
, lv_name
= device_name
.split('/')
110 lv
= api
.get_single_lv(filters
={'lv_name': lv_name
,
118 tags
['ceph.%s_uuid' % device_type
] = lv_uuid
119 tags
['ceph.%s_device' % device_type
] = path
121 elif disk
.is_device(device_name
):
122 # We got a disk, create an lv
123 lv_type
= "osd-{}".format(device_type
)
124 name_uuid
= system
.generate_uuid()
126 'device': device_name
,
130 #TODO use get_block_db_size and co here to get configured size in
133 kwargs
['size'] = size
139 tags
['ceph.{}_device'.format(device_type
)] = path
140 tags
['ceph.{}_uuid'.format(device_type
)] = lv
.lv_uuid
144 # otherwise assume this is a regular disk partition
145 name_uuid
= self
.get_ptuuid(device_name
)
147 tags
['ceph.%s_uuid' % device_type
] = name_uuid
148 tags
['ceph.%s_device' % device_type
] = path
150 return path
, lv_uuid
, tags
152 def prepare_data_device(self
, device_type
, osd_uuid
):
154 Check if ``arg`` is a device or partition to create an LV out of it
155 with a distinct volume group name, assigning LV tags on it and
156 ultimately, returning the logical volume object. Failing to detect
157 a device or partition will result in error.
159 :param arg: The value of ``--data`` when parsing args
160 :param device_type: Usually ``block``
161 :param osd_uuid: The OSD uuid
163 device
= self
.args
.data
164 if disk
.is_partition(device
) or disk
.is_device(device
):
165 # we must create a vg, and then a single lv
166 lv_name_prefix
= "osd-{}".format(device_type
)
167 kwargs
= {'device': device
,
168 'tags': {'ceph.type': device_type
},
169 'slots': self
.args
.data_slots
,
171 logger
.debug('data device size: {}'.format(self
.args
.data_size
))
172 if self
.args
.data_size
!= 0:
173 kwargs
['size'] = self
.args
.data_size
174 return api
.create_lv(
180 'Cannot use device ({}).'.format(device
),
181 'A vg/lv path or an existing device is needed']
182 raise RuntimeError(' '.join(error
))
184 raise RuntimeError('no data logical volume found with: {}'.format(device
))
186 def safe_prepare(self
, args
=None):
188 An intermediate step between `main()` and `prepare()` so that we can
189 capture the `self.osd_id` in case we need to rollback
191 :param args: Injected args, usually from `lvm create` which compounds
192 both `prepare` and `create`
198 vgname
, lvname
= self
.args
.data
.split('/')
199 lv
= api
.get_single_lv(filters
={'lv_name': lvname
,
204 if api
.is_ceph_device(lv
):
205 logger
.info("device {} is already used".format(self
.args
.data
))
206 raise RuntimeError("skipping {}, it is already prepared".format(self
.args
.data
))
210 logger
.exception('lvm prepare was unable to complete')
211 logger
.info('will rollback OSD ID creation')
212 rollback_osd(self
.args
, self
.osd_id
)
214 terminal
.success("ceph-volume lvm prepare successful for: %s" % self
.args
.data
)
216 def get_cluster_fsid(self
):
218 Allows using --cluster-fsid as an argument, but can fallback to reading
219 from ceph.conf if that is unset (the default behavior).
221 if self
.args
.cluster_fsid
:
222 return self
.args
.cluster_fsid
224 return conf
.ceph
.get('global', 'fsid')
226 @decorators.needs_root
228 # FIXME we don't allow re-using a keyring, we always generate one for the
229 # OSD, this needs to be fixed. This could either be a file (!) or a string
230 # (!!) or some flags that we would need to compound into a dict so that we
231 # can convert to JSON (!!!)
232 secrets
= {'cephx_secret': prepare_utils
.create_key()}
233 cephx_lockbox_secret
= ''
234 encrypted
= 1 if self
.args
.dmcrypt
else 0
235 cephx_lockbox_secret
= '' if not encrypted
else prepare_utils
.create_key()
238 secrets
['dmcrypt_key'] = encryption_utils
.create_dmcrypt_key()
239 secrets
['cephx_lockbox_secret'] = cephx_lockbox_secret
241 cluster_fsid
= self
.get_cluster_fsid()
243 osd_fsid
= self
.args
.osd_fsid
or system
.generate_uuid()
244 crush_device_class
= self
.args
.crush_device_class
245 if crush_device_class
:
246 secrets
['crush_device_class'] = crush_device_class
247 # reuse a given ID if it exists, otherwise create a new ID
248 self
.osd_id
= prepare_utils
.create_id(osd_fsid
, json
.dumps(secrets
), osd_id
=self
.args
.osd_id
)
250 'ceph.osd_fsid': osd_fsid
,
251 'ceph.osd_id': self
.osd_id
,
252 'ceph.cluster_fsid': cluster_fsid
,
253 'ceph.cluster_name': conf
.cluster
,
254 'ceph.crush_device_class': crush_device_class
,
255 'ceph.osdspec_affinity': prepare_utils
.get_osdspec_affinity()
257 if self
.args
.bluestore
:
259 vg_name
, lv_name
= self
.args
.data
.split('/')
260 block_lv
= api
.get_single_lv(filters
={'lv_name': lv_name
,
266 block_lv
= self
.prepare_data_device('block', osd_fsid
)
268 tags
['ceph.block_device'] = block_lv
.lv_path
269 tags
['ceph.block_uuid'] = block_lv
.lv_uuid
270 tags
['ceph.cephx_lockbox_secret'] = cephx_lockbox_secret
271 tags
['ceph.encrypted'] = encrypted
272 tags
['ceph.vdo'] = api
.is_vdo(block_lv
.lv_path
)
274 wal_device
, wal_uuid
, tags
= self
.setup_device(
278 self
.args
.block_wal_size
,
279 self
.args
.block_wal_slots
)
280 db_device
, db_uuid
, tags
= self
.setup_device(
284 self
.args
.block_db_size
,
285 self
.args
.block_db_slots
)
287 tags
['ceph.type'] = 'block'
288 block_lv
.set_tags(tags
)
301 sub_command_help
= dedent("""
302 Prepare an OSD by assigning an ID and FSID, registering them with the
303 cluster with an ID and FSID, formatting and mounting the volume, and
304 finally by adding all the metadata to the logical volumes using LVM
305 tags, so that it can later be discovered.
307 Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
308 it can later get activated and the OSD daemon can get started.
310 Encryption is supported via dmcrypt and the --dmcrypt flag.
312 Existing logical volume (lv):
314 ceph-volume lvm prepare --data {vg/lv}
316 Existing block device (a logical volume will be created):
318 ceph-volume lvm prepare --data /path/to/device
320 Optionally, can consume db and wal devices, partitions or logical
321 volumes. A device will get a logical volume, partitions and existing
322 logical volumes will be used as is:
324 ceph-volume lvm prepare --data {vg/lv} --block.wal {partition} --block.db {/path/to/device}
326 parser
= prepare_parser(
327 prog
='ceph-volume lvm prepare',
328 description
=sub_command_help
,
330 if len(self
.argv
) == 0:
331 print(sub_command_help
)
333 exclude_group_options(parser
, argv
=self
.argv
, groups
=['bluestore'])
334 self
.args
= parser
.parse_args(self
.argv
)
335 # Default to bluestore here since defaulting it in add_argument may
336 # cause both to be True
337 if not self
.args
.bluestore
:
338 self
.args
.bluestore
= True