X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/197478f258b27bc9e900862fdbc5217528fcf528..52f3310349e34843fdcb00ae993766eeb6c05cc5:/lib/bdev.py diff --git a/lib/bdev.py b/lib/bdev.py index 3352885..2430b1d 100644 --- a/lib/bdev.py +++ b/lib/bdev.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2006, 2007, 2010 Google Inc. +# Copyright (C) 2006, 2007, 2010, 2011 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ import re import time import errno +import stat import pyparsing as pyp import os import logging @@ -230,6 +231,20 @@ class BlockDev(object): result = result and child.SetSyncSpeed(speed) return result + def PauseResumeSync(self, pause): + """Pause/Resume the sync of the mirror. + + In case this is not a mirroring device, this is no-op. + + @param pause: Wheater to pause or resume + + """ + result = True + if self._children: + for child in self._children: + result = result and child.PauseResumeSync(pause) + return result + def GetSyncStatus(self): """Returns the sync status of the device. @@ -488,24 +503,26 @@ class LogicalVolume(BlockDev): @param filter_readonly: whether to skip over readonly VGs @rtype: list - @return: list of tuples (free_space, name) with free_space in mebibytes + @return: list of tuples (free_space, total_size, name) with free_space in + MiB """ try: - info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr"]) + info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr", + "vg_size"]) except errors.GenericError, err: logging.error("Can't get VG information: %s", err) return None data = [] - for vg_name, vg_free, vg_attr in info: + for vg_name, vg_free, vg_attr, vg_size in info: # (possibly) skip over vgs which are not writable if filter_readonly and vg_attr[0] == "r": continue # (possibly) skip over vgs which are not in the right volume group(s) if vg_names and vg_name not in vg_names: continue - data.append((float(vg_free), vg_name)) + data.append((float(vg_free), float(vg_size), vg_name)) return data @@ -692,6 +709,8 @@ class LogicalVolume(BlockDev): def Snapshot(self, size): """Create a snapshot copy of an lvm block device. + @returns: tuple (vg, lv) + """ snap_name = self._lv_name + ".snap" @@ -702,7 +721,7 @@ class LogicalVolume(BlockDev): vg_info = self.GetVGInfo([self._vg_name]) if not vg_info: _ThrowError("Can't compute VG info for vg %s", self._vg_name) - free_size, _ = vg_info[0] + free_size, _, _ = vg_info[0] if free_size < size: _ThrowError("Not enough free space: required %s," " available %s", size, free_size) @@ -713,7 +732,7 @@ class LogicalVolume(BlockDev): _ThrowError("command: %s error: %s - %s", result.cmd, result.fail_reason, result.output) - return snap_name + return (self._vg_name, snap_name) def SetInfo(self, text): """Update metadata with info text. @@ -1484,6 +1503,30 @@ class DRBD8(BaseDRBD): children_result = super(DRBD8, self).SetSyncSpeed(kbytes) return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result + def PauseResumeSync(self, pause): + """Pauses or resumes the sync of a DRBD device. + + @param pause: Wether to pause or resume + @return: the success of the operation + + """ + if self.minor is None: + logging.info("Not attached during PauseSync") + return False + + children_result = super(DRBD8, self).PauseResumeSync(pause) + + if pause: + cmd = "pause-sync" + else: + cmd = "resume-sync" + + result = utils.RunCmd(["drbdsetup", self.dev_path, cmd]) + if result.failed: + logging.error("Can't %s: %s - %s", cmd, + result.fail_reason, result.output) + return not result.failed and children_result + def GetProcStatus(self): """Return device data from /proc. @@ -2027,12 +2070,123 @@ class FileStorage(BlockDev): return FileStorage(unique_id, children, size) +class PersistentBlockDevice(BlockDev): + """A block device with persistent node + + May be either directly attached, or exposed through DM (e.g. dm-multipath). + udev helpers are probably required to give persistent, human-friendly + names. + + For the time being, pathnames are required to lie under /dev. + + """ + def __init__(self, unique_id, children, size): + """Attaches to a static block device. + + The unique_id is a path under /dev. + + """ + super(PersistentBlockDevice, self).__init__(unique_id, children, size) + if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: + raise ValueError("Invalid configuration data %s" % str(unique_id)) + self.dev_path = unique_id[1] + if not os.path.realpath(self.dev_path).startswith('/dev/'): + raise ValueError("Full path '%s' lies outside /dev" % + os.path.realpath(self.dev_path)) + # TODO: this is just a safety guard checking that we only deal with devices + # we know how to handle. In the future this will be integrated with + # external storage backends and possible values will probably be collected + # from the cluster configuration. + if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL: + raise ValueError("Got persistent block device of invalid type: %s" % + unique_id[0]) + + self.major = self.minor = None + self.Attach() + + @classmethod + def Create(cls, unique_id, children, size): + """Create a new device + + This is a noop, we only return a PersistentBlockDevice instance + + """ + return PersistentBlockDevice(unique_id, children, 0) + + def Remove(self): + """Remove a device + + This is a noop + + """ + pass + + def Rename(self, new_id): + """Rename this device. + + """ + _ThrowError("Rename is not supported for PersistentBlockDev storage") + + def Attach(self): + """Attach to an existing block device. + + + """ + self.attached = False + try: + st = os.stat(self.dev_path) + except OSError, err: + logging.error("Error stat()'ing %s: %s", self.dev_path, str(err)) + return False + + if not stat.S_ISBLK(st.st_mode): + logging.error("%s is not a block device", self.dev_path) + return False + + self.major = os.major(st.st_rdev) + self.minor = os.minor(st.st_rdev) + self.attached = True + + return True + + def Assemble(self): + """Assemble the device. + + """ + pass + + def Shutdown(self): + """Shutdown the device. + + """ + pass + + def Open(self, force=False): + """Make the device ready for I/O. + + """ + pass + + def Close(self): + """Notifies that the device will no longer be used for I/O. + + """ + pass + + def Grow(self, amount): + """Grow the logical volume. + + """ + _ThrowError("Grow is not supported for PersistentBlockDev storage") + + DEV_MAP = { constants.LD_LV: LogicalVolume, constants.LD_DRBD8: DRBD8, + constants.LD_BLOCKDEV: PersistentBlockDevice, } -if constants.ENABLE_FILE_STORAGE: +if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE: DEV_MAP[constants.LD_FILE] = FileStorage