Add a generic write file function
[ganeti-local] / lib / bdev.py
index f7eef6e..94ce454 100644 (file)
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#
 #
 
 # Copyright (C) 2006, 2007 Google Inc.
@@ -302,8 +302,8 @@ class LogicalVolume(BlockDev):
     vg_name, lv_name = unique_id
     pvs_info = cls.GetPVInfo(vg_name)
     if not pvs_info:
-      raise errors.BlockDeviceError, ("Can't compute PV info for vg %s" %
-                                      vg_name)
+      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
+                                    vg_name)
     pvs_info.sort()
     pvs_info.reverse()
 
@@ -313,8 +313,8 @@ class LogicalVolume(BlockDev):
     # The size constraint should have been checked from the master before
     # calling the create function.
     if free_size < size:
-      raise errors.BlockDeviceError, ("Not enough free space: required %s,"
-                                      " available %s" % (size, free_size))
+      raise errors.BlockDeviceError("Not enough free space: required %s,"
+                                    " available %s" % (size, free_size))
     result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-n%s" % lv_name,
                            vg_name] + pvlist)
     if result.failed:
@@ -469,20 +469,20 @@ class LogicalVolume(BlockDev):
 
     pvs_info = self.GetPVInfo(self._vg_name)
     if not pvs_info:
-      raise errors.BlockDeviceError, ("Can't compute PV info for vg %s" %
-                                      self._vg_name)
+      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
+                                    self._vg_name)
     pvs_info.sort()
     pvs_info.reverse()
     free_size, pv_name = pvs_info[0]
     if free_size < size:
-      raise errors.BlockDeviceError, ("Not enough free space: required %s,"
-                                      " available %s" % (size, free_size))
+      raise errors.BlockDeviceError("Not enough free space: required %s,"
+                                    " available %s" % (size, free_size))
 
     result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
                            "-n%s" % snap_name, self.dev_path])
     if result.failed:
-      raise errors.BlockDeviceError, ("command: %s error: %s" %
-                                      (result.cmd, result.fail_reason))
+      raise errors.BlockDeviceError("command: %s error: %s" %
+                                    (result.cmd, result.fail_reason))
 
     return snap_name
 
@@ -503,8 +503,8 @@ class LogicalVolume(BlockDev):
     result = utils.RunCmd(["lvchange", "--addtag", text,
                            self.dev_path])
     if result.failed:
-      raise errors.BlockDeviceError, ("Command: %s error: %s" %
-                                      (result.cmd, result.fail_reason))
+      raise errors.BlockDeviceError("Command: %s error: %s" %
+                                    (result.cmd, result.fail_reason))
 
 
 class MDRaid1(BlockDev):
@@ -573,7 +573,7 @@ class MDRaid1(BlockDev):
       kv = line.split(" : ", 1)
       if kv:
         if kv[0] == "UUID":
-          retval["uuid"] = kv[1]
+          retval["uuid"] = kv[1].split()[0]
         elif kv[0] == "State":
           retval["state"] = kv[1].split(", ")
     return retval
@@ -611,6 +611,49 @@ class MDRaid1(BlockDev):
     return None
 
 
+  @staticmethod
+  def _ZeroSuperblock(dev_path):
+    """Zero the possible locations for an MD superblock.
+
+    The zero-ing can't be done via ``mdadm --zero-superblock`` as that
+    fails in versions 2.x with the same error code as non-writable
+    device.
+
+    The superblocks are located at (negative values are relative to
+    the end of the block device):
+      - -128k to end for version 0.90 superblock
+      - -8k to -12k for version 1.0 superblock (included in the above)
+      - 0k to 4k for version 1.1 superblock
+      - 4k to 8k for version 1.2 superblock
+
+    To cover all situations, the zero-ing will be:
+      - 0k to 128k
+      - -128k to end
+
+    As such, the minimum device size must be 128k, otherwise we'll get
+    I/O errors.
+
+    Note that this function depends on the fact that one can open,
+    read and write block devices normally.
+
+    """
+    overwrite_size = 128 * 1024
+    empty_buf = '\0' * overwrite_size
+    fd = open(dev_path, "r+")
+    try:
+      fd.seek(0, 0)
+      p1 = fd.tell()
+      fd.write(empty_buf)
+      p2 = fd.tell()
+      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
+      fd.seek(-overwrite_size, 2)
+      p1 = fd.tell()
+      fd.write(empty_buf)
+      p2 = fd.tell()
+      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
+    finally:
+      fd.close()
+
   @classmethod
   def Create(cls, unique_id, children, size):
     """Create a new MD raid1 array.
@@ -623,10 +666,11 @@ class MDRaid1(BlockDev):
       if not isinstance(i, BlockDev):
         raise ValueError("Invalid member in MDRaid1 dev: %s" % type(i))
     for i in children:
-      result = utils.RunCmd(["mdadm", "--zero-superblock", "--force",
-                             i.dev_path])
-      if result.failed:
-        logger.Error("Can't zero superblock: %s" % result.fail_reason)
+      try:
+        cls._ZeroSuperblock(i.dev_path)
+      except EnvironmentError, err:
+        logger.Error("Can't zero superblock for %s: %s" %
+                     (i.dev_path, str(err)))
         return None
     minor = cls._FindUnusedMinor()
     result = utils.RunCmd(["mdadm", "--create", "/dev/md%d" % minor,
@@ -635,7 +679,8 @@ class MDRaid1(BlockDev):
                           [dev.dev_path for dev in children])
 
     if result.failed:
-      logger.Error("Can't create md: %s" % result.fail_reason)
+      logger.Error("Can't create md: %s: %s" % (result.fail_reason,
+                                                result.output))
       return None
     info = cls._GetDevInfo(minor)
     if not info or not "uuid" in info:
@@ -659,18 +704,18 @@ class MDRaid1(BlockDev):
 
     """
     if self.minor is None and not self.Attach():
-      raise errors.BlockDeviceError, "Can't attach to device"
+      raise errors.BlockDeviceError("Can't attach to device")
     if device.dev_path is None:
-      raise errors.BlockDeviceError, "New child is not initialised"
+      raise errors.BlockDeviceError("New child is not initialised")
     result = utils.RunCmd(["mdadm", "-a", self.dev_path, device.dev_path])
     if result.failed:
-      raise errors.BlockDeviceError, ("Failed to add new device to array: %s" %
-                                      result.output)
+      raise errors.BlockDeviceError("Failed to add new device to array: %s" %
+                                    result.output)
     new_len = len(self._children) + 1
     result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
     if result.failed:
-      raise errors.BlockDeviceError, ("Can't grow md array: %s" %
-                                      result.output)
+      raise errors.BlockDeviceError("Can't grow md array: %s" %
+                                    result.output)
     self._children.append(device)
 
 
@@ -679,33 +724,33 @@ class MDRaid1(BlockDev):
 
     """
     if self.minor is None and not self.Attach():
-      raise errors.BlockDeviceError, "Can't attach to device"
+      raise errors.BlockDeviceError("Can't attach to device")
     if len(self._children) == 1:
-      raise errors.BlockDeviceError, ("Can't reduce member when only one"
-                                      " child left")
+      raise errors.BlockDeviceError("Can't reduce member when only one"
+                                    " child left")
     for device in self._children:
       if device.dev_path == dev_path:
         break
     else:
-      raise errors.BlockDeviceError, "Can't find child with this path"
+      raise errors.BlockDeviceError("Can't find child with this path")
     new_len = len(self._children) - 1
     result = utils.RunCmd(["mdadm", "-f", self.dev_path, dev_path])
     if result.failed:
-      raise errors.BlockDeviceError, ("Failed to mark device as failed: %s" %
-                                      result.output)
+      raise errors.BlockDeviceError("Failed to mark device as failed: %s" %
+                                    result.output)
 
     # it seems here we need a short delay for MD to update its
     # superblocks
     time.sleep(0.5)
     result = utils.RunCmd(["mdadm", "-r", self.dev_path, dev_path])
     if result.failed:
-      raise errors.BlockDeviceError, ("Failed to remove device from array:"
-                                      " %s" % result.output)
+      raise errors.BlockDeviceError("Failed to remove device from array:"
+                                        " %s" % result.output)
     result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
                            "-n", new_len])
     if result.failed:
-      raise errors.BlockDeviceError, ("Can't shrink md array: %s" %
-                                      result.output)
+      raise errors.BlockDeviceError("Can't shrink md array: %s" %
+                                    result.output)
     self._children.remove(device)
 
 
@@ -754,7 +799,8 @@ class MDRaid1(BlockDev):
                            self.unique_id, "/dev/md%d" % free_minor] +
                           [bdev.dev_path for bdev in self._children])
     if result.failed:
-      logger.Error("Can't assemble MD array: %s" % result.fail_reason)
+      logger.Error("Can't assemble MD array: %s: %s" %
+                   (result.fail_reason, result.output))
       self.minor = None
     else:
       self.minor = free_minor
@@ -1218,7 +1264,7 @@ class DRBDev(BlockDev):
 
     minor = self._FindUnusedMinor()
     if minor is None:
-      raise errors.BlockDeviceError, "Not enough free minors for DRBD!"
+      raise errors.BlockDeviceError("Not enough free minors for DRBD!")
     need_localdev_teardown = False
     if self._children[0]:
       result = self._AssembleLocal(minor, self._children[0].dev_path,