- result = super(MDRaid1, self).SetSyncSpeed(kbytes)
- if self.minor is None:
- logger.Error("MD array not attached to a device")
- return False
- f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
- try:
- f.write("%d" % kbytes)
- finally:
- f.close()
- f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
- try:
- f.write("%d" % (kbytes/2))
- finally:
- f.close()
- return result
-
-
- def GetSyncStatus(self):
- """Returns the sync status of the device.
-
- Returns:
- (sync_percent, estimated_time)
-
- If sync_percent is None, it means all is ok
- If estimated_time is None, it means we can't esimate
- the time needed, otherwise it's the time left in seconds
-
- """
- if self.minor is None and not self.Attach():
- raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
- dev_info = self._GetDevInfo(self.minor)
- is_clean = ("state" in dev_info and
- len(dev_info["state"]) == 1 and
- dev_info["state"][0] in ("clean", "active"))
- sys_path = "/sys/block/md%s/md/" % self.minor
- f = file(sys_path + "sync_action")
- sync_status = f.readline().strip()
- f.close()
- if sync_status == "idle":
- return None, None, not is_clean
- f = file(sys_path + "sync_completed")
- sync_completed = f.readline().strip().split(" / ")
- f.close()
- if len(sync_completed) != 2:
- return 0, None, not is_clean
- sync_done, sync_total = [float(i) for i in sync_completed]
- sync_percent = 100.0*sync_done/sync_total
- f = file(sys_path + "sync_speed")
- sync_speed_k = int(f.readline().strip())
- if sync_speed_k == 0:
- time_est = None
- else:
- time_est = (sync_total - sync_done) / 2 / sync_speed_k
- return sync_percent, time_est, not is_clean
-
-
- def Open(self, force=False):
- """Make the device ready for I/O.
-
- This is a no-op for the MDRaid1 device type, although we could use
- the 2.6.18's new array_state thing.
-
- """
- return True
-
-
- def Close(self):
- """Notifies that the device will no longer be used for I/O.
-
- This is a no-op for the MDRaid1 device type, but see comment for
- `Open()`.
-
- """
- return True
-
-
-class BaseDRBD(BlockDev):
- """Base DRBD class.
-
- This class contains a few bits of common functionality between the
- 0.7 and 8.x versions of DRBD.
-
- """
- _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
- r" \(api:(\d+)/proto:(\d+)\)")
- _DRBD_MAJOR = 147
- _ST_UNCONFIGURED = "Unconfigured"
- _ST_WFCONNECTION = "WFConnection"
- _ST_CONNECTED = "Connected"
-
- @staticmethod
- def _GetProcData():
- """Return data from /proc/drbd.
-
- """
- stat = open("/proc/drbd", "r")
- try:
- data = stat.read().splitlines()
- finally:
- stat.close()
- if not data:
- raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
- return data
-
- @classmethod
- def _GetVersion(cls):
- """Return the DRBD version.
-
- This will return a list [k_major, k_minor, k_point, api, proto].
-
- """
- proc_data = cls._GetProcData()
- first_line = proc_data[0].strip()
- version = cls._VERSION_RE.match(first_line)
- if not version:
- raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
- first_line)
- return [int(val) for val in version.groups()]
-
- @staticmethod
- def _DevPath(minor):
- """Return the path to a drbd device for a given minor.
-
- """
- return "/dev/drbd%d" % minor
-
- @classmethod
- def _GetUsedDevs(cls):
- """Compute the list of used DRBD devices.
-
- """
- data = cls._GetProcData()
-
- used_devs = {}
- valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
- for line in data:
- match = valid_line.match(line)
- if not match:
- continue
- minor = int(match.group(1))
- state = match.group(2)
- if state == cls._ST_UNCONFIGURED:
- continue
- used_devs[minor] = state, line
-
- return used_devs
-
-
-class DRBDev(BaseDRBD):
- """DRBD block device.
-
- This implements the local host part of the DRBD device, i.e. it
- doesn't do anything to the supposed peer. If you need a fully
- connected DRBD pair, you need to use this class on both hosts.
-
- The unique_id for the drbd device is the (local_ip, local_port,
- remote_ip, remote_port) tuple, and it must have two children: the
- data device and the meta_device. The meta device is checked for
- valid size and is zeroed on create.
-
- """
- def __init__(self, unique_id, children):
- super(DRBDev, self).__init__(unique_id, children)
- self.major = self._DRBD_MAJOR
- [kmaj, kmin, kfix, api, proto] = self._GetVersion()
- if kmaj != 0 and kmin != 7:
- raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
- " requested ganeti usage: kernel is"
- " %s.%s, ganeti wants 0.7" % (kmaj, kmin))
-
- if len(children) != 2:
- raise ValueError("Invalid configuration data %s" % str(children))
- if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
- raise ValueError("Invalid configuration data %s" % str(unique_id))
- self._lhost, self._lport, self._rhost, self._rport = unique_id
- self.Attach()
-
- @classmethod
- def _FindUnusedMinor(cls):
- """Find an unused DRBD device.
-
- """
- data = cls._GetProcData()
-
- valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
- for line in data:
- match = valid_line.match(line)
- if match:
- return int(match.group(1))
- logger.Error("Error: no free drbd minors!")
- return None
-