"""
_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"])
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
"""
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)
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)
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.
"""
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
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
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)
"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()