rlib2: Convert /2/groups to OpcodeResource
[ganeti-local] / lib / utils / io.py
index e6f9873..c64c125 100644 (file)
@@ -38,17 +38,22 @@ from ganeti.utils import filelock
 _RANDOM_UUID_FILE = "/proc/sys/kernel/random/uuid"
 
 
-def ReadFile(file_name, size=-1):
+def ReadFile(file_name, size=-1, preread=None):
   """Reads a file.
 
   @type size: int
   @param size: Read at most size bytes (if negative, entire file)
+  @type preread: callable receiving file handle as single parameter
+  @param preread: Function called before file is read
   @rtype: str
   @return: the (possibly partial) content of the file
 
   """
   f = open(file_name, "r")
   try:
+    if preread:
+      preread(f)
+
     return f.read(size)
   finally:
     f.close()
@@ -117,36 +122,55 @@ def WriteFile(file_name, fn=None, data=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)
+  # Whether temporary file needs to be removed (e.g. if any error occurs)
   do_remove = True
-  # here we need to make sure we remove the temp file, if any error
-  # leaves it in place
+
+  # Function result
+  result = None
+
+  (dir_name, base_name) = os.path.split(file_name)
+  (fd, new_name) = tempfile.mkstemp(suffix=".new", prefix=base_name,
+                                    dir=dir_name)
   try:
-    if uid != -1 or gid != -1:
-      os.chown(new_name, uid, gid)
-    if mode:
-      os.chmod(new_name, mode)
-    if callable(prewrite):
-      prewrite(fd)
-    if data is not None:
-      os.write(fd, data)
-    else:
-      fn(fd)
-    if callable(postwrite):
-      postwrite(fd)
-    os.fsync(fd)
-    if atime is not None and mtime is not None:
-      os.utime(new_name, (atime, mtime))
+    try:
+      if uid != -1 or gid != -1:
+        os.chown(new_name, uid, gid)
+      if mode:
+        os.chmod(new_name, mode)
+      if callable(prewrite):
+        prewrite(fd)
+      if data is not None:
+        if isinstance(data, unicode):
+          data = data.encode()
+        assert isinstance(data, str)
+        to_write = len(data)
+        offset = 0
+        while offset < to_write:
+          written = os.write(fd, buffer(data, offset))
+          assert written >= 0
+          assert written <= to_write - offset
+          offset += written
+        assert offset == to_write
+      else:
+        fn(fd)
+      if callable(postwrite):
+        postwrite(fd)
+      os.fsync(fd)
+      if atime is not None and mtime is not None:
+        os.utime(new_name, (atime, mtime))
+    finally:
+      # Close file unless the file descriptor should be returned
+      if close:
+        os.close(fd)
+      else:
+        result = fd
+
+    # Rename file to destination name
     if not dry_run:
       os.rename(new_name, file_name)
+      # Successful, no need to remove anymore
       do_remove = False
   finally:
-    if close:
-      os.close(fd)
-      result = None
-    else:
-      result = fd
     if do_remove:
       RemoveFile(new_name)
 
@@ -343,10 +367,10 @@ def CreateBackup(file_name):
             (os.path.basename(file_name), TimestampForFilename()))
   dir_name = os.path.dirname(file_name)
 
-  fsrc = open(file_name, 'rb')
+  fsrc = open(file_name, "rb")
   try:
     (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
-    fdst = os.fdopen(fd, 'wb')
+    fdst = os.fdopen(fd, "wb")
     try:
       logging.debug("Backing up %s at %s", file_name, backup_name)
       shutil.copyfileobj(fsrc, fdst)
@@ -487,7 +511,7 @@ def TailFile(fname, lines=20):
   try:
     fd.seek(0, 2)
     pos = fd.tell()
-    pos = max(0, pos-4096)
+    pos = max(0, pos - 4096)
     fd.seek(pos, 0)
     raw_data = fd.read()
   finally:
@@ -613,7 +637,7 @@ def AddAuthorizedKey(file_obj, key):
   key_fields = key.split()
 
   if isinstance(file_obj, basestring):
-    f = open(file_obj, 'a+')
+    f = open(file_obj, "a+")
   else:
     f = file_obj
 
@@ -623,11 +647,11 @@ def AddAuthorizedKey(file_obj, key):
       # Ignore whitespace changes
       if line.split() == key_fields:
         break
-      nl = line.endswith('\n')
+      nl = line.endswith("\n")
     else:
       if not nl:
         f.write("\n")
-      f.write(key.rstrip('\r\n'))
+      f.write(key.rstrip("\r\n"))
       f.write("\n")
       f.flush()
   finally:
@@ -647,9 +671,9 @@ def RemoveAuthorizedKey(file_name, key):
 
   fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
   try:
-    out = os.fdopen(fd, 'w')
+    out = os.fdopen(fd, "w")
     try:
-      f = open(file_name, 'r')
+      f = open(file_name, "r")
       try:
         for line in f:
           # Ignore whitespace changes while comparing lines
@@ -707,22 +731,6 @@ def WritePidFile(pidfile):
   return fd_pidfile
 
 
-def RemovePidFile(pidfile):
-  """Remove the current process pidfile.
-
-  Any errors are ignored.
-
-  @type pidfile: string
-  @param pidfile: Path to the file to be removed
-
-  """
-  # TODO: we could check here that the file contains our pid
-  try:
-    RemoveFile(pidfile)
-  except Exception: # pylint: disable-msg=W0703
-    pass
-
-
 def ReadWatcherPauseFile(filename, now=None, remove_after=3600):
   """Reads the watcher pause file.