- raise errors.BlockDeviceError("Command: %s error: %s" %
- (result.cmd, result.fail_reason))
-
-
-class MDRaid1(BlockDev):
- """raid1 device implemented via md.
-
- """
- def __init__(self, unique_id, children):
- super(MDRaid1, self).__init__(unique_id, children)
- self.major = 9
- self.Attach()
-
-
- def Attach(self):
- """Find an array which matches our config and attach to it.
-
- This tries to find a MD array which has the same UUID as our own.
-
- """
- minor = self._FindMDByUUID(self.unique_id)
- if minor is not None:
- self._SetFromMinor(minor)
- else:
- self.minor = None
- self.dev_path = None
-
- return (minor is not None)
-
-
- @staticmethod
- def _GetUsedDevs():
- """Compute the list of in-use MD devices.
-
- It doesn't matter if the used device have other raid level, just
- that they are in use.
-
- """
- mdstat = open("/proc/mdstat", "r")
- data = mdstat.readlines()
- mdstat.close()
-
- used_md = {}
- valid_line = re.compile("^md([0-9]+) : .*$")
- for line in data:
- match = valid_line.match(line)
- if match:
- md_no = int(match.group(1))
- used_md[md_no] = line
-
- return used_md
-
-
- @staticmethod
- def _GetDevInfo(minor):
- """Get info about a MD device.
-
- Currently only uuid is returned.
-
- """
- result = utils.RunCmd(["mdadm", "-D", "/dev/md%d" % minor])
- if result.failed:
- logger.Error("Can't display md: %s" % result.fail_reason)
- return None
- retval = {}
- for line in result.stdout.splitlines():
- line = line.strip()
- kv = line.split(" : ", 1)
- if kv:
- if kv[0] == "UUID":
- retval["uuid"] = kv[1].split()[0]
- elif kv[0] == "State":
- retval["state"] = kv[1].split(", ")
- return retval
-
-
- @staticmethod
- def _FindUnusedMinor():
- """Compute an unused MD minor.
-
- This code assumes that there are 256 minors only.
-
- """
- used_md = MDRaid1._GetUsedDevs()
- i = 0
- while i < 256:
- if i not in used_md:
- break
- i += 1
- if i == 256:
- logger.Error("Critical: Out of md minor numbers.")
- raise errors.BlockDeviceError("Can't find a free MD minor")
- return i
-
-
- @classmethod
- def _FindMDByUUID(cls, uuid):
- """Find the minor of an MD array with a given UUID.
-
- """
- md_list = cls._GetUsedDevs()
- for minor in md_list:
- info = cls._GetDevInfo(minor)
- if info and info["uuid"] == uuid:
- return minor
- 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.
-
- """
- if not isinstance(children, (tuple, list)):
- raise ValueError("Invalid setup data for MDRaid1 dev: %s" %
- str(children))
- for i in children:
- if not isinstance(i, BlockDev):
- raise ValueError("Invalid member in MDRaid1 dev: %s" % type(i))
- for i in children:
- 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,
- "--auto=yes", "--force", "-l1",
- "-n%d" % len(children)] +
- [dev.dev_path for dev in children])
-
- if result.failed:
- 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:
- logger.Error("Wrong information returned from mdadm -D: %s" % str(info))
- return None
- return MDRaid1(info["uuid"], children)
-
-
- def Remove(self):
- """Stub remove function for MD RAID 1 arrays.
-
- We don't remove the superblock right now. Mark a to do.
-
- """
- #TODO: maybe zero superblock on child devices?
- return self.Shutdown()
-
-
- def AddChild(self, device):
- """Add a new member to the md raid1.
-
- """
- if self.minor is None and not self.Attach():
- raise errors.BlockDeviceError("Can't attach to device")
- if device.dev_path is None:
- 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)
- 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)
- self._children.append(device)
-
-
- def RemoveChild(self, dev_path):
- """Remove member from the md raid1.
-
- """
- if self.minor is None and not self.Attach():
- 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")
- for device in self._children:
- if device.dev_path == dev_path:
- break
- else:
- 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)
-
- # 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)
- 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)
- self._children.remove(device)