Commit b5d48e87 authored by Bernardo Dal Seno's avatar Bernardo Dal Seno
Browse files

Attaching a logical volume builds a list of PVs



When an LV gets attached, the list of the PVs used by the LV is built. This
will be used to count spindles for exclusive_storage, but it could also be
useful to optimize disk growing and snapshotting.
Signed-off-by: default avatarBernardo Dal Seno <bdalseno@google.com>
Reviewed-by: default avatarHelga Velroyen <helgav@google.com>
parent 688b5752
......@@ -172,6 +172,7 @@ class LogicalVolume(base.BlockDev):
"""
_VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
_PARSE_PV_DEV_RE = re.compile("^([^ ()]+)\([0-9]+\)$")
_INVALID_NAMES = compat.UniqueFrozenset([".", "..", "snapshot", "pvmove"])
_INVALID_SUBSTRINGS = compat.UniqueFrozenset(["_mlog", "_mimage"])
......@@ -190,6 +191,7 @@ class LogicalVolume(base.BlockDev):
self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
self._degraded = True
self.major = self.minor = self.pe_size = self.stripe_count = None
self.pv_names = None
self.Attach()
@staticmethod
......@@ -501,10 +503,10 @@ class LogicalVolume(base.BlockDev):
"""
elems = line.strip().rstrip(sep).split(sep)
if len(elems) != 5:
base.ThrowError("Can't parse LVS output, len(%s) != 5", str(elems))
if len(elems) != 6:
base.ThrowError("Can't parse LVS output, len(%s) != 6", str(elems))
(status, major, minor, pe_size, stripes) = elems
(status, major, minor, pe_size, stripes, pvs) = elems
if len(status) < 6:
base.ThrowError("lvs lv_attr is not at least 6 characters (%s)", status)
......@@ -524,17 +526,26 @@ class LogicalVolume(base.BlockDev):
except (TypeError, ValueError), err:
base.ThrowError("Can't parse the number of stripes: %s", err)
return (status, major, minor, pe_size, stripes)
pv_names = []
for pv in pvs.split(","):
m = re.match(cls._PARSE_PV_DEV_RE, pv)
if not m:
base.ThrowError("Can't parse this device list: %s", pvs)
pv_names.append(m.group(1))
assert len(pv_names) > 0
return (status, major, minor, pe_size, stripes, pv_names)
@classmethod
def _GetLvInfo(cls, dev_path, _run_cmd=utils.RunCmd):
"""Get info about the given existing LV to be used.
"""
result = _run_cmd(["lvs", "--noheadings", "--separator=,",
sep = "|"
result = _run_cmd(["lvs", "--noheadings", "--separator=%s" % sep,
"--units=k", "--nosuffix",
"-olv_attr,lv_kernel_major,lv_kernel_minor,"
"vg_extent_size,stripes", dev_path])
"vg_extent_size,stripes,devices", dev_path])
if result.failed:
base.ThrowError("Can't find LV %s: %s, %s",
dev_path, result.fail_reason, result.output)
......@@ -547,7 +558,12 @@ class LogicalVolume(base.BlockDev):
if not out: # totally empty result? splitlines() returns at least
# one line for any non-empty string
base.ThrowError("Can't parse LVS output, no lines? Got '%s'", str(out))
return cls._ParseLvInfoLine(out[-1], ",")
pv_names = set()
for line in out:
(status, major, minor, pe_size, stripes, more_pvs) = \
cls._ParseLvInfoLine(line, sep)
pv_names.update(more_pvs)
return (status, major, minor, pe_size, stripes, pv_names)
def Attach(self):
"""Attach to an existing LV.
......@@ -559,7 +575,7 @@ class LogicalVolume(base.BlockDev):
"""
self.attached = False
try:
(status, major, minor, pe_size, stripes) = \
(status, major, minor, pe_size, stripes, pv_names) = \
self._GetLvInfo(self.dev_path)
except errors.BlockDeviceError:
return False
......@@ -570,6 +586,7 @@ class LogicalVolume(base.BlockDev):
self.stripe_count = stripes
self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
# storage
self.pv_names = pv_names
self.attached = True
return True
......
......@@ -305,31 +305,36 @@ class TestLogicalVolume(unittest.TestCase):
def testParseLvInfoLine(self):
"""Tests for LogicalVolume._ParseLvInfoLine."""
broken_lines = [
" toomuch#-wi-ao#253#3#4096.00#2",
" -wi-ao#253#3#4096.00",
" -wi-a#253#3#4096.00#2",
" -wi-ao#25.3#3#4096.00#2",
" -wi-ao#twenty#3#4096.00#2",
" -wi-ao#253#3.1#4096.00#2",
" -wi-ao#253#three#4096.00#2",
" -wi-ao#253#3#four#2",
" -wi-ao#253#3#4096..00#2",
" -wi-ao#253#3#4096.00#2.0",
" -wi-ao#253#3#4096.00#two",
" toomuch#-wi-ao#253#3#4096.00#2#/dev/abc(20)",
" -wi-ao#253#3#4096.00#/dev/abc(20)",
" -wi-a#253#3#4096.00#2#/dev/abc(20)",
" -wi-ao#25.3#3#4096.00#2#/dev/abc(20)",
" -wi-ao#twenty#3#4096.00#2#/dev/abc(20)",
" -wi-ao#253#3.1#4096.00#2#/dev/abc(20)",
" -wi-ao#253#three#4096.00#2#/dev/abc(20)",
" -wi-ao#253#3#four#2#/dev/abc(20)",
" -wi-ao#253#3#4096..00#2#/dev/abc(20)",
" -wi-ao#253#3#4096.00#2.0#/dev/abc(20)",
" -wi-ao#253#3#4096.00#two#/dev/abc(20)",
]
for broken in broken_lines:
self.assertRaises(errors.BlockDeviceError,
bdev.LogicalVolume._ParseLvInfoLine, broken, "#")
# Examples of good lines from "lvs":
# -wi-ao|253|3|4096.00|2|/dev/sdb(144),/dev/sdc(0)
# -wi-a-|253|4|4096.00|1|/dev/sdb(208)
true_out = [
("-wi-ao", 253, 3, 4096.00, 2),
("-wi-a-", 253, 7, 4096.00, 4),
("-ri-a-", 253, 4, 4.00, 5),
("-wc-ao", 15, 18, 4096.00, 32),
("-wi-ao", 253, 3, 4096.00, 2, ["/dev/abc"]),
("-wi-a-", 253, 7, 4096.00, 4, ["/dev/abc"]),
("-ri-a-", 253, 4, 4.00, 5, ["/dev/abc", "/dev/def"]),
("-wc-ao", 15, 18, 4096.00, 32, ["/dev/abc", "/dev/def", "/dev/ghi0"]),
]
for exp in true_out:
for sep in "#;|,":
lvs_line = sep.join((" %s", "%d", "%d", "%.2f", "%d")) % exp
for sep in "#;|":
pvs = ",".join("%s(%s)" % (d, i * 12) for (i, d) in enumerate(exp[-1]))
lvs_line = (sep.join((" %s", "%d", "%d", "%.2f", "%d", "%s")) %
(exp[0:-1] + (pvs,)))
parsed = bdev.LogicalVolume._ParseLvInfoLine(lvs_line, sep)
self.assertEqual(parsed, exp)
......@@ -351,19 +356,36 @@ class TestLogicalVolume(unittest.TestCase):
"fake_path", _run_cmd=self._FakeRunCmd(True, ""))
self.assertRaises(errors.BlockDeviceError, bdev.LogicalVolume._GetLvInfo,
"fake_path", _run_cmd=self._FakeRunCmd(True, "BadStdOut"))
good_line = " -wi-ao,253,3,4096.00,2"
good_line = " -wi-ao|253|3|4096.00|2|/dev/abc(20)"
fake_cmd = self._FakeRunCmd(True, good_line)
good_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd)
# Only the last line should be parsed and taken into account
# If the same line is repeated, the result should be the same
for lines in [
[good_line] * 2,
[good_line] * 3,
["bad line", good_line],
]:
fake_cmd = self._FakeRunCmd(True, "\n".join(lines))
same_res = bdev.LogicalVolume._GetLvInfo("fake_path", fake_cmd)
self.assertEqual(same_res, good_res)
# Complex multi-line examples
one_line = " -wi-ao|253|3|4096.00|2|/dev/sda(20),/dev/sdb(50),/dev/sdc(0)"
fake_cmd = self._FakeRunCmd(True, one_line)
one_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd)
# These should give the same results
for multi_lines in [
(" -wi-ao|253|3|4096.00|2|/dev/sda(30),/dev/sdb(50)\n"
" -wi-ao|253|3|4096.00|2|/dev/sdb(200),/dev/sdc(300)"),
(" -wi-ao|253|3|4096.00|2|/dev/sda(0)\n"
" -wi-ao|253|3|4096.00|2|/dev/sdb(20)\n"
" -wi-ao|253|3|4096.00|2|/dev/sdc(30)"),
(" -wi-ao|253|3|4096.00|2|/dev/sda(20)\n"
" -wi-ao|253|3|4096.00|2|/dev/sdb(50),/dev/sdc(0)"),
]:
fake_cmd = self._FakeRunCmd(True, multi_lines)
multi_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd)
self.assertEqual(multi_res, one_res)
if __name__ == "__main__":
testutils.GanetiTestProgram()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment