Statistics
| Branch: | Tag: | Revision:

root / lib / server / rapi.py @ 6fe4baf0

History | View | Annotate | Download (10.1 kB)

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

59 7e9760c3 Michael Hanselmann
  """
60 7e9760c3 Michael Hanselmann
  def __init__(self):
61 7e9760c3 Michael Hanselmann
    self.handler = None
62 7e9760c3 Michael Hanselmann
    self.handler_fn = None
63 b5b67ef9 Michael Hanselmann
    self.handler_access = None
64 ab221ddf Michael Hanselmann
    self.body_data = None
65 7e9760c3 Michael Hanselmann
66 7e9760c3 Michael Hanselmann
67 1f8588f6 Iustin Pop
class JsonErrorRequestExecutor(http.server.HttpServerRequestExecutor):
68 1f8588f6 Iustin Pop
  """Custom Request Executor class that formats HTTP errors in JSON.
69 1f8588f6 Iustin Pop

70 1f8588f6 Iustin Pop
  """
71 16b037a9 Michael Hanselmann
  error_content_type = http.HTTP_APP_JSON
72 1f8588f6 Iustin Pop
73 1f8588f6 Iustin Pop
  def _FormatErrorMessage(self, values):
74 1f8588f6 Iustin Pop
    """Formats the body of an error message.
75 1f8588f6 Iustin Pop

76 1f8588f6 Iustin Pop
    @type values: dict
77 1f8588f6 Iustin Pop
    @param values: dictionary with keys code, message and explain.
78 1f8588f6 Iustin Pop
    @rtype: string
79 1f8588f6 Iustin Pop
    @return: the body of the message
80 1f8588f6 Iustin Pop

81 1f8588f6 Iustin Pop
    """
82 1f8588f6 Iustin Pop
    return serializer.DumpJson(values, indent=True)
83 1f8588f6 Iustin Pop
84 1f8588f6 Iustin Pop
85 b5b67ef9 Michael Hanselmann
class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
86 b5b67ef9 Michael Hanselmann
                          http.server.HttpServer):
87 3cd62121 Michael Hanselmann
  """REST Request Handler Class.
88 3cd62121 Michael Hanselmann

89 3cd62121 Michael Hanselmann
  """
90 b5b67ef9 Michael Hanselmann
  AUTH_REALM = "Ganeti Remote API"
91 b5b67ef9 Michael Hanselmann
92 16a8967d Michael Hanselmann
  def __init__(self, *args, **kwargs):
93 b459a848 Andrea Spadaccini
    # pylint: disable=W0233
94 e4ef4343 Michael Hanselmann
    # it seems pylint doesn't see the second parent class there
95 bc2929fc Michael Hanselmann
    http.server.HttpServer.__init__(self, *args, **kwargs)
96 b5b67ef9 Michael Hanselmann
    http.auth.HttpServerRequestAuthentication.__init__(self)
97 3cd62121 Michael Hanselmann
    self._resmap = connector.Mapper()
98 e4ef4343 Michael Hanselmann
    self._users = None
99 3cd62121 Michael Hanselmann
100 e4ef4343 Michael Hanselmann
  def LoadUsers(self, filename):
101 e4ef4343 Michael Hanselmann
    """Loads a file containing users and passwords.
102 a2e60f14 René Nussbaumer

103 e4ef4343 Michael Hanselmann
    @type filename: string
104 e4ef4343 Michael Hanselmann
    @param filename: Path to file
105 a2e60f14 René Nussbaumer

106 a2e60f14 René Nussbaumer
    """
107 073c31a5 Michael Hanselmann
    logging.info("Reading users file at %s", filename)
108 2287b920 Michael Hanselmann
    try:
109 073c31a5 Michael Hanselmann
      try:
110 073c31a5 Michael Hanselmann
        contents = utils.ReadFile(filename)
111 073c31a5 Michael Hanselmann
      except EnvironmentError, err:
112 073c31a5 Michael Hanselmann
        self._users = None
113 073c31a5 Michael Hanselmann
        if err.errno == errno.ENOENT:
114 073c31a5 Michael Hanselmann
          logging.warning("No users file at %s", filename)
115 073c31a5 Michael Hanselmann
        else:
116 073c31a5 Michael Hanselmann
          logging.warning("Error while reading %s: %s", filename, err)
117 073c31a5 Michael Hanselmann
        return False
118 a2e60f14 René Nussbaumer
119 2287b920 Michael Hanselmann
      users = http.auth.ParsePasswordFile(contents)
120 073c31a5 Michael Hanselmann
121 b459a848 Andrea Spadaccini
    except Exception, err: # pylint: disable=W0703
122 a2e60f14 René Nussbaumer
      # We don't care about the type of exception
123 2287b920 Michael Hanselmann
      logging.error("Error while parsing %s: %s", filename, err)
124 e4ef4343 Michael Hanselmann
      return False
125 a2e60f14 René Nussbaumer
126 e4ef4343 Michael Hanselmann
    self._users = users
127 073c31a5 Michael Hanselmann
128 e4ef4343 Michael Hanselmann
    return True
129 a2e60f14 René Nussbaumer
130 7e9760c3 Michael Hanselmann
  def _GetRequestContext(self, req):
131 7e9760c3 Michael Hanselmann
    """Returns the context for a request.
132 7e9760c3 Michael Hanselmann

133 7e9760c3 Michael Hanselmann
    The context is cached in the req.private variable.
134 7e9760c3 Michael Hanselmann

135 7e9760c3 Michael Hanselmann
    """
136 7e9760c3 Michael Hanselmann
    if req.private is None:
137 85414b69 Iustin Pop
      (HandlerClass, items, args) = \
138 85414b69 Iustin Pop
                     self._resmap.getController(req.request_path)
139 7e9760c3 Michael Hanselmann
140 7e9760c3 Michael Hanselmann
      ctx = RemoteApiRequestContext()
141 7e9760c3 Michael Hanselmann
      ctx.handler = HandlerClass(items, args, req)
142 7e9760c3 Michael Hanselmann
143 7e9760c3 Michael Hanselmann
      method = req.request_method.upper()
144 7e9760c3 Michael Hanselmann
      try:
145 7e9760c3 Michael Hanselmann
        ctx.handler_fn = getattr(ctx.handler, method)
146 f4ad2ef0 Iustin Pop
      except AttributeError:
147 33664046 René Nussbaumer
        raise http.HttpNotImplemented("Method %s is unsupported for path %s" %
148 33664046 René Nussbaumer
                                      (method, req.request_path))
149 7e9760c3 Michael Hanselmann
150 b5b67ef9 Michael Hanselmann
      ctx.handler_access = getattr(ctx.handler, "%s_ACCESS" % method, None)
151 b5b67ef9 Michael Hanselmann
152 b5b67ef9 Michael Hanselmann
      # Require permissions definition (usually in the base class)
153 b5b67ef9 Michael Hanselmann
      if ctx.handler_access is None:
154 b5b67ef9 Michael Hanselmann
        raise AssertionError("Permissions definition missing")
155 b5b67ef9 Michael Hanselmann
156 ab221ddf Michael Hanselmann
      # This is only made available in HandleRequest
157 ab221ddf Michael Hanselmann
      ctx.body_data = None
158 ab221ddf Michael Hanselmann
159 7e9760c3 Michael Hanselmann
      req.private = ctx
160 7e9760c3 Michael Hanselmann
161 23ccba04 Michael Hanselmann
    # Check for expected attributes
162 23ccba04 Michael Hanselmann
    assert req.private.handler
163 23ccba04 Michael Hanselmann
    assert req.private.handler_fn
164 23ccba04 Michael Hanselmann
    assert req.private.handler_access is not None
165 23ccba04 Michael Hanselmann
166 7e9760c3 Michael Hanselmann
    return req.private
167 7e9760c3 Michael Hanselmann
168 23ccba04 Michael Hanselmann
  def AuthenticationRequired(self, req):
169 23ccba04 Michael Hanselmann
    """Determine whether authentication is required.
170 85414b69 Iustin Pop

171 85414b69 Iustin Pop
    """
172 23ccba04 Michael Hanselmann
    return bool(self._GetRequestContext(req).handler_access)
173 85414b69 Iustin Pop
174 b5b67ef9 Michael Hanselmann
  def Authenticate(self, req, username, password):
175 b5b67ef9 Michael Hanselmann
    """Checks whether a user can access a resource.
176 b5b67ef9 Michael Hanselmann

177 b5b67ef9 Michael Hanselmann
    """
178 b5b67ef9 Michael Hanselmann
    ctx = self._GetRequestContext(req)
179 b5b67ef9 Michael Hanselmann
180 b5b67ef9 Michael Hanselmann
    # Check username and password
181 b5b67ef9 Michael Hanselmann
    valid_user = False
182 b5b67ef9 Michael Hanselmann
    if self._users:
183 b5b67ef9 Michael Hanselmann
      user = self._users.get(username, None)
184 0b08f096 Michael Hanselmann
      if user and self.VerifyBasicAuthPassword(req, username, password,
185 0b08f096 Michael Hanselmann
                                               user.password):
186 b5b67ef9 Michael Hanselmann
        valid_user = True
187 b5b67ef9 Michael Hanselmann
188 b5b67ef9 Michael Hanselmann
    if not valid_user:
189 b5b67ef9 Michael Hanselmann
      # Unknown user or password wrong
190 b5b67ef9 Michael Hanselmann
      return False
191 b5b67ef9 Michael Hanselmann
192 b5b67ef9 Michael Hanselmann
    if (not ctx.handler_access or
193 b5b67ef9 Michael Hanselmann
        set(user.options).intersection(ctx.handler_access)):
194 b5b67ef9 Michael Hanselmann
      # Allow access
195 b5b67ef9 Michael Hanselmann
      return True
196 b5b67ef9 Michael Hanselmann
197 b5b67ef9 Michael Hanselmann
    # Access forbidden
198 b5b67ef9 Michael Hanselmann
    raise http.HttpForbidden()
199 b5b67ef9 Michael Hanselmann
200 16a8967d Michael Hanselmann
  def HandleRequest(self, req):
201 16a8967d Michael Hanselmann
    """Handles a request.
202 3cd62121 Michael Hanselmann

203 3cd62121 Michael Hanselmann
    """
204 7e9760c3 Michael Hanselmann
    ctx = self._GetRequestContext(req)
205 3cd62121 Michael Hanselmann
206 ab221ddf Michael Hanselmann
    # Deserialize request parameters
207 ab221ddf Michael Hanselmann
    if req.request_body:
208 ab221ddf Michael Hanselmann
      # RFC2616, 7.2.1: Any HTTP/1.1 message containing an entity-body SHOULD
209 ab221ddf Michael Hanselmann
      # include a Content-Type header field defining the media type of that
210 ab221ddf Michael Hanselmann
      # body. [...] If the media type remains unknown, the recipient SHOULD
211 ab221ddf Michael Hanselmann
      # treat it as type "application/octet-stream".
212 ab221ddf Michael Hanselmann
      req_content_type = req.request_headers.get(http.HTTP_CONTENT_TYPE,
213 ab221ddf Michael Hanselmann
                                                 http.HTTP_APP_OCTET_STREAM)
214 16b037a9 Michael Hanselmann
      if req_content_type.lower() != http.HTTP_APP_JSON.lower():
215 ab221ddf Michael Hanselmann
        raise http.HttpUnsupportedMediaType()
216 ab221ddf Michael Hanselmann
217 ab221ddf Michael Hanselmann
      try:
218 ab221ddf Michael Hanselmann
        ctx.body_data = serializer.LoadJson(req.request_body)
219 ab221ddf Michael Hanselmann
      except Exception:
220 ab221ddf Michael Hanselmann
        raise http.HttpBadRequest(message="Unable to parse JSON data")
221 ab221ddf Michael Hanselmann
    else:
222 ab221ddf Michael Hanselmann
      ctx.body_data = None
223 ab221ddf Michael Hanselmann
224 3cd62121 Michael Hanselmann
    try:
225 7e9760c3 Michael Hanselmann
      result = ctx.handler_fn()
226 77e1d753 Iustin Pop
    except luxi.TimeoutError:
227 77e1d753 Iustin Pop
      raise http.HttpGatewayTimeout()
228 77e1d753 Iustin Pop
    except luxi.ProtocolError, err:
229 77e1d753 Iustin Pop
      raise http.HttpBadGateway(str(err))
230 16a8967d Michael Hanselmann
    except:
231 e09fdcfa Iustin Pop
      method = req.request_method.upper()
232 16a8967d Michael Hanselmann
      logging.exception("Error while handling the %s request", method)
233 16a8967d Michael Hanselmann
      raise
234 3cd62121 Michael Hanselmann
235 16b037a9 Michael Hanselmann
    req.resp_headers[http.HTTP_CONTENT_TYPE] = http.HTTP_APP_JSON
236 ab221ddf Michael Hanselmann
237 ab221ddf Michael Hanselmann
    return serializer.DumpJson(result)
238 8c229cc7 Oleksiy Mishchenko
239 8c229cc7 Oleksiy Mishchenko
240 073c31a5 Michael Hanselmann
class FileEventHandler(asyncnotifier.FileEventHandlerBase):
241 073c31a5 Michael Hanselmann
  def __init__(self, wm, path, cb):
242 e4ef4343 Michael Hanselmann
    """Initializes this class.
243 e4ef4343 Michael Hanselmann

244 073c31a5 Michael Hanselmann
    @param wm: Inotify watch manager
245 073c31a5 Michael Hanselmann
    @type path: string
246 073c31a5 Michael Hanselmann
    @param path: File path
247 e4ef4343 Michael Hanselmann
    @type cb: callable
248 e4ef4343 Michael Hanselmann
    @param cb: Function called on file change
249 e4ef4343 Michael Hanselmann

250 e4ef4343 Michael Hanselmann
    """
251 073c31a5 Michael Hanselmann
    asyncnotifier.FileEventHandlerBase.__init__(self, wm)
252 073c31a5 Michael Hanselmann
253 e4ef4343 Michael Hanselmann
    self._cb = cb
254 073c31a5 Michael Hanselmann
    self._filename = os.path.basename(path)
255 e4ef4343 Michael Hanselmann
256 ac96953d Michael Hanselmann
    # Different Pyinotify versions have the flag constants at different places,
257 ac96953d Michael Hanselmann
    # hence not accessing them directly
258 ac96953d Michael Hanselmann
    mask = (pyinotify.EventsCodes.ALL_FLAGS["IN_CLOSE_WRITE"] |
259 ac96953d Michael Hanselmann
            pyinotify.EventsCodes.ALL_FLAGS["IN_DELETE"] |
260 ac96953d Michael Hanselmann
            pyinotify.EventsCodes.ALL_FLAGS["IN_MOVED_FROM"] |
261 ac96953d Michael Hanselmann
            pyinotify.EventsCodes.ALL_FLAGS["IN_MOVED_TO"])
262 e4ef4343 Michael Hanselmann
263 073c31a5 Michael Hanselmann
    self._handle = self.AddWatch(os.path.dirname(path), mask)
264 e4ef4343 Michael Hanselmann
265 073c31a5 Michael Hanselmann
  def process_default(self, event):
266 073c31a5 Michael Hanselmann
    """Called upon inotify event.
267 e4ef4343 Michael Hanselmann

268 e4ef4343 Michael Hanselmann
    """
269 073c31a5 Michael Hanselmann
    if event.name == self._filename:
270 073c31a5 Michael Hanselmann
      logging.debug("Received inotify event %s", event)
271 073c31a5 Michael Hanselmann
      self._cb()
272 073c31a5 Michael Hanselmann
273 073c31a5 Michael Hanselmann
274 073c31a5 Michael Hanselmann
def SetupFileWatcher(filename, cb):
275 073c31a5 Michael Hanselmann
  """Configures an inotify watcher for a file.
276 e4ef4343 Michael Hanselmann

277 073c31a5 Michael Hanselmann
  @type filename: string
278 073c31a5 Michael Hanselmann
  @param filename: File to watch
279 073c31a5 Michael Hanselmann
  @type cb: callable
280 073c31a5 Michael Hanselmann
  @param cb: Function called on file change
281 e4ef4343 Michael Hanselmann

282 073c31a5 Michael Hanselmann
  """
283 073c31a5 Michael Hanselmann
  wm = pyinotify.WatchManager()
284 073c31a5 Michael Hanselmann
  handler = FileEventHandler(wm, filename, cb)
285 073c31a5 Michael Hanselmann
  asyncnotifier.AsyncNotifier(wm, default_proc_fun=handler)
286 e4ef4343 Michael Hanselmann
287 e4ef4343 Michael Hanselmann
288 6c948699 Michael Hanselmann
def CheckRapi(options, args):
289 6c948699 Michael Hanselmann
  """Initial checks whether to run or exit with a failure.
290 8c229cc7 Oleksiy Mishchenko

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

310 8c229cc7 Oleksiy Mishchenko
  """
311 2ed6a7d6 Iustin Pop
312 04ccf5e9 Guido Trotter
  mainloop = daemon.Mainloop()
313 04ccf5e9 Guido Trotter
  server = RemoteApiHttpServer(mainloop, options.bind_address, options.port,
314 8b72b05c René Nussbaumer
                               ssl_params=options.ssl_params,
315 8b72b05c René Nussbaumer
                               ssl_verify_peer=False,
316 04ccf5e9 Guido Trotter
                               request_executor_class=JsonErrorRequestExecutor)
317 e4ef4343 Michael Hanselmann
318 073c31a5 Michael Hanselmann
  # Setup file watcher (it'll be driven by asyncore)
319 073c31a5 Michael Hanselmann
  SetupFileWatcher(constants.RAPI_USERS_FILE,
320 073c31a5 Michael Hanselmann
                   compat.partial(server.LoadUsers, constants.RAPI_USERS_FILE))
321 e4ef4343 Michael Hanselmann
322 e4ef4343 Michael Hanselmann
  server.LoadUsers(constants.RAPI_USERS_FILE)
323 e4ef4343 Michael Hanselmann
324 b459a848 Andrea Spadaccini
  # pylint: disable=E1101
325 71d23b33 Iustin Pop
  # it seems pylint doesn't see the second parent class there
326 04ccf5e9 Guido Trotter
  server.Start()
327 073c31a5 Michael Hanselmann
328 3ee53f1f Iustin Pop
  return (mainloop, server)
329 3ee53f1f Iustin Pop
330 073c31a5 Michael Hanselmann
331 b459a848 Andrea Spadaccini
def ExecRapi(options, args, prep_data): # pylint: disable=W0613
332 3ee53f1f Iustin Pop
  """Main remote API function, executed with the PID file held.
333 3ee53f1f Iustin Pop

334 3ee53f1f Iustin Pop
  """
335 3ee53f1f Iustin Pop
  (mainloop, server) = prep_data
336 04ccf5e9 Guido Trotter
  try:
337 04ccf5e9 Guido Trotter
    mainloop.Run()
338 04ccf5e9 Guido Trotter
  finally:
339 04ccf5e9 Guido Trotter
    server.Stop()
340 5675cd1f Iustin Pop
341 3cd62121 Michael Hanselmann
342 d9c82a4e Michael Hanselmann
def Main():
343 04ccf5e9 Guido Trotter
  """Main function.
344 441e7cfd Oleksiy Mishchenko

345 04ccf5e9 Guido Trotter
  """
346 04ccf5e9 Guido Trotter
  parser = optparse.OptionParser(description="Ganeti Remote API",
347 04ccf5e9 Guido Trotter
                    usage="%prog [-f] [-d] [-p port] [-b ADDRESS]",
348 9e47cad8 Iustin Pop
                    version="%%prog (ganeti) %s" % constants.RELEASE_VERSION)
349 04ccf5e9 Guido Trotter
350 3ee53f1f Iustin Pop
  daemon.GenericMain(constants.RAPI, parser, CheckRapi, PrepRapi, ExecRapi,
351 0648750e Michael Hanselmann
                     default_ssl_cert=constants.RAPI_CERT_FILE,
352 69d89cb5 René Nussbaumer
                     default_ssl_key=constants.RAPI_CERT_FILE)