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