]> git.proxmox.com Git - ceph.git/blame - ceph/src/ceph-volume/ceph_volume/devices/simple/activate.py
update sources to 12.2.10
[ceph.git] / ceph / src / ceph-volume / ceph_volume / devices / simple / activate.py
CommitLineData
3efd9988
FG
1from __future__ import print_function
2import argparse
b32b8144 3import base64
3efd9988
FG
4import json
5import logging
6import os
7from textwrap import dedent
b32b8144 8from ceph_volume import process, decorators, terminal, conf
3efd9988 9from ceph_volume.util import system, disk
b32b8144 10from ceph_volume.util import encryption as encryption_utils
3efd9988
FG
11from ceph_volume.systemd import systemctl
12
13
14logger = logging.getLogger(__name__)
b32b8144 15mlogger = terminal.MultiLogger(__name__)
3efd9988
FG
16
17
18class Activate(object):
19
20 help = 'Enable systemd units to mount configured devices and start a Ceph OSD'
21
91327a77 22 def __init__(self, argv, from_trigger=False):
3efd9988 23 self.argv = argv
91327a77
AA
24 self.from_trigger = from_trigger
25 self.skip_systemd = False
3efd9988 26
b32b8144
FG
27 def validate_devices(self, json_config):
28 """
29 ``json_config`` is the loaded dictionary coming from the JSON file. It is usually mixed with
30 other non-device items, but for sakes of comparison it doesn't really matter. This method is
31 just making sure that the keys needed exist
32 """
33 devices = json_config.keys()
34 try:
35 objectstore = json_config['type']
36 except KeyError:
37 logger.warning('"type" was not defined, will assume "bluestore"')
38 objectstore = 'bluestore'
39
40 # Go through all the device combinations that are absolutely required,
41 # raise an error describing what was expected and what was found
42 # otherwise.
43 if objectstore == 'filestore':
44 if {'data', 'journal'}.issubset(set(devices)):
45 return True
46 else:
47 found = [i for i in devices if i in ['data', 'journal']]
48 mlogger.error("Required devices (data, and journal) not present for filestore")
49 mlogger.error('filestore devices found: %s', found)
50 raise RuntimeError('Unable to activate filestore OSD due to missing devices')
51 else:
52 # This is a bit tricky, with newer bluestore we don't need data, older implementations
53 # do (e.g. with ceph-disk). ceph-volume just uses a tmpfs that doesn't require data.
54 if {'block', 'data'}.issubset(set(devices)):
55 return True
56 else:
57 bluestore_devices = ['block.db', 'block.wal', 'block', 'data']
58 found = [i for i in devices if i in bluestore_devices]
59 mlogger.error("Required devices (block and data) not present for bluestore")
60 mlogger.error('bluestore devices found: %s', found)
61 raise RuntimeError('Unable to activate bluestore OSD due to missing devices')
62
63 def get_device(self, uuid):
64 """
65 If a device is encrypted, it will decrypt/open and return the mapper
66 path, if it isn't encrypted it will just return the device found that
67 is mapped to the uuid. This will make it easier for the caller to
68 avoid if/else to check if devices need decrypting
69
70 :param uuid: The partition uuid of the device (PARTUUID)
71 """
72 device = disk.get_device_from_partuuid(uuid)
73
74 # If device is not found, it is fine to return an empty string from the
75 # helper that finds `device`. If it finds anything and it is not
76 # encrypted, just return what was found
77 if not self.is_encrypted or not device:
78 return device
79
80 if self.encryption_type == 'luks':
81 encryption_utils.luks_open(self.dmcrypt_secret, device, uuid)
82 else:
83 encryption_utils.plain_open(self.dmcrypt_secret, device, uuid)
84
85 return '/dev/mapper/%s' % uuid
86
91327a77
AA
87 def enable_systemd_units(self, osd_id, osd_fsid):
88 """
89 * disables the ceph-disk systemd units to prevent them from running when
90 a UDEV event matches Ceph rules
91 * creates the ``simple`` systemd units to handle the activation and
92 startup of the OSD with ``osd_id`` and ``osd_fsid``
93 * enables the OSD systemd unit and finally starts the OSD.
94 """
95 if not self.from_trigger and not self.skip_systemd:
96 # means it was scanned and now activated directly, so ensure that
97 # ceph-disk units are disabled, and that the `simple` systemd unit
98 # is created and enabled
99
100 # enable the ceph-volume unit for this OSD
101 systemctl.enable_volume(osd_id, osd_fsid, 'simple')
102
103 # disable any/all ceph-disk units
104 systemctl.mask_ceph_disk()
105 terminal.warning(
106 ('All ceph-disk systemd units have been disabled to '
107 'prevent OSDs getting triggered by UDEV events')
108 )
109 else:
110 terminal.info('Skipping enabling of `simple` systemd unit')
111 terminal.info('Skipping masking of ceph-disk systemd units')
112
113 if not self.skip_systemd:
114 # enable the OSD
115 systemctl.enable_osd(osd_id)
116
117 # start the OSD
118 systemctl.start_osd(osd_id)
119 else:
120 terminal.info(
121 'Skipping enabling and starting OSD simple systemd unit because --no-systemd was used'
122 )
123
3efd9988
FG
124 @decorators.needs_root
125 def activate(self, args):
126 with open(args.json_config, 'r') as fp:
127 osd_metadata = json.load(fp)
128
b32b8144
FG
129 # Make sure that required devices are configured
130 self.validate_devices(osd_metadata)
131
3efd9988
FG
132 osd_id = osd_metadata.get('whoami', args.osd_id)
133 osd_fsid = osd_metadata.get('fsid', args.osd_fsid)
3efd9988 134 data_uuid = osd_metadata.get('data', {}).get('uuid')
b32b8144 135 conf.cluster = osd_metadata.get('cluster_name', 'ceph')
3efd9988
FG
136 if not data_uuid:
137 raise RuntimeError(
138 'Unable to activate OSD %s - no "uuid" key found for data' % args.osd_id
139 )
b32b8144
FG
140
141 # Encryption detection, and capturing of the keys to decrypt
142 self.is_encrypted = osd_metadata.get('encrypted', False)
143 self.encryption_type = osd_metadata.get('encryption_type')
144 if self.is_encrypted:
145 lockbox_secret = osd_metadata.get('lockbox.keyring')
146 # write the keyring always so that we can unlock
147 encryption_utils.write_lockbox_keyring(osd_id, osd_fsid, lockbox_secret)
148 # Store the secret around so that the decrypt method can reuse
149 raw_dmcrypt_secret = encryption_utils.get_dmcrypt_key(osd_id, osd_fsid)
150 # Note how both these calls need b64decode. For some reason, the
151 # way ceph-disk creates these keys, it stores them in the monitor
152 # *undecoded*, requiring this decode call again. The lvm side of
153 # encryption doesn't need it, so we are assuming here that anything
154 # that `simple` scans, will come from ceph-disk and will need this
155 # extra decode call here
156 self.dmcrypt_secret = base64.b64decode(raw_dmcrypt_secret)
157
158 cluster_name = osd_metadata.get('cluster_name', 'ceph')
159 osd_dir = '/var/lib/ceph/osd/%s-%s' % (cluster_name, osd_id)
160
161 # XXX there is no support for LVM here
162 data_device = self.get_device(data_uuid)
163 journal_device = self.get_device(osd_metadata.get('journal', {}).get('uuid'))
164 block_device = self.get_device(osd_metadata.get('block', {}).get('uuid'))
165 block_db_device = self.get_device(osd_metadata.get('block.db', {}).get('uuid'))
166 block_wal_device = self.get_device(osd_metadata.get('block.wal', {}).get('uuid'))
3efd9988
FG
167
168 if not system.device_is_mounted(data_device, destination=osd_dir):
b32b8144 169 process.run(['mount', '-v', data_device, osd_dir])
3efd9988
FG
170
171 device_map = {
172 'journal': journal_device,
173 'block': block_device,
174 'block.db': block_db_device,
175 'block.wal': block_wal_device
176 }
177
178 for name, device in device_map.items():
179 if not device:
180 continue
181 # always re-do the symlink regardless if it exists, so that the journal
182 # device path that may have changed can be mapped correctly every time
183 destination = os.path.join(osd_dir, name)
b32b8144 184 process.run(['ln', '-snf', device, destination])
3efd9988
FG
185
186 # make sure that the journal has proper permissions
187 system.chown(device)
188
91327a77 189 self.enable_systemd_units(osd_id, osd_fsid)
3efd9988 190
b32b8144 191 terminal.success('Successfully activated OSD %s with FSID %s' % (osd_id, osd_fsid))
3efd9988
FG
192
193 def main(self):
194 sub_command_help = dedent("""
195 Activate OSDs by mounting devices previously configured to their
196 appropriate destination::
197
198 ceph-volume simple activate {ID} {FSID}
199
200 Or using a JSON file directly::
201
202 ceph-volume simple activate --file /etc/ceph/osd/{ID}-{FSID}.json
203
204 The OSD must have been "scanned" previously (see ``ceph-volume simple
205 scan``), so that all needed OSD device information and metadata exist.
206
207 A previously scanned OSD would exist like::
208
209 /etc/ceph/osd/{ID}-{FSID}.json
210
211
212 Environment variables supported:
213
214 CEPH_VOLUME_SIMPLE_JSON_DIR: Directory location for scanned OSD JSON configs
215 """)
216 parser = argparse.ArgumentParser(
217 prog='ceph-volume simple activate',
218 formatter_class=argparse.RawDescriptionHelpFormatter,
219 description=sub_command_help,
220 )
221 parser.add_argument(
222 'osd_id',
223 metavar='ID',
224 nargs='?',
225 help='The ID of the OSD, usually an integer, like 0'
226 )
227 parser.add_argument(
228 'osd_fsid',
229 metavar='FSID',
230 nargs='?',
231 help='The FSID of the OSD, similar to a SHA1'
232 )
233 parser.add_argument(
234 '--file',
235 help='The path to a JSON file, from a scanned OSD'
236 )
91327a77
AA
237 parser.add_argument(
238 '--no-systemd',
239 dest='skip_systemd',
240 action='store_true',
241 help='Skip creating and enabling systemd units and starting OSD services',
242 )
3efd9988
FG
243 if len(self.argv) == 0:
244 print(sub_command_help)
245 return
246 args = parser.parse_args(self.argv)
247 if not args.file:
248 if not args.osd_id and not args.osd_fsid:
249 terminal.error('ID and FSID are required to find the right OSD to activate')
250 terminal.error('from a scanned OSD location in /etc/ceph/osd/')
251 raise RuntimeError('Unable to activate without both ID and FSID')
252 # don't allow a CLI flag to specify the JSON dir, because that might
253 # implicitly indicate that it would be possible to activate a json file
254 # at a non-default location which would not work at boot time if the
255 # custom location is not exposed through an ENV var
256 json_dir = os.environ.get('CEPH_VOLUME_SIMPLE_JSON_DIR', '/etc/ceph/osd/')
257 if args.file:
258 json_config = args.file
259 else:
260 json_config = os.path.join(json_dir, '%s-%s.json' % (args.osd_id, args.osd_fsid))
261 if not os.path.exists(json_config):
262 raise RuntimeError('Expected JSON config path not found: %s' % json_config)
263 args.json_config = json_config
91327a77 264 self.skip_systemd = args.skip_systemd
3efd9988 265 self.activate(args)