Merge branch 'devel-2.1'
[ganeti-local] / lib / utils.py
index e324dfe..f08e75c 100644 (file)
@@ -42,7 +42,6 @@ import fcntl
 import resource
 import logging
 import signal
-import string
 
 from cStringIO import StringIO
 
@@ -320,8 +319,17 @@ def RenameFile(old, new, mkdir=False, mkdir_mode=0750):
     # as efficient.
     if mkdir and err.errno == errno.ENOENT:
       # Create directory and try again
-      os.makedirs(os.path.dirname(new), mkdir_mode)
+      dirname = os.path.dirname(new)
+      try:
+        os.makedirs(dirname, mode=mkdir_mode)
+      except OSError, err:
+        # Ignore EEXIST. This is only handled in os.makedirs as included in
+        # Python 2.5 and above.
+        if err.errno != errno.EEXIST or not os.path.exists(dirname):
+          raise
+
       return os.rename(old, new)
+
     raise
 
 
@@ -519,14 +527,14 @@ def MatchNameComponent(key, name_list, case_sensitive=True):
   re_flags = 0
   if not case_sensitive:
     re_flags |= re.IGNORECASE
-    key = string.upper(key)
+    key = key.upper()
   mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags)
   names_filtered = []
   string_matches = []
   for name in name_list:
     if mo.match(name) is not None:
       names_filtered.append(name)
-      if not case_sensitive and key == string.upper(name):
+      if not case_sensitive and key == name.upper():
         string_matches.append(name)
 
   if len(string_matches) == 1:
@@ -591,6 +599,16 @@ class HostInfo:
     return result
 
 
+def GetHostInfo(name=None):
+  """Lookup host name and raise an OpPrereqError for failures"""
+
+  try:
+    return HostInfo(name)
+  except errors.ResolverError, err:
+    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
+                               (err[0], err[2]), errors.ECODE_RESOLVER)
+
+
 def ListVolumeGroups():
   """List volume groups and their size
 
@@ -1107,7 +1125,7 @@ def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
     success = True
   except socket.timeout:
     success = False
-  except socket.error, (errcode, errstring):
+  except socket.error, (errcode, _):
     success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
 
   return success
@@ -1208,21 +1226,18 @@ def EnsureDirs(dirs):
       raise errors.GenericError("%s is not a directory" % dir_name)
 
 
-def ReadFile(file_name, size=None):
+def ReadFile(file_name, size=-1):
   """Reads a file.
 
-  @type size: None or int
-  @param size: Read at most size bytes
+  @type size: int
+  @param size: Read at most size bytes (if negative, entire file)
   @rtype: str
   @return: the (possibly partial) content of the file
 
   """
   f = open(file_name, "r")
   try:
-    if size is None:
-      return f.read()
-    else:
-      return f.read(size)
+    return f.read(size)
   finally:
     f.close()
 
@@ -1589,24 +1604,31 @@ def KillProcess(pid, signal_=signal.SIGTERM, timeout=30,
 
   if not IsProcessAlive(pid):
     return
+
   _helper(pid, signal_, waitpid)
+
   if timeout <= 0:
     return
 
-  # Wait up to $timeout seconds
-  end = time.time() + timeout
-  wait = 0.01
-  while time.time() < end and IsProcessAlive(pid):
+  def _CheckProcess():
+    if not IsProcessAlive(pid):
+      return
+
     try:
       (result_pid, _) = os.waitpid(pid, os.WNOHANG)
-      if result_pid > 0:
-        break
     except OSError:
-      pass
-    time.sleep(wait)
-    # Make wait time longer for next try
-    if wait < 0.1:
-      wait *= 1.5
+      raise RetryAgain()
+
+    if result_pid > 0:
+      return
+
+    raise RetryAgain()
+
+  try:
+    # Wait up to $timeout seconds
+    Retry(_CheckProcess, (0.01, 1.5, 0.1), timeout)
+  except RetryTimeout:
+    pass
 
   if IsProcessAlive(pid):
     # Kill process if it's still alive
@@ -1859,6 +1881,16 @@ def SafeEncode(text):
   return resu
 
 
+def CommaJoin(names):
+  """Nicely join a set of identifiers.
+
+  @param names: set, list or tuple
+  @return: a string with the formatted results
+
+  """
+  return ", ".join([str(val) for val in names])
+
+
 def BytesToMebibyte(value):
   """Converts bytes to mebibytes.
 
@@ -2197,6 +2229,8 @@ class FileLock(object):
       flag |= fcntl.LOCK_NB
       timeout_end = None
 
+    # TODO: Convert to utils.Retry
+
     retry = True
     while retry:
       try:
@@ -2395,12 +2429,12 @@ class FieldSet(object):
 
     @type field: str
     @param field: the string to match
-    @return: either False or a regular expression match object
+    @return: either None or a regular expression match object
 
     """
     for m in itertools.ifilter(None, (val.match(field) for val in self.items)):
       return m
-    return False
+    return None
 
   def NonMatching(self, items):
     """Returns the list of fields not matching the current set