]>
Commit | Line | Data |
---|---|---|
d2e6a577 FG |
1 | from __future__ import print_function |
2 | import json | |
3 | import os | |
4 | from textwrap import dedent | |
5 | from ceph_volume.util import prepare as prepare_utils | |
6 | from ceph_volume.util import system | |
7 | from ceph_volume import conf, decorators | |
8 | from . import api | |
9 | from .common import prepare_parser | |
10 | ||
11 | ||
12 | def canonical_device_path(device): | |
13 | """ | |
14 | Ensure that a device is canonical (full path) and that it exists so that | |
15 | it can be used throughout the prepare/activate process | |
16 | """ | |
17 | # FIXME: this is obviously super naive | |
18 | inferred = os.path.join('/dev', device) | |
19 | if os.path.exists(os.path.abspath(device)): | |
20 | return device | |
21 | elif os.path.exists(inferred): | |
22 | return inferred | |
23 | raise RuntimeError('Selected device does not exist: %s' % device) | |
24 | ||
25 | ||
26 | def prepare_filestore(device, journal, secrets, id_=None, fsid=None): | |
27 | """ | |
28 | :param device: The name of the volume group or lvm to work with | |
29 | :param journal: similar to device but can also be a regular/plain disk | |
30 | :param secrets: A dict with the secrets needed to create the osd (e.g. cephx) | |
31 | :param id_: The OSD id | |
32 | :param fsid: The OSD fsid, also known as the OSD UUID | |
33 | """ | |
34 | cephx_secret = secrets.get('cephx_secret', prepare_utils.create_key()) | |
35 | json_secrets = json.dumps(secrets) | |
36 | ||
37 | # allow re-using an existing fsid, in case prepare failed | |
38 | fsid = fsid or system.generate_uuid() | |
39 | # allow re-using an id, in case a prepare failed | |
40 | osd_id = id_ or prepare_utils.create_id(fsid, json_secrets) | |
41 | # create the directory | |
42 | prepare_utils.create_path(osd_id) | |
43 | # format the device | |
44 | prepare_utils.format_device(device) | |
45 | # mount the data device | |
46 | prepare_utils.mount_osd(device, osd_id) | |
47 | # symlink the journal | |
48 | prepare_utils.link_journal(journal, osd_id) | |
49 | # get the latest monmap | |
50 | prepare_utils.get_monmap(osd_id) | |
51 | # prepare the osd filesystem | |
52 | prepare_utils.osd_mkfs(osd_id, fsid) | |
53 | # write the OSD keyring if it doesn't exist already | |
54 | prepare_utils.write_keyring(osd_id, cephx_secret) | |
55 | ||
56 | ||
57 | def prepare_bluestore(): | |
58 | raise NotImplemented() | |
59 | ||
60 | ||
61 | class Prepare(object): | |
62 | ||
63 | help = 'Format an LVM device and associate it with an OSD' | |
64 | ||
65 | def __init__(self, argv): | |
66 | self.argv = argv | |
67 | ||
68 | @decorators.needs_root | |
69 | def prepare(self, args): | |
70 | # FIXME we don't allow re-using a keyring, we always generate one for the | |
71 | # OSD, this needs to be fixed. This could either be a file (!) or a string | |
72 | # (!!) or some flags that we would need to compound into a dict so that we | |
73 | # can convert to JSON (!!!) | |
74 | secrets = {'cephx_secret': prepare_utils.create_key()} | |
75 | ||
76 | cluster_fsid = conf.ceph.get('global', 'fsid') | |
77 | fsid = args.osd_fsid or system.generate_uuid() | |
78 | #osd_id = args.osd_id or prepare_utils.create_id(fsid) | |
79 | # allow re-using an id, in case a prepare failed | |
80 | osd_id = args.osd_id or prepare_utils.create_id(fsid, json.dumps(secrets)) | |
81 | journal_name = "journal_%s" % fsid | |
82 | osd_name = "osd_%s" % fsid | |
83 | ||
84 | if args.filestore: | |
85 | data_vg = api.get_vg(vg_name=args.data) | |
86 | data_lv = api.get_lv(lv_name=args.data) | |
87 | journal_vg = api.get_vg(vg_name=args.journal) | |
88 | journal_lv = api.get_lv(lv_name=args.journal) | |
89 | journal_device = None | |
90 | # it is possible to pass a device as a journal that is not | |
91 | # an actual logical volume (or group) | |
92 | if not args.journal: | |
93 | if data_lv: | |
94 | raise RuntimeError('--journal is required when not using a vg for OSD data') | |
95 | # collocated: carve out the journal from the data vg | |
96 | if data_vg: | |
97 | journal_lv = api.create_lv( | |
98 | name=journal_name, | |
99 | group=data_vg.name, | |
100 | size=args.journal_size, | |
101 | osd_fsid=fsid, | |
102 | osd_id=osd_id, | |
103 | type='journal', | |
104 | cluster_fsid=cluster_fsid | |
105 | ) | |
106 | ||
107 | # if a volume group was defined for the journal create that first | |
108 | if journal_vg: | |
109 | journal_lv = api.create_lv( | |
110 | name=journal_name, | |
111 | group=args.journal, | |
112 | size=args.journal_size, | |
113 | osd_fsid=fsid, | |
114 | osd_id=osd_id, | |
115 | type='journal', | |
116 | cluster_fsid=cluster_fsid | |
117 | ) | |
118 | if journal_lv: | |
119 | journal_device = journal_lv.lv_path | |
120 | # The journal is probably a device, not in LVM | |
121 | elif args.journal: | |
122 | journal_device = canonical_device_path(args.journal) | |
123 | # At this point we must have a journal_lv or a journal device | |
124 | # now create the osd from the group if that was found | |
125 | if data_vg: | |
126 | # XXX make sure that a there aren't more OSDs than physical | |
127 | # devices from this volume group | |
128 | data_lv = api.create_lv( | |
129 | name=osd_name, | |
130 | group=args.data, | |
131 | osd_fsid=fsid, | |
132 | osd_id=osd_id, | |
133 | type='data', | |
134 | journal_device=journal_device, | |
135 | cluster_fsid=cluster_fsid | |
136 | ) | |
137 | # we must have either an existing data_lv or a newly created, so lets make | |
138 | # sure that the tags are correct | |
139 | if not data_lv: | |
140 | raise RuntimeError('no data logical volume found with: %s' % args.data) | |
141 | data_lv.set_tags({ | |
142 | 'ceph.type': 'data', | |
143 | 'ceph.osd_fsid': fsid, | |
144 | 'ceph.osd_id': osd_id, | |
145 | 'ceph.cluster_fsid': cluster_fsid, | |
146 | 'ceph.journal_device': journal_device, | |
147 | 'ceph.data_device': data_lv.lv_path, | |
148 | }) | |
149 | ||
150 | prepare_filestore( | |
151 | data_lv.lv_path, | |
152 | journal_device, | |
153 | secrets, | |
154 | id_=osd_id, | |
155 | fsid=fsid, | |
156 | ) | |
157 | elif args.bluestore: | |
158 | prepare_bluestore(args) | |
159 | ||
160 | def main(self): | |
161 | sub_command_help = dedent(""" | |
162 | Prepare an OSD by assigning an ID and FSID, registering them with the | |
163 | cluster with an ID and FSID, formatting and mounting the volume, and | |
164 | finally by adding all the metadata to the logical volumes using LVM | |
165 | tags, so that it can later be discovered. | |
166 | ||
167 | Once the OSD is ready, an ad-hoc systemd unit will be enabled so that | |
168 | it can later get activated and the OSD daemon can get started. | |
169 | ||
170 | Most basic Usage looks like (journal will be collocated from the same volume group): | |
171 | ||
172 | ceph-volume lvm prepare --data {volume group name} | |
173 | ||
174 | ||
175 | Example calls for supported scenarios: | |
176 | ||
177 | Dedicated volume group for Journal(s) | |
178 | ------------------------------------- | |
179 | ||
180 | Existing logical volume (lv) or device: | |
181 | ||
182 | ceph-volume lvm prepare --data {logical volume} --journal /path/to/{lv}|{device} | |
183 | ||
184 | Or: | |
185 | ||
186 | ceph-volume lvm prepare --data {data volume group} --journal {journal volume group} | |
187 | ||
188 | Collocated (same group) for data and journal | |
189 | -------------------------------------------- | |
190 | ||
191 | ceph-volume lvm prepare --data {volume group} | |
192 | ||
193 | """) | |
194 | parser = prepare_parser( | |
195 | prog='ceph-volume lvm prepare', | |
196 | description=sub_command_help, | |
197 | ) | |
198 | if len(self.argv) == 0: | |
199 | print(sub_command_help) | |
200 | return | |
201 | args = parser.parse_args(self.argv) | |
202 | self.prepare(args) |