]> git.proxmox.com Git - ceph.git/blame - ceph/src/ceph-volume/ceph_volume/devices/lvm/activate.py
Import ceph 15.2.8
[ceph.git] / ceph / src / ceph-volume / ceph_volume / devices / lvm / activate.py
CommitLineData
d2e6a577
FG
1from __future__ import print_function
2import argparse
3efd9988
FG
3import logging
4import os
d2e6a577 5from textwrap import dedent
91327a77 6from ceph_volume import process, conf, decorators, terminal, __release__, configuration
181888fb 7from ceph_volume.util import system, disk
3efd9988 8from ceph_volume.util import prepare as prepare_utils
b32b8144 9from ceph_volume.util import encryption as encryption_utils
d2e6a577 10from ceph_volume.systemd import systemctl
3efd9988 11from ceph_volume.api import lvm as api
94b18763 12from .listing import direct_report
3efd9988
FG
13
14
15logger = logging.getLogger(__name__)
d2e6a577
FG
16
17
f6b5b4d7 18def activate_filestore(osd_lvs, no_systemd=False):
d2e6a577 19 # find the osd
f6b5b4d7
TL
20 for osd_lv in osd_lvs:
21 if osd_lv.tags.get('ceph.type') == 'data':
22 data_lv = osd_lv
23 break
24 else:
3efd9988 25 raise RuntimeError('Unable to find a data LV for filestore activation')
b32b8144 26
f6b5b4d7
TL
27 is_encrypted = data_lv.tags.get('ceph.encrypted', '0') == '1'
28 is_vdo = data_lv.tags.get('ceph.vdo', '0')
29
30 osd_id = data_lv.tags['ceph.osd_id']
31 configuration.load_ceph_conf_path(data_lv.tags['ceph.cluster_name'])
91327a77 32 configuration.load()
d2e6a577 33 # it may have a volume with a journal
f6b5b4d7
TL
34 for osd_lv in osd_lvs:
35 if osd_lv.tags.get('ceph.type') == 'journal':
36 osd_journal_lv = osd_lv
37 break
38 else:
39 osd_journal_lv = None
40
d2e6a577
FG
41 # TODO: add sensible error reporting if this is ever the case
42 # blow up with a KeyError if this doesn't exist
f6b5b4d7 43 osd_fsid = data_lv.tags['ceph.osd_fsid']
d2e6a577 44 if not osd_journal_lv:
11fdf7f2 45 # must be a disk partition, by querying blkid by the uuid we are ensuring that the
181888fb 46 # device path is always correct
f6b5b4d7 47 journal_uuid = data_lv.tags['ceph.journal_uuid']
b32b8144 48 osd_journal = disk.get_device_from_partuuid(journal_uuid)
d2e6a577 49 else:
b32b8144 50 journal_uuid = osd_journal_lv.lv_uuid
f6b5b4d7 51 osd_journal = data_lv.tags['ceph.journal_device']
d2e6a577
FG
52
53 if not osd_journal:
54 raise RuntimeError('unable to detect an lv or device journal for OSD %s' % osd_id)
55
b32b8144
FG
56 # this is done here, so that previous checks that ensure path availability
57 # and correctness can still be enforced, and report if any issues are found
58 if is_encrypted:
f6b5b4d7 59 lockbox_secret = data_lv.tags['ceph.cephx_lockbox_secret']
b32b8144
FG
60 # this keyring writing is idempotent
61 encryption_utils.write_lockbox_keyring(osd_id, osd_fsid, lockbox_secret)
62 dmcrypt_secret = encryption_utils.get_dmcrypt_key(osd_id, osd_fsid)
f6b5b4d7 63 encryption_utils.luks_open(dmcrypt_secret, data_lv.lv_path, data_lv.lv_uuid)
b32b8144
FG
64 encryption_utils.luks_open(dmcrypt_secret, osd_journal, journal_uuid)
65
66 osd_journal = '/dev/mapper/%s' % journal_uuid
f6b5b4d7 67 source = '/dev/mapper/%s' % data_lv.lv_uuid
b32b8144 68 else:
f6b5b4d7 69 source = data_lv.lv_path
94b18763 70
d2e6a577 71 # mount the osd
d2e6a577 72 destination = '/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id)
3efd9988 73 if not system.device_is_mounted(source, destination=destination):
94b18763 74 prepare_utils.mount_osd(source, osd_id, is_vdo=is_vdo)
d2e6a577 75
f64942e4
AA
76 # ensure that the OSD destination is always chowned properly
77 system.chown(destination)
78
181888fb
FG
79 # always re-do the symlink regardless if it exists, so that the journal
80 # device path that may have changed can be mapped correctly every time
81 destination = '/var/lib/ceph/osd/%s-%s/journal' % (conf.cluster, osd_id)
b32b8144 82 process.run(['ln', '-snf', osd_journal, destination])
d2e6a577
FG
83
84 # make sure that the journal has proper permissions
85 system.chown(osd_journal)
86
94b18763
FG
87 if no_systemd is False:
88 # enable the ceph-volume unit for this OSD
89 systemctl.enable_volume(osd_id, osd_fsid, 'lvm')
d2e6a577 90
1adf2230
AA
91 # enable the OSD
92 systemctl.enable_osd(osd_id)
93
94b18763
FG
94 # start the OSD
95 systemctl.start_osd(osd_id)
b32b8144 96 terminal.success("ceph-volume lvm activate successful for osd ID: %s" % osd_id)
d2e6a577
FG
97
98
f6b5b4d7 99def get_osd_device_path(osd_lvs, device_type, dmcrypt_secret=None):
3efd9988 100 """
f6b5b4d7
TL
101 ``device_type`` can be one of ``db``, ``wal`` or ``block`` so that we can
102 query LVs on system and fallback to querying the uuid if that is not
103 present.
3efd9988 104
f6b5b4d7
TL
105 Return a path if possible, failing to do that a ``None``, since some of
106 these devices are optional.
3efd9988 107 """
f6b5b4d7
TL
108 osd_block_lv = None
109 for lv in osd_lvs:
110 if lv.tags.get('ceph.type') == 'block':
111 osd_block_lv = lv
112 break
113 if osd_block_lv:
114 is_encrypted = osd_block_lv.tags.get('ceph.encrypted', '0') == '1'
115 logger.debug('Found block device (%s) with encryption: %s', osd_block_lv.name, is_encrypted)
116 uuid_tag = 'ceph.%s_uuid' % device_type
117 device_uuid = osd_block_lv.tags.get(uuid_tag)
118 if not device_uuid:
119 return None
120
121 device_lv = None
122 for lv in osd_lvs:
123 if lv.tags.get('ceph.type') == device_type:
124 device_lv = lv
125 break
3efd9988 126 if device_lv:
b32b8144
FG
127 if is_encrypted:
128 encryption_utils.luks_open(dmcrypt_secret, device_lv.lv_path, device_uuid)
129 return '/dev/mapper/%s' % device_uuid
3efd9988 130 return device_lv.lv_path
494da23a
TL
131
132 # this could be a regular device, so query it with blkid
133 physical_device = disk.get_device_from_partuuid(device_uuid)
134 if physical_device:
135 if is_encrypted:
b32b8144
FG
136 encryption_utils.luks_open(dmcrypt_secret, physical_device, device_uuid)
137 return '/dev/mapper/%s' % device_uuid
494da23a
TL
138 return physical_device
139
140 raise RuntimeError('could not find %s with uuid %s' % (device_type, device_uuid))
3efd9988
FG
141
142
f6b5b4d7
TL
143def activate_bluestore(osd_lvs, no_systemd=False):
144 for lv in osd_lvs:
145 if lv.tags.get('ceph.type') == 'block':
146 osd_block_lv = lv
147 break
148 else:
94b18763 149 raise RuntimeError('could not find a bluestore OSD to activate')
f6b5b4d7
TL
150
151 is_encrypted = osd_block_lv.tags.get('ceph.encrypted', '0') == '1'
b32b8144 152 dmcrypt_secret = None
f6b5b4d7
TL
153 osd_id = osd_block_lv.tags['ceph.osd_id']
154 conf.cluster = osd_block_lv.tags['ceph.cluster_name']
155 osd_fsid = osd_block_lv.tags['ceph.osd_fsid']
3efd9988
FG
156
157 # mount on tmpfs the osd directory
158 osd_path = '/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id)
159 if not system.path_is_mounted(osd_path):
160 # mkdir -p and mount as tmpfs
161 prepare_utils.create_osd_path(osd_id, tmpfs=True)
162 # XXX This needs to be removed once ceph-bluestore-tool can deal with
163 # symlinks that exist in the osd dir
164 for link_name in ['block', 'block.db', 'block.wal']:
165 link_path = os.path.join(osd_path, link_name)
166 if os.path.exists(link_path):
167 os.unlink(os.path.join(osd_path, link_name))
b32b8144
FG
168 # encryption is handled here, before priming the OSD dir
169 if is_encrypted:
f6b5b4d7
TL
170 osd_lv_path = '/dev/mapper/%s' % osd_block_lv.lv_uuid
171 lockbox_secret = osd_block_lv.tags['ceph.cephx_lockbox_secret']
b32b8144
FG
172 encryption_utils.write_lockbox_keyring(osd_id, osd_fsid, lockbox_secret)
173 dmcrypt_secret = encryption_utils.get_dmcrypt_key(osd_id, osd_fsid)
f6b5b4d7 174 encryption_utils.luks_open(dmcrypt_secret, osd_block_lv.lv_path, osd_block_lv.lv_uuid)
b32b8144 175 else:
f6b5b4d7 176 osd_lv_path = osd_block_lv.lv_path
b32b8144 177
f6b5b4d7
TL
178 db_device_path = get_osd_device_path(osd_lvs, 'db', dmcrypt_secret=dmcrypt_secret)
179 wal_device_path = get_osd_device_path(osd_lvs, 'wal', dmcrypt_secret=dmcrypt_secret)
b32b8144 180
f64942e4
AA
181 # Once symlinks are removed, the osd dir can be 'primed again. chown first,
182 # regardless of what currently exists so that ``prime-osd-dir`` can succeed
183 # even if permissions are somehow messed up
184 system.chown(osd_path)
1adf2230 185 prime_command = [
b32b8144
FG
186 'ceph-bluestore-tool', '--cluster=%s' % conf.cluster,
187 'prime-osd-dir', '--dev', osd_lv_path,
1adf2230
AA
188 '--path', osd_path]
189
190 if __release__ != "luminous":
191 # mon-config changes are not available in Luminous
192 prime_command.append('--no-mon-config')
193
194 process.run(prime_command)
3efd9988
FG
195 # always re-do the symlink regardless if it exists, so that the block,
196 # block.wal, and block.db devices that may have changed can be mapped
197 # correctly every time
b32b8144 198 process.run(['ln', '-snf', osd_lv_path, os.path.join(osd_path, 'block')])
3efd9988
FG
199 system.chown(os.path.join(osd_path, 'block'))
200 system.chown(osd_path)
201 if db_device_path:
202 destination = os.path.join(osd_path, 'block.db')
b32b8144 203 process.run(['ln', '-snf', db_device_path, destination])
3efd9988 204 system.chown(db_device_path)
28e407b8 205 system.chown(destination)
3efd9988
FG
206 if wal_device_path:
207 destination = os.path.join(osd_path, 'block.wal')
b32b8144 208 process.run(['ln', '-snf', wal_device_path, destination])
3efd9988 209 system.chown(wal_device_path)
28e407b8 210 system.chown(destination)
3efd9988 211
94b18763
FG
212 if no_systemd is False:
213 # enable the ceph-volume unit for this OSD
214 systemctl.enable_volume(osd_id, osd_fsid, 'lvm')
3efd9988 215
1adf2230
AA
216 # enable the OSD
217 systemctl.enable_osd(osd_id)
218
94b18763
FG
219 # start the OSD
220 systemctl.start_osd(osd_id)
b32b8144 221 terminal.success("ceph-volume lvm activate successful for osd ID: %s" % osd_id)
d2e6a577
FG
222
223
224class Activate(object):
225
226 help = 'Discover and mount the LVM device associated with an OSD ID and start the Ceph OSD'
227
228 def __init__(self, argv):
229 self.argv = argv
230
231 @decorators.needs_root
94b18763
FG
232 def activate_all(self, args):
233 listed_osds = direct_report()
234 osds = {}
235 for osd_id, devices in listed_osds.items():
236 # the metadata for all devices in each OSD will contain
237 # the FSID which is required for activation
238 for device in devices:
239 fsid = device.get('tags', {}).get('ceph.osd_fsid')
240 if fsid:
241 osds[fsid] = osd_id
242 break
243 if not osds:
244 terminal.warning('Was unable to find any OSDs to activate')
245 terminal.warning('Verify OSDs are present with "ceph-volume lvm list"')
246 return
247 for osd_fsid, osd_id in osds.items():
248 if systemctl.osd_is_active(osd_id):
249 terminal.warning(
250 'OSD ID %s FSID %s process is active. Skipping activation' % (osd_id, osd_fsid)
251 )
252 else:
253 terminal.info('Activating OSD ID %s FSID %s' % (osd_id, osd_fsid))
254 self.activate(args, osd_id=osd_id, osd_fsid=osd_fsid)
255
256 @decorators.needs_root
257 def activate(self, args, osd_id=None, osd_fsid=None):
258 """
259 :param args: The parsed arguments coming from the CLI
f6b5b4d7
TL
260 :param osd_id: When activating all, this gets populated with an
261 existing OSD ID
262 :param osd_fsid: When activating all, this gets populated with an
263 existing OSD FSID
94b18763 264 """
f6b5b4d7
TL
265 osd_id = osd_id if osd_id else args.osd_id
266 osd_fsid = osd_fsid if osd_fsid else args.osd_fsid
94b18763 267
94b18763 268 if osd_id and osd_fsid:
f6b5b4d7
TL
269 tags = {'ceph.osd_id': osd_id, 'ceph.osd_fsid': osd_fsid}
270 elif not osd_id and osd_fsid:
271 tags = {'ceph.osd_fsid': osd_fsid}
272 lvs = api.get_lvs(tags=tags)
d2e6a577 273 if not lvs:
f6b5b4d7
TL
274 raise RuntimeError('could not find osd.%s with osd_fsid %s' %
275 (osd_id, osd_fsid))
276
3efd9988
FG
277 # This argument is only available when passed in directly or via
278 # systemd, not when ``create`` is being used
279 if getattr(args, 'auto_detect_objectstore', False):
280 logger.info('auto detecting objectstore')
f6b5b4d7 281 # may get multiple lvs, so can't do get_the_lvs() calls here
3efd9988
FG
282 for lv in lvs:
283 has_journal = lv.tags.get('ceph.journal_uuid')
284 if has_journal:
f6b5b4d7
TL
285 logger.info('found a journal associated with the OSD, '
286 'assuming filestore')
287 return activate_filestore(lvs, args.no_systemd)
288
289 logger.info('unable to find a journal associated with the OSD, '
290 'assuming bluestore')
291
292 return activate_bluestore(lvs, args.no_systemd)
3efd9988 293 if args.bluestore:
f6b5b4d7 294 activate_bluestore(lvs, args.no_systemd)
3efd9988 295 elif args.filestore:
f6b5b4d7 296 activate_filestore(lvs, args.no_systemd)
d2e6a577
FG
297
298 def main(self):
299 sub_command_help = dedent("""
300 Activate OSDs by discovering them with LVM and mounting them in their
301 appropriate destination:
302
303 ceph-volume lvm activate {ID} {FSID}
304
305 The lvs associated with the OSD need to have been prepared previously,
306 so that all needed tags and metadata exist.
307
94b18763
FG
308 When migrating OSDs, or a multiple-osd activation is needed, the
309 ``--all`` flag can be used instead of the individual ID and FSID:
310
311 ceph-volume lvm activate --all
312
d2e6a577
FG
313 """)
314 parser = argparse.ArgumentParser(
315 prog='ceph-volume lvm activate',
316 formatter_class=argparse.RawDescriptionHelpFormatter,
317 description=sub_command_help,
318 )
319
320 parser.add_argument(
321 'osd_id',
322 metavar='ID',
323 nargs='?',
324 help='The ID of the OSD, usually an integer, like 0'
325 )
326 parser.add_argument(
327 'osd_fsid',
328 metavar='FSID',
329 nargs='?',
330 help='The FSID of the OSD, similar to a SHA1'
331 )
3efd9988
FG
332 parser.add_argument(
333 '--auto-detect-objectstore',
334 action='store_true',
335 help='Autodetect the objectstore by inspecting the OSD',
336 )
d2e6a577
FG
337 parser.add_argument(
338 '--bluestore',
3efd9988 339 action='store_true',
94b18763 340 help='bluestore objectstore (default)',
d2e6a577
FG
341 )
342 parser.add_argument(
343 '--filestore',
3efd9988 344 action='store_true',
94b18763
FG
345 help='filestore objectstore',
346 )
347 parser.add_argument(
348 '--all',
349 dest='activate_all',
350 action='store_true',
351 help='Activate all OSDs found in the system',
352 )
353 parser.add_argument(
354 '--no-systemd',
355 dest='no_systemd',
356 action='store_true',
357 help='Skip creating and enabling systemd units and starting OSD services',
d2e6a577
FG
358 )
359 if len(self.argv) == 0:
360 print(sub_command_help)
361 return
362 args = parser.parse_args(self.argv)
3efd9988
FG
363 # Default to bluestore here since defaulting it in add_argument may
364 # cause both to be True
365 if not args.bluestore and not args.filestore:
366 args.bluestore = True
94b18763
FG
367 if args.activate_all:
368 self.activate_all(args)
369 else:
370 self.activate(args)