]>
git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/util/prepare.py
2 These utilities for prepare provide all the pieces needed to prepare a device
3 but also a compounded ("single call") helper to do them in order. Some plugins
4 may want to change some part of the process, while others might want to consume
10 from ceph_volume
import process
, conf
, __release__
, terminal
11 from ceph_volume
.util
import system
, constants
, str_to_int
, disk
13 logger
= logging
.getLogger(__name__
)
14 mlogger
= terminal
.MultiLogger(__name__
)
18 stdout
, stderr
, returncode
= process
.call(
19 ['ceph-authtool', '--gen-print-key'],
22 raise RuntimeError('Unable to generate a new auth key')
23 return ' '.join(stdout
).strip()
26 def write_keyring(osd_id
, secret
, keyring_name
='keyring', name
=None):
28 Create a keyring file with the ``ceph-authtool`` utility. Constructs the
29 path over well-known conventions for the OSD, and allows any other custom
32 :param osd_id: The ID for the OSD to be used
33 :param secret: The key to be added as (as a string)
34 :param name: Defaults to 'osd.{ID}' but can be used to add other client
35 names, specifically for 'lockbox' type of keys
36 :param keyring_name: Alternative keyring name, for supporting other
37 types of keys like for lockbox
39 osd_keyring
= '/var/lib/ceph/osd/%s-%s/%s' % (conf
.cluster
, osd_id
, keyring_name
)
40 name
= name
or 'osd.%s' % str(osd_id
)
43 'ceph-authtool', osd_keyring
,
48 system
.chown(osd_keyring
)
51 def get_journal_size(lv_format
=True):
53 Helper to retrieve the size (defined in megabytes in ceph.conf) to create
54 the journal logical volume, it "translates" the string into a float value,
55 then converts that into gigabytes, and finally (optionally) it formats it
56 back as a string so that it can be used for creating the LV.
58 :param lv_format: Return a string to be used for ``lv_create``. A 5 GB size
59 would result in '5G', otherwise it will return a ``Size`` object.
61 conf_journal_size
= conf
.ceph
.get_safe('osd', 'osd_journal_size', '5120')
62 logger
.debug('osd_journal_size set to %s' % conf_journal_size
)
63 journal_size
= disk
.Size(mb
=str_to_int(conf_journal_size
))
65 if journal_size
< disk
.Size(gb
=2):
66 mlogger
.error('Refusing to continue with configured size for journal')
67 raise RuntimeError('journal sizes must be larger than 2GB, detected: %s' % journal_size
)
69 return '%sG' % journal_size
.gb
.as_int()
73 def create_id(fsid
, json_secrets
, osd_id
=None):
75 :param fsid: The osd fsid to create, always required
76 :param json_secrets: a json-ready object with whatever secrets are wanted
77 to be passed to the monitor
78 :param osd_id: Reuse an existing ID from an OSD that's been destroyed, if the
79 id does not exist in the cluster a new ID will be created
81 bootstrap_keyring
= '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf
.cluster
84 '--cluster', conf
.cluster
,
85 '--name', 'client.bootstrap-osd',
86 '--keyring', bootstrap_keyring
,
90 if osd_id
is not None:
91 if osd_id_available(osd_id
):
94 raise RuntimeError("The osd ID {} is already in use or does not exist.".format(osd_id
))
95 stdout
, stderr
, returncode
= process
.call(
101 raise RuntimeError('Unable to create a new OSD id')
102 return ' '.join(stdout
).strip()
105 def osd_id_available(osd_id
):
107 Checks to see if an osd ID exists and if it's available for
108 reuse. Returns True if it is, False if it isn't.
110 :param osd_id: The osd ID to check
114 bootstrap_keyring
= '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf
.cluster
115 stdout
, stderr
, returncode
= process
.call(
118 '--cluster', conf
.cluster
,
119 '--name', 'client.bootstrap-osd',
120 '--keyring', bootstrap_keyring
,
128 raise RuntimeError('Unable check if OSD id exists: %s' % osd_id
)
130 output
= json
.loads(''.join(stdout
).strip())
131 osds
= output
['nodes']
132 osd
= [osd
for osd
in osds
if str(osd
['id']) == str(osd_id
)]
133 if osd
and osd
[0].get('status') == "destroyed":
138 def mount_tmpfs(path
):
146 # Restore SELinux context
147 system
.set_context(path
)
150 def create_osd_path(osd_id
, tmpfs
=False):
151 path
= '/var/lib/ceph/osd/%s-%s' % (conf
.cluster
, osd_id
)
152 system
.mkdir_p('/var/lib/ceph/osd/%s-%s' % (conf
.cluster
, osd_id
))
157 def format_device(device
):
159 command
= ['mkfs', '-t', 'xfs']
161 # get the mkfs options if any for xfs,
162 # fallback to the default options defined in constants.mkfs
163 flags
= conf
.ceph
.get_list(
165 'osd_mkfs_options_xfs',
166 default
=constants
.mkfs
.get('xfs'),
171 if '-f' not in flags
:
172 flags
.insert(0, '-f')
174 command
.extend(flags
)
175 command
.append(device
)
179 def _normalize_mount_flags(flags
, extras
=None):
181 Mount flag options have to be a single string, separated by a comma. If the
182 flags are separated by spaces, or with commas and spaces in ceph.conf, the
183 mount options will be passed incorrectly.
185 This will help when parsing ceph.conf values return something like::
193 :param flags: A list of flags, or a single string of mount flags
194 :param extras: Extra set of mount flags, useful when custom devices like VDO need
195 ad-hoc mount configurations
197 # Instead of using set(), we append to this new list here, because set()
198 # will create an arbitrary order on the items that is made worst when
199 # testing with tools like tox that includes a randomizer seed. By
200 # controlling the order, it is easier to correctly assert the expectation
202 if isinstance(flags
, list):
206 # ensure that spaces and commas are removed so that they can join
207 # correctly, remove duplicates
209 if f
and f
not in unique_flags
:
210 unique_flags
.append(f
.strip().strip(','))
211 return ','.join(unique_flags
)
213 # split them, clean them, and join them back again
214 flags
= flags
.strip().split(' ')
218 # remove possible duplicates
220 if f
and f
not in unique_flags
:
221 unique_flags
.append(f
.strip().strip(','))
222 flags
= ','.join(unique_flags
)
223 # Before returning, split them again, since strings can be mashed up
224 # together, preventing removal of duplicate entries
225 return ','.join(set(flags
.split(',')))
228 def mount_osd(device
, osd_id
, **kw
):
230 is_vdo
= kw
.get('is_vdo', '0')
233 destination
= '/var/lib/ceph/osd/%s-%s' % (conf
.cluster
, osd_id
)
234 command
= ['mount', '-t', 'xfs', '-o']
235 flags
= conf
.ceph
.get_list(
237 'osd_mount_options_xfs',
238 default
=constants
.mount
.get('xfs'),
242 _normalize_mount_flags(flags
, extras
=extras
)
244 command
.append(device
)
245 command
.append(destination
)
248 # Restore SELinux context
249 system
.set_context(destination
)
252 def _link_device(device
, device_type
, osd_id
):
254 Allow linking any device type in an OSD directory. ``device`` must the be
255 source, with an absolute path and ``device_type`` will be the destination
256 name, like 'journal', or 'block'
258 device_path
= '/var/lib/ceph/osd/%s-%s/%s' % (
263 command
= ['ln', '-s', device
, device_path
]
269 def link_journal(journal_device
, osd_id
):
270 _link_device(journal_device
, 'journal', osd_id
)
273 def link_block(block_device
, osd_id
):
274 _link_device(block_device
, 'block', osd_id
)
277 def link_wal(wal_device
, osd_id
):
278 _link_device(wal_device
, 'block.wal', osd_id
)
281 def link_db(db_device
, osd_id
):
282 _link_device(db_device
, 'block.db', osd_id
)
285 def get_monmap(osd_id
):
287 Before creating the OSD files, a monmap needs to be retrieved so that it
288 can be used to tell the monitor(s) about the new OSD. A call will look like::
290 ceph --cluster ceph --name client.bootstrap-osd \
291 --keyring /var/lib/ceph/bootstrap-osd/ceph.keyring \
292 mon getmap -o /var/lib/ceph/osd/ceph-0/activate.monmap
294 path
= '/var/lib/ceph/osd/%s-%s/' % (conf
.cluster
, osd_id
)
295 bootstrap_keyring
= '/var/lib/ceph/bootstrap-osd/%s.keyring' % conf
.cluster
296 monmap_destination
= os
.path
.join(path
, 'activate.monmap')
300 '--cluster', conf
.cluster
,
301 '--name', 'client.bootstrap-osd',
302 '--keyring', bootstrap_keyring
,
303 'mon', 'getmap', '-o', monmap_destination
307 def osd_mkfs_bluestore(osd_id
, fsid
, keyring
=None, wal
=False, db
=False):
309 Create the files for the OSD to function. A normal call will look like:
311 ceph-osd --cluster ceph --mkfs --mkkey -i 0 \
312 --monmap /var/lib/ceph/osd/ceph-0/activate.monmap \
313 --osd-data /var/lib/ceph/osd/ceph-0 \
314 --osd-uuid 8d208665-89ae-4733-8888-5d3bfbeeec6c \
315 --keyring /var/lib/ceph/osd/ceph-0/keyring \
316 --setuser ceph --setgroup ceph
318 In some cases it is required to use the keyring, when it is passed in as
319 a keywork argument it is used as part of the ceph-osd command
321 path
= '/var/lib/ceph/osd/%s-%s/' % (conf
.cluster
, osd_id
)
322 monmap
= os
.path
.join(path
, 'activate.monmap')
328 '--cluster', conf
.cluster
,
329 # undocumented flag, sets the `type` file to contain 'bluestore'
330 '--osd-objectstore', 'bluestore',
336 supplementary_command
= [
343 if keyring
is not None:
344 base_command
.extend(['--keyfile', '-'])
348 ['--bluestore-block-wal-path', wal
]
354 ['--bluestore-block-db-path', db
]
358 command
= base_command
+ supplementary_command
360 _
, _
, returncode
= process
.call(command
, stdin
=keyring
, show_command
=True)
362 raise RuntimeError('Command failed with exit code %s: %s' % (returncode
, ' '.join(command
)))
365 def osd_mkfs_filestore(osd_id
, fsid
, keyring
):
367 Create the files for the OSD to function. A normal call will look like:
369 ceph-osd --cluster ceph --mkfs --mkkey -i 0 \
370 --monmap /var/lib/ceph/osd/ceph-0/activate.monmap \
371 --osd-data /var/lib/ceph/osd/ceph-0 \
372 --osd-journal /var/lib/ceph/osd/ceph-0/journal \
373 --osd-uuid 8d208665-89ae-4733-8888-5d3bfbeeec6c \
374 --keyring /var/lib/ceph/osd/ceph-0/keyring \
375 --setuser ceph --setgroup ceph
378 path
= '/var/lib/ceph/osd/%s-%s/' % (conf
.cluster
, osd_id
)
379 monmap
= os
.path
.join(path
, 'activate.monmap')
380 journal
= os
.path
.join(path
, 'journal')
382 system
.chown(journal
)
387 '--cluster', conf
.cluster
,
388 # undocumented flag, sets the `type` file to contain 'filestore'
389 '--osd-objectstore', 'filestore',
395 if __release__
!= 'luminous':
397 command
.extend(['--keyfile', '-'])
401 '--osd-journal', journal
,
407 _
, _
, returncode
= process
.call(
408 command
, stdin
=keyring
, terminal_verbose
=True, show_command
=True
411 raise RuntimeError('Command failed with exit code %s: %s' % (returncode
, ' '.join(command
)))