root / lib / server / rapi.py @ 8ef418bb
History | View | Annotate | Download (10.7 kB)
1 | 69cf3abd | Michael Hanselmann | #
|
---|---|---|---|
2 | 8c229cc7 | Oleksiy Mishchenko | #
|
3 | 8c229cc7 | Oleksiy Mishchenko | |
4 | 5224c9bf | Michael Hanselmann | # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012, 2013 Google Inc.
|
5 | 8c229cc7 | Oleksiy Mishchenko | #
|
6 | 8c229cc7 | Oleksiy Mishchenko | # This program is free software; you can redistribute it and/or modify
|
7 | 8c229cc7 | Oleksiy Mishchenko | # it under the terms of the GNU General Public License as published by
|
8 | 8c229cc7 | Oleksiy Mishchenko | # the Free Software Foundation; either version 2 of the License, or
|
9 | 8c229cc7 | Oleksiy Mishchenko | # (at your option) any later version.
|
10 | 8c229cc7 | Oleksiy Mishchenko | #
|
11 | 8c229cc7 | Oleksiy Mishchenko | # This program is distributed in the hope that it will be useful, but
|
12 | 8c229cc7 | Oleksiy Mishchenko | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 8c229cc7 | Oleksiy Mishchenko | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 8c229cc7 | Oleksiy Mishchenko | # General Public License for more details.
|
15 | 8c229cc7 | Oleksiy Mishchenko | #
|
16 | 8c229cc7 | Oleksiy Mishchenko | # You should have received a copy of the GNU General Public License
|
17 | 8c229cc7 | Oleksiy Mishchenko | # along with this program; if not, write to the Free Software
|
18 | 8c229cc7 | Oleksiy Mishchenko | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 8c229cc7 | Oleksiy Mishchenko | # 02110-1301, USA.
|
20 | 8c229cc7 | Oleksiy Mishchenko | |
21 | 7260cfbe | Iustin Pop | """Ganeti Remote API master script.
|
22 | 7260cfbe | Iustin Pop |
|
23 | 8c229cc7 | Oleksiy Mishchenko | """
|
24 | 8c229cc7 | Oleksiy Mishchenko | |
25 | b459a848 | Andrea Spadaccini | # pylint: disable=C0103,W0142
|
26 | 7260cfbe | Iustin Pop | |
27 | 7260cfbe | Iustin Pop | # C0103: Invalid name ganeti-watcher
|
28 | 7260cfbe | Iustin Pop | |
29 | 441e7cfd | Oleksiy Mishchenko | import logging |
30 | 8c229cc7 | Oleksiy Mishchenko | import optparse |
31 | 8c229cc7 | Oleksiy Mishchenko | import sys |
32 | 8c229cc7 | Oleksiy Mishchenko | import os |
33 | b5b67ef9 | Michael Hanselmann | import os.path |
34 | 073c31a5 | Michael Hanselmann | import errno |
35 | 8c229cc7 | Oleksiy Mishchenko | |
36 | a2e60f14 | René Nussbaumer | try:
|
37 | b459a848 | Andrea Spadaccini | from pyinotify import pyinotify # pylint: disable=E0611 |
38 | a2e60f14 | René Nussbaumer | except ImportError: |
39 | a2e60f14 | René Nussbaumer | import pyinotify |
40 | a2e60f14 | René Nussbaumer | |
41 | a2e60f14 | René Nussbaumer | from ganeti import asyncnotifier |
42 | 8c229cc7 | Oleksiy Mishchenko | from ganeti import constants |
43 | 3cd62121 | Michael Hanselmann | from ganeti import http |
44 | 16a8967d | Michael Hanselmann | from ganeti import daemon |
45 | 5675cd1f | Iustin Pop | from ganeti import ssconf |
46 | 77e1d753 | Iustin Pop | from ganeti import luxi |
47 | 1f8588f6 | Iustin Pop | from ganeti import serializer |
48 | e4ef4343 | Michael Hanselmann | from ganeti import compat |
49 | 2287b920 | Michael Hanselmann | from ganeti import utils |
50 | a5ce2ea2 | Michael Hanselmann | from ganeti import pathutils |
51 | 3cd62121 | Michael Hanselmann | from ganeti.rapi import connector |
52 | 5224c9bf | Michael Hanselmann | from ganeti.rapi import baserlib |
53 | 3cd62121 | Michael Hanselmann | |
54 | b459a848 | Andrea Spadaccini | import ganeti.http.auth # pylint: disable=W0611 |
55 | bc2929fc | Michael Hanselmann | import ganeti.http.server |
56 | 3cd62121 | Michael Hanselmann | |
57 | bc2929fc | Michael Hanselmann | |
58 | 7e9760c3 | Michael Hanselmann | class RemoteApiRequestContext(object): |
59 | 7e9760c3 | Michael Hanselmann | """Data structure for Remote API requests.
|
60 | 7e9760c3 | Michael Hanselmann |
|
61 | 7e9760c3 | Michael Hanselmann | """
|
62 | 7e9760c3 | Michael Hanselmann | def __init__(self): |
63 | 7e9760c3 | Michael Hanselmann | self.handler = None |
64 | 7e9760c3 | Michael Hanselmann | self.handler_fn = None |
65 | b5b67ef9 | Michael Hanselmann | self.handler_access = None |
66 | ab221ddf | Michael Hanselmann | self.body_data = None |
67 | 7e9760c3 | Michael Hanselmann | |
68 | 7e9760c3 | Michael Hanselmann | |
69 | e0003509 | Michael Hanselmann | class RemoteApiHandler(http.auth.HttpServerRequestAuthentication, |
70 | e0003509 | Michael Hanselmann | http.server.HttpServerHandler): |
71 | 3cd62121 | Michael Hanselmann | """REST Request Handler Class.
|
72 | 3cd62121 | Michael Hanselmann |
|
73 | 3cd62121 | Michael Hanselmann | """
|
74 | b5b67ef9 | Michael Hanselmann | AUTH_REALM = "Ganeti Remote API"
|
75 | b5b67ef9 | Michael Hanselmann | |
76 | 27a8a190 | Michael Hanselmann | def __init__(self, user_fn, reqauth, _client_cls=None): |
77 | abe318b3 | Michael Hanselmann | """Initializes this class.
|
78 | abe318b3 | Michael Hanselmann |
|
79 | abe318b3 | Michael Hanselmann | @type user_fn: callable
|
80 | abe318b3 | Michael Hanselmann | @param user_fn: Function receiving username as string and returning
|
81 | abe318b3 | Michael Hanselmann | L{http.auth.PasswordFileUser} or C{None} if user is not found
|
82 | 27a8a190 | Michael Hanselmann | @type reqauth: bool
|
83 | 27a8a190 | Michael Hanselmann | @param reqauth: Whether to require authentication
|
84 | abe318b3 | Michael Hanselmann |
|
85 | abe318b3 | Michael Hanselmann | """
|
86 | b459a848 | Andrea Spadaccini | # pylint: disable=W0233
|
87 | e4ef4343 | Michael Hanselmann | # it seems pylint doesn't see the second parent class there
|
88 | e0003509 | Michael Hanselmann | http.server.HttpServerHandler.__init__(self)
|
89 | b5b67ef9 | Michael Hanselmann | http.auth.HttpServerRequestAuthentication.__init__(self)
|
90 | da04c447 | Michael Hanselmann | self._client_cls = _client_cls
|
91 | 3cd62121 | Michael Hanselmann | self._resmap = connector.Mapper()
|
92 | abe318b3 | Michael Hanselmann | self._user_fn = user_fn
|
93 | 27a8a190 | Michael Hanselmann | self._reqauth = reqauth
|
94 | a2e60f14 | René Nussbaumer | |
95 | 377ae13e | Michael Hanselmann | @staticmethod
|
96 | 377ae13e | Michael Hanselmann | def FormatErrorMessage(values): |
97 | 377ae13e | Michael Hanselmann | """Formats the body of an error message.
|
98 | 377ae13e | Michael Hanselmann |
|
99 | 377ae13e | Michael Hanselmann | @type values: dict
|
100 | 377ae13e | Michael Hanselmann | @param values: dictionary with keys C{code}, C{message} and C{explain}.
|
101 | 377ae13e | Michael Hanselmann | @rtype: tuple; (string, string)
|
102 | 377ae13e | Michael Hanselmann | @return: Content-type and response body
|
103 | 377ae13e | Michael Hanselmann |
|
104 | 377ae13e | Michael Hanselmann | """
|
105 | 377ae13e | Michael Hanselmann | return (http.HTTP_APP_JSON, serializer.DumpJson(values))
|
106 | 377ae13e | Michael Hanselmann | |
107 | 7e9760c3 | Michael Hanselmann | def _GetRequestContext(self, req): |
108 | 7e9760c3 | Michael Hanselmann | """Returns the context for a request.
|
109 | 7e9760c3 | Michael Hanselmann |
|
110 | 7e9760c3 | Michael Hanselmann | The context is cached in the req.private variable.
|
111 | 7e9760c3 | Michael Hanselmann |
|
112 | 7e9760c3 | Michael Hanselmann | """
|
113 | 7e9760c3 | Michael Hanselmann | if req.private is None: |
114 | 85414b69 | Iustin Pop | (HandlerClass, items, args) = \ |
115 | 85414b69 | Iustin Pop | self._resmap.getController(req.request_path)
|
116 | 7e9760c3 | Michael Hanselmann | |
117 | 7e9760c3 | Michael Hanselmann | ctx = RemoteApiRequestContext() |
118 | da04c447 | Michael Hanselmann | ctx.handler = HandlerClass(items, args, req, _client_cls=self._client_cls)
|
119 | 7e9760c3 | Michael Hanselmann | |
120 | 7e9760c3 | Michael Hanselmann | method = req.request_method.upper() |
121 | 7e9760c3 | Michael Hanselmann | try:
|
122 | 7e9760c3 | Michael Hanselmann | ctx.handler_fn = getattr(ctx.handler, method)
|
123 | f4ad2ef0 | Iustin Pop | except AttributeError: |
124 | 33664046 | René Nussbaumer | raise http.HttpNotImplemented("Method %s is unsupported for path %s" % |
125 | 33664046 | René Nussbaumer | (method, req.request_path)) |
126 | 7e9760c3 | Michael Hanselmann | |
127 | 5224c9bf | Michael Hanselmann | ctx.handler_access = baserlib.GetHandlerAccess(ctx.handler, method) |
128 | b5b67ef9 | Michael Hanselmann | |
129 | b5b67ef9 | Michael Hanselmann | # Require permissions definition (usually in the base class)
|
130 | b5b67ef9 | Michael Hanselmann | if ctx.handler_access is None: |
131 | b5b67ef9 | Michael Hanselmann | raise AssertionError("Permissions definition missing") |
132 | b5b67ef9 | Michael Hanselmann | |
133 | ab221ddf | Michael Hanselmann | # This is only made available in HandleRequest
|
134 | ab221ddf | Michael Hanselmann | ctx.body_data = None
|
135 | ab221ddf | Michael Hanselmann | |
136 | 7e9760c3 | Michael Hanselmann | req.private = ctx |
137 | 7e9760c3 | Michael Hanselmann | |
138 | 23ccba04 | Michael Hanselmann | # Check for expected attributes
|
139 | 23ccba04 | Michael Hanselmann | assert req.private.handler
|
140 | 23ccba04 | Michael Hanselmann | assert req.private.handler_fn
|
141 | 23ccba04 | Michael Hanselmann | assert req.private.handler_access is not None |
142 | 23ccba04 | Michael Hanselmann | |
143 | 7e9760c3 | Michael Hanselmann | return req.private
|
144 | 7e9760c3 | Michael Hanselmann | |
145 | 23ccba04 | Michael Hanselmann | def AuthenticationRequired(self, req): |
146 | 23ccba04 | Michael Hanselmann | """Determine whether authentication is required.
|
147 | 85414b69 | Iustin Pop |
|
148 | 85414b69 | Iustin Pop | """
|
149 | 27a8a190 | Michael Hanselmann | return self._reqauth or bool(self._GetRequestContext(req).handler_access) |
150 | 85414b69 | Iustin Pop | |
151 | b5b67ef9 | Michael Hanselmann | def Authenticate(self, req, username, password): |
152 | b5b67ef9 | Michael Hanselmann | """Checks whether a user can access a resource.
|
153 | b5b67ef9 | Michael Hanselmann |
|
154 | b5b67ef9 | Michael Hanselmann | """
|
155 | b5b67ef9 | Michael Hanselmann | ctx = self._GetRequestContext(req)
|
156 | b5b67ef9 | Michael Hanselmann | |
157 | abe318b3 | Michael Hanselmann | user = self._user_fn(username)
|
158 | abe318b3 | Michael Hanselmann | if not (user and |
159 | abe318b3 | Michael Hanselmann | self.VerifyBasicAuthPassword(req, username, password,
|
160 | abe318b3 | Michael Hanselmann | user.password)): |
161 | b5b67ef9 | Michael Hanselmann | # Unknown user or password wrong
|
162 | b5b67ef9 | Michael Hanselmann | return False |
163 | b5b67ef9 | Michael Hanselmann | |
164 | b5b67ef9 | Michael Hanselmann | if (not ctx.handler_access or |
165 | b5b67ef9 | Michael Hanselmann | set(user.options).intersection(ctx.handler_access)):
|
166 | b5b67ef9 | Michael Hanselmann | # Allow access
|
167 | b5b67ef9 | Michael Hanselmann | return True |
168 | b5b67ef9 | Michael Hanselmann | |
169 | b5b67ef9 | Michael Hanselmann | # Access forbidden
|
170 | b5b67ef9 | Michael Hanselmann | raise http.HttpForbidden()
|
171 | b5b67ef9 | Michael Hanselmann | |
172 | 16a8967d | Michael Hanselmann | def HandleRequest(self, req): |
173 | 16a8967d | Michael Hanselmann | """Handles a request.
|
174 | 3cd62121 | Michael Hanselmann |
|
175 | 3cd62121 | Michael Hanselmann | """
|
176 | 7e9760c3 | Michael Hanselmann | ctx = self._GetRequestContext(req)
|
177 | 3cd62121 | Michael Hanselmann | |
178 | ab221ddf | Michael Hanselmann | # Deserialize request parameters
|
179 | ab221ddf | Michael Hanselmann | if req.request_body:
|
180 | ab221ddf | Michael Hanselmann | # RFC2616, 7.2.1: Any HTTP/1.1 message containing an entity-body SHOULD
|
181 | ab221ddf | Michael Hanselmann | # include a Content-Type header field defining the media type of that
|
182 | ab221ddf | Michael Hanselmann | # body. [...] If the media type remains unknown, the recipient SHOULD
|
183 | ab221ddf | Michael Hanselmann | # treat it as type "application/octet-stream".
|
184 | ab221ddf | Michael Hanselmann | req_content_type = req.request_headers.get(http.HTTP_CONTENT_TYPE, |
185 | ab221ddf | Michael Hanselmann | http.HTTP_APP_OCTET_STREAM) |
186 | 16b037a9 | Michael Hanselmann | if req_content_type.lower() != http.HTTP_APP_JSON.lower():
|
187 | ab221ddf | Michael Hanselmann | raise http.HttpUnsupportedMediaType()
|
188 | ab221ddf | Michael Hanselmann | |
189 | ab221ddf | Michael Hanselmann | try:
|
190 | ab221ddf | Michael Hanselmann | ctx.body_data = serializer.LoadJson(req.request_body) |
191 | ab221ddf | Michael Hanselmann | except Exception: |
192 | ab221ddf | Michael Hanselmann | raise http.HttpBadRequest(message="Unable to parse JSON data") |
193 | ab221ddf | Michael Hanselmann | else:
|
194 | ab221ddf | Michael Hanselmann | ctx.body_data = None
|
195 | ab221ddf | Michael Hanselmann | |
196 | 3cd62121 | Michael Hanselmann | try:
|
197 | 7e9760c3 | Michael Hanselmann | result = ctx.handler_fn() |
198 | 77e1d753 | Iustin Pop | except luxi.TimeoutError:
|
199 | 77e1d753 | Iustin Pop | raise http.HttpGatewayTimeout()
|
200 | 77e1d753 | Iustin Pop | except luxi.ProtocolError, err:
|
201 | 77e1d753 | Iustin Pop | raise http.HttpBadGateway(str(err)) |
202 | 3cd62121 | Michael Hanselmann | |
203 | 16b037a9 | Michael Hanselmann | req.resp_headers[http.HTTP_CONTENT_TYPE] = http.HTTP_APP_JSON |
204 | ab221ddf | Michael Hanselmann | |
205 | ab221ddf | Michael Hanselmann | return serializer.DumpJson(result)
|
206 | 8c229cc7 | Oleksiy Mishchenko | |
207 | 8c229cc7 | Oleksiy Mishchenko | |
208 | abe318b3 | Michael Hanselmann | class RapiUsers: |
209 | abe318b3 | Michael Hanselmann | def __init__(self): |
210 | abe318b3 | Michael Hanselmann | """Initializes this class.
|
211 | abe318b3 | Michael Hanselmann |
|
212 | abe318b3 | Michael Hanselmann | """
|
213 | abe318b3 | Michael Hanselmann | self._users = None |
214 | abe318b3 | Michael Hanselmann | |
215 | abe318b3 | Michael Hanselmann | def Get(self, username): |
216 | abe318b3 | Michael Hanselmann | """Checks whether a user exists.
|
217 | abe318b3 | Michael Hanselmann |
|
218 | abe318b3 | Michael Hanselmann | """
|
219 | abe318b3 | Michael Hanselmann | if self._users: |
220 | abe318b3 | Michael Hanselmann | return self._users.get(username, None) |
221 | abe318b3 | Michael Hanselmann | else:
|
222 | abe318b3 | Michael Hanselmann | return None |
223 | abe318b3 | Michael Hanselmann | |
224 | abe318b3 | Michael Hanselmann | def Load(self, filename): |
225 | abe318b3 | Michael Hanselmann | """Loads a file containing users and passwords.
|
226 | abe318b3 | Michael Hanselmann |
|
227 | abe318b3 | Michael Hanselmann | @type filename: string
|
228 | abe318b3 | Michael Hanselmann | @param filename: Path to file
|
229 | abe318b3 | Michael Hanselmann |
|
230 | abe318b3 | Michael Hanselmann | """
|
231 | abe318b3 | Michael Hanselmann | logging.info("Reading users file at %s", filename)
|
232 | abe318b3 | Michael Hanselmann | try:
|
233 | abe318b3 | Michael Hanselmann | try:
|
234 | abe318b3 | Michael Hanselmann | contents = utils.ReadFile(filename) |
235 | abe318b3 | Michael Hanselmann | except EnvironmentError, err: |
236 | abe318b3 | Michael Hanselmann | self._users = None |
237 | abe318b3 | Michael Hanselmann | if err.errno == errno.ENOENT:
|
238 | abe318b3 | Michael Hanselmann | logging.warning("No users file at %s", filename)
|
239 | abe318b3 | Michael Hanselmann | else:
|
240 | abe318b3 | Michael Hanselmann | logging.warning("Error while reading %s: %s", filename, err)
|
241 | abe318b3 | Michael Hanselmann | return False |
242 | abe318b3 | Michael Hanselmann | |
243 | abe318b3 | Michael Hanselmann | users = http.auth.ParsePasswordFile(contents) |
244 | abe318b3 | Michael Hanselmann | |
245 | abe318b3 | Michael Hanselmann | except Exception, err: # pylint: disable=W0703 |
246 | abe318b3 | Michael Hanselmann | # We don't care about the type of exception
|
247 | abe318b3 | Michael Hanselmann | logging.error("Error while parsing %s: %s", filename, err)
|
248 | abe318b3 | Michael Hanselmann | return False |
249 | abe318b3 | Michael Hanselmann | |
250 | abe318b3 | Michael Hanselmann | self._users = users
|
251 | abe318b3 | Michael Hanselmann | |
252 | abe318b3 | Michael Hanselmann | return True |
253 | abe318b3 | Michael Hanselmann | |
254 | abe318b3 | Michael Hanselmann | |
255 | 073c31a5 | Michael Hanselmann | class FileEventHandler(asyncnotifier.FileEventHandlerBase): |
256 | 073c31a5 | Michael Hanselmann | def __init__(self, wm, path, cb): |
257 | e4ef4343 | Michael Hanselmann | """Initializes this class.
|
258 | e4ef4343 | Michael Hanselmann |
|
259 | 073c31a5 | Michael Hanselmann | @param wm: Inotify watch manager
|
260 | 073c31a5 | Michael Hanselmann | @type path: string
|
261 | 073c31a5 | Michael Hanselmann | @param path: File path
|
262 | e4ef4343 | Michael Hanselmann | @type cb: callable
|
263 | e4ef4343 | Michael Hanselmann | @param cb: Function called on file change
|
264 | e4ef4343 | Michael Hanselmann |
|
265 | e4ef4343 | Michael Hanselmann | """
|
266 | 073c31a5 | Michael Hanselmann | asyncnotifier.FileEventHandlerBase.__init__(self, wm)
|
267 | 073c31a5 | Michael Hanselmann | |
268 | e4ef4343 | Michael Hanselmann | self._cb = cb
|
269 | 073c31a5 | Michael Hanselmann | self._filename = os.path.basename(path)
|
270 | e4ef4343 | Michael Hanselmann | |
271 | ac96953d | Michael Hanselmann | # Different Pyinotify versions have the flag constants at different places,
|
272 | ac96953d | Michael Hanselmann | # hence not accessing them directly
|
273 | ac96953d | Michael Hanselmann | mask = (pyinotify.EventsCodes.ALL_FLAGS["IN_CLOSE_WRITE"] |
|
274 | ac96953d | Michael Hanselmann | pyinotify.EventsCodes.ALL_FLAGS["IN_DELETE"] |
|
275 | ac96953d | Michael Hanselmann | pyinotify.EventsCodes.ALL_FLAGS["IN_MOVED_FROM"] |
|
276 | ac96953d | Michael Hanselmann | pyinotify.EventsCodes.ALL_FLAGS["IN_MOVED_TO"])
|
277 | e4ef4343 | Michael Hanselmann | |
278 | 073c31a5 | Michael Hanselmann | self._handle = self.AddWatch(os.path.dirname(path), mask) |
279 | e4ef4343 | Michael Hanselmann | |
280 | 073c31a5 | Michael Hanselmann | def process_default(self, event): |
281 | 073c31a5 | Michael Hanselmann | """Called upon inotify event.
|
282 | e4ef4343 | Michael Hanselmann |
|
283 | e4ef4343 | Michael Hanselmann | """
|
284 | 073c31a5 | Michael Hanselmann | if event.name == self._filename: |
285 | 073c31a5 | Michael Hanselmann | logging.debug("Received inotify event %s", event)
|
286 | 073c31a5 | Michael Hanselmann | self._cb()
|
287 | 073c31a5 | Michael Hanselmann | |
288 | 073c31a5 | Michael Hanselmann | |
289 | 073c31a5 | Michael Hanselmann | def SetupFileWatcher(filename, cb): |
290 | 073c31a5 | Michael Hanselmann | """Configures an inotify watcher for a file.
|
291 | e4ef4343 | Michael Hanselmann |
|
292 | 073c31a5 | Michael Hanselmann | @type filename: string
|
293 | 073c31a5 | Michael Hanselmann | @param filename: File to watch
|
294 | 073c31a5 | Michael Hanselmann | @type cb: callable
|
295 | 073c31a5 | Michael Hanselmann | @param cb: Function called on file change
|
296 | e4ef4343 | Michael Hanselmann |
|
297 | 073c31a5 | Michael Hanselmann | """
|
298 | 073c31a5 | Michael Hanselmann | wm = pyinotify.WatchManager() |
299 | 073c31a5 | Michael Hanselmann | handler = FileEventHandler(wm, filename, cb) |
300 | 073c31a5 | Michael Hanselmann | asyncnotifier.AsyncNotifier(wm, default_proc_fun=handler) |
301 | e4ef4343 | Michael Hanselmann | |
302 | e4ef4343 | Michael Hanselmann | |
303 | 6c948699 | Michael Hanselmann | def CheckRapi(options, args): |
304 | 6c948699 | Michael Hanselmann | """Initial checks whether to run or exit with a failure.
|
305 | 8c229cc7 | Oleksiy Mishchenko |
|
306 | 8c229cc7 | Oleksiy Mishchenko | """
|
307 | f93427cd | Iustin Pop | if args: # rapi doesn't take any arguments |
308 | f93427cd | Iustin Pop | print >> sys.stderr, ("Usage: %s [-f] [-d] [-p port] [-b ADDRESS]" % |
309 | f93427cd | Iustin Pop | sys.argv[0])
|
310 | be73fc79 | Guido Trotter | sys.exit(constants.EXIT_FAILURE) |
311 | 8c229cc7 | Oleksiy Mishchenko | |
312 | 04ccf5e9 | Guido Trotter | ssconf.CheckMaster(options.debug) |
313 | 8c229cc7 | Oleksiy Mishchenko | |
314 | 8b72b05c | René Nussbaumer | # Read SSL certificate (this is a little hackish to read the cert as root)
|
315 | 8b72b05c | René Nussbaumer | if options.ssl:
|
316 | 8b72b05c | René Nussbaumer | options.ssl_params = http.HttpSslParams(ssl_key_path=options.ssl_key, |
317 | 8b72b05c | René Nussbaumer | ssl_cert_path=options.ssl_cert) |
318 | 8b72b05c | René Nussbaumer | else:
|
319 | 8b72b05c | René Nussbaumer | options.ssl_params = None
|
320 | 8b72b05c | René Nussbaumer | |
321 | 8c229cc7 | Oleksiy Mishchenko | |
322 | 3ee53f1f | Iustin Pop | def PrepRapi(options, _): |
323 | 3ee53f1f | Iustin Pop | """Prep remote API function, executed with the PID file held.
|
324 | 8c229cc7 | Oleksiy Mishchenko |
|
325 | 8c229cc7 | Oleksiy Mishchenko | """
|
326 | 04ccf5e9 | Guido Trotter | mainloop = daemon.Mainloop() |
327 | abe318b3 | Michael Hanselmann | |
328 | abe318b3 | Michael Hanselmann | users = RapiUsers() |
329 | abe318b3 | Michael Hanselmann | |
330 | 27a8a190 | Michael Hanselmann | handler = RemoteApiHandler(users.Get, options.reqauth) |
331 | e4ef4343 | Michael Hanselmann | |
332 | 073c31a5 | Michael Hanselmann | # Setup file watcher (it'll be driven by asyncore)
|
333 | a5ce2ea2 | Michael Hanselmann | SetupFileWatcher(pathutils.RAPI_USERS_FILE, |
334 | a5ce2ea2 | Michael Hanselmann | compat.partial(users.Load, pathutils.RAPI_USERS_FILE)) |
335 | e4ef4343 | Michael Hanselmann | |
336 | a5ce2ea2 | Michael Hanselmann | users.Load(pathutils.RAPI_USERS_FILE) |
337 | e4ef4343 | Michael Hanselmann | |
338 | e0003509 | Michael Hanselmann | server = \ |
339 | e0003509 | Michael Hanselmann | http.server.HttpServer(mainloop, options.bind_address, options.port, |
340 | 5ae4945a | Iustin Pop | handler, |
341 | 5ae4945a | Iustin Pop | ssl_params=options.ssl_params, ssl_verify_peer=False)
|
342 | 04ccf5e9 | Guido Trotter | server.Start() |
343 | 073c31a5 | Michael Hanselmann | |
344 | 3ee53f1f | Iustin Pop | return (mainloop, server)
|
345 | 3ee53f1f | Iustin Pop | |
346 | 073c31a5 | Michael Hanselmann | |
347 | b459a848 | Andrea Spadaccini | def ExecRapi(options, args, prep_data): # pylint: disable=W0613 |
348 | 3ee53f1f | Iustin Pop | """Main remote API function, executed with the PID file held.
|
349 | 3ee53f1f | Iustin Pop |
|
350 | 3ee53f1f | Iustin Pop | """
|
351 | 3ee53f1f | Iustin Pop | (mainloop, server) = prep_data |
352 | 04ccf5e9 | Guido Trotter | try:
|
353 | 04ccf5e9 | Guido Trotter | mainloop.Run() |
354 | 04ccf5e9 | Guido Trotter | finally:
|
355 | 04ccf5e9 | Guido Trotter | server.Stop() |
356 | 5675cd1f | Iustin Pop | |
357 | 3cd62121 | Michael Hanselmann | |
358 | d9c82a4e | Michael Hanselmann | def Main(): |
359 | 04ccf5e9 | Guido Trotter | """Main function.
|
360 | 441e7cfd | Oleksiy Mishchenko |
|
361 | 04ccf5e9 | Guido Trotter | """
|
362 | 04ccf5e9 | Guido Trotter | parser = optparse.OptionParser(description="Ganeti Remote API",
|
363 | 802ed2aa | Klaus Aehlig | usage="%prog [-f] [-d] [-p port] [-b ADDRESS]\
|
364 | 802ed2aa | Klaus Aehlig | \ [-i INTERFACE]",
|
365 | 5ae4945a | Iustin Pop | version="%%prog (ganeti) %s" %
|
366 | 5ae4945a | Iustin Pop | constants.RELEASE_VERSION) |
367 | 27a8a190 | Michael Hanselmann | parser.add_option("--require-authentication", dest="reqauth", |
368 | 27a8a190 | Michael Hanselmann | default=False, action="store_true", |
369 | 27a8a190 | Michael Hanselmann | help=("Disable anonymous HTTP requests and require"
|
370 | 27a8a190 | Michael Hanselmann | " authentication"))
|
371 | 04ccf5e9 | Guido Trotter | |
372 | 3ee53f1f | Iustin Pop | daemon.GenericMain(constants.RAPI, parser, CheckRapi, PrepRapi, ExecRapi, |
373 | a5ce2ea2 | Michael Hanselmann | default_ssl_cert=pathutils.RAPI_CERT_FILE, |
374 | a5ce2ea2 | Michael Hanselmann | default_ssl_key=pathutils.RAPI_CERT_FILE) |