"""
raise NotImplementedError
+ def GetActualSize(self):
+ """Return the actual disk size.
+
+ @note: the device needs to be active when this is called
+
+ """
+ assert self.attached, "BlockDevice not attached in GetActualSize()"
+ result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
+ if result.failed:
+ _ThrowError("blockdev failed (%s): %s",
+ result.fail_reason, result.output)
+ try:
+ sz = int(result.output.strip())
+ except (ValueError, TypeError), err:
+ _ThrowError("Failed to parse blockdev output: %s", str(err))
+ return sz
+
def __repr__(self):
return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
(self.__class__, self.unique_id, self._children,
self._vg_name, self._lv_name = unique_id
self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
self._degraded = True
- self.major = self.minor = None
+ self.major = self.minor = self.pe_size = self.stripe_count = None
self.Attach()
@classmethod
"""
self.attached = False
result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
- "-olv_attr,lv_kernel_major,lv_kernel_minor",
- self.dev_path])
+ "--units=m", "--nosuffix",
+ "-olv_attr,lv_kernel_major,lv_kernel_minor,"
+ "vg_extent_size,stripes", self.dev_path])
if result.failed:
logging.error("Can't find LV %s: %s, %s",
self.dev_path, result.fail_reason, result.output)
return False
- out = result.stdout.strip().rstrip(',')
+ # the output can (and will) have multiple lines for multi-segment
+ # LVs, as the 'stripes' parameter is a segment one, so we take
+ # only the last entry, which is the one we're interested in; note
+ # that with LVM2 anyway the 'stripes' value must be constant
+ # across segments, so this is a no-op actually
+ out = result.stdout.splitlines()
+ if not out: # totally empty result? splitlines() returns at least
+ # one line for any non-empty string
+ logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
+ return False
+ out = out[-1].strip().rstrip(',')
out = out.split(",")
- if len(out) != 3:
- logging.error("Can't parse LVS output, len(%s) != 3", str(out))
+ if len(out) != 5:
+ logging.error("Can't parse LVS output, len(%s) != 5", str(out))
return False
- status, major, minor = out[:3]
+ status, major, minor, pe_size, stripes = out
if len(status) != 6:
logging.error("lvs lv_attr is not 6 characters (%s)", status)
return False
except ValueError, err:
logging.error("lvs major/minor cannot be parsed: %s", str(err))
+ try:
+ pe_size = int(float(pe_size))
+ except (TypeError, ValueError), err:
+ logging.error("Can't parse vg extent size: %s", err)
+ return False
+
+ try:
+ stripes = int(stripes)
+ except (TypeError, ValueError), err:
+ logging.error("Can't parse the number of stripes: %s", err)
+ return False
+
self.major = major
self.minor = minor
+ self.pe_size = pe_size
+ self.stripe_count = stripes
self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
# storage
self.attached = True
"""Grow the logical volume.
"""
+ if self.pe_size is None or self.stripe_count is None:
+ if not self.Attach():
+ _ThrowError("Can't attach to LV during Grow()")
+ full_stripe_size = self.pe_size * self.stripe_count
+ rest = amount % full_stripe_size
+ if rest != 0:
+ amount += full_stripe_size - rest
# we try multiple algorithms since the 'best' ones might not have
# space available in the right place, but later ones might (since
# they have less constraints); also note that only recent LVM
bytes = sectors * 512
if bytes < 128 * 1024 * 1024: # less than 128MiB
_ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
- if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
+ # the maximum *valid* size of the meta device when living on top
+ # of LVM is hard to compute: it depends on the number of stripes
+ # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
+ # (normal size), but an eight-stripe 128MB PE will result in a 1GB
+ # size meta device; as such, we restrict it to 1GB (a little bit
+ # too generous, but making assumptions about PE size is hard)
+ if bytes > 1024 * 1024 * 1024:
_ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
def Rename(self, new_id):
"""
args = ["drbdsetup", cls._DevPath(minor), "disk",
backend, meta, "0",
- "-d", "%sm" % size,
"-e", "detach",
"--create-device"]
+ if size:
+ args.extend(["-d", "%sm" % size])
result = utils.RunCmd(args)
if result.failed:
_ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
if len(self._children) != 2 or None in self._children:
_ThrowError("drbd%d: cannot grow diskless device", self.minor)
self._children[0].Grow(amount)
- result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
+ result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
+ "%dm" % (self.size + amount)])
if result.failed:
_ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
self.attached = os.path.exists(self.dev_path)
return self.attached
+ def GetActualSize(self):
+ """Return the actual disk size.
+
+ @note: the device needs to be active when this is called
+
+ """
+ assert self.attached, "BlockDevice not attached in GetActualSize()"
+ try:
+ st = os.stat(self.dev_path)
+ return st.st_size
+ except OSError, err:
+ _ThrowError("Can't stat %s: %s", self.dev_path, err)
+
@classmethod
def Create(cls, unique_id, children, size):
"""Create a new file.