]>
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, or data/journal 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_filestore(device
, journal
, secrets
, tags
, osd_id
, fsid
):
42 :param device: The name of the logical volume to work with
43 :param journal: similar to device but can also be a regular/plain disk
44 :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
45 :param id_: The OSD id
46 :param fsid: The OSD fsid, also known as the OSD UUID
48 cephx_secret
= secrets
.get('cephx_secret', prepare_utils
.create_key())
50 # encryption-only operations
51 if secrets
.get('dmcrypt_key'):
52 # format and open ('decrypt' devices) and re-assign the device and journal
53 # variables so that the rest of the process can use the mapper paths
54 key
= secrets
['dmcrypt_key']
55 device
= prepare_dmcrypt(key
, device
, 'data', tags
)
56 journal
= prepare_dmcrypt(key
, journal
, 'journal', tags
)
59 is_vdo
= api
.is_vdo(device
)
60 # create the directory
61 prepare_utils
.create_osd_path(osd_id
)
63 prepare_utils
.format_device(device
)
64 # mount the data device
65 prepare_utils
.mount_osd(device
, osd_id
, is_vdo
=is_vdo
)
67 prepare_utils
.link_journal(journal
, osd_id
)
68 # get the latest monmap
69 prepare_utils
.get_monmap(osd_id
)
70 # prepare the osd filesystem
71 prepare_utils
.osd_mkfs_filestore(osd_id
, fsid
, cephx_secret
)
72 # write the OSD keyring if it doesn't exist already
73 prepare_utils
.write_keyring(osd_id
, cephx_secret
)
74 if secrets
.get('dmcrypt_key'):
75 # if the device is going to get activated right away, this can be done
76 # here, otherwise it will be recreated
77 encryption_utils
.write_lockbox_keyring(
80 tags
['ceph.cephx_lockbox_secret']
84 def prepare_bluestore(block
, wal
, db
, secrets
, tags
, osd_id
, fsid
):
86 :param block: The name of the logical volume for the bluestore data
87 :param wal: a regular/plain disk or logical volume, to be used for block.wal
88 :param db: a regular/plain disk or logical volume, to be used for block.db
89 :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
90 :param id_: The OSD id
91 :param fsid: The OSD fsid, also known as the OSD UUID
93 cephx_secret
= secrets
.get('cephx_secret', prepare_utils
.create_key())
94 # encryption-only operations
95 if secrets
.get('dmcrypt_key'):
96 # If encrypted, there is no need to create the lockbox keyring file because
97 # bluestore re-creates the files and does not have support for other files
98 # like the custom lockbox one. This will need to be done on activation.
99 # format and open ('decrypt' devices) and re-assign the device and journal
100 # variables so that the rest of the process can use the mapper paths
101 key
= secrets
['dmcrypt_key']
102 block
= prepare_dmcrypt(key
, block
, 'block', tags
)
103 wal
= prepare_dmcrypt(key
, wal
, 'wal', tags
)
104 db
= prepare_dmcrypt(key
, db
, 'db', tags
)
106 # create the directory
107 prepare_utils
.create_osd_path(osd_id
, tmpfs
=True)
109 prepare_utils
.link_block(block
, osd_id
)
110 # get the latest monmap
111 prepare_utils
.get_monmap(osd_id
)
112 # write the OSD keyring if it doesn't exist already
113 prepare_utils
.write_keyring(osd_id
, cephx_secret
)
114 # prepare the osd filesystem
115 prepare_utils
.osd_mkfs_bluestore(
117 keyring
=cephx_secret
,
123 class Prepare(object):
125 help = 'Format an LVM device and associate it with an OSD'
127 def __init__(self
, argv
):
131 def get_ptuuid(self
, argument
):
132 uuid
= disk
.get_partuuid(argument
)
134 terminal
.error('blkid could not detect a PARTUUID for device: %s' % argument
)
135 raise RuntimeError('unable to use device')
138 def get_lv(self
, argument
):
140 Perform some parsing of the command-line value so that the process
141 can determine correctly if it got a device path or an lv.
143 :param argument: The command-line value that will need to be split to
144 retrieve the actual lv
147 vg_name
, lv_name
= argument
.split('/')
148 except (ValueError, AttributeError):
150 return api
.get_lv(lv_name
=lv_name
, vg_name
=vg_name
)
152 def setup_device(self
, device_type
, device_name
, tags
):
154 Check if ``device`` is an lv, if so, set the tags, making sure to
155 update the tags with the lv_uuid and lv_path which the incoming tags
158 If the device is not a logical volume, then retrieve the partition UUID
159 by querying ``blkid``
161 if device_name
is None:
163 tags
['ceph.type'] = device_type
164 tags
['ceph.vdo'] = api
.is_vdo(device_name
)
165 lv
= self
.get_lv(device_name
)
169 tags
['ceph.%s_uuid' % device_type
] = uuid
170 tags
['ceph.%s_device' % device_type
] = path
173 # otherwise assume this is a regular disk partition
174 uuid
= self
.get_ptuuid(device_name
)
176 tags
['ceph.%s_uuid' % device_type
] = uuid
177 tags
['ceph.%s_device' % device_type
] = path
178 return path
, uuid
, tags
180 def prepare_device(self
, arg
, device_type
, cluster_fsid
, osd_fsid
):
182 Check if ``arg`` is a device or partition to create an LV out of it
183 with a distinct volume group name, assigning LV tags on it and
184 ultimately, returning the logical volume object. Failing to detect
185 a device or partition will result in error.
187 :param arg: The value of ``--data`` when parsing args
188 :param device_type: Usually, either ``data`` or ``block`` (filestore vs. bluestore)
189 :param cluster_fsid: The cluster fsid/uuid
190 :param osd_fsid: The OSD fsid/uuid
192 if disk
.is_partition(arg
) or disk
.is_device(arg
):
193 # we must create a vg, and then a single lv
194 vg
= api
.create_vg(arg
)
195 lv_name
= "osd-%s-%s" % (device_type
, osd_fsid
)
196 return api
.create_lv(
198 vg
.name
, # the volume group
199 tags
={'ceph.type': device_type
})
202 'Cannot use device (%s).' % arg
,
203 'A vg/lv path or an existing device is needed']
204 raise RuntimeError(' '.join(error
))
206 raise RuntimeError('no data logical volume found with: %s' % arg
)
208 def safe_prepare(self
, args
):
210 An intermediate step between `main()` and `prepare()` so that we can
211 capture the `self.osd_id` in case we need to rollback
216 logger
.exception('lvm prepare was unable to complete')
217 logger
.info('will rollback OSD ID creation')
218 rollback_osd(args
, self
.osd_id
)
220 terminal
.success("ceph-volume lvm prepare successful for: %s" % args
.data
)
222 @decorators.needs_root
223 def prepare(self
, args
):
224 # FIXME we don't allow re-using a keyring, we always generate one for the
225 # OSD, this needs to be fixed. This could either be a file (!) or a string
226 # (!!) or some flags that we would need to compound into a dict so that we
227 # can convert to JSON (!!!)
228 secrets
= {'cephx_secret': prepare_utils
.create_key()}
229 cephx_lockbox_secret
= ''
230 encrypted
= 1 if args
.dmcrypt
else 0
231 cephx_lockbox_secret
= '' if not encrypted
else prepare_utils
.create_key()
234 secrets
['dmcrypt_key'] = encryption_utils
.create_dmcrypt_key()
235 secrets
['cephx_lockbox_secret'] = cephx_lockbox_secret
237 cluster_fsid
= conf
.ceph
.get('global', 'fsid')
238 osd_fsid
= args
.osd_fsid
or system
.generate_uuid()
239 crush_device_class
= args
.crush_device_class
240 if crush_device_class
:
241 secrets
['crush_device_class'] = crush_device_class
242 # reuse a given ID if it exists, otherwise create a new ID
243 self
.osd_id
= prepare_utils
.create_id(osd_fsid
, json
.dumps(secrets
), osd_id
=args
.osd_id
)
245 'ceph.osd_fsid': osd_fsid
,
246 'ceph.osd_id': self
.osd_id
,
247 'ceph.cluster_fsid': cluster_fsid
,
248 'ceph.cluster_name': conf
.cluster
,
249 'ceph.crush_device_class': crush_device_class
,
253 raise RuntimeError('--journal is required when using --filestore')
255 data_lv
= self
.get_lv(args
.data
)
257 data_lv
= self
.prepare_device(args
.data
, 'data', cluster_fsid
, osd_fsid
)
259 tags
['ceph.data_device'] = data_lv
.lv_path
260 tags
['ceph.data_uuid'] = data_lv
.lv_uuid
261 tags
['ceph.cephx_lockbox_secret'] = cephx_lockbox_secret
262 tags
['ceph.encrypted'] = encrypted
263 tags
['ceph.vdo'] = api
.is_vdo(data_lv
.lv_path
)
265 journal_device
, journal_uuid
, tags
= self
.setup_device('journal', args
.journal
, tags
)
267 tags
['ceph.type'] = 'data'
268 data_lv
.set_tags(tags
)
279 block_lv
= self
.get_lv(args
.data
)
281 block_lv
= self
.prepare_device(args
.data
, 'block', cluster_fsid
, osd_fsid
)
283 tags
['ceph.block_device'] = block_lv
.lv_path
284 tags
['ceph.block_uuid'] = block_lv
.lv_uuid
285 tags
['ceph.cephx_lockbox_secret'] = cephx_lockbox_secret
286 tags
['ceph.encrypted'] = encrypted
287 tags
['ceph.vdo'] = api
.is_vdo(block_lv
.lv_path
)
289 wal_device
, wal_uuid
, tags
= self
.setup_device('wal', args
.block_wal
, tags
)
290 db_device
, db_uuid
, tags
= self
.setup_device('db', args
.block_db
, tags
)
292 tags
['ceph.type'] = 'block'
293 block_lv
.set_tags(tags
)
306 sub_command_help
= dedent("""
307 Prepare an OSD by assigning an ID and FSID, registering them with the
308 cluster with an ID and FSID, formatting and mounting the volume, and
309 finally by adding all the metadata to the logical volumes using LVM
310 tags, so that it can later be discovered.
312 Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
313 it can later get activated and the OSD daemon can get started.
315 Most basic Usage looks like (journal will be collocated from the same volume group):
317 ceph-volume lvm prepare --data {volume group name}
319 Encryption is supported via dmcrypt and the --dmcrypt flag.
321 Example calls for supported scenarios:
323 Dedicated volume group for Journal(s)
324 -------------------------------------
326 Existing logical volume (lv) or device:
328 ceph-volume lvm prepare --filestore --data {vg/lv} --journal /path/to/device
332 ceph-volume lvm prepare --filestore --data {vg/lv} --journal {vg/lv}
334 Existing block device, that will be made a group and logical volume:
336 ceph-volume lvm prepare --filestore --data /path/to/device --journal {vg/lv}
341 Existing logical volume (lv):
343 ceph-volume lvm prepare --bluestore --data {vg/lv}
345 Existing block device, that will be made a group and logical volume:
347 ceph-volume lvm prepare --bluestore --data /path/to/device
349 Optionally, can consume db and wal devices or logical volumes:
351 ceph-volume lvm prepare --bluestore --data {vg/lv} --block.wal {device} --block-db {vg/lv}
353 parser
= prepare_parser(
354 prog
='ceph-volume lvm prepare',
355 description
=sub_command_help
,
357 if len(self
.argv
) == 0:
358 print(sub_command_help
)
360 exclude_group_options(parser
, argv
=self
.argv
, groups
=['filestore', 'bluestore'])
361 args
= parser
.parse_args(self
.argv
)
362 # Default to bluestore here since defaulting it in add_argument may
363 # cause both to be True
364 if not args
.bluestore
and not args
.filestore
:
365 args
.bluestore
= True
366 self
.safe_prepare(args
)