Cluster verify: make "instance runs in wrong node" node-driven
[ganeti-local] / lib / daemon.py
index eceeac4..6d6bd74 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007, 2008, 2010 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2010, 2011 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@ from ganeti import errors
 from ganeti import netutils
 from ganeti import ssconf
 from ganeti import runtime
+from ganeti import compat
 
 
 class SchedulerBreakout(Exception):
@@ -491,8 +492,8 @@ class Mainloop(object):
 def _VerifyDaemonUser(daemon_name):
   """Verifies the process uid matches the configured uid.
 
-  This method verifies that a daemon is started as the user it is intended to be
-  run
+  This method verifies that a daemon is started as the user it is
+  intended to be run
 
   @param daemon_name: The name of daemon to be started
   @return: A tuple with the first item indicating success or not,
@@ -512,6 +513,44 @@ def _VerifyDaemonUser(daemon_name):
           daemon_uids[daemon_name])
 
 
+def _BeautifyError(err):
+  """Try to format an error better.
+
+  Since we're dealing with daemon startup errors, in many cases this
+  will be due to socket error and such, so we try to format these cases better.
+
+  @param err: an exception object
+  @rtype: string
+  @return: the formatted error description
+
+  """
+  try:
+    if isinstance(err, socket.error):
+      return "Socket-related error: %s (errno=%s)" % (err.args[1], err.args[0])
+    elif isinstance(err, EnvironmentError):
+      if err.filename is None:
+        return "%s (errno=%s)" % (err.strerror, err.errno)
+      else:
+        return "%s (file %s) (errno=%s)" % (err.strerror, err.filename,
+                                            err.errno)
+    else:
+      return str(err)
+  except Exception: # pylint: disable-msg=W0703
+    logging.exception("Error while handling existing error %s", err)
+    return "%s" % str(err)
+
+
+def _HandleSigHup(reopen_cb, signum, frame): # pylint: disable-msg=W0613
+  """Handler for SIGHUP.
+
+  @param reopen_cb: Callback function for reopening log files
+
+  """
+  assert callable(reopen_cb)
+  logging.info("Reopening log files after receiving SIGHUP")
+  reopen_cb()
+
+
 def GenericMain(daemon_name, optionparser,
                 check_fn, prepare_fn, exec_fn,
                 multithreaded=False, console_logging=False,
@@ -593,7 +632,8 @@ def GenericMain(daemon_name, optionparser,
                             metavar="SSL_CERT_PATH")
 
   # Disable the use of fork(2) if the daemon uses threads
-  utils.no_fork = multithreaded
+  if multithreaded:
+    utils.DisableFork()
 
   options, args = optionparser.parse_args()
 
@@ -624,23 +664,38 @@ def GenericMain(daemon_name, optionparser,
 
   if options.fork:
     utils.CloseFDs()
-    utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
+    wpipe = utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
+  else:
+    wpipe = None
 
-  utils.WritePidFile(utils.DaemonPidFileName(daemon_name))
-  try:
-    utils.SetupLogging(logfile=constants.DAEMONS_LOGFILES[daemon_name],
+  log_reopen_fn = \
+    utils.SetupLogging(constants.DAEMONS_LOGFILES[daemon_name], daemon_name,
                        debug=options.debug,
                        stderr_logging=not options.fork,
                        multithreaded=multithreaded,
-                       program=daemon_name,
                        syslog=options.syslog,
                        console_logging=console_logging)
-    logging.info("%s daemon startup", daemon_name)
-    if callable(prepare_fn):
-      prep_results = prepare_fn(options, args)
-    else:
-      prep_results = None
+
+  # Reopen log file(s) on SIGHUP
+  signal.signal(signal.SIGHUP, compat.partial(_HandleSigHup, log_reopen_fn))
+
+  utils.WritePidFile(utils.DaemonPidFileName(daemon_name))
+  try:
+    try:
+      logging.info("%s daemon startup", daemon_name)
+      if callable(prepare_fn):
+        prep_results = prepare_fn(options, args)
+      else:
+        prep_results = None
+    except Exception, err:
+      utils.WriteErrorToFD(wpipe, _BeautifyError(err))
+      raise
+
+    if wpipe is not None:
+      # we're done with the preparation phase, we close the pipe to
+      # let the parent know it's safe to exit
+      os.close(wpipe)
 
     exec_fn(options, args, prep_results)
   finally:
-    utils.RemovePidFile(daemon_name)
+    utils.RemoveFile(utils.DaemonPidFileName(daemon_name))