]> git.proxmox.com Git - ceph.git/blame - ceph/src/ceph-volume/ceph_volume/util/prepare.py
update sources to 12.2.7
[ceph.git] / ceph / src / ceph-volume / ceph_volume / util / prepare.py
CommitLineData
d2e6a577
FG
1"""
2These utilities for prepare provide all the pieces needed to prepare a device
3but also a compounded ("single call") helper to do them in order. Some plugins
4may want to change some part of the process, while others might want to consume
5the single-call helper
6"""
7import os
8import logging
b32b8144 9import json
d2e6a577
FG
10from ceph_volume import process, conf
11from ceph_volume.util import system, constants
12
13logger = logging.getLogger(__name__)
14
15
16def create_key():
b32b8144
FG
17 stdout, stderr, returncode = process.call(
18 ['ceph-authtool', '--gen-print-key'],
19 show_command=True)
d2e6a577
FG
20 if returncode != 0:
21 raise RuntimeError('Unable to generate a new auth key')
22 return ' '.join(stdout).strip()
23
24
b32b8144
FG
25def write_keyring(osd_id, secret, keyring_name='keyring', name=None):
26 """
27 Create a keyring file with the ``ceph-authtool`` utility. Constructs the
28 path over well-known conventions for the OSD, and allows any other custom
29 ``name`` to be set.
30
31 :param osd_id: The ID for the OSD to be used
32 :param secret: The key to be added as (as a string)
33 :param name: Defaults to 'osd.{ID}' but can be used to add other client
34 names, specifically for 'lockbox' type of keys
35 :param keyring_name: Alternative keyring name, for supporting other
36 types of keys like for lockbox
37 """
38 osd_keyring = '/var/lib/ceph/osd/%s-%s/%s' % (conf.cluster, osd_id, keyring_name)
39 name = name or 'osd.%s' % str(osd_id)
d2e6a577
FG
40 process.run(
41 [
42 'ceph-authtool', osd_keyring,
43 '--create-keyring',
b32b8144 44 '--name', name,
d2e6a577
FG
45 '--add-key', secret
46 ])
47 system.chown(osd_keyring)
d2e6a577
FG
48
49
b32b8144 50def create_id(fsid, json_secrets, osd_id=None):
d2e6a577
FG
51 """
52 :param fsid: The osd fsid to create, always required
53 :param json_secrets: a json-ready object with whatever secrets are wanted
54 to be passed to the monitor
b32b8144
FG
55 :param osd_id: Reuse an existing ID from an OSD that's been destroyed, if the
56 id does not exist in the cluster a new ID will be created
d2e6a577
FG
57 """
58 bootstrap_keyring = '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf.cluster
b32b8144
FG
59 cmd = [
60 'ceph',
61 '--cluster', conf.cluster,
62 '--name', 'client.bootstrap-osd',
63 '--keyring', bootstrap_keyring,
64 '-i', '-',
65 'osd', 'new', fsid
66 ]
67 if check_id(osd_id):
68 cmd.append(osd_id)
69 stdout, stderr, returncode = process.call(
70 cmd,
71 stdin=json_secrets,
72 show_command=True
73 )
74 if returncode != 0:
75 raise RuntimeError('Unable to create a new OSD id')
76 return ' '.join(stdout).strip()
77
78
79def check_id(osd_id):
80 """
81 Checks to see if an osd ID exists or not. Returns True
82 if it does exist, False if it doesn't.
83
84 :param osd_id: The osd ID to check
85 """
86 if osd_id is None:
87 return False
88 bootstrap_keyring = '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf.cluster
d2e6a577
FG
89 stdout, stderr, returncode = process.call(
90 [
91 'ceph',
92 '--cluster', conf.cluster,
93 '--name', 'client.bootstrap-osd',
94 '--keyring', bootstrap_keyring,
b32b8144
FG
95 'osd',
96 'tree',
97 '-f', 'json',
d2e6a577 98 ],
b32b8144 99 show_command=True
d2e6a577
FG
100 )
101 if returncode != 0:
b32b8144
FG
102 raise RuntimeError('Unable check if OSD id exists: %s' % osd_id)
103
104 output = json.loads(''.join(stdout).strip())
105 osds = output['nodes']
106 return any([str(osd['id']) == str(osd_id) for osd in osds])
d2e6a577
FG
107
108
3efd9988
FG
109def mount_tmpfs(path):
110 process.run([
3efd9988
FG
111 'mount',
112 '-t',
113 'tmpfs', 'tmpfs',
114 path
115 ])
116
117
118def create_osd_path(osd_id, tmpfs=False):
119 path = '/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id)
d2e6a577 120 system.mkdir_p('/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id))
3efd9988
FG
121 if tmpfs:
122 mount_tmpfs(path)
d2e6a577
FG
123
124
125def format_device(device):
126 # only supports xfs
b32b8144 127 command = ['mkfs', '-t', 'xfs']
d2e6a577
FG
128
129 # get the mkfs options if any for xfs,
130 # fallback to the default options defined in constants.mkfs
131 flags = conf.ceph.get_list(
132 'osd',
133 'osd_mkfs_options_xfs',
134 default=constants.mkfs.get('xfs'),
135 split=' ',
136 )
137
138 # always force
139 if '-f' not in flags:
140 flags.insert(0, '-f')
141
142 command.extend(flags)
143 command.append(device)
144 process.run(command)
145
146
94b18763 147def _normalize_mount_flags(flags, extras=None):
3a9019d9
FG
148 """
149 Mount flag options have to be a single string, separated by a comma. If the
150 flags are separated by spaces, or with commas and spaces in ceph.conf, the
151 mount options will be passed incorrectly.
152
153 This will help when parsing ceph.conf values return something like::
154
155 ["rw,", "exec,"]
156
157 Or::
158
159 [" rw ,", "exec"]
160
161 :param flags: A list of flags, or a single string of mount flags
94b18763
FG
162 :param extras: Extra set of mount flags, useful when custom devices like VDO need
163 ad-hoc mount configurations
3a9019d9 164 """
94b18763
FG
165 # Instead of using set(), we append to this new list here, because set()
166 # will create an arbitrary order on the items that is made worst when
167 # testing with tools like tox that includes a randomizer seed. By
168 # controlling the order, it is easier to correctly assert the expectation
169 unique_flags = []
3a9019d9 170 if isinstance(flags, list):
94b18763
FG
171 if extras:
172 flags.extend(extras)
173
3a9019d9 174 # ensure that spaces and commas are removed so that they can join
94b18763
FG
175 # correctly, remove duplicates
176 for f in flags:
177 if f and f not in unique_flags:
178 unique_flags.append(f.strip().strip(','))
179 return ','.join(unique_flags)
3a9019d9
FG
180
181 # split them, clean them, and join them back again
182 flags = flags.strip().split(' ')
94b18763
FG
183 if extras:
184 flags.extend(extras)
185
186 # remove possible duplicates
187 for f in flags:
188 if f and f not in unique_flags:
189 unique_flags.append(f.strip().strip(','))
190 flags = ','.join(unique_flags)
191 # Before returning, split them again, since strings can be mashed up
192 # together, preventing removal of duplicate entries
193 return ','.join(set(flags.split(',')))
194
195
196def mount_osd(device, osd_id, **kw):
197 extras = []
198 is_vdo = kw.get('is_vdo', '0')
199 if is_vdo == '1':
200 extras = ['discard']
d2e6a577 201 destination = '/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id)
b32b8144 202 command = ['mount', '-t', 'xfs', '-o']
d2e6a577
FG
203 flags = conf.ceph.get_list(
204 'osd',
205 'osd_mount_options_xfs',
206 default=constants.mount.get('xfs'),
207 split=' ',
208 )
94b18763
FG
209 command.append(
210 _normalize_mount_flags(flags, extras=extras)
211 )
d2e6a577
FG
212 command.append(device)
213 command.append(destination)
214 process.run(command)
215
216
3efd9988
FG
217def _link_device(device, device_type, osd_id):
218 """
219 Allow linking any device type in an OSD directory. ``device`` must the be
220 source, with an absolute path and ``device_type`` will be the destination
221 name, like 'journal', or 'block'
222 """
223 device_path = '/var/lib/ceph/osd/%s-%s/%s' % (
d2e6a577 224 conf.cluster,
3efd9988
FG
225 osd_id,
226 device_type
d2e6a577 227 )
b32b8144 228 command = ['ln', '-s', device, device_path]
3efd9988
FG
229 system.chown(device)
230
d2e6a577
FG
231 process.run(command)
232
233
3efd9988
FG
234def link_journal(journal_device, osd_id):
235 _link_device(journal_device, 'journal', osd_id)
236
237
238def link_block(block_device, osd_id):
239 _link_device(block_device, 'block', osd_id)
240
241
242def link_wal(wal_device, osd_id):
243 _link_device(wal_device, 'block.wal', osd_id)
244
245
246def link_db(db_device, osd_id):
247 _link_device(db_device, 'block.db', osd_id)
248
249
d2e6a577
FG
250def get_monmap(osd_id):
251 """
252 Before creating the OSD files, a monmap needs to be retrieved so that it
253 can be used to tell the monitor(s) about the new OSD. A call will look like::
254
255 ceph --cluster ceph --name client.bootstrap-osd \
256 --keyring /var/lib/ceph/bootstrap-osd/ceph.keyring \
257 mon getmap -o /var/lib/ceph/osd/ceph-0/activate.monmap
258 """
259 path = '/var/lib/ceph/osd/%s-%s/' % (conf.cluster, osd_id)
260 bootstrap_keyring = '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf.cluster
261 monmap_destination = os.path.join(path, 'activate.monmap')
262
263 process.run([
d2e6a577
FG
264 'ceph',
265 '--cluster', conf.cluster,
266 '--name', 'client.bootstrap-osd',
267 '--keyring', bootstrap_keyring,
268 'mon', 'getmap', '-o', monmap_destination
269 ])
270
271
3efd9988
FG
272def osd_mkfs_bluestore(osd_id, fsid, keyring=None, wal=False, db=False):
273 """
274 Create the files for the OSD to function. A normal call will look like:
275
276 ceph-osd --cluster ceph --mkfs --mkkey -i 0 \
277 --monmap /var/lib/ceph/osd/ceph-0/activate.monmap \
278 --osd-data /var/lib/ceph/osd/ceph-0 \
279 --osd-uuid 8d208665-89ae-4733-8888-5d3bfbeeec6c \
280 --keyring /var/lib/ceph/osd/ceph-0/keyring \
281 --setuser ceph --setgroup ceph
282
283 In some cases it is required to use the keyring, when it is passed in as
284 a keywork argument it is used as part of the ceph-osd command
285 """
286 path = '/var/lib/ceph/osd/%s-%s/' % (conf.cluster, osd_id)
287 monmap = os.path.join(path, 'activate.monmap')
288
289 system.chown(path)
290
291 base_command = [
3efd9988
FG
292 'ceph-osd',
293 '--cluster', conf.cluster,
294 # undocumented flag, sets the `type` file to contain 'bluestore'
295 '--osd-objectstore', 'bluestore',
296 '--mkfs',
297 '-i', osd_id,
298 '--monmap', monmap,
299 ]
300
301 supplementary_command = [
302 '--osd-data', path,
303 '--osd-uuid', fsid,
304 '--setuser', 'ceph',
305 '--setgroup', 'ceph'
306 ]
307
308 if keyring is not None:
b32b8144 309 base_command.extend(['--keyfile', '-'])
3efd9988
FG
310
311 if wal:
312 base_command.extend(
313 ['--bluestore-block-wal-path', wal]
314 )
315 system.chown(wal)
316
317 if db:
318 base_command.extend(
319 ['--bluestore-block-db-path', db]
320 )
321 system.chown(db)
322
323 command = base_command + supplementary_command
324
28e407b8
AA
325 _, _, returncode = process.call(command, stdin=keyring, show_command=True)
326 if returncode != 0:
327 raise RuntimeError('Command failed with exit code %s: %s' % (returncode, ' '.join(command)))
3efd9988
FG
328
329
330def osd_mkfs_filestore(osd_id, fsid):
d2e6a577
FG
331 """
332 Create the files for the OSD to function. A normal call will look like:
333
334 ceph-osd --cluster ceph --mkfs --mkkey -i 0 \
335 --monmap /var/lib/ceph/osd/ceph-0/activate.monmap \
336 --osd-data /var/lib/ceph/osd/ceph-0 \
337 --osd-journal /var/lib/ceph/osd/ceph-0/journal \
338 --osd-uuid 8d208665-89ae-4733-8888-5d3bfbeeec6c \
339 --keyring /var/lib/ceph/osd/ceph-0/keyring \
340 --setuser ceph --setgroup ceph
341
342 """
343 path = '/var/lib/ceph/osd/%s-%s/' % (conf.cluster, osd_id)
344 monmap = os.path.join(path, 'activate.monmap')
345 journal = os.path.join(path, 'journal')
346
347 system.chown(journal)
348 system.chown(path)
349
350 process.run([
d2e6a577
FG
351 'ceph-osd',
352 '--cluster', conf.cluster,
3efd9988
FG
353 # undocumented flag, sets the `type` file to contain 'filestore'
354 '--osd-objectstore', 'filestore',
d2e6a577
FG
355 '--mkfs',
356 '-i', osd_id,
357 '--monmap', monmap,
358 '--osd-data', path,
359 '--osd-journal', journal,
360 '--osd-uuid', fsid,
361 '--setuser', 'ceph',
362 '--setgroup', 'ceph'
363 ])