Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / ceph-volume / ceph_volume / devices / lvm / prepare.py
1 from __future__ import print_function
2 import json
3 import uuid
4 from textwrap import dedent
5 from ceph_volume.util import prepare as prepare_utils
6 from ceph_volume.util import system, disk
7 from ceph_volume import conf, decorators, terminal
8 from ceph_volume.api import lvm as api
9 from .common import prepare_parser
10
11
12 def prepare_filestore(device, journal, secrets, id_=None, fsid=None):
13     """
14     :param device: The name of the logical volume to work with
15     :param journal: similar to device but can also be a regular/plain disk
16     :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
17     :param id_: The OSD id
18     :param fsid: The OSD fsid, also known as the OSD UUID
19     """
20     cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key())
21     json_secrets = json.dumps(secrets)
22
23     # allow re-using an existing fsid, in case prepare failed
24     fsid = fsid or system.generate_uuid()
25     # allow re-using an id, in case a prepare failed
26     osd_id = id_ or prepare_utils.create_id(fsid, json_secrets)
27     # create the directory
28     prepare_utils.create_osd_path(osd_id)
29     # format the device
30     prepare_utils.format_device(device)
31     # mount the data device
32     prepare_utils.mount_osd(device, osd_id)
33     # symlink the journal
34     prepare_utils.link_journal(journal, osd_id)
35     # get the latest monmap
36     prepare_utils.get_monmap(osd_id)
37     # prepare the osd filesystem
38     prepare_utils.osd_mkfs_filestore(osd_id, fsid)
39     # write the OSD keyring if it doesn't exist already
40     prepare_utils.write_keyring(osd_id, cephx_secret)
41
42
43 def prepare_bluestore(block, wal, db, secrets, id_=None, fsid=None):
44     """
45     :param block: The name of the logical volume for the bluestore data
46     :param wal: a regular/plain disk or logical volume, to be used for block.wal
47     :param db: a regular/plain disk or logical volume, to be used for block.db
48     :param secrets: A dict with the secrets needed to create the osd (e.g. cephx)
49     :param id_: The OSD id
50     :param fsid: The OSD fsid, also known as the OSD UUID
51     """
52     cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key())
53     json_secrets = json.dumps(secrets)
54
55     # allow re-using an existing fsid, in case prepare failed
56     fsid = fsid or system.generate_uuid()
57     # allow re-using an id, in case a prepare failed
58     osd_id = id_ or prepare_utils.create_id(fsid, json_secrets)
59     # create the directory
60     prepare_utils.create_osd_path(osd_id, tmpfs=True)
61     # symlink the block
62     prepare_utils.link_block(block, osd_id)
63     # get the latest monmap
64     prepare_utils.get_monmap(osd_id)
65     # write the OSD keyring if it doesn't exist already
66     prepare_utils.write_keyring(osd_id, cephx_secret)
67     # prepare the osd filesystem
68     prepare_utils.osd_mkfs_bluestore(
69         osd_id, fsid,
70         keyring=cephx_secret,
71         wal=wal,
72         db=db
73     )
74
75
76 class Prepare(object):
77
78     help = 'Format an LVM device and associate it with an OSD'
79
80     def __init__(self, argv):
81         self.argv = argv
82
83     def get_ptuuid(self, argument):
84         uuid = disk.get_partuuid(argument)
85         if not uuid:
86             terminal.error('blkid could not detect a PARTUUID for device: %s' % argument)
87             raise RuntimeError('unable to use device')
88         return uuid
89
90     def get_lv(self, argument):
91         """
92         Perform some parsing of the command-line value so that the process
93         can determine correctly if it got a device path or an lv.
94
95         :param argument: The command-line value that will need to be split to
96                          retrieve the actual lv
97         """
98         try:
99             vg_name, lv_name = argument.split('/')
100         except (ValueError, AttributeError):
101             return None
102         return api.get_lv(lv_name=lv_name, vg_name=vg_name)
103
104     def setup_device(self, device_type, device_name, tags):
105         """
106         Check if ``device`` is an lv, if so, set the tags, making sure to
107         update the tags with the lv_uuid and lv_path which the incoming tags
108         will not have.
109
110         If the device is not a logical volume, then retrieve the partition UUID
111         by querying ``blkid``
112         """
113         if device_name is None:
114             return '', '', tags
115         tags['ceph.type'] = device_type
116         lv = self.get_lv(device_name)
117         if lv:
118             uuid = lv.lv_uuid
119             path = lv.lv_path
120             tags['ceph.%s_uuid' % device_type] = uuid
121             tags['ceph.%s_device' % device_type] = path
122             lv.set_tags(tags)
123         else:
124             # otherwise assume this is a regular disk partition
125             uuid = self.get_ptuuid(device_name)
126             path = device_name
127             tags['ceph.%s_uuid' % device_type] = uuid
128             tags['ceph.%s_device' % device_type] = path
129         return path, uuid, tags
130
131     def prepare_device(self, arg, device_type, cluster_fsid, osd_fsid):
132         """
133         Check if ``arg`` is a device or partition to create an LV out of it
134         with a distinct volume group name, assigning LV tags on it and
135         ultimately, returning the logical volume object.  Failing to detect
136         a device or partition will result in error.
137
138         :param arg: The value of ``--data`` when parsing args
139         :param device_type: Usually, either ``data`` or ``block`` (filestore vs. bluestore)
140         :param cluster_fsid: The cluster fsid/uuid
141         :param osd_fsid: The OSD fsid/uuid
142         """
143         if disk.is_partition(arg) or disk.is_device(arg):
144             # we must create a vg, and then a single lv
145             vg_name = "ceph-%s" % cluster_fsid
146             if api.get_vg(vg_name=vg_name):
147                 # means we already have a group for this, make a different one
148                 # XXX this could end up being annoying for an operator, maybe?
149                 vg_name = "ceph-%s" % str(uuid.uuid4())
150             api.create_vg(vg_name, arg)
151             lv_name = "osd-%s-%s" % (device_type, osd_fsid)
152             return api.create_lv(
153                 lv_name,
154                 vg_name,  # the volume group
155                 tags={'ceph.type': device_type})
156         else:
157             error = [
158                 'Cannot use device (%s).' % arg,
159                 'A vg/lv path or an existing device is needed']
160             raise RuntimeError(' '.join(error))
161
162         raise RuntimeError('no data logical volume found with: %s' % arg)
163
164     @decorators.needs_root
165     def prepare(self, args):
166         # FIXME we don't allow re-using a keyring, we always generate one for the
167         # OSD, this needs to be fixed. This could either be a file (!) or a string
168         # (!!) or some flags that we would need to compound into a dict so that we
169         # can convert to JSON (!!!)
170         secrets = {'cephx_secret': prepare_utils.create_key()}
171
172         cluster_fsid = conf.ceph.get('global', 'fsid')
173         osd_fsid = args.osd_fsid or system.generate_uuid()
174         # allow re-using an id, in case a prepare failed
175         osd_id = args.osd_id or prepare_utils.create_id(osd_fsid, json.dumps(secrets))
176         if args.filestore:
177             if not args.journal:
178                 raise RuntimeError('--journal is required when using --filestore')
179
180             data_lv = self.get_lv(args.data)
181             if not data_lv:
182                 data_lv = self.prepare_device(args.data, 'data', cluster_fsid, osd_fsid)
183
184             tags = {
185                 'ceph.osd_fsid': osd_fsid,
186                 'ceph.osd_id': osd_id,
187                 'ceph.cluster_fsid': cluster_fsid,
188                 'ceph.cluster_name': conf.cluster,
189                 'ceph.data_device': data_lv.lv_path,
190                 'ceph.data_uuid': data_lv.lv_uuid,
191             }
192
193             journal_device, journal_uuid, tags = self.setup_device('journal', args.journal, tags)
194
195             tags['ceph.type'] = 'data'
196             data_lv.set_tags(tags)
197
198             prepare_filestore(
199                 data_lv.lv_path,
200                 journal_device,
201                 secrets,
202                 id_=osd_id,
203                 fsid=osd_fsid,
204             )
205         elif args.bluestore:
206             block_lv = self.get_lv(args.data)
207             if not block_lv:
208                 block_lv = self.prepare_device(args.data, 'block', cluster_fsid, osd_fsid)
209
210             tags = {
211                 'ceph.osd_fsid': osd_fsid,
212                 'ceph.osd_id': osd_id,
213                 'ceph.cluster_fsid': cluster_fsid,
214                 'ceph.cluster_name': conf.cluster,
215                 'ceph.block_device': block_lv.lv_path,
216                 'ceph.block_uuid': block_lv.lv_uuid,
217             }
218
219             wal_device, wal_uuid, tags = self.setup_device('wal', args.block_wal, tags)
220             db_device, db_uuid, tags = self.setup_device('db', args.block_db, tags)
221
222             tags['ceph.type'] = 'block'
223             block_lv.set_tags(tags)
224
225             prepare_bluestore(
226                 block_lv.lv_path,
227                 wal_device,
228                 db_device,
229                 secrets,
230                 id_=osd_id,
231                 fsid=osd_fsid,
232             )
233
234     def main(self):
235         sub_command_help = dedent("""
236         Prepare an OSD by assigning an ID and FSID, registering them with the
237         cluster with an ID and FSID, formatting and mounting the volume, and
238         finally by adding all the metadata to the logical volumes using LVM
239         tags, so that it can later be discovered.
240
241         Once the OSD is ready, an ad-hoc systemd unit will be enabled so that
242         it can later get activated and the OSD daemon can get started.
243
244         Most basic Usage looks like (journal will be collocated from the same volume group):
245
246             ceph-volume lvm prepare --data {volume group name}
247
248
249         Example calls for supported scenarios:
250
251         Dedicated volume group for Journal(s)
252         -------------------------------------
253
254           Existing logical volume (lv) or device:
255
256               ceph-volume lvm prepare --filestore --data {vg/lv} --journal /path/to/device
257
258           Or:
259
260               ceph-volume lvm prepare --filestore --data {vg/lv} --journal {vg/lv}
261
262           Existing block device, that will be made a group and logical volume:
263
264               ceph-volume lvm prepare --filestore --data /path/to/device --journal {vg/lv}
265
266         Bluestore
267         ---------
268
269           Existing logical volume (lv):
270
271               ceph-volume lvm prepare --bluestore --data {vg/lv}
272
273           Existing block device, that will be made a group and logical volume:
274
275               ceph-volume lvm prepare --bluestore --data /path/to/device
276
277           Optionally, can consume db and wal devices or logical volumes:
278
279               ceph-volume lvm prepare --bluestore --data {vg/lv} --block.wal {device} --block-db {vg/lv}
280         """)
281         parser = prepare_parser(
282             prog='ceph-volume lvm prepare',
283             description=sub_command_help,
284         )
285         if len(self.argv) == 0:
286             print(sub_command_help)
287             return
288         args = parser.parse_args(self.argv)
289         # Default to bluestore here since defaulting it in add_argument may
290         # cause both to be True
291         if args.bluestore is None and args.filestore is None:
292             args.bluestore = True
293         self.prepare(args)