]>
Commit | Line | Data |
---|---|---|
d2e6a577 FG |
1 | from __future__ import print_function |
2 | import json | |
3efd9988 | 3 | import uuid |
d2e6a577 FG |
4 | from textwrap import dedent |
5 | from ceph_volume.util import prepare as prepare_utils | |
181888fb FG |
6 | from ceph_volume.util import system, disk |
7 | from ceph_volume import conf, decorators, terminal | |
3efd9988 | 8 | from ceph_volume.api import lvm as api |
d2e6a577 FG |
9 | from .common import prepare_parser |
10 | ||
11 | ||
d2e6a577 FG |
12 | def prepare_filestore(device, journal, secrets, id_=None, fsid=None): |
13 | """ | |
3efd9988 | 14 | :param device: The name of the logical volume to work with |
d2e6a577 FG |
15 | :param journal: similar to device but can also be a regular/plain disk |
16 | :param secrets: A dict with the secrets needed to create the osd (e.g. cephx) | |
17 | :param id_: The OSD id | |
18 | :param fsid: The OSD fsid, also known as the OSD UUID | |
19 | """ | |
20 | cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key()) | |
21 | json_secrets = json.dumps(secrets) | |
22 | ||
23 | # allow re-using an existing fsid, in case prepare failed | |
24 | fsid = fsid or system.generate_uuid() | |
25 | # allow re-using an id, in case a prepare failed | |
26 | osd_id = id_ or prepare_utils.create_id(fsid, json_secrets) | |
27 | # create the directory | |
3efd9988 | 28 | prepare_utils.create_osd_path(osd_id) |
d2e6a577 FG |
29 | # format the device |
30 | prepare_utils.format_device(device) | |
31 | # mount the data device | |
32 | prepare_utils.mount_osd(device, osd_id) | |
33 | # symlink the journal | |
34 | prepare_utils.link_journal(journal, osd_id) | |
35 | # get the latest monmap | |
36 | prepare_utils.get_monmap(osd_id) | |
37 | # prepare the osd filesystem | |
3efd9988 | 38 | prepare_utils.osd_mkfs_filestore(osd_id, fsid) |
d2e6a577 FG |
39 | # write the OSD keyring if it doesn't exist already |
40 | prepare_utils.write_keyring(osd_id, cephx_secret) | |
41 | ||
42 | ||
3efd9988 FG |
43 | def prepare_bluestore(block, wal, db, secrets, id_=None, fsid=None): |
44 | """ | |
45 | :param block: The name of the logical volume for the bluestore data | |
46 | :param wal: a regular/plain disk or logical volume, to be used for block.wal | |
47 | :param db: a regular/plain disk or logical volume, to be used for block.db | |
48 | :param secrets: A dict with the secrets needed to create the osd (e.g. cephx) | |
49 | :param id_: The OSD id | |
50 | :param fsid: The OSD fsid, also known as the OSD UUID | |
51 | """ | |
52 | cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key()) | |
53 | json_secrets = json.dumps(secrets) | |
54 | ||
55 | # allow re-using an existing fsid, in case prepare failed | |
56 | fsid = fsid or system.generate_uuid() | |
57 | # allow re-using an id, in case a prepare failed | |
58 | osd_id = id_ or prepare_utils.create_id(fsid, json_secrets) | |
59 | # create the directory | |
60 | prepare_utils.create_osd_path(osd_id, tmpfs=True) | |
61 | # symlink the block | |
62 | prepare_utils.link_block(block, osd_id) | |
63 | # get the latest monmap | |
64 | prepare_utils.get_monmap(osd_id) | |
65 | # write the OSD keyring if it doesn't exist already | |
66 | prepare_utils.write_keyring(osd_id, cephx_secret) | |
67 | # prepare the osd filesystem | |
68 | prepare_utils.osd_mkfs_bluestore( | |
69 | osd_id, fsid, | |
70 | keyring=cephx_secret, | |
71 | wal=wal, | |
72 | db=db | |
73 | ) | |
d2e6a577 FG |
74 | |
75 | ||
76 | class Prepare(object): | |
77 | ||
78 | help = 'Format an LVM device and associate it with an OSD' | |
79 | ||
80 | def __init__(self, argv): | |
81 | self.argv = argv | |
82 | ||
3efd9988 | 83 | def get_ptuuid(self, argument): |
181888fb FG |
84 | uuid = disk.get_partuuid(argument) |
85 | if not uuid: | |
86 | terminal.error('blkid could not detect a PARTUUID for device: %s' % argument) | |
3efd9988 | 87 | raise RuntimeError('unable to use device') |
181888fb FG |
88 | return uuid |
89 | ||
3efd9988 | 90 | def get_lv(self, argument): |
b5b8bbf5 | 91 | """ |
3efd9988 FG |
92 | Perform some parsing of the command-line value so that the process |
93 | can determine correctly if it got a device path or an lv. | |
94 | ||
95 | :param argument: The command-line value that will need to be split to | |
96 | retrieve the actual lv | |
b5b8bbf5 FG |
97 | """ |
98 | try: | |
99 | vg_name, lv_name = argument.split('/') | |
100 | except (ValueError, AttributeError): | |
101 | return None | |
102 | return api.get_lv(lv_name=lv_name, vg_name=vg_name) | |
103 | ||
3efd9988 FG |
104 | def setup_device(self, device_type, device_name, tags): |
105 | """ | |
106 | Check if ``device`` is an lv, if so, set the tags, making sure to | |
107 | update the tags with the lv_uuid and lv_path which the incoming tags | |
108 | will not have. | |
109 | ||
110 | If the device is not a logical volume, then retrieve the partition UUID | |
111 | by querying ``blkid`` | |
112 | """ | |
113 | if device_name is None: | |
114 | return '', '', tags | |
115 | tags['ceph.type'] = device_type | |
116 | lv = self.get_lv(device_name) | |
117 | if lv: | |
118 | uuid = lv.lv_uuid | |
119 | path = lv.lv_path | |
120 | tags['ceph.%s_uuid' % device_type] = uuid | |
121 | tags['ceph.%s_device' % device_type] = path | |
122 | lv.set_tags(tags) | |
123 | else: | |
124 | # otherwise assume this is a regular disk partition | |
125 | uuid = self.get_ptuuid(device_name) | |
126 | path = device_name | |
127 | tags['ceph.%s_uuid' % device_type] = uuid | |
128 | tags['ceph.%s_device' % device_type] = path | |
129 | return path, uuid, tags | |
130 | ||
131 | def prepare_device(self, arg, device_type, cluster_fsid, osd_fsid): | |
132 | """ | |
133 | Check if ``arg`` is a device or partition to create an LV out of it | |
134 | with a distinct volume group name, assigning LV tags on it and | |
135 | ultimately, returning the logical volume object. Failing to detect | |
136 | a device or partition will result in error. | |
137 | ||
138 | :param arg: The value of ``--data`` when parsing args | |
139 | :param device_type: Usually, either ``data`` or ``block`` (filestore vs. bluestore) | |
140 | :param cluster_fsid: The cluster fsid/uuid | |
141 | :param osd_fsid: The OSD fsid/uuid | |
142 | """ | |
143 | if disk.is_partition(arg) or disk.is_device(arg): | |
144 | # we must create a vg, and then a single lv | |
145 | vg_name = "ceph-%s" % cluster_fsid | |
146 | if api.get_vg(vg_name=vg_name): | |
147 | # means we already have a group for this, make a different one | |
148 | # XXX this could end up being annoying for an operator, maybe? | |
149 | vg_name = "ceph-%s" % str(uuid.uuid4()) | |
150 | api.create_vg(vg_name, arg) | |
151 | lv_name = "osd-%s-%s" % (device_type, osd_fsid) | |
152 | return api.create_lv( | |
153 | lv_name, | |
154 | vg_name, # the volume group | |
155 | tags={'ceph.type': device_type}) | |
156 | else: | |
157 | error = [ | |
158 | 'Cannot use device (%s).', | |
159 | 'A vg/lv path or an existing device is needed' % arg] | |
160 | raise RuntimeError(' '.join(error)) | |
161 | ||
162 | raise RuntimeError('no data logical volume found with: %s' % arg) | |
163 | ||
d2e6a577 FG |
164 | @decorators.needs_root |
165 | def prepare(self, args): | |
166 | # FIXME we don't allow re-using a keyring, we always generate one for the | |
167 | # OSD, this needs to be fixed. This could either be a file (!) or a string | |
168 | # (!!) or some flags that we would need to compound into a dict so that we | |
169 | # can convert to JSON (!!!) | |
170 | secrets = {'cephx_secret': prepare_utils.create_key()} | |
171 | ||
172 | cluster_fsid = conf.ceph.get('global', 'fsid') | |
3efd9988 | 173 | osd_fsid = args.osd_fsid or system.generate_uuid() |
d2e6a577 | 174 | # allow re-using an id, in case a prepare failed |
3efd9988 | 175 | osd_id = args.osd_id or prepare_utils.create_id(osd_fsid, json.dumps(secrets)) |
d2e6a577 | 176 | if args.filestore: |
b5b8bbf5 FG |
177 | if not args.journal: |
178 | raise RuntimeError('--journal is required when using --filestore') | |
b5b8bbf5 | 179 | |
3efd9988 FG |
180 | data_lv = self.get_lv(args.data) |
181 | if not data_lv: | |
182 | data_lv = self.prepare_device(args.data, 'data', cluster_fsid, osd_fsid) | |
181888fb | 183 | |
3efd9988 FG |
184 | tags = { |
185 | 'ceph.osd_fsid': osd_fsid, | |
d2e6a577 FG |
186 | 'ceph.osd_id': osd_id, |
187 | 'ceph.cluster_fsid': cluster_fsid, | |
3efd9988 | 188 | 'ceph.cluster_name': conf.cluster, |
d2e6a577 | 189 | 'ceph.data_device': data_lv.lv_path, |
181888fb | 190 | 'ceph.data_uuid': data_lv.lv_uuid, |
3efd9988 FG |
191 | } |
192 | ||
193 | journal_device, journal_uuid, tags = self.setup_device('journal', args.journal, tags) | |
194 | ||
195 | tags['ceph.type'] = 'data' | |
196 | data_lv.set_tags(tags) | |
d2e6a577 FG |
197 | |
198 | prepare_filestore( | |
199 | data_lv.lv_path, | |
200 | journal_device, | |
201 | secrets, | |
202 | id_=osd_id, | |
3efd9988 | 203 | fsid=osd_fsid, |
d2e6a577 FG |
204 | ) |
205 | elif args.bluestore: | |
3efd9988 FG |
206 | block_lv = self.get_lv(args.data) |
207 | if not block_lv: | |
208 | block_lv = self.prepare_device(args.data, 'block', cluster_fsid, osd_fsid) | |
209 | ||
210 | tags = { | |
211 | 'ceph.osd_fsid': osd_fsid, | |
212 | 'ceph.osd_id': osd_id, | |
213 | 'ceph.cluster_fsid': cluster_fsid, | |
214 | 'ceph.cluster_name': conf.cluster, | |
215 | 'ceph.block_device': block_lv.lv_path, | |
216 | 'ceph.block_uuid': block_lv.lv_uuid, | |
217 | } | |
218 | ||
219 | wal_device, wal_uuid, tags = self.setup_device('wal', args.block_wal, tags) | |
220 | db_device, db_uuid, tags = self.setup_device('db', args.block_db, tags) | |
221 | ||
222 | tags['ceph.type'] = 'block' | |
223 | block_lv.set_tags(tags) | |
224 | ||
225 | prepare_bluestore( | |
226 | block_lv.lv_path, | |
227 | wal_device, | |
228 | db_device, | |
229 | secrets, | |
230 | id_=osd_id, | |
231 | fsid=osd_fsid, | |
232 | ) | |
d2e6a577 FG |
233 | |
234 | def main(self): | |
235 | sub_command_help = dedent(""" | |
236 | Prepare an OSD by assigning an ID and FSID, registering them with the | |
237 | cluster with an ID and FSID, formatting and mounting the volume, and | |
238 | finally by adding all the metadata to the logical volumes using LVM | |
239 | tags, so that it can later be discovered. | |
240 | ||
241 | Once the OSD is ready, an ad-hoc systemd unit will be enabled so that | |
242 | it can later get activated and the OSD daemon can get started. | |
243 | ||
244 | Most basic Usage looks like (journal will be collocated from the same volume group): | |
245 | ||
246 | ceph-volume lvm prepare --data {volume group name} | |
247 | ||
248 | ||
249 | Example calls for supported scenarios: | |
250 | ||
251 | Dedicated volume group for Journal(s) | |
252 | ------------------------------------- | |
253 | ||
254 | Existing logical volume (lv) or device: | |
255 | ||
3efd9988 | 256 | ceph-volume lvm prepare --filestore --data {vg/lv} --journal /path/to/device |
d2e6a577 FG |
257 | |
258 | Or: | |
259 | ||
3efd9988 FG |
260 | ceph-volume lvm prepare --filestore --data {vg/lv} --journal {vg/lv} |
261 | ||
262 | Existing block device, that will be made a group and logical volume: | |
263 | ||
264 | ceph-volume lvm prepare --filestore --data /path/to/device --journal {vg/lv} | |
265 | ||
266 | Bluestore | |
267 | --------- | |
268 | ||
269 | Existing logical volume (lv): | |
270 | ||
271 | ceph-volume lvm prepare --bluestore --data {vg/lv} | |
272 | ||
273 | Existing block device, that will be made a group and logical volume: | |
d2e6a577 | 274 | |
3efd9988 | 275 | ceph-volume lvm prepare --bluestore --data /path/to/device |
d2e6a577 | 276 | |
3efd9988 | 277 | Optionally, can consume db and wal devices or logical volumes: |
d2e6a577 | 278 | |
3efd9988 | 279 | ceph-volume lvm prepare --bluestore --data {vg/lv} --block.wal {device} --block-db {vg/lv} |
d2e6a577 FG |
280 | """) |
281 | parser = prepare_parser( | |
282 | prog='ceph-volume lvm prepare', | |
283 | description=sub_command_help, | |
284 | ) | |
285 | if len(self.argv) == 0: | |
286 | print(sub_command_help) | |
287 | return | |
288 | args = parser.parse_args(self.argv) | |
3efd9988 FG |
289 | # Default to bluestore here since defaulting it in add_argument may |
290 | # cause both to be True | |
291 | if args.bluestore is None and args.filestore is None: | |
292 | args.bluestore = True | |
d2e6a577 | 293 | self.prepare(args) |