Collapse SSL key checking/overriding for daemons
[ganeti-local] / daemons / ganeti-rapi
index 3ce84c0..3208b54 100755 (executable)
@@ -35,6 +35,8 @@ from ganeti import http
 from ganeti import daemon
 from ganeti import ssconf
 from ganeti import utils
+from ganeti import luxi
+from ganeti import serializer
 from ganeti.rapi import connector
 
 import ganeti.http.auth
@@ -51,6 +53,24 @@ class RemoteApiRequestContext(object):
     self.handler_access = None
 
 
+class JsonErrorRequestExecutor(http.server.HttpServerRequestExecutor):
+  """Custom Request Executor class that formats HTTP errors in JSON.
+
+  """
+  error_content_type = "application/json"
+
+  def _FormatErrorMessage(self, values):
+    """Formats the body of an error message.
+
+    @type values: dict
+    @param values: dictionary with keys code, message and explain.
+    @rtype: string
+    @return: the body of the message
+
+    """
+    return serializer.DumpJson(values, indent=True)
+
+
 class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
                           http.server.HttpServer):
   """REST Request Handler Class.
@@ -86,7 +106,8 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
       try:
         ctx.handler_fn = getattr(ctx.handler, method)
       except AttributeError, err:
-        raise http.HttpBadRequest()
+        raise http.HttpBadRequest("Method %s is unsupported for path %s" %
+                                  (method, req.request_path))
 
       ctx.handler_access = getattr(ctx.handler, "%s_ACCESS" % method, None)
 
@@ -144,6 +165,10 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
       sn = ctx.handler.getSerialNumber()
       if sn:
         req.response_headers[http.HTTP_ETAG] = str(sn)
+    except luxi.TimeoutError:
+      raise http.HttpGatewayTimeout()
+    except luxi.ProtocolError, err:
+      raise http.HttpBadGateway(str(err))
     except:
       method = req.request_method.upper()
       logging.exception("Error while handling the %s request", method)
@@ -152,90 +177,51 @@ class RemoteApiHttpServer(http.auth.HttpServerRequestAuthentication,
     return result
 
 
-def ParseOptions():
-  """Parse the command line options.
-
-  @return: (options, args) as from OptionParser.parse_args()
+def CheckRAPI(options, args):
+  """Initial checks whether to run or exit with a failure
 
   """
-  parser = optparse.OptionParser(description="Ganeti Remote API",
-                    usage="%prog [-d] [-p port]",
-                    version="%%prog (ganeti) %s" %
-                                 constants.RAPI_VERSION)
-  parser.add_option("-d", "--debug", dest="debug",
-                    help="Enable some debug messages",
-                    default=False, action="store_true")
-  parser.add_option("-p", "--port", dest="port",
-                    help="Port to run API (%s default)." %
-                                 constants.RAPI_PORT,
-                    default=constants.RAPI_PORT, type="int")
-  parser.add_option("--no-ssl", dest="ssl",
-                    help="Do not secure HTTP protocol with SSL",
-                    default=True, action="store_false")
-  parser.add_option("-K", "--ssl-key", dest="ssl_key",
-                    help="SSL key",
-                    default=constants.RAPI_CERT_FILE, type="string")
-  parser.add_option("-C", "--ssl-cert", dest="ssl_cert",
-                    help="SSL certificate",
-                    default=constants.RAPI_CERT_FILE, type="string")
-  parser.add_option("-f", "--foreground", dest="fork",
-                    help="Don't detach from the current terminal",
-                    default=True, action="store_false")
-
-  options, args = parser.parse_args()
-
   if len(args) != 0:
-    print >> sys.stderr, "Usage: %s [-d] [-p port]" % sys.argv[0]
-    sys.exit(1)
-
-  if options.ssl and not (options.ssl_cert and options.ssl_key):
-    print >> sys.stderr, ("For secure mode please provide "
-                         "--ssl-key and --ssl-cert arguments")
-    sys.exit(1)
+    print >> sys.stderr, "Usage: %s [-f] [-d] [-p port] [-b ADDRESS]" % \
+        sys.argv[0]
+    sys.exit(constants.EXIT_FAILURE)
 
-  return options, args
+  ssconf.CheckMaster(options.debug)
 
 
-def main():
-  """Main function.
+def ExecRAPI(options, args):
+  """Main RAPI function, executed with the pidfile held.
 
   """
-  options, args = ParseOptions()
-
-  if options.fork:
-    utils.CloseFDs()
-
+  # Read SSL certificate
   if options.ssl:
-    # Read SSL certificate
-    try:
-      ssl_params = http.HttpSslParams(ssl_key_path=options.ssl_key,
-                                      ssl_cert_path=options.ssl_cert)
-    except Exception, err:
-      sys.stderr.write("Can't load the SSL certificate/key: %s\n" % (err,))
-      sys.exit(1)
+    ssl_params = http.HttpSslParams(ssl_key_path=options.ssl_key,
+                                    ssl_cert_path=options.ssl_cert)
   else:
     ssl_params = None
 
-  ssconf.CheckMaster(options.debug)
+  mainloop = daemon.Mainloop()
+  server = RemoteApiHttpServer(mainloop, options.bind_address, options.port,
+                               ssl_params=ssl_params, ssl_verify_peer=False,
+                               request_executor_class=JsonErrorRequestExecutor)
+  server.Start()
+  try:
+    mainloop.Run()
+  finally:
+    server.Stop()
+
 
-  if options.fork:
-    utils.Daemonize(logfile=constants.LOG_RAPISERVER)
+def main():
+  """Main function.
 
-  utils.SetupLogging(constants.LOG_RAPISERVER, debug=options.debug,
-                     stderr_logging=not options.fork)
+  """
+  parser = optparse.OptionParser(description="Ganeti Remote API",
+                    usage="%prog [-f] [-d] [-p port] [-b ADDRESS]",
+                    version="%%prog (ganeti) %s" % constants.RAPI_VERSION)
 
-  utils.WritePidFile(constants.RAPI_PID)
-  try:
-    mainloop = daemon.Mainloop()
-    server = RemoteApiHttpServer(mainloop, "", options.port,
-                                 ssl_params=ssl_params, ssl_verify_peer=False)
-    server.Start()
-    try:
-      mainloop.Run()
-    finally:
-      server.Stop()
-  finally:
-    utils.RemovePidFile(constants.RAPI_PID)
+  dirs = [(val, constants.RUN_DIRS_MODE) for val in constants.SUB_RUN_DIRS]
+  dirs.append((constants.LOG_OS_DIR, 0750))
+  daemon.GenericMain(constants.RAPI, parser, dirs, CheckRAPI, ExecRAPI)
 
 
 if __name__ == '__main__':