Move function cleaning directory to module level
[ganeti-local] / lib / utils.py
index 143ca09..f263637 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):
@@ -314,7 +314,7 @@ def ReadPidFile(pidfile):
   try:
     pid = int(pf.read())
   except ValueError, err:
-    logging.info("Can't parse pid file contents", exc_info=err)
+    logging.info("Can't parse pid file contents", exc_info=True)
     return 0
 
   return pid
@@ -1092,13 +1092,13 @@ def RemovePidFile(name):
     pass
 
 
-def KillProcess(pid, signal=signal.SIGTERM, timeout=30):
+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 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
@@ -1111,7 +1111,7 @@ def KillProcess(pid, signal=signal.SIGTERM, timeout=30):
 
   if not IsProcessAlive(pid):
     return
-  os.kill(pid, signal)
+  os.kill(pid, signal_)
   if timeout <= 0:
     return
   end = time.time() + timeout
@@ -1192,6 +1192,62 @@ def LockFile(fd):
     raise
 
 
+class FileLock(object):
+  """Utility class for file locks.
+
+  """
+  def __init__(self, filename):
+    self.filename = filename
+    self.fd = open(self.filename, "w")
+
+  def __del__(self):
+    self.Close()
+
+  def Close(self):
+    if self.fd:
+      self.fd.close()
+      self.fd = None
+
+  def _flock(self, flag, blocking, errmsg):
+    assert self.fd, "Lock was closed"
+
+    if not blocking:
+      flag |= fcntl.LOCK_NB
+
+    try:
+      fcntl.flock(self.fd, flag)
+    except IOError, err:
+      logging.exception("fcntl.flock failed")
+      if err.errno in (errno.EAGAIN, ):
+        raise errors.LockError(errmsg)
+      raise
+
+  def Exclusive(self, blocking=False):
+    """Locks the file in exclusive mode.
+
+    """
+    self._flock(fcntl.LOCK_EX, blocking,
+                "Failed to lock %s in exclusive mode" % self.filename)
+
+  def Shared(self, blocking=False):
+    """Locks the file in shared mode.
+
+    """
+    self._flock(fcntl.LOCK_SH, blocking,
+                "Failed to lock %s in shared mode" % self.filename)
+
+  def Unlock(self, blocking=True):
+    """Unlocks the file.
+
+    According to "man flock", unlocking can also be a nonblocking operation:
+    "To make a non-blocking request, include LOCK_NB with any of the above
+    operations"
+
+    """
+    self._flock(fcntl.LOCK_UN, blocking,
+                "Failed to unlock %s" % self.filename)
+
+
 class SignalHandler(object):
   """Generic signal handler class.