Revision b5d48e87
b/lib/storage/bdev.py | ||
---|---|---|
172 | 172 |
|
173 | 173 |
""" |
174 | 174 |
_VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$") |
175 |
_PARSE_PV_DEV_RE = re.compile("^([^ ()]+)\([0-9]+\)$") |
|
175 | 176 |
_INVALID_NAMES = compat.UniqueFrozenset([".", "..", "snapshot", "pvmove"]) |
176 | 177 |
_INVALID_SUBSTRINGS = compat.UniqueFrozenset(["_mlog", "_mimage"]) |
177 | 178 |
|
... | ... | |
190 | 191 |
self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name) |
191 | 192 |
self._degraded = True |
192 | 193 |
self.major = self.minor = self.pe_size = self.stripe_count = None |
194 |
self.pv_names = None |
|
193 | 195 |
self.Attach() |
194 | 196 |
|
195 | 197 |
@staticmethod |
... | ... | |
501 | 503 |
|
502 | 504 |
""" |
503 | 505 |
elems = line.strip().rstrip(sep).split(sep) |
504 |
if len(elems) != 5:
|
|
505 |
base.ThrowError("Can't parse LVS output, len(%s) != 5", str(elems))
|
|
506 |
if len(elems) != 6:
|
|
507 |
base.ThrowError("Can't parse LVS output, len(%s) != 6", str(elems))
|
|
506 | 508 |
|
507 |
(status, major, minor, pe_size, stripes) = elems |
|
509 |
(status, major, minor, pe_size, stripes, pvs) = elems
|
|
508 | 510 |
if len(status) < 6: |
509 | 511 |
base.ThrowError("lvs lv_attr is not at least 6 characters (%s)", status) |
510 | 512 |
|
... | ... | |
524 | 526 |
except (TypeError, ValueError), err: |
525 | 527 |
base.ThrowError("Can't parse the number of stripes: %s", err) |
526 | 528 |
|
527 |
return (status, major, minor, pe_size, stripes) |
|
529 |
pv_names = [] |
|
530 |
for pv in pvs.split(","): |
|
531 |
m = re.match(cls._PARSE_PV_DEV_RE, pv) |
|
532 |
if not m: |
|
533 |
base.ThrowError("Can't parse this device list: %s", pvs) |
|
534 |
pv_names.append(m.group(1)) |
|
535 |
assert len(pv_names) > 0 |
|
536 |
|
|
537 |
return (status, major, minor, pe_size, stripes, pv_names) |
|
528 | 538 |
|
529 | 539 |
@classmethod |
530 | 540 |
def _GetLvInfo(cls, dev_path, _run_cmd=utils.RunCmd): |
531 | 541 |
"""Get info about the given existing LV to be used. |
532 | 542 |
|
533 | 543 |
""" |
534 |
result = _run_cmd(["lvs", "--noheadings", "--separator=,", |
|
544 |
sep = "|" |
|
545 |
result = _run_cmd(["lvs", "--noheadings", "--separator=%s" % sep, |
|
535 | 546 |
"--units=k", "--nosuffix", |
536 | 547 |
"-olv_attr,lv_kernel_major,lv_kernel_minor," |
537 |
"vg_extent_size,stripes", dev_path]) |
|
548 |
"vg_extent_size,stripes,devices", dev_path])
|
|
538 | 549 |
if result.failed: |
539 | 550 |
base.ThrowError("Can't find LV %s: %s, %s", |
540 | 551 |
dev_path, result.fail_reason, result.output) |
... | ... | |
547 | 558 |
if not out: # totally empty result? splitlines() returns at least |
548 | 559 |
# one line for any non-empty string |
549 | 560 |
base.ThrowError("Can't parse LVS output, no lines? Got '%s'", str(out)) |
550 |
return cls._ParseLvInfoLine(out[-1], ",") |
|
561 |
pv_names = set() |
|
562 |
for line in out: |
|
563 |
(status, major, minor, pe_size, stripes, more_pvs) = \ |
|
564 |
cls._ParseLvInfoLine(line, sep) |
|
565 |
pv_names.update(more_pvs) |
|
566 |
return (status, major, minor, pe_size, stripes, pv_names) |
|
551 | 567 |
|
552 | 568 |
def Attach(self): |
553 | 569 |
"""Attach to an existing LV. |
... | ... | |
559 | 575 |
""" |
560 | 576 |
self.attached = False |
561 | 577 |
try: |
562 |
(status, major, minor, pe_size, stripes) = \ |
|
578 |
(status, major, minor, pe_size, stripes, pv_names) = \
|
|
563 | 579 |
self._GetLvInfo(self.dev_path) |
564 | 580 |
except errors.BlockDeviceError: |
565 | 581 |
return False |
... | ... | |
570 | 586 |
self.stripe_count = stripes |
571 | 587 |
self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing |
572 | 588 |
# storage |
589 |
self.pv_names = pv_names |
|
573 | 590 |
self.attached = True |
574 | 591 |
return True |
575 | 592 |
|
b/test/py/ganeti.storage.bdev_unittest.py | ||
---|---|---|
305 | 305 |
def testParseLvInfoLine(self): |
306 | 306 |
"""Tests for LogicalVolume._ParseLvInfoLine.""" |
307 | 307 |
broken_lines = [ |
308 |
" toomuch#-wi-ao#253#3#4096.00#2", |
|
309 |
" -wi-ao#253#3#4096.00", |
|
310 |
" -wi-a#253#3#4096.00#2", |
|
311 |
" -wi-ao#25.3#3#4096.00#2", |
|
312 |
" -wi-ao#twenty#3#4096.00#2", |
|
313 |
" -wi-ao#253#3.1#4096.00#2", |
|
314 |
" -wi-ao#253#three#4096.00#2", |
|
315 |
" -wi-ao#253#3#four#2", |
|
316 |
" -wi-ao#253#3#4096..00#2", |
|
317 |
" -wi-ao#253#3#4096.00#2.0", |
|
318 |
" -wi-ao#253#3#4096.00#two", |
|
308 |
" toomuch#-wi-ao#253#3#4096.00#2#/dev/abc(20)",
|
|
309 |
" -wi-ao#253#3#4096.00#/dev/abc(20)",
|
|
310 |
" -wi-a#253#3#4096.00#2#/dev/abc(20)",
|
|
311 |
" -wi-ao#25.3#3#4096.00#2#/dev/abc(20)",
|
|
312 |
" -wi-ao#twenty#3#4096.00#2#/dev/abc(20)",
|
|
313 |
" -wi-ao#253#3.1#4096.00#2#/dev/abc(20)",
|
|
314 |
" -wi-ao#253#three#4096.00#2#/dev/abc(20)",
|
|
315 |
" -wi-ao#253#3#four#2#/dev/abc(20)",
|
|
316 |
" -wi-ao#253#3#4096..00#2#/dev/abc(20)",
|
|
317 |
" -wi-ao#253#3#4096.00#2.0#/dev/abc(20)",
|
|
318 |
" -wi-ao#253#3#4096.00#two#/dev/abc(20)",
|
|
319 | 319 |
] |
320 | 320 |
for broken in broken_lines: |
321 | 321 |
self.assertRaises(errors.BlockDeviceError, |
322 | 322 |
bdev.LogicalVolume._ParseLvInfoLine, broken, "#") |
323 | 323 |
|
324 |
# Examples of good lines from "lvs": |
|
325 |
# -wi-ao|253|3|4096.00|2|/dev/sdb(144),/dev/sdc(0) |
|
326 |
# -wi-a-|253|4|4096.00|1|/dev/sdb(208) |
|
324 | 327 |
true_out = [ |
325 |
("-wi-ao", 253, 3, 4096.00, 2), |
|
326 |
("-wi-a-", 253, 7, 4096.00, 4), |
|
327 |
("-ri-a-", 253, 4, 4.00, 5), |
|
328 |
("-wc-ao", 15, 18, 4096.00, 32), |
|
328 |
("-wi-ao", 253, 3, 4096.00, 2, ["/dev/abc"]),
|
|
329 |
("-wi-a-", 253, 7, 4096.00, 4, ["/dev/abc"]),
|
|
330 |
("-ri-a-", 253, 4, 4.00, 5, ["/dev/abc", "/dev/def"]),
|
|
331 |
("-wc-ao", 15, 18, 4096.00, 32, ["/dev/abc", "/dev/def", "/dev/ghi0"]),
|
|
329 | 332 |
] |
330 | 333 |
for exp in true_out: |
331 |
for sep in "#;|,": |
|
332 |
lvs_line = sep.join((" %s", "%d", "%d", "%.2f", "%d")) % exp |
|
334 |
for sep in "#;|": |
|
335 |
pvs = ",".join("%s(%s)" % (d, i * 12) for (i, d) in enumerate(exp[-1])) |
|
336 |
lvs_line = (sep.join((" %s", "%d", "%d", "%.2f", "%d", "%s")) % |
|
337 |
(exp[0:-1] + (pvs,))) |
|
333 | 338 |
parsed = bdev.LogicalVolume._ParseLvInfoLine(lvs_line, sep) |
334 | 339 |
self.assertEqual(parsed, exp) |
335 | 340 |
|
... | ... | |
351 | 356 |
"fake_path", _run_cmd=self._FakeRunCmd(True, "")) |
352 | 357 |
self.assertRaises(errors.BlockDeviceError, bdev.LogicalVolume._GetLvInfo, |
353 | 358 |
"fake_path", _run_cmd=self._FakeRunCmd(True, "BadStdOut")) |
354 |
good_line = " -wi-ao,253,3,4096.00,2"
|
|
359 |
good_line = " -wi-ao|253|3|4096.00|2|/dev/abc(20)"
|
|
355 | 360 |
fake_cmd = self._FakeRunCmd(True, good_line) |
356 | 361 |
good_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd) |
357 |
# Only the last line should be parsed and taken into account
|
|
362 |
# If the same line is repeated, the result should be the same
|
|
358 | 363 |
for lines in [ |
359 | 364 |
[good_line] * 2, |
360 | 365 |
[good_line] * 3, |
361 |
["bad line", good_line], |
|
362 | 366 |
]: |
363 | 367 |
fake_cmd = self._FakeRunCmd(True, "\n".join(lines)) |
364 | 368 |
same_res = bdev.LogicalVolume._GetLvInfo("fake_path", fake_cmd) |
365 | 369 |
self.assertEqual(same_res, good_res) |
366 | 370 |
|
371 |
# Complex multi-line examples |
|
372 |
one_line = " -wi-ao|253|3|4096.00|2|/dev/sda(20),/dev/sdb(50),/dev/sdc(0)" |
|
373 |
fake_cmd = self._FakeRunCmd(True, one_line) |
|
374 |
one_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd) |
|
375 |
# These should give the same results |
|
376 |
for multi_lines in [ |
|
377 |
(" -wi-ao|253|3|4096.00|2|/dev/sda(30),/dev/sdb(50)\n" |
|
378 |
" -wi-ao|253|3|4096.00|2|/dev/sdb(200),/dev/sdc(300)"), |
|
379 |
(" -wi-ao|253|3|4096.00|2|/dev/sda(0)\n" |
|
380 |
" -wi-ao|253|3|4096.00|2|/dev/sdb(20)\n" |
|
381 |
" -wi-ao|253|3|4096.00|2|/dev/sdc(30)"), |
|
382 |
(" -wi-ao|253|3|4096.00|2|/dev/sda(20)\n" |
|
383 |
" -wi-ao|253|3|4096.00|2|/dev/sdb(50),/dev/sdc(0)"), |
|
384 |
]: |
|
385 |
fake_cmd = self._FakeRunCmd(True, multi_lines) |
|
386 |
multi_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd) |
|
387 |
self.assertEqual(multi_res, one_res) |
|
388 |
|
|
367 | 389 |
|
368 | 390 |
if __name__ == "__main__": |
369 | 391 |
testutils.GanetiTestProgram() |
Also available in: Unified diff