]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #!/usr/bin/env python |
2 | import argparse | |
3 | import errno | |
4 | import json | |
5 | import logging | |
6 | import os | |
7 | import subprocess | |
8 | import sys | |
9 | import time | |
10 | import pwd | |
11 | import grp | |
12 | ||
13 | ||
14 | LOG = logging.getLogger(os.path.basename(sys.argv[0])) | |
15 | ||
16 | QUORUM_STATES = ['leader', 'peon'] | |
17 | ||
18 | def get_ceph_uid(): | |
19 | try: | |
20 | uid = pwd.getpwnam('ceph').pw_uid | |
21 | except: | |
22 | uid = -1 | |
23 | return uid | |
24 | ||
25 | def get_ceph_gid(): | |
26 | try: | |
27 | gid = grp.getgrnam('ceph').gr_gid | |
28 | except: | |
29 | gid = -1 | |
30 | return gid | |
31 | ||
32 | def wait_for_quorum(cluster, mon_id): | |
33 | wait_count = 600 # 10 minutes | |
34 | while wait_count > 0: | |
35 | p = subprocess.Popen( | |
36 | args=[ | |
37 | 'ceph', | |
38 | '--cluster={cluster}'.format(cluster=cluster), | |
39 | '--admin-daemon=/var/run/ceph/{cluster}-mon.{mon_id}.asok'.format( | |
40 | cluster=cluster, | |
41 | mon_id=mon_id, | |
42 | ), | |
43 | 'mon_status', | |
44 | ], | |
45 | stdout=subprocess.PIPE, | |
46 | ) | |
47 | out = p.stdout.read() | |
48 | returncode = p.wait() | |
49 | if returncode != 0: | |
50 | LOG.info('ceph-mon admin socket not ready yet.') | |
51 | time.sleep(1) | |
52 | wait_count -= 1 | |
53 | continue | |
54 | ||
55 | if out == '': | |
56 | LOG.info('ceph-mon admin socket returned no data') | |
57 | time.sleep(1) | |
58 | wait_count -= 1 | |
59 | continue | |
60 | ||
61 | try: | |
62 | data = json.loads(out) | |
63 | except: | |
64 | LOG.info('failed to parse json %s', out) | |
65 | sys.exit(errno.EINVAL) | |
66 | ||
67 | state = data['state'] | |
68 | if state not in QUORUM_STATES: | |
69 | LOG.info('ceph-mon is not in quorum: %r', state) | |
70 | time.sleep(1) | |
71 | wait_count -= 1 | |
72 | continue | |
73 | ||
74 | break | |
75 | ||
76 | if wait_count == 0: | |
77 | raise SystemExit("ceph-mon was not able to join quorum within 10 minutes") | |
78 | ||
79 | ||
80 | def get_key(cluster, mon_id): | |
81 | path = '/etc/ceph/{cluster}.client.admin.keyring'.format( | |
82 | cluster=cluster, | |
83 | ) | |
84 | if os.path.exists(path): | |
85 | LOG.info('Key exists already: %s', path) | |
86 | return | |
87 | tmp = '{path}.{pid}.tmp'.format( | |
88 | path=path, | |
89 | pid=os.getpid(), | |
90 | ) | |
91 | pathdir = os.path.dirname(path) | |
92 | if not os.path.exists(pathdir): | |
93 | os.makedirs(pathdir) | |
94 | os.chmod(pathdir, 0770) | |
95 | os.chown(pathdir, get_ceph_uid(), get_ceph_gid()) | |
96 | wait_count = 600 # 10 minutes | |
97 | while wait_count > 0: | |
98 | try: | |
99 | with file(tmp, 'w') as f: | |
100 | os.fchmod(f.fileno(), 0600) | |
101 | os.fchown(f.fileno(), get_ceph_uid(), get_ceph_gid()) | |
102 | LOG.info('Talking to monitor...') | |
103 | ||
104 | args_prefix = [ | |
105 | "ceph", | |
106 | '--connect-timeout=20', | |
107 | '--cluster={cluster}'.format(cluster=cluster), | |
108 | '--name=mon.', | |
109 | '--keyring=/var/lib/ceph/mon/{cluster}-{mon_id}/keyring'.format( | |
110 | cluster=cluster, | |
111 | mon_id=mon_id, | |
112 | ), | |
113 | ] | |
114 | ||
115 | # First try getting the key if it already exists, to handle | |
116 | # the case where it exists but doesn't match the caps | |
117 | # we would pass into get-or-create. | |
118 | returncode = subprocess.call( | |
119 | args=args_prefix + [ | |
120 | 'auth', | |
121 | 'get', | |
122 | 'client.admin', | |
123 | ], | |
124 | stdout=f, | |
125 | ) | |
126 | if returncode == errno.ENOENT: | |
127 | returncode = subprocess.call( | |
128 | args=args_prefix + [ | |
129 | 'auth', | |
130 | 'get-or-create', | |
131 | 'client.admin', | |
132 | 'mon', 'allow *', | |
133 | 'osd', 'allow *', | |
134 | 'mds', 'allow *', | |
135 | 'mgr', 'allow *', | |
136 | ], | |
137 | stdout=f, | |
138 | ) | |
31f18b77 FG |
139 | else: |
140 | returncode = subprocess.call( | |
141 | args=args_prefix + [ | |
142 | 'auth', | |
143 | 'caps', | |
144 | 'client.admin', | |
145 | 'mon', 'allow *', | |
146 | 'osd', 'allow *', | |
147 | 'mds', 'allow *', | |
148 | 'mgr', 'allow *', | |
149 | ], | |
150 | stdout=f, | |
151 | ) | |
7c673cae FG |
152 | |
153 | if returncode != 0: | |
154 | if returncode == errno.EPERM or returncode == errno.EACCES: | |
155 | LOG.info('Cannot get or create admin key, permission denied') | |
156 | sys.exit(returncode) | |
157 | else: | |
158 | LOG.info('Cannot get or create admin key') | |
159 | time.sleep(1) | |
160 | wait_count -= 1 | |
161 | continue | |
162 | ||
163 | os.rename(tmp, path) | |
164 | break | |
165 | finally: | |
166 | try: | |
167 | os.unlink(tmp) | |
168 | except OSError as e: | |
169 | if e.errno == errno.ENOENT: | |
170 | pass | |
171 | else: | |
172 | raise | |
173 | ||
174 | if wait_count == 0: | |
175 | raise SystemExit("Could not get or create the admin key after 10 minutes") | |
176 | ||
177 | ||
178 | def bootstrap_key(cluster, type_): | |
179 | path = '/var/lib/ceph/bootstrap-{type}/{cluster}.keyring'.format( | |
180 | type=type_, | |
181 | cluster=cluster, | |
182 | ) | |
183 | if os.path.exists(path): | |
184 | LOG.info('Key exists already: %s', path) | |
185 | return | |
186 | tmp = '{path}.{pid}.tmp'.format( | |
187 | path=path, | |
188 | pid=os.getpid(), | |
189 | ) | |
190 | ||
191 | args = [ | |
192 | 'ceph', | |
193 | '--connect-timeout=20', | |
194 | '--cluster={cluster}'.format(cluster=cluster), | |
195 | 'auth', | |
196 | 'get-or-create', | |
197 | 'client.bootstrap-{type}'.format(type=type_), | |
198 | 'mon', | |
199 | 'allow profile bootstrap-{type}'.format(type=type_), | |
200 | ] | |
201 | ||
202 | pathdir = os.path.dirname(path) | |
203 | if not os.path.exists(pathdir): | |
204 | os.makedirs(pathdir) | |
205 | os.chmod(pathdir, 0770) | |
206 | os.chown(pathdir, get_ceph_uid(), get_ceph_gid()) | |
207 | ||
208 | wait_count = 600 # 10 minutes | |
209 | while wait_count > 0: | |
210 | try: | |
211 | with file(tmp, 'w') as f: | |
212 | os.fchmod(f.fileno(), 0600) | |
213 | os.fchown(f.fileno(), get_ceph_uid(), get_ceph_gid()) | |
214 | LOG.info('Talking to monitor...') | |
215 | returncode = subprocess.call( | |
216 | args=args, | |
217 | stdout=f, | |
218 | ) | |
219 | if returncode != 0: | |
220 | if returncode == errno.EPERM or returncode == errno.EACCES: | |
221 | LOG.info('Cannot get or create bootstrap key for %s, permission denied', type_) | |
222 | break | |
223 | else: | |
224 | LOG.info('Cannot get or create bootstrap key for %s', type_) | |
225 | time.sleep(1) | |
226 | wait_count -= 1 | |
227 | continue | |
228 | ||
229 | os.rename(tmp, path) | |
230 | break | |
231 | finally: | |
232 | try: | |
233 | os.unlink(tmp) | |
234 | except OSError as e: | |
235 | if e.errno == errno.ENOENT: | |
236 | pass | |
237 | else: | |
238 | raise | |
239 | if wait_count == 0: | |
240 | raise SystemExit("Could not get or create %s bootstrap key after 10 minutes" % type_) | |
241 | ||
242 | ||
243 | def parse_args(): | |
244 | parser = argparse.ArgumentParser( | |
245 | description='Create Ceph client.admin key when ceph-mon is ready', | |
246 | ) | |
247 | parser.add_argument( | |
248 | '-v', '--verbose', | |
249 | action='store_true', default=None, | |
250 | help='be more verbose', | |
251 | ) | |
252 | parser.add_argument( | |
253 | '--cluster', | |
254 | metavar='NAME', | |
255 | help='name of the cluster', | |
256 | ) | |
257 | parser.add_argument( | |
258 | '--id', '-i', | |
259 | metavar='ID', | |
260 | help='id of a ceph-mon that is coming up', | |
261 | required=True, | |
262 | ) | |
263 | parser.set_defaults( | |
264 | cluster='ceph', | |
265 | ) | |
266 | parser.set_defaults( | |
267 | # we want to hold on to this, for later | |
268 | prog=parser.prog, | |
269 | ) | |
270 | args = parser.parse_args() | |
271 | return args | |
272 | ||
273 | ||
274 | def main(): | |
275 | args = parse_args() | |
276 | ||
277 | loglevel = logging.INFO | |
278 | if args.verbose: | |
279 | loglevel = logging.DEBUG | |
280 | ||
281 | logging.basicConfig( | |
282 | level=loglevel, | |
283 | ) | |
284 | ||
285 | wait_for_quorum(cluster=args.cluster, mon_id=args.id) | |
286 | get_key(cluster=args.cluster, mon_id=args.id) | |
287 | ||
288 | bootstrap_key( | |
289 | cluster=args.cluster, | |
290 | type_='osd', | |
291 | ) | |
292 | bootstrap_key( | |
293 | cluster=args.cluster, | |
294 | type_='rgw', | |
295 | ) | |
296 | bootstrap_key( | |
297 | cluster=args.cluster, | |
298 | type_='mds', | |
299 | ) | |
300 | ||
301 | ||
302 | if __name__ == '__main__': | |
303 | main() |