Fix a misuse of exc_info in logging.info
[ganeti-local] / lib / utils.py
index e900352..23fd1eb 100644 (file)
@@ -74,13 +74,13 @@ class RunResult(object):
                "failed", "fail_reason", "cmd"]
 
 
-  def __init__(self, exit_code, signal, stdout, stderr, cmd):
+  def __init__(self, exit_code, signal_, stdout, stderr, cmd):
     self.cmd = cmd
     self.exit_code = exit_code
-    self.signal = signal
+    self.signal = signal_
     self.stdout = stdout
     self.stderr = stderr
-    self.failed = (signal is not None or exit_code != 0)
+    self.failed = (signal_ is not None or exit_code != 0)
 
     if self.signal is not None:
       self.fail_reason = "terminated by signal %s" % self.signal
@@ -168,12 +168,12 @@ def RunCmd(cmd):
   status = child.wait()
   if status >= 0:
     exitcode = status
-    signal = None
+    signal_ = None
   else:
     exitcode = None
-    signal = -status
+    signal_ = -status
 
-  return RunResult(exitcode, signal, out, err, strcmd)
+  return RunResult(exitcode, signal_, out, err, strcmd)
 
 
 def RemoveFile(filename):
@@ -268,9 +268,13 @@ def IsProcessAlive(pid):
 
   Returns: true or false, depending on if the pid exists or not
 
-  Remarks: zombie processes treated as not alive
+  Remarks: zombie processes treated as not alive, and giving a pid <=
+  0 makes the function to return False.
 
   """
+  if pid <= 0:
+    return False
+
   try:
     f = open("/proc/%d/status" % pid)
   except IOError, err:
@@ -290,29 +294,30 @@ def IsProcessAlive(pid):
   return alive
 
 
-def IsPidFileAlive(pidfile):
-  """Check whether the given pidfile points to a live process.
+def ReadPidFile(pidfile):
+  """Read the pid from a file.
 
-    @param pidfile: Path to a file containing the pid to be checked
-    @type  pidfile: string (filename)
+  @param pidfile: Path to a file containing the pid to be checked
+  @type  pidfile: string (filename)
+  @return: The process id, if the file exista and contains a valid PID,
+           otherwise 0
+  @rtype: int
 
   """
   try:
     pf = open(pidfile, 'r')
-  except EnvironmentError, open_err:
-    if open_err.errno == errno.ENOENT:
-      return False
-    else:
-      raise errors.GenericError("Cannot open file %s. Error: %s" %
-                                (pidfile, str(open_err)))
+  except EnvironmentError, err:
+    if err.errno != errno.ENOENT:
+      logging.exception("Can't read pid file?!")
+    return 0
 
   try:
     pid = int(pf.read())
-  except ValueError:
-    raise errors.GenericError("Invalid pid string in %s" %
-                              (pidfile,))
+  except ValueError, err:
+    logging.info("Can't parse pid file contents", exc_info=True)
+    return 0
 
-  return IsProcessAlive(pid)
+  return pid
 
 
 def MatchNameComponent(key, name_list):
@@ -1051,7 +1056,7 @@ def Daemonize(logfile, noclose_fds=None):
   return 0
 
 
-def _DaemonPidFileName(name):
+def DaemonPidFileName(name):
   """Compute a ganeti pid file absolute path, given the daemon name.
 
   """
@@ -1065,9 +1070,9 @@ def WritePidFile(name):
 
   """
   pid = os.getpid()
-  pidfilename = _DaemonPidFileName(name)
-  if IsPidFileAlive(pidfilename):
-    raise GenericError("%s contains a live process" % pidfilename)
+  pidfilename = DaemonPidFileName(name)
+  if IsProcessAlive(ReadPidFile(pidfilename)):
+    raise errors.GenericError("%s contains a live process" % pidfilename)
 
   WriteFile(pidfilename, data="%d\n" % pid)
 
@@ -1079,7 +1084,7 @@ def RemovePidFile(name):
 
   """
   pid = os.getpid()
-  pidfilename = _DaemonPidFileName(name)
+  pidfilename = DaemonPidFileName(name)
   # TODO: we could check here that the file contains our pid
   try:
     RemoveFile(pidfilename)
@@ -1087,6 +1092,35 @@ def RemovePidFile(name):
     pass
 
 
+def KillProcess(pid, signal_=signal.SIGTERM, timeout=30):
+  """Kill a process given by its pid.
+
+  @type pid: int
+  @param pid: The PID to terminate.
+  @type signal_: int
+  @param signal_: The signal to send, by default SIGTERM
+  @type timeout: int
+  @param timeout: The timeout after which, if the process is still alive,
+                  a SIGKILL will be sent. If not positive, no such checking
+                  will be done
+
+  """
+  if pid <= 0:
+    # kill with pid=0 == suicide
+    raise errors.ProgrammerError("Invalid pid given '%s'" % pid)
+
+  if not IsProcessAlive(pid):
+    return
+  os.kill(pid, signal_)
+  if timeout <= 0:
+    return
+  end = time.time() + timeout
+  while time.time() < end and IsProcessAlive(pid):
+    time.sleep(0.1)
+  if IsProcessAlive(pid):
+    os.kill(pid, signal.SIGKILL)
+
+
 def FindFile(name, search_path, test=os.path.exists):
   """Look for a filesystem object in a given path.