Statistics
| Branch: | Tag: | Revision:

root / lib / server / rapi.py @ ec5af888

History | View | Annotate | Download (10.2 kB)

1 69cf3abd Michael Hanselmann
#
2 8c229cc7 Oleksiy Mishchenko
#
3 8c229cc7 Oleksiy Mishchenko
4 5ae4945a Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012 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 3cd62121 Michael Hanselmann
53 b459a848 Andrea Spadaccini
import ganeti.http.auth   # pylint: disable=W0611
54 bc2929fc Michael Hanselmann
import ganeti.http.server
55 3cd62121 Michael Hanselmann
56 bc2929fc Michael Hanselmann
57 7e9760c3 Michael Hanselmann
class RemoteApiRequestContext(object):
58 7e9760c3 Michael Hanselmann
  """Data structure for Remote API requests.
59 7e9760c3 Michael Hanselmann

60 7e9760c3 Michael Hanselmann
  """
61 7e9760c3 Michael Hanselmann
  def __init__(self):
62 7e9760c3 Michael Hanselmann
    self.handler = None
63 7e9760c3 Michael Hanselmann
    self.handler_fn = None
64 b5b67ef9 Michael Hanselmann
    self.handler_access = None
65 ab221ddf Michael Hanselmann
    self.body_data = None
66 7e9760c3 Michael Hanselmann
67 7e9760c3 Michael Hanselmann
68 e0003509 Michael Hanselmann
class RemoteApiHandler(http.auth.HttpServerRequestAuthentication,
69 e0003509 Michael Hanselmann
                       http.server.HttpServerHandler):
70 3cd62121 Michael Hanselmann
  """REST Request Handler Class.
71 3cd62121 Michael Hanselmann

72 3cd62121 Michael Hanselmann
  """
73 b5b67ef9 Michael Hanselmann
  AUTH_REALM = "Ganeti Remote API"
74 b5b67ef9 Michael Hanselmann
75 abe318b3 Michael Hanselmann
  def __init__(self, user_fn, _client_cls=None):
76 abe318b3 Michael Hanselmann
    """Initializes this class.
77 abe318b3 Michael Hanselmann

78 abe318b3 Michael Hanselmann
    @type user_fn: callable
79 abe318b3 Michael Hanselmann
    @param user_fn: Function receiving username as string and returning
80 abe318b3 Michael Hanselmann
      L{http.auth.PasswordFileUser} or C{None} if user is not found
81 abe318b3 Michael Hanselmann

82 abe318b3 Michael Hanselmann
    """
83 b459a848 Andrea Spadaccini
    # pylint: disable=W0233
84 e4ef4343 Michael Hanselmann
    # it seems pylint doesn't see the second parent class there
85 e0003509 Michael Hanselmann
    http.server.HttpServerHandler.__init__(self)
86 b5b67ef9 Michael Hanselmann
    http.auth.HttpServerRequestAuthentication.__init__(self)
87 da04c447 Michael Hanselmann
    self._client_cls = _client_cls
88 3cd62121 Michael Hanselmann
    self._resmap = connector.Mapper()
89 abe318b3 Michael Hanselmann
    self._user_fn = user_fn
90 a2e60f14 René Nussbaumer
91 377ae13e Michael Hanselmann
  @staticmethod
92 377ae13e Michael Hanselmann
  def FormatErrorMessage(values):
93 377ae13e Michael Hanselmann
    """Formats the body of an error message.
94 377ae13e Michael Hanselmann

95 377ae13e Michael Hanselmann
    @type values: dict
96 377ae13e Michael Hanselmann
    @param values: dictionary with keys C{code}, C{message} and C{explain}.
97 377ae13e Michael Hanselmann
    @rtype: tuple; (string, string)
98 377ae13e Michael Hanselmann
    @return: Content-type and response body
99 377ae13e Michael Hanselmann

100 377ae13e Michael Hanselmann
    """
101 377ae13e Michael Hanselmann
    return (http.HTTP_APP_JSON, serializer.DumpJson(values))
102 377ae13e Michael Hanselmann
103 7e9760c3 Michael Hanselmann
  def _GetRequestContext(self, req):
104 7e9760c3 Michael Hanselmann
    """Returns the context for a request.
105 7e9760c3 Michael Hanselmann

106 7e9760c3 Michael Hanselmann
    The context is cached in the req.private variable.
107 7e9760c3 Michael Hanselmann

108 7e9760c3 Michael Hanselmann
    """
109 7e9760c3 Michael Hanselmann
    if req.private is None:
110 85414b69 Iustin Pop
      (HandlerClass, items, args) = \
111 85414b69 Iustin Pop
                     self._resmap.getController(req.request_path)
112 7e9760c3 Michael Hanselmann
113 7e9760c3 Michael Hanselmann
      ctx = RemoteApiRequestContext()
114 da04c447 Michael Hanselmann
      ctx.handler = HandlerClass(items, args, req, _client_cls=self._client_cls)
115 7e9760c3 Michael Hanselmann
116 7e9760c3 Michael Hanselmann
      method = req.request_method.upper()
117 7e9760c3 Michael Hanselmann
      try:
118 7e9760c3 Michael Hanselmann
        ctx.handler_fn = getattr(ctx.handler, method)
119 f4ad2ef0 Iustin Pop
      except AttributeError:
120 33664046 René Nussbaumer
        raise http.HttpNotImplemented("Method %s is unsupported for path %s" %
121 33664046 René Nussbaumer
                                      (method, req.request_path))
122 7e9760c3 Michael Hanselmann
123 b5b67ef9 Michael Hanselmann
      ctx.handler_access = getattr(ctx.handler, "%s_ACCESS" % method, None)
124 b5b67ef9 Michael Hanselmann
125 b5b67ef9 Michael Hanselmann
      # Require permissions definition (usually in the base class)
126 b5b67ef9 Michael Hanselmann
      if ctx.handler_access is None:
127 b5b67ef9 Michael Hanselmann
        raise AssertionError("Permissions definition missing")
128 b5b67ef9 Michael Hanselmann
129 ab221ddf Michael Hanselmann
      # This is only made available in HandleRequest
130 ab221ddf Michael Hanselmann
      ctx.body_data = None
131 ab221ddf Michael Hanselmann
132 7e9760c3 Michael Hanselmann
      req.private = ctx
133 7e9760c3 Michael Hanselmann
134 23ccba04 Michael Hanselmann
    # Check for expected attributes
135 23ccba04 Michael Hanselmann
    assert req.private.handler
136 23ccba04 Michael Hanselmann
    assert req.private.handler_fn
137 23ccba04 Michael Hanselmann
    assert req.private.handler_access is not None
138 23ccba04 Michael Hanselmann
139 7e9760c3 Michael Hanselmann
    return req.private
140 7e9760c3 Michael Hanselmann
141 23ccba04 Michael Hanselmann
  def AuthenticationRequired(self, req):
142 23ccba04 Michael Hanselmann
    """Determine whether authentication is required.
143 85414b69 Iustin Pop

144 85414b69 Iustin Pop
    """
145 23ccba04 Michael Hanselmann
    return bool(self._GetRequestContext(req).handler_access)
146 85414b69 Iustin Pop
147 b5b67ef9 Michael Hanselmann
  def Authenticate(self, req, username, password):
148 b5b67ef9 Michael Hanselmann
    """Checks whether a user can access a resource.
149 b5b67ef9 Michael Hanselmann

150 b5b67ef9 Michael Hanselmann
    """
151 b5b67ef9 Michael Hanselmann
    ctx = self._GetRequestContext(req)
152 b5b67ef9 Michael Hanselmann
153 abe318b3 Michael Hanselmann
    user = self._user_fn(username)
154 abe318b3 Michael Hanselmann
    if not (user and
155 abe318b3 Michael Hanselmann
            self.VerifyBasicAuthPassword(req, username, password,
156 abe318b3 Michael Hanselmann
                                         user.password)):
157 b5b67ef9 Michael Hanselmann
      # Unknown user or password wrong
158 b5b67ef9 Michael Hanselmann
      return False
159 b5b67ef9 Michael Hanselmann
160 b5b67ef9 Michael Hanselmann
    if (not ctx.handler_access or
161 b5b67ef9 Michael Hanselmann
        set(user.options).intersection(ctx.handler_access)):
162 b5b67ef9 Michael Hanselmann
      # Allow access
163 b5b67ef9 Michael Hanselmann
      return True
164 b5b67ef9 Michael Hanselmann
165 b5b67ef9 Michael Hanselmann
    # Access forbidden
166 b5b67ef9 Michael Hanselmann
    raise http.HttpForbidden()
167 b5b67ef9 Michael Hanselmann
168 16a8967d Michael Hanselmann
  def HandleRequest(self, req):
169 16a8967d Michael Hanselmann
    """Handles a request.
170 3cd62121 Michael Hanselmann

171 3cd62121 Michael Hanselmann
    """
172 7e9760c3 Michael Hanselmann
    ctx = self._GetRequestContext(req)
173 3cd62121 Michael Hanselmann
174 ab221ddf Michael Hanselmann
    # Deserialize request parameters
175 ab221ddf Michael Hanselmann
    if req.request_body:
176 ab221ddf Michael Hanselmann
      # RFC2616, 7.2.1: Any HTTP/1.1 message containing an entity-body SHOULD
177 ab221ddf Michael Hanselmann
      # include a Content-Type header field defining the media type of that
178 ab221ddf Michael Hanselmann
      # body. [...] If the media type remains unknown, the recipient SHOULD
179 ab221ddf Michael Hanselmann
      # treat it as type "application/octet-stream".
180 ab221ddf Michael Hanselmann
      req_content_type = req.request_headers.get(http.HTTP_CONTENT_TYPE,
181 ab221ddf Michael Hanselmann
                                                 http.HTTP_APP_OCTET_STREAM)
182 16b037a9 Michael Hanselmann
      if req_content_type.lower() != http.HTTP_APP_JSON.lower():
183 ab221ddf Michael Hanselmann
        raise http.HttpUnsupportedMediaType()
184 ab221ddf Michael Hanselmann
185 ab221ddf Michael Hanselmann
      try:
186 ab221ddf Michael Hanselmann
        ctx.body_data = serializer.LoadJson(req.request_body)
187 ab221ddf Michael Hanselmann
      except Exception:
188 ab221ddf Michael Hanselmann
        raise http.HttpBadRequest(message="Unable to parse JSON data")
189 ab221ddf Michael Hanselmann
    else:
190 ab221ddf Michael Hanselmann
      ctx.body_data = None
191 ab221ddf Michael Hanselmann
192 3cd62121 Michael Hanselmann
    try:
193 7e9760c3 Michael Hanselmann
      result = ctx.handler_fn()
194 77e1d753 Iustin Pop
    except luxi.TimeoutError:
195 77e1d753 Iustin Pop
      raise http.HttpGatewayTimeout()
196 77e1d753 Iustin Pop
    except luxi.ProtocolError, err:
197 77e1d753 Iustin Pop
      raise http.HttpBadGateway(str(err))
198 3cd62121 Michael Hanselmann
199 16b037a9 Michael Hanselmann
    req.resp_headers[http.HTTP_CONTENT_TYPE] = http.HTTP_APP_JSON
200 ab221ddf Michael Hanselmann
201 ab221ddf Michael Hanselmann
    return serializer.DumpJson(result)
202 8c229cc7 Oleksiy Mishchenko
203 8c229cc7 Oleksiy Mishchenko
204 abe318b3 Michael Hanselmann
class RapiUsers:
205 abe318b3 Michael Hanselmann
  def __init__(self):
206 abe318b3 Michael Hanselmann
    """Initializes this class.
207 abe318b3 Michael Hanselmann

208 abe318b3 Michael Hanselmann
    """
209 abe318b3 Michael Hanselmann
    self._users = None
210 abe318b3 Michael Hanselmann
211 abe318b3 Michael Hanselmann
  def Get(self, username):
212 abe318b3 Michael Hanselmann
    """Checks whether a user exists.
213 abe318b3 Michael Hanselmann

214 abe318b3 Michael Hanselmann
    """
215 abe318b3 Michael Hanselmann
    if self._users:
216 abe318b3 Michael Hanselmann
      return self._users.get(username, None)
217 abe318b3 Michael Hanselmann
    else:
218 abe318b3 Michael Hanselmann
      return None
219 abe318b3 Michael Hanselmann
220 abe318b3 Michael Hanselmann
  def Load(self, filename):
221 abe318b3 Michael Hanselmann
    """Loads a file containing users and passwords.
222 abe318b3 Michael Hanselmann

223 abe318b3 Michael Hanselmann
    @type filename: string
224 abe318b3 Michael Hanselmann
    @param filename: Path to file
225 abe318b3 Michael Hanselmann

226 abe318b3 Michael Hanselmann
    """
227 abe318b3 Michael Hanselmann
    logging.info("Reading users file at %s", filename)
228 abe318b3 Michael Hanselmann
    try:
229 abe318b3 Michael Hanselmann
      try:
230 abe318b3 Michael Hanselmann
        contents = utils.ReadFile(filename)
231 abe318b3 Michael Hanselmann
      except EnvironmentError, err:
232 abe318b3 Michael Hanselmann
        self._users = None
233 abe318b3 Michael Hanselmann
        if err.errno == errno.ENOENT:
234 abe318b3 Michael Hanselmann
          logging.warning("No users file at %s", filename)
235 abe318b3 Michael Hanselmann
        else:
236 abe318b3 Michael Hanselmann
          logging.warning("Error while reading %s: %s", filename, err)
237 abe318b3 Michael Hanselmann
        return False
238 abe318b3 Michael Hanselmann
239 abe318b3 Michael Hanselmann
      users = http.auth.ParsePasswordFile(contents)
240 abe318b3 Michael Hanselmann
241 abe318b3 Michael Hanselmann
    except Exception, err: # pylint: disable=W0703
242 abe318b3 Michael Hanselmann
      # We don't care about the type of exception
243 abe318b3 Michael Hanselmann
      logging.error("Error while parsing %s: %s", filename, err)
244 abe318b3 Michael Hanselmann
      return False
245 abe318b3 Michael Hanselmann
246 abe318b3 Michael Hanselmann
    self._users = users
247 abe318b3 Michael Hanselmann
248 abe318b3 Michael Hanselmann
    return True
249 abe318b3 Michael Hanselmann
250 abe318b3 Michael Hanselmann
251 073c31a5 Michael Hanselmann
class FileEventHandler(asyncnotifier.FileEventHandlerBase):
252 073c31a5 Michael Hanselmann
  def __init__(self, wm, path, cb):
253 e4ef4343 Michael Hanselmann
    """Initializes this class.
254 e4ef4343 Michael Hanselmann

255 073c31a5 Michael Hanselmann
    @param wm: Inotify watch manager
256 073c31a5 Michael Hanselmann
    @type path: string
257 073c31a5 Michael Hanselmann
    @param path: File path
258 e4ef4343 Michael Hanselmann
    @type cb: callable
259 e4ef4343 Michael Hanselmann
    @param cb: Function called on file change
260 e4ef4343 Michael Hanselmann

261 e4ef4343 Michael Hanselmann
    """
262 073c31a5 Michael Hanselmann
    asyncnotifier.FileEventHandlerBase.__init__(self, wm)
263 073c31a5 Michael Hanselmann
264 e4ef4343 Michael Hanselmann
    self._cb = cb
265 073c31a5 Michael Hanselmann
    self._filename = os.path.basename(path)
266 e4ef4343 Michael Hanselmann
267 ac96953d Michael Hanselmann
    # Different Pyinotify versions have the flag constants at different places,
268 ac96953d Michael Hanselmann
    # hence not accessing them directly
269 ac96953d Michael Hanselmann
    mask = (pyinotify.EventsCodes.ALL_FLAGS["IN_CLOSE_WRITE"] |
270 ac96953d Michael Hanselmann
            pyinotify.EventsCodes.ALL_FLAGS["IN_DELETE"] |
271 ac96953d Michael Hanselmann
            pyinotify.EventsCodes.ALL_FLAGS["IN_MOVED_FROM"] |
272 ac96953d Michael Hanselmann
            pyinotify.EventsCodes.ALL_FLAGS["IN_MOVED_TO"])
273 e4ef4343 Michael Hanselmann
274 073c31a5 Michael Hanselmann
    self._handle = self.AddWatch(os.path.dirname(path), mask)
275 e4ef4343 Michael Hanselmann
276 073c31a5 Michael Hanselmann
  def process_default(self, event):
277 073c31a5 Michael Hanselmann
    """Called upon inotify event.
278 e4ef4343 Michael Hanselmann

279 e4ef4343 Michael Hanselmann
    """
280 073c31a5 Michael Hanselmann
    if event.name == self._filename:
281 073c31a5 Michael Hanselmann
      logging.debug("Received inotify event %s", event)
282 073c31a5 Michael Hanselmann
      self._cb()
283 073c31a5 Michael Hanselmann
284 073c31a5 Michael Hanselmann
285 073c31a5 Michael Hanselmann
def SetupFileWatcher(filename, cb):
286 073c31a5 Michael Hanselmann
  """Configures an inotify watcher for a file.
287 e4ef4343 Michael Hanselmann

288 073c31a5 Michael Hanselmann
  @type filename: string
289 073c31a5 Michael Hanselmann
  @param filename: File to watch
290 073c31a5 Michael Hanselmann
  @type cb: callable
291 073c31a5 Michael Hanselmann
  @param cb: Function called on file change
292 e4ef4343 Michael Hanselmann

293 073c31a5 Michael Hanselmann
  """
294 073c31a5 Michael Hanselmann
  wm = pyinotify.WatchManager()
295 073c31a5 Michael Hanselmann
  handler = FileEventHandler(wm, filename, cb)
296 073c31a5 Michael Hanselmann
  asyncnotifier.AsyncNotifier(wm, default_proc_fun=handler)
297 e4ef4343 Michael Hanselmann
298 e4ef4343 Michael Hanselmann
299 6c948699 Michael Hanselmann
def CheckRapi(options, args):
300 6c948699 Michael Hanselmann
  """Initial checks whether to run or exit with a failure.
301 8c229cc7 Oleksiy Mishchenko

302 8c229cc7 Oleksiy Mishchenko
  """
303 f93427cd Iustin Pop
  if args: # rapi doesn't take any arguments
304 f93427cd Iustin Pop
    print >> sys.stderr, ("Usage: %s [-f] [-d] [-p port] [-b ADDRESS]" %
305 f93427cd Iustin Pop
                          sys.argv[0])
306 be73fc79 Guido Trotter
    sys.exit(constants.EXIT_FAILURE)
307 8c229cc7 Oleksiy Mishchenko
308 04ccf5e9 Guido Trotter
  ssconf.CheckMaster(options.debug)
309 8c229cc7 Oleksiy Mishchenko
310 8b72b05c René Nussbaumer
  # Read SSL certificate (this is a little hackish to read the cert as root)
311 8b72b05c René Nussbaumer
  if options.ssl:
312 8b72b05c René Nussbaumer
    options.ssl_params = http.HttpSslParams(ssl_key_path=options.ssl_key,
313 8b72b05c René Nussbaumer
                                            ssl_cert_path=options.ssl_cert)
314 8b72b05c René Nussbaumer
  else:
315 8b72b05c René Nussbaumer
    options.ssl_params = None
316 8b72b05c René Nussbaumer
317 8c229cc7 Oleksiy Mishchenko
318 3ee53f1f Iustin Pop
def PrepRapi(options, _):
319 3ee53f1f Iustin Pop
  """Prep remote API function, executed with the PID file held.
320 8c229cc7 Oleksiy Mishchenko

321 8c229cc7 Oleksiy Mishchenko
  """
322 04ccf5e9 Guido Trotter
  mainloop = daemon.Mainloop()
323 abe318b3 Michael Hanselmann
324 abe318b3 Michael Hanselmann
  users = RapiUsers()
325 abe318b3 Michael Hanselmann
326 abe318b3 Michael Hanselmann
  handler = RemoteApiHandler(users.Get)
327 e4ef4343 Michael Hanselmann
328 073c31a5 Michael Hanselmann
  # Setup file watcher (it'll be driven by asyncore)
329 a5ce2ea2 Michael Hanselmann
  SetupFileWatcher(pathutils.RAPI_USERS_FILE,
330 a5ce2ea2 Michael Hanselmann
                   compat.partial(users.Load, pathutils.RAPI_USERS_FILE))
331 e4ef4343 Michael Hanselmann
332 a5ce2ea2 Michael Hanselmann
  users.Load(pathutils.RAPI_USERS_FILE)
333 e4ef4343 Michael Hanselmann
334 e0003509 Michael Hanselmann
  server = \
335 e0003509 Michael Hanselmann
    http.server.HttpServer(mainloop, options.bind_address, options.port,
336 5ae4945a Iustin Pop
                           handler,
337 5ae4945a Iustin Pop
                           ssl_params=options.ssl_params, ssl_verify_peer=False)
338 04ccf5e9 Guido Trotter
  server.Start()
339 073c31a5 Michael Hanselmann
340 3ee53f1f Iustin Pop
  return (mainloop, server)
341 3ee53f1f Iustin Pop
342 073c31a5 Michael Hanselmann
343 b459a848 Andrea Spadaccini
def ExecRapi(options, args, prep_data): # pylint: disable=W0613
344 3ee53f1f Iustin Pop
  """Main remote API function, executed with the PID file held.
345 3ee53f1f Iustin Pop

346 3ee53f1f Iustin Pop
  """
347 3ee53f1f Iustin Pop
  (mainloop, server) = prep_data
348 04ccf5e9 Guido Trotter
  try:
349 04ccf5e9 Guido Trotter
    mainloop.Run()
350 04ccf5e9 Guido Trotter
  finally:
351 04ccf5e9 Guido Trotter
    server.Stop()
352 5675cd1f Iustin Pop
353 3cd62121 Michael Hanselmann
354 d9c82a4e Michael Hanselmann
def Main():
355 04ccf5e9 Guido Trotter
  """Main function.
356 441e7cfd Oleksiy Mishchenko

357 04ccf5e9 Guido Trotter
  """
358 04ccf5e9 Guido Trotter
  parser = optparse.OptionParser(description="Ganeti Remote API",
359 5ae4945a Iustin Pop
                                 usage="%prog [-f] [-d] [-p port] [-b ADDRESS]",
360 5ae4945a Iustin Pop
                                 version="%%prog (ganeti) %s" %
361 5ae4945a Iustin Pop
                                 constants.RELEASE_VERSION)
362 04ccf5e9 Guido Trotter
363 3ee53f1f Iustin Pop
  daemon.GenericMain(constants.RAPI, parser, CheckRapi, PrepRapi, ExecRapi,
364 a5ce2ea2 Michael Hanselmann
                     default_ssl_cert=pathutils.RAPI_CERT_FILE,
365 a5ce2ea2 Michael Hanselmann
                     default_ssl_key=pathutils.RAPI_CERT_FILE)