]> git.proxmox.com Git - ceph.git/blob - ceph/src/ceph-volume/ceph_volume/devices/lvm/activate.py
update ceph source to reef 18.2.0
[ceph.git] / ceph / src / ceph-volume / ceph_volume / devices / lvm / activate.py
1 from __future__ import print_function
2 import argparse
3 import logging
4 import os
5 from textwrap import dedent
6 from ceph_volume import process, conf, decorators, terminal, configuration
7 from ceph_volume.util import system, disk
8 from ceph_volume.util import prepare as prepare_utils
9 from ceph_volume.util import encryption as encryption_utils
10 from ceph_volume.systemd import systemctl
11 from ceph_volume.api import lvm as api
12 from .listing import direct_report
13
14
15 logger = logging.getLogger(__name__)
16
17
18
19 def get_osd_device_path(osd_lvs, device_type, dmcrypt_secret=None):
20 """
21 ``device_type`` can be one of ``db``, ``wal`` or ``block`` so that we can
22 query LVs on system and fallback to querying the uuid if that is not
23 present.
24
25 Return a path if possible, failing to do that a ``None``, since some of
26 these devices are optional.
27 """
28 osd_block_lv = None
29 for lv in osd_lvs:
30 if lv.tags.get('ceph.type') == 'block':
31 osd_block_lv = lv
32 break
33 if osd_block_lv:
34 is_encrypted = osd_block_lv.tags.get('ceph.encrypted', '0') == '1'
35 logger.debug('Found block device (%s) with encryption: %s', osd_block_lv.name, is_encrypted)
36 uuid_tag = 'ceph.%s_uuid' % device_type
37 device_uuid = osd_block_lv.tags.get(uuid_tag)
38 if not device_uuid:
39 return None
40
41 device_lv = None
42 for lv in osd_lvs:
43 if lv.tags.get('ceph.type') == device_type:
44 device_lv = lv
45 break
46 if device_lv:
47 if is_encrypted:
48 encryption_utils.luks_open(dmcrypt_secret, device_lv.lv_path, device_uuid)
49 return '/dev/mapper/%s' % device_uuid
50 return device_lv.lv_path
51
52 # this could be a regular device, so query it with blkid
53 physical_device = disk.get_device_from_partuuid(device_uuid)
54 if physical_device:
55 if is_encrypted:
56 encryption_utils.luks_open(dmcrypt_secret, physical_device, device_uuid)
57 return '/dev/mapper/%s' % device_uuid
58 return physical_device
59
60 raise RuntimeError('could not find %s with uuid %s' % (device_type, device_uuid))
61
62
63 def activate_bluestore(osd_lvs, no_systemd=False, no_tmpfs=False):
64 for lv in osd_lvs:
65 if lv.tags.get('ceph.type') == 'block':
66 osd_block_lv = lv
67 break
68 else:
69 raise RuntimeError('could not find a bluestore OSD to activate')
70
71 is_encrypted = osd_block_lv.tags.get('ceph.encrypted', '0') == '1'
72 dmcrypt_secret = None
73 osd_id = osd_block_lv.tags['ceph.osd_id']
74 conf.cluster = osd_block_lv.tags['ceph.cluster_name']
75 osd_fsid = osd_block_lv.tags['ceph.osd_fsid']
76 configuration.load_ceph_conf_path(osd_block_lv.tags['ceph.cluster_name'])
77 configuration.load()
78
79 # mount on tmpfs the osd directory
80 osd_path = '/var/lib/ceph/osd/%s-%s' % (conf.cluster, osd_id)
81 if not system.path_is_mounted(osd_path):
82 # mkdir -p and mount as tmpfs
83 prepare_utils.create_osd_path(osd_id, tmpfs=not no_tmpfs)
84 # XXX This needs to be removed once ceph-bluestore-tool can deal with
85 # symlinks that exist in the osd dir
86 for link_name in ['block', 'block.db', 'block.wal']:
87 link_path = os.path.join(osd_path, link_name)
88 if os.path.exists(link_path):
89 os.unlink(os.path.join(osd_path, link_name))
90 # encryption is handled here, before priming the OSD dir
91 if is_encrypted:
92 osd_lv_path = '/dev/mapper/%s' % osd_block_lv.lv_uuid
93 lockbox_secret = osd_block_lv.tags['ceph.cephx_lockbox_secret']
94 encryption_utils.write_lockbox_keyring(osd_id, osd_fsid, lockbox_secret)
95 dmcrypt_secret = encryption_utils.get_dmcrypt_key(osd_id, osd_fsid)
96 encryption_utils.luks_open(dmcrypt_secret, osd_block_lv.lv_path, osd_block_lv.lv_uuid)
97 else:
98 osd_lv_path = osd_block_lv.lv_path
99
100 db_device_path = get_osd_device_path(osd_lvs, 'db', dmcrypt_secret=dmcrypt_secret)
101 wal_device_path = get_osd_device_path(osd_lvs, 'wal', dmcrypt_secret=dmcrypt_secret)
102
103 # Once symlinks are removed, the osd dir can be 'primed again. chown first,
104 # regardless of what currently exists so that ``prime-osd-dir`` can succeed
105 # even if permissions are somehow messed up
106 system.chown(osd_path)
107 prime_command = [
108 'ceph-bluestore-tool', '--cluster=%s' % conf.cluster,
109 'prime-osd-dir', '--dev', osd_lv_path,
110 '--path', osd_path, '--no-mon-config']
111
112 process.run(prime_command)
113 # always re-do the symlink regardless if it exists, so that the block,
114 # block.wal, and block.db devices that may have changed can be mapped
115 # correctly every time
116 process.run(['ln', '-snf', osd_lv_path, os.path.join(osd_path, 'block')])
117 system.chown(os.path.join(osd_path, 'block'))
118 system.chown(osd_path)
119 if db_device_path:
120 destination = os.path.join(osd_path, 'block.db')
121 process.run(['ln', '-snf', db_device_path, destination])
122 system.chown(db_device_path)
123 system.chown(destination)
124 if wal_device_path:
125 destination = os.path.join(osd_path, 'block.wal')
126 process.run(['ln', '-snf', wal_device_path, destination])
127 system.chown(wal_device_path)
128 system.chown(destination)
129
130 if no_systemd is False:
131 # enable the ceph-volume unit for this OSD
132 systemctl.enable_volume(osd_id, osd_fsid, 'lvm')
133
134 # enable the OSD
135 systemctl.enable_osd(osd_id)
136
137 # start the OSD
138 systemctl.start_osd(osd_id)
139 terminal.success("ceph-volume lvm activate successful for osd ID: %s" % osd_id)
140
141
142 class Activate(object):
143
144 help = 'Discover and mount the LVM device associated with an OSD ID and start the Ceph OSD'
145
146 def __init__(self, argv):
147 self.argv = argv
148
149 @decorators.needs_root
150 def activate_all(self, args):
151 listed_osds = direct_report()
152 osds = {}
153 for osd_id, devices in listed_osds.items():
154 # the metadata for all devices in each OSD will contain
155 # the FSID which is required for activation
156 for device in devices:
157 fsid = device.get('tags', {}).get('ceph.osd_fsid')
158 if fsid:
159 osds[fsid] = osd_id
160 break
161 if not osds:
162 terminal.warning('Was unable to find any OSDs to activate')
163 terminal.warning('Verify OSDs are present with "ceph-volume lvm list"')
164 return
165 for osd_fsid, osd_id in osds.items():
166 if not args.no_systemd and systemctl.osd_is_active(osd_id):
167 terminal.warning(
168 'OSD ID %s FSID %s process is active. Skipping activation' % (osd_id, osd_fsid)
169 )
170 else:
171 terminal.info('Activating OSD ID %s FSID %s' % (osd_id, osd_fsid))
172 self.activate(args, osd_id=osd_id, osd_fsid=osd_fsid)
173
174 @decorators.needs_root
175 def activate(self, args, osd_id=None, osd_fsid=None):
176 """
177 :param args: The parsed arguments coming from the CLI
178 :param osd_id: When activating all, this gets populated with an
179 existing OSD ID
180 :param osd_fsid: When activating all, this gets populated with an
181 existing OSD FSID
182 """
183 osd_id = osd_id if osd_id else args.osd_id
184 osd_fsid = osd_fsid if osd_fsid else args.osd_fsid
185
186 if osd_id and osd_fsid:
187 tags = {'ceph.osd_id': osd_id, 'ceph.osd_fsid': osd_fsid}
188 elif not osd_id and osd_fsid:
189 tags = {'ceph.osd_fsid': osd_fsid}
190 elif osd_id and not osd_fsid:
191 raise RuntimeError('could not activate osd.{}, please provide the '
192 'osd_fsid too'.format(osd_id))
193 else:
194 raise RuntimeError('Please provide both osd_id and osd_fsid')
195 lvs = api.get_lvs(tags=tags)
196 if not lvs:
197 raise RuntimeError('could not find osd.%s with osd_fsid %s' %
198 (osd_id, osd_fsid))
199
200 # This argument is only available when passed in directly or via
201 # systemd, not when ``create`` is being used
202 # placeholder when a new objectstore support will be added
203 if getattr(args, 'auto_detect_objectstore', False):
204 logger.info('auto detecting objectstore')
205 return activate_bluestore(lvs, args.no_systemd)
206
207 # explicit 'objectstore' flags take precedence
208 if getattr(args, 'bluestore', False):
209 activate_bluestore(lvs, args.no_systemd, getattr(args, 'no_tmpfs', False))
210 elif any('ceph.block_device' in lv.tags for lv in lvs):
211 activate_bluestore(lvs, args.no_systemd, getattr(args, 'no_tmpfs', False))
212
213 def main(self):
214 sub_command_help = dedent("""
215 Activate OSDs by discovering them with LVM and mounting them in their
216 appropriate destination:
217
218 ceph-volume lvm activate {ID} {FSID}
219
220 The lvs associated with the OSD need to have been prepared previously,
221 so that all needed tags and metadata exist.
222
223 When migrating OSDs, or a multiple-osd activation is needed, the
224 ``--all`` flag can be used instead of the individual ID and FSID:
225
226 ceph-volume lvm activate --all
227
228 """)
229 parser = argparse.ArgumentParser(
230 prog='ceph-volume lvm activate',
231 formatter_class=argparse.RawDescriptionHelpFormatter,
232 description=sub_command_help,
233 )
234
235 parser.add_argument(
236 'osd_id',
237 metavar='ID',
238 nargs='?',
239 help='The ID of the OSD, usually an integer, like 0'
240 )
241 parser.add_argument(
242 'osd_fsid',
243 metavar='FSID',
244 nargs='?',
245 help='The FSID of the OSD, similar to a SHA1'
246 )
247 parser.add_argument(
248 '--auto-detect-objectstore',
249 action='store_true',
250 help='Autodetect the objectstore by inspecting the OSD',
251 )
252 parser.add_argument(
253 '--bluestore',
254 action='store_true',
255 help='force bluestore objectstore activation',
256 )
257 parser.add_argument(
258 '--all',
259 dest='activate_all',
260 action='store_true',
261 help='Activate all OSDs found in the system',
262 )
263 parser.add_argument(
264 '--no-systemd',
265 dest='no_systemd',
266 action='store_true',
267 help='Skip creating and enabling systemd units and starting OSD services',
268 )
269 parser.add_argument(
270 '--no-tmpfs',
271 action='store_true',
272 help='Do not use a tmpfs mount for OSD data dir'
273 )
274 if len(self.argv) == 0:
275 print(sub_command_help)
276 return
277 args = parser.parse_args(self.argv)
278 if args.activate_all:
279 self.activate_all(args)
280 else:
281 self.activate(args)