Add method to update a disk object size
[ganeti-local] / lib / utils.py
index 509f8be..fda1f6b 100644 (file)
@@ -51,6 +51,7 @@ _locksheld = []
 _re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
 
 debug = False
+no_fork = False
 
 
 class RunResult(object):
@@ -219,6 +220,9 @@ def RunCmd(cmd):
   Returns: `RunResult` instance
 
   """
+  if no_fork:
+    raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled")
+
   if isinstance(cmd, list):
     cmd = [str(val) for val in cmd]
     strcmd = " ".join(cmd)
@@ -565,59 +569,6 @@ def NiceSort(name_list):
   return [tup[1] for tup in to_sort]
 
 
-def CheckDaemonAlive(pid_file, process_string):
-  """Check wether the specified daemon is alive.
-
-  Args:
-   - pid_file: file to read the daemon pid from, the file is
-               expected to contain only a single line containing
-               only the PID
-   - process_string: a substring that we expect to find in
-                     the command line of the daemon process
-
-  Returns:
-   - True if the daemon is judged to be alive (that is:
-      - the PID file exists, is readable and contains a number
-      - a process of the specified PID is running
-      - that process contains the specified string in its
-        command line
-      - the process is not in state Z (zombie))
-   - False otherwise
-
-  """
-  try:
-    pid_file = file(pid_file, 'r')
-    try:
-      pid = int(pid_file.readline())
-    finally:
-      pid_file.close()
-
-    cmdline_file_path = "/proc/%s/cmdline" % (pid)
-    cmdline_file = open(cmdline_file_path, 'r')
-    try:
-      cmdline = cmdline_file.readline()
-    finally:
-      cmdline_file.close()
-
-    if not process_string in cmdline:
-      return False
-
-    stat_file_path =  "/proc/%s/stat" % (pid)
-    stat_file = open(stat_file_path, 'r')
-    try:
-      process_state = stat_file.readline().split()[2]
-    finally:
-      stat_file.close()
-
-    if process_state == 'Z':
-      return False
-
-  except (IndexError, IOError, ValueError):
-    return False
-
-  return True
-
-
 def TryConvert(fn, val):
   """Try to convert a value ignoring errors.
 
@@ -830,6 +781,14 @@ def SetEtcHostsEntry(file_name, ip, hostname, aliases):
     raise
 
 
+def AddHostToEtcHosts(hostname):
+  """Wrapper around SetEtcHostsEntry.
+
+  """
+  hi = HostInfo(name=hostname)
+  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
+
+
 def RemoveEtcHostsEntry(file_name, hostname):
   """Removes a hostname from /etc/hosts.
 
@@ -866,6 +825,15 @@ def RemoveEtcHostsEntry(file_name, hostname):
     raise
 
 
+def RemoveHostFromEtcHosts(hostname):
+  """Wrapper around RemoveEtcHostsEntry.
+
+  """
+  hi = HostInfo(name=hostname)
+  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
+  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
+
+
 def CreateBackup(file_name):
   """Creates a backup of a file.
 
@@ -991,7 +959,8 @@ def NewUUID():
 
 def WriteFile(file_name, fn=None, data=None,
               mode=None, uid=-1, gid=-1,
-              atime=None, mtime=None):
+              atime=None, mtime=None,
+              check_abspath=True, dry_run=False, backup=False):
   """(Over)write a file atomically.
 
   The file_name and either fn (a function taking one argument, the
@@ -1006,7 +975,7 @@ def WriteFile(file_name, fn=None, data=None,
   temporary file should be removed.
 
   """
-  if not os.path.isabs(file_name):
+  if check_abspath and not os.path.isabs(file_name):
     raise errors.ProgrammerError("Path passed to WriteFile is not"
                                  " absolute: '%s'" % file_name)
 
@@ -1017,6 +986,8 @@ def WriteFile(file_name, fn=None, data=None,
     raise errors.ProgrammerError("Both atime and mtime must be either"
                                  " set or None")
 
+  if backup and not dry_run and os.path.isfile(file_name):
+    CreateBackup(file_name)
 
   dir_name, base_name = os.path.split(file_name)
   fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
@@ -1034,7 +1005,8 @@ def WriteFile(file_name, fn=None, data=None,
     os.fsync(fd)
     if atime is not None and mtime is not None:
       os.utime(new_name, (atime, mtime))
-    os.rename(new_name, file_name)
+    if not dry_run:
+      os.rename(new_name, file_name)
   finally:
     os.close(fd)
     RemoveFile(new_name)
@@ -1083,7 +1055,7 @@ def TestDelay(duration):
   return True
 
 
-def Daemonize(logfile):
+def Daemonize(logfile, noclose_fds=None):
   """Daemonize the current process.
 
   This detaches the current process from the controlling terminal and
@@ -1123,6 +1095,8 @@ def Daemonize(logfile):
 
   # Iterate through and close all file descriptors.
   for fd in range(0, maxfd):
+    if noclose_fds and fd in noclose_fds:
+      continue
     try:
       os.close(fd)
     except OSError: # ERROR, fd wasn't open to begin with (ignored)
@@ -1132,3 +1106,43 @@ def Daemonize(logfile):
   os.dup2(0, 1)     # standard output (1)
   os.dup2(0, 2)     # standard error (2)
   return 0
+
+
+def FindFile(name, search_path, test=os.path.exists):
+  """Look for a filesystem object in a given path.
+
+  This is an abstract method to search for filesystem object (files,
+  dirs) under a given search path.
+
+  Args:
+    - name: the name to look for
+    - search_path: list of directory names
+    - test: the test which the full path must satisfy
+      (defaults to os.path.exists)
+
+  Returns:
+    - full path to the item if found
+    - None otherwise
+
+  """
+  for dir_name in search_path:
+    item_name = os.path.sep.join([dir_name, name])
+    if test(item_name):
+      return item_name
+  return None
+
+
+def CheckVolumeGroupSize(vglist, vgname, minsize):
+  """Checks if the volume group list is valid.
+
+  A non-None return value means there's an error, and the return value
+  is the error message.
+
+  """
+  vgsize = vglist.get(vgname, None)
+  if vgsize is None:
+    return "volume group '%s' missing" % vgname
+  elif vgsize < minsize:
+    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
+            (vgname, minsize, vgsize))
+  return None