]>
git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/devices/lvm/prepare.py
17ac5e1b3942d6d77099a43b61b161fe00fad551
1 from __future__
import print_function
5 from textwrap
import dedent
6 from ceph_volume
.util
import prepare
as prepare_utils
7 from ceph_volume
.util
import encryption
as encryption_utils
8 from ceph_volume
.util
import system
, disk
9 from ceph_volume
.util
.arg_validators
import exclude_group_options
10 from ceph_volume
import conf
, decorators
, terminal
11 from ceph_volume
.api
import lvm
as api
12 from .common
import prepare_parser
, rollback_osd
15 logger
= logging
.getLogger(__name__
)
18 def prepare_dmcrypt(key
, device
, device_type
, tags
):
20 Helper for devices that are encrypted. The operations needed for
21 block, db, wal, or data/journal devices are all the same
25 tag_name
= 'ceph.%s_uuid' % device_type
28 encryption_utils
.luks_format(
32 encryption_utils
.luks_open(
38 return '/dev/mapper/%s' % uuid
41 def prepare_filestore(device
, journal
, secrets
, tags
, osd_id
, fsid
):
43 :param device: The name of the logical volume to work with
44 :param journal: similar to device but can also be a regular/plain disk
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())
51 # encryption-only operations
52 if secrets
.get('dmcrypt_key'):
53 # format and open ('decrypt' devices) and re-assign the device and journal
54 # variables so that the rest of the process can use the mapper paths
55 key
= secrets
['dmcrypt_key']
56 device
= prepare_dmcrypt(key
, device
, 'data', tags
)
57 journal
= prepare_dmcrypt(key
, journal
, 'journal', tags
)
60 is_vdo
= api
.is_vdo(device
)
61 # create the directory
62 prepare_utils
.create_osd_path(osd_id
)
64 prepare_utils
.format_device(device
)
65 # mount the data device
66 prepare_utils
.mount_osd(device
, osd_id
, is_vdo
=is_vdo
)
68 prepare_utils
.link_journal(journal
, osd_id
)
69 # get the latest monmap
70 prepare_utils
.get_monmap(osd_id
)
71 # prepare the osd filesystem
72 prepare_utils
.osd_mkfs_filestore(osd_id
, fsid
)
73 # write the OSD keyring if it doesn't exist already
74 prepare_utils
.write_keyring(osd_id
, cephx_secret
)
75 if secrets
.get('dmcrypt_key'):
76 # if the device is going to get activated right away, this can be done
77 # here, otherwise it will be recreated
78 encryption_utils
.write_lockbox_keyring(
81 tags
['ceph.cephx_lockbox_secret']
85 def prepare_bluestore(block
, wal
, db
, secrets
, tags
, osd_id
, fsid
):
87 :param block: The name of the logical volume for the bluestore data
88 :param wal: a regular/plain disk or logical volume, to be used for block.wal
89 :param db: a regular/plain disk or logical volume, to be used for block.db
90 :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
91 :param id_: The OSD id
92 :param fsid: The OSD fsid, also known as the OSD UUID
94 cephx_secret
= secrets
.get('cephx_secret', prepare_utils
.create_key())
95 # encryption-only operations
96 if secrets
.get('dmcrypt_key'):
97 # If encrypted, there is no need to create the lockbox keyring file because
98 # bluestore re-creates the files and does not have support for other files
99 # like the custom lockbox one. This will need to be done on activation.
100 # format and open ('decrypt' devices) and re-assign the device and journal
101 # variables so that the rest of the process can use the mapper paths
102 key
= secrets
['dmcrypt_key']
103 block
= prepare_dmcrypt(key
, block
, 'block', tags
)
104 wal
= prepare_dmcrypt(key
, wal
, 'wal', tags
)
105 db
= prepare_dmcrypt(key
, db
, 'db', tags
)
107 # create the directory
108 prepare_utils
.create_osd_path(osd_id
, tmpfs
=True)
110 prepare_utils
.link_block(block
, osd_id
)
111 # get the latest monmap
112 prepare_utils
.get_monmap(osd_id
)
113 # write the OSD keyring if it doesn't exist already
114 prepare_utils
.write_keyring(osd_id
, cephx_secret
)
115 # prepare the osd filesystem
116 prepare_utils
.osd_mkfs_bluestore(
118 keyring
=cephx_secret
,
124 class Prepare(object):
126 help = 'Format an LVM device and associate it with an OSD'
128 def __init__(self
, argv
):
132 def get_ptuuid(self
, argument
):
133 uuid
= disk
.get_partuuid(argument
)
135 terminal
.error('blkid could not detect a PARTUUID for device: %s' % argument
)
136 raise RuntimeError('unable to use device')
139 def get_lv(self
, argument
):
141 Perform some parsing of the command-line value so that the process
142 can determine correctly if it got a device path or an lv.
144 :param argument: The command-line value that will need to be split to
145 retrieve the actual lv
148 vg_name
, lv_name
= argument
.split('/')
149 except (ValueError, AttributeError):
151 return api
.get_lv(lv_name
=lv_name
, vg_name
=vg_name
)
153 def setup_device(self
, device_type
, device_name
, tags
):
155 Check if ``device`` is an lv, if so, set the tags, making sure to
156 update the tags with the lv_uuid and lv_path which the incoming tags
159 If the device is not a logical volume, then retrieve the partition UUID
160 by querying ``blkid``
162 if device_name
is None:
164 tags
['ceph.type'] = device_type
165 tags
['ceph.vdo'] = api
.is_vdo(device_name
)
166 lv
= self
.get_lv(device_name
)
170 tags
['ceph.%s_uuid' % device_type
] = uuid
171 tags
['ceph.%s_device' % device_type
] = path
174 # otherwise assume this is a regular disk partition
175 uuid
= self
.get_ptuuid(device_name
)
177 tags
['ceph.%s_uuid' % device_type
] = uuid
178 tags
['ceph.%s_device' % device_type
] = path
179 return path
, uuid
, tags
181 def prepare_device(self
, arg
, device_type
, cluster_fsid
, osd_fsid
):
183 Check if ``arg`` is a device or partition to create an LV out of it
184 with a distinct volume group name, assigning LV tags on it and
185 ultimately, returning the logical volume object. Failing to detect
186 a device or partition will result in error.
188 :param arg: The value of ``--data`` when parsing args
189 :param device_type: Usually, either ``data`` or ``block`` (filestore vs. bluestore)
190 :param cluster_fsid: The cluster fsid/uuid
191 :param osd_fsid: The OSD fsid/uuid
193 if disk
.is_partition(arg
) or disk
.is_device(arg
):
194 # we must create a vg, and then a single lv
195 vg_name
= "ceph-%s" % str(uuid
.uuid4())
196 api
.create_vg(vg_name
, arg
)
197 lv_name
= "osd-%s-%s" % (device_type
, osd_fsid
)
198 return api
.create_lv(
200 vg_name
, # the volume group
201 tags
={'ceph.type': device_type
})
204 'Cannot use device (%s).' % arg
,
205 'A vg/lv path or an existing device is needed']
206 raise RuntimeError(' '.join(error
))
208 raise RuntimeError('no data logical volume found with: %s' % arg
)
210 def safe_prepare(self
, args
):
212 An intermediate step between `main()` and `prepare()` so that we can
213 capture the `self.osd_id` in case we need to rollback
218 logger
.error('lvm prepare was unable to complete')
219 logger
.info('will rollback OSD ID creation')
220 rollback_osd(args
, self
.osd_id
)
222 terminal
.success("ceph-volume lvm prepare successful for: %s" % args
.data
)
224 @decorators.needs_root
225 def prepare(self
, args
):
226 # FIXME we don't allow re-using a keyring, we always generate one for the
227 # OSD, this needs to be fixed. This could either be a file (!) or a string
228 # (!!) or some flags that we would need to compound into a dict so that we
229 # can convert to JSON (!!!)
230 secrets
= {'cephx_secret': prepare_utils
.create_key()}
231 cephx_lockbox_secret
= ''
232 encrypted
= 1 if args
.dmcrypt
else 0
233 cephx_lockbox_secret
= '' if not encrypted
else prepare_utils
.create_key()
236 secrets
['dmcrypt_key'] = encryption_utils
.create_dmcrypt_key()
237 secrets
['cephx_lockbox_secret'] = cephx_lockbox_secret
239 cluster_fsid
= conf
.ceph
.get('global', 'fsid')
240 osd_fsid
= args
.osd_fsid
or system
.generate_uuid()
241 crush_device_class
= args
.crush_device_class
242 if crush_device_class
:
243 secrets
['crush_device_class'] = crush_device_class
244 # reuse a given ID if it exists, otherwise create a new ID
245 self
.osd_id
= prepare_utils
.create_id(osd_fsid
, json
.dumps(secrets
), osd_id
=args
.osd_id
)
247 'ceph.osd_fsid': osd_fsid
,
248 'ceph.osd_id': self
.osd_id
,
249 'ceph.cluster_fsid': cluster_fsid
,
250 'ceph.cluster_name': conf
.cluster
,
251 'ceph.crush_device_class': crush_device_class
,
255 raise RuntimeError('--journal is required when using --filestore')
257 data_lv
= self
.get_lv(args
.data
)
259 data_lv
= self
.prepare_device(args
.data
, 'data', cluster_fsid
, osd_fsid
)
261 tags
['ceph.data_device'] = data_lv
.lv_path
262 tags
['ceph.data_uuid'] = data_lv
.lv_uuid
263 tags
['ceph.cephx_lockbox_secret'] = cephx_lockbox_secret
264 tags
['ceph.encrypted'] = encrypted
265 tags
['ceph.vdo'] = api
.is_vdo(data_lv
.lv_path
)
267 journal_device
, journal_uuid
, tags
= self
.setup_device('journal', args
.journal
, tags
)
269 tags
['ceph.type'] = 'data'
270 data_lv
.set_tags(tags
)
281 block_lv
= self
.get_lv(args
.data
)
283 block_lv
= self
.prepare_device(args
.data
, 'block', cluster_fsid
, osd_fsid
)
285 tags
['ceph.block_device'] = block_lv
.lv_path
286 tags
['ceph.block_uuid'] = block_lv
.lv_uuid
287 tags
['ceph.cephx_lockbox_secret'] = cephx_lockbox_secret
288 tags
['ceph.encrypted'] = encrypted
289 tags
['ceph.vdo'] = api
.is_vdo(block_lv
.lv_path
)
291 wal_device
, wal_uuid
, tags
= self
.setup_device('wal', args
.block_wal
, tags
)
292 db_device
, db_uuid
, tags
= self
.setup_device('db', args
.block_db
, tags
)
294 tags
['ceph.type'] = 'block'
295 block_lv
.set_tags(tags
)
308 sub_command_help
= dedent("""
309 Prepare an OSD by assigning an ID and FSID, registering them with the
310 cluster with an ID and FSID, formatting and mounting the volume, and
311 finally by adding all the metadata to the logical volumes using LVM
312 tags, so that it can later be discovered.
314 Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
315 it can later get activated and the OSD daemon can get started.
317 Most basic Usage looks like (journal will be collocated from the same volume group):
319 ceph-volume lvm prepare --data {volume group name}
321 Encryption is supported via dmcrypt and the --dmcrypt flag.
323 Example calls for supported scenarios:
325 Dedicated volume group for Journal(s)
326 -------------------------------------
328 Existing logical volume (lv) or device:
330 ceph-volume lvm prepare --filestore --data {vg/lv} --journal /path/to/device
334 ceph-volume lvm prepare --filestore --data {vg/lv} --journal {vg/lv}
336 Existing block device, that will be made a group and logical volume:
338 ceph-volume lvm prepare --filestore --data /path/to/device --journal {vg/lv}
343 Existing logical volume (lv):
345 ceph-volume lvm prepare --bluestore --data {vg/lv}
347 Existing block device, that will be made a group and logical volume:
349 ceph-volume lvm prepare --bluestore --data /path/to/device
351 Optionally, can consume db and wal devices or logical volumes:
353 ceph-volume lvm prepare --bluestore --data {vg/lv} --block.wal {device} --block-db {vg/lv}
355 parser
= prepare_parser(
356 prog
='ceph-volume lvm prepare',
357 description
=sub_command_help
,
359 if len(self
.argv
) == 0:
360 print(sub_command_help
)
362 exclude_group_options(parser
, argv
=self
.argv
, groups
=['filestore', 'bluestore'])
363 args
= parser
.parse_args(self
.argv
)
364 # Default to bluestore here since defaulting it in add_argument may
365 # cause both to be True
366 if not args
.bluestore
and not args
.filestore
:
367 args
.bluestore
= True
368 self
.safe_prepare(args
)