]>
Commit | Line | Data |
---|---|---|
d2e6a577 FG |
1 | from __future__ import print_function |
2 | import argparse | |
3efd9988 FG |
3 | import logging |
4 | import os | |
d2e6a577 | 5 | from textwrap import dedent |
20effc67 | 6 | from ceph_volume import process, conf, decorators, terminal, configuration |
181888fb | 7 | from ceph_volume.util import system, disk |
3efd9988 | 8 | from ceph_volume.util import prepare as prepare_utils |
b32b8144 | 9 | from ceph_volume.util import encryption as encryption_utils |
d2e6a577 | 10 | from ceph_volume.systemd import systemctl |
3efd9988 | 11 | from ceph_volume.api import lvm as api |
94b18763 | 12 | from .listing import direct_report |
3efd9988 FG |
13 | |
14 | ||
15 | logger = logging.getLogger(__name__) | |
d2e6a577 FG |
16 | |
17 | ||
d2e6a577 | 18 | |
f6b5b4d7 | 19 | def get_osd_device_path(osd_lvs, device_type, dmcrypt_secret=None): |
3efd9988 | 20 | """ |
f6b5b4d7 TL |
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. | |
3efd9988 | 24 | |
f6b5b4d7 TL |
25 | Return a path if possible, failing to do that a ``None``, since some of |
26 | these devices are optional. | |
3efd9988 | 27 | """ |
f6b5b4d7 TL |
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 | |
3efd9988 | 46 | if device_lv: |
b32b8144 FG |
47 | if is_encrypted: |
48 | encryption_utils.luks_open(dmcrypt_secret, device_lv.lv_path, device_uuid) | |
49 | return '/dev/mapper/%s' % device_uuid | |
3efd9988 | 50 | return device_lv.lv_path |
494da23a TL |
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: | |
b32b8144 FG |
56 | encryption_utils.luks_open(dmcrypt_secret, physical_device, device_uuid) |
57 | return '/dev/mapper/%s' % device_uuid | |
494da23a TL |
58 | return physical_device |
59 | ||
60 | raise RuntimeError('could not find %s with uuid %s' % (device_type, device_uuid)) | |
3efd9988 FG |
61 | |
62 | ||
20effc67 | 63 | def activate_bluestore(osd_lvs, no_systemd=False, no_tmpfs=False): |
f6b5b4d7 TL |
64 | for lv in osd_lvs: |
65 | if lv.tags.get('ceph.type') == 'block': | |
66 | osd_block_lv = lv | |
67 | break | |
68 | else: | |
94b18763 | 69 | raise RuntimeError('could not find a bluestore OSD to activate') |
f6b5b4d7 TL |
70 | |
71 | is_encrypted = osd_block_lv.tags.get('ceph.encrypted', '0') == '1' | |
b32b8144 | 72 | dmcrypt_secret = None |
f6b5b4d7 TL |
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'] | |
20effc67 TL |
76 | configuration.load_ceph_conf_path(osd_block_lv.tags['ceph.cluster_name']) |
77 | configuration.load() | |
3efd9988 FG |
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 | |
20effc67 | 83 | prepare_utils.create_osd_path(osd_id, tmpfs=not no_tmpfs) |
3efd9988 FG |
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)) | |
b32b8144 FG |
90 | # encryption is handled here, before priming the OSD dir |
91 | if is_encrypted: | |
f6b5b4d7 TL |
92 | osd_lv_path = '/dev/mapper/%s' % osd_block_lv.lv_uuid |
93 | lockbox_secret = osd_block_lv.tags['ceph.cephx_lockbox_secret'] | |
b32b8144 FG |
94 | encryption_utils.write_lockbox_keyring(osd_id, osd_fsid, lockbox_secret) |
95 | dmcrypt_secret = encryption_utils.get_dmcrypt_key(osd_id, osd_fsid) | |
f6b5b4d7 | 96 | encryption_utils.luks_open(dmcrypt_secret, osd_block_lv.lv_path, osd_block_lv.lv_uuid) |
b32b8144 | 97 | else: |
f6b5b4d7 | 98 | osd_lv_path = osd_block_lv.lv_path |
b32b8144 | 99 | |
f6b5b4d7 TL |
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) | |
b32b8144 | 102 | |
f64942e4 AA |
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) | |
1adf2230 | 107 | prime_command = [ |
b32b8144 FG |
108 | 'ceph-bluestore-tool', '--cluster=%s' % conf.cluster, |
109 | 'prime-osd-dir', '--dev', osd_lv_path, | |
20effc67 | 110 | '--path', osd_path, '--no-mon-config'] |
1adf2230 AA |
111 | |
112 | process.run(prime_command) | |
3efd9988 FG |
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 | |
b32b8144 | 116 | process.run(['ln', '-snf', osd_lv_path, os.path.join(osd_path, 'block')]) |
3efd9988 FG |
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') | |
b32b8144 | 121 | process.run(['ln', '-snf', db_device_path, destination]) |
3efd9988 | 122 | system.chown(db_device_path) |
28e407b8 | 123 | system.chown(destination) |
3efd9988 FG |
124 | if wal_device_path: |
125 | destination = os.path.join(osd_path, 'block.wal') | |
b32b8144 | 126 | process.run(['ln', '-snf', wal_device_path, destination]) |
3efd9988 | 127 | system.chown(wal_device_path) |
28e407b8 | 128 | system.chown(destination) |
3efd9988 | 129 | |
94b18763 FG |
130 | if no_systemd is False: |
131 | # enable the ceph-volume unit for this OSD | |
132 | systemctl.enable_volume(osd_id, osd_fsid, 'lvm') | |
3efd9988 | 133 | |
1adf2230 AA |
134 | # enable the OSD |
135 | systemctl.enable_osd(osd_id) | |
136 | ||
94b18763 FG |
137 | # start the OSD |
138 | systemctl.start_osd(osd_id) | |
b32b8144 | 139 | terminal.success("ceph-volume lvm activate successful for osd ID: %s" % osd_id) |
d2e6a577 FG |
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 | |
94b18763 FG |
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(): | |
a4b75251 | 166 | if not args.no_systemd and systemctl.osd_is_active(osd_id): |
94b18763 FG |
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 | |
f6b5b4d7 TL |
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 | |
94b18763 | 182 | """ |
f6b5b4d7 TL |
183 | osd_id = osd_id if osd_id else args.osd_id |
184 | osd_fsid = osd_fsid if osd_fsid else args.osd_fsid | |
94b18763 | 185 | |
94b18763 | 186 | if osd_id and osd_fsid: |
f6b5b4d7 TL |
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} | |
522d829b TL |
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') | |
f6b5b4d7 | 195 | lvs = api.get_lvs(tags=tags) |
d2e6a577 | 196 | if not lvs: |
f6b5b4d7 TL |
197 | raise RuntimeError('could not find osd.%s with osd_fsid %s' % |
198 | (osd_id, osd_fsid)) | |
199 | ||
3efd9988 FG |
200 | # This argument is only available when passed in directly or via |
201 | # systemd, not when ``create`` is being used | |
05a536ef | 202 | # placeholder when a new objectstore support will be added |
3efd9988 FG |
203 | if getattr(args, 'auto_detect_objectstore', False): |
204 | logger.info('auto detecting objectstore') | |
f6b5b4d7 | 205 | return activate_bluestore(lvs, args.no_systemd) |
20effc67 | 206 | |
05a536ef | 207 | # explicit 'objectstore' flags take precedence |
20effc67 TL |
208 | if getattr(args, 'bluestore', False): |
209 | activate_bluestore(lvs, args.no_systemd, getattr(args, 'no_tmpfs', False)) | |
20effc67 TL |
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)) | |
d2e6a577 FG |
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 | ||
94b18763 FG |
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 | ||
d2e6a577 FG |
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 | ) | |
3efd9988 FG |
247 | parser.add_argument( |
248 | '--auto-detect-objectstore', | |
249 | action='store_true', | |
250 | help='Autodetect the objectstore by inspecting the OSD', | |
251 | ) | |
d2e6a577 FG |
252 | parser.add_argument( |
253 | '--bluestore', | |
3efd9988 | 254 | action='store_true', |
20effc67 | 255 | help='force bluestore objectstore activation', |
d2e6a577 | 256 | ) |
94b18763 FG |
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', | |
d2e6a577 | 268 | ) |
20effc67 TL |
269 | parser.add_argument( |
270 | '--no-tmpfs', | |
271 | action='store_true', | |
272 | help='Do not use a tmpfs mount for OSD data dir' | |
273 | ) | |
d2e6a577 FG |
274 | if len(self.argv) == 0: |
275 | print(sub_command_help) | |
276 | return | |
277 | args = parser.parse_args(self.argv) | |
94b18763 FG |
278 | if args.activate_all: |
279 | self.activate_all(args) | |
280 | else: | |
281 | self.activate(args) |