Revision d01e51a5 lib/block/drbd.py
b/lib/block/drbd.py | ||
---|---|---|
265 | 265 |
return DRBD8Info.CreateFromLines(lines) |
266 | 266 |
|
267 | 267 |
|
268 |
class DRBD8ShowInfo(object): |
|
269 |
"""Helper class which parses the output of drbdsetup show |
|
270 |
|
|
271 |
""" |
|
272 |
_PARSE_SHOW = None |
|
273 |
|
|
274 |
@classmethod |
|
275 |
def _GetShowParser(cls): |
|
276 |
"""Return a parser for `drbd show` output. |
|
277 |
|
|
278 |
This will either create or return an already-created parser for the |
|
279 |
output of the command `drbd show`. |
|
280 |
|
|
281 |
""" |
|
282 |
if cls._PARSE_SHOW is not None: |
|
283 |
return cls._PARSE_SHOW |
|
284 |
|
|
285 |
# pyparsing setup |
|
286 |
lbrace = pyp.Literal("{").suppress() |
|
287 |
rbrace = pyp.Literal("}").suppress() |
|
288 |
lbracket = pyp.Literal("[").suppress() |
|
289 |
rbracket = pyp.Literal("]").suppress() |
|
290 |
semi = pyp.Literal(";").suppress() |
|
291 |
colon = pyp.Literal(":").suppress() |
|
292 |
# this also converts the value to an int |
|
293 |
number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0])) |
|
294 |
|
|
295 |
comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine) |
|
296 |
defa = pyp.Literal("_is_default").suppress() |
|
297 |
dbl_quote = pyp.Literal('"').suppress() |
|
298 |
|
|
299 |
keyword = pyp.Word(pyp.alphanums + "-") |
|
300 |
|
|
301 |
# value types |
|
302 |
value = pyp.Word(pyp.alphanums + "_-/.:") |
|
303 |
quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote |
|
304 |
ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() + |
|
305 |
pyp.Word(pyp.nums + ".") + colon + number) |
|
306 |
ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() + |
|
307 |
pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") + |
|
308 |
pyp.Optional(rbracket) + colon + number) |
|
309 |
# meta device, extended syntax |
|
310 |
meta_value = ((value ^ quoted) + lbracket + number + rbracket) |
|
311 |
# device name, extended syntax |
|
312 |
device_value = pyp.Literal("minor").suppress() + number |
|
313 |
|
|
314 |
# a statement |
|
315 |
stmt = (~rbrace + keyword + ~lbrace + |
|
316 |
pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^ |
|
317 |
device_value) + |
|
318 |
pyp.Optional(defa) + semi + |
|
319 |
pyp.Optional(pyp.restOfLine).suppress()) |
|
320 |
|
|
321 |
# an entire section |
|
322 |
section_name = pyp.Word(pyp.alphas + "_") |
|
323 |
section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace |
|
324 |
|
|
325 |
bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt)) |
|
326 |
bnf.ignore(comment) |
|
327 |
|
|
328 |
cls._PARSE_SHOW = bnf |
|
329 |
|
|
330 |
return bnf |
|
331 |
|
|
332 |
@classmethod |
|
333 |
def GetDevInfo(cls, show_data): |
|
334 |
"""Parse details about a given DRBD minor. |
|
335 |
|
|
336 |
This returns, if available, the local backing device (as a path) |
|
337 |
and the local and remote (ip, port) information from a string |
|
338 |
containing the output of the `drbdsetup show` command as returned |
|
339 |
by DRBD8._GetShowData. |
|
340 |
|
|
341 |
This will return a dict with keys: |
|
342 |
- local_dev |
|
343 |
- meta_dev |
|
344 |
- meta_index |
|
345 |
- local_addr |
|
346 |
- remote_addr |
|
347 |
|
|
348 |
""" |
|
349 |
retval = {} |
|
350 |
if not show_data: |
|
351 |
return retval |
|
352 |
|
|
353 |
try: |
|
354 |
# run pyparse |
|
355 |
results = (cls._GetShowParser()).parseString(show_data) |
|
356 |
except pyp.ParseException, err: |
|
357 |
base.ThrowError("Can't parse drbdsetup show output: %s", str(err)) |
|
358 |
|
|
359 |
# and massage the results into our desired format |
|
360 |
for section in results: |
|
361 |
sname = section[0] |
|
362 |
if sname == "_this_host": |
|
363 |
for lst in section[1:]: |
|
364 |
if lst[0] == "disk": |
|
365 |
retval["local_dev"] = lst[1] |
|
366 |
elif lst[0] == "meta-disk": |
|
367 |
retval["meta_dev"] = lst[1] |
|
368 |
retval["meta_index"] = lst[2] |
|
369 |
elif lst[0] == "address": |
|
370 |
retval["local_addr"] = tuple(lst[1:]) |
|
371 |
elif sname == "_remote_host": |
|
372 |
for lst in section[1:]: |
|
373 |
if lst[0] == "address": |
|
374 |
retval["remote_addr"] = tuple(lst[1:]) |
|
375 |
return retval |
|
376 |
|
|
377 |
|
|
268 | 378 |
class DRBD8(base.BlockDev): |
269 | 379 |
"""DRBD v8.x block device. |
270 | 380 |
|
... | ... | |
283 | 393 |
_USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper" |
284 | 394 |
|
285 | 395 |
_MAX_MINORS = 255 |
286 |
_PARSE_SHOW = None |
|
287 | 396 |
|
288 | 397 |
# timeout constants |
289 | 398 |
_NET_RECONFIG_TIMEOUT = 60 |
... | ... | |
450 | 559 |
return highest + 1 |
451 | 560 |
|
452 | 561 |
@classmethod |
453 |
def _GetShowParser(cls): |
|
454 |
"""Return a parser for `drbd show` output. |
|
455 |
|
|
456 |
This will either create or return an already-created parser for the |
|
457 |
output of the command `drbd show`. |
|
458 |
|
|
459 |
""" |
|
460 |
if cls._PARSE_SHOW is not None: |
|
461 |
return cls._PARSE_SHOW |
|
462 |
|
|
463 |
# pyparsing setup |
|
464 |
lbrace = pyp.Literal("{").suppress() |
|
465 |
rbrace = pyp.Literal("}").suppress() |
|
466 |
lbracket = pyp.Literal("[").suppress() |
|
467 |
rbracket = pyp.Literal("]").suppress() |
|
468 |
semi = pyp.Literal(";").suppress() |
|
469 |
colon = pyp.Literal(":").suppress() |
|
470 |
# this also converts the value to an int |
|
471 |
number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0])) |
|
472 |
|
|
473 |
comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine) |
|
474 |
defa = pyp.Literal("_is_default").suppress() |
|
475 |
dbl_quote = pyp.Literal('"').suppress() |
|
476 |
|
|
477 |
keyword = pyp.Word(pyp.alphanums + "-") |
|
478 |
|
|
479 |
# value types |
|
480 |
value = pyp.Word(pyp.alphanums + "_-/.:") |
|
481 |
quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote |
|
482 |
ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() + |
|
483 |
pyp.Word(pyp.nums + ".") + colon + number) |
|
484 |
ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() + |
|
485 |
pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") + |
|
486 |
pyp.Optional(rbracket) + colon + number) |
|
487 |
# meta device, extended syntax |
|
488 |
meta_value = ((value ^ quoted) + lbracket + number + rbracket) |
|
489 |
# device name, extended syntax |
|
490 |
device_value = pyp.Literal("minor").suppress() + number |
|
491 |
|
|
492 |
# a statement |
|
493 |
stmt = (~rbrace + keyword + ~lbrace + |
|
494 |
pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^ |
|
495 |
device_value) + |
|
496 |
pyp.Optional(defa) + semi + |
|
497 |
pyp.Optional(pyp.restOfLine).suppress()) |
|
498 |
|
|
499 |
# an entire section |
|
500 |
section_name = pyp.Word(pyp.alphas + "_") |
|
501 |
section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace |
|
502 |
|
|
503 |
bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt)) |
|
504 |
bnf.ignore(comment) |
|
505 |
|
|
506 |
cls._PARSE_SHOW = bnf |
|
507 |
|
|
508 |
return bnf |
|
509 |
|
|
510 |
@classmethod |
|
511 | 562 |
def _GetShowData(cls, minor): |
512 | 563 |
"""Return the `drbdsetup show` data for a minor. |
513 | 564 |
|
... | ... | |
519 | 570 |
return None |
520 | 571 |
return result.stdout |
521 | 572 |
|
522 |
@classmethod |
|
523 |
def _GetDevInfo(cls, out): |
|
524 |
"""Parse details about a given DRBD minor. |
|
525 |
|
|
526 |
This return, if available, the local backing device (as a path) |
|
527 |
and the local and remote (ip, port) information from a string |
|
528 |
containing the output of the `drbdsetup show` command as returned |
|
529 |
by _GetShowData. |
|
530 |
|
|
531 |
""" |
|
532 |
data = {} |
|
533 |
if not out: |
|
534 |
return data |
|
535 |
|
|
536 |
bnf = cls._GetShowParser() |
|
537 |
# run pyparse |
|
538 |
|
|
539 |
try: |
|
540 |
results = bnf.parseString(out) |
|
541 |
except pyp.ParseException, err: |
|
542 |
base.ThrowError("Can't parse drbdsetup show output: %s", str(err)) |
|
543 |
|
|
544 |
# and massage the results into our desired format |
|
545 |
for section in results: |
|
546 |
sname = section[0] |
|
547 |
if sname == "_this_host": |
|
548 |
for lst in section[1:]: |
|
549 |
if lst[0] == "disk": |
|
550 |
data["local_dev"] = lst[1] |
|
551 |
elif lst[0] == "meta-disk": |
|
552 |
data["meta_dev"] = lst[1] |
|
553 |
data["meta_index"] = lst[2] |
|
554 |
elif lst[0] == "address": |
|
555 |
data["local_addr"] = tuple(lst[1:]) |
|
556 |
elif sname == "_remote_host": |
|
557 |
for lst in section[1:]: |
|
558 |
if lst[0] == "address": |
|
559 |
data["remote_addr"] = tuple(lst[1:]) |
|
560 |
return data |
|
561 |
|
|
562 | 573 |
def _MatchesLocal(self, info): |
563 | 574 |
"""Test if our local config matches with an existing device. |
564 | 575 |
|
... | ... | |
775 | 786 |
minor, result.fail_reason, result.output) |
776 | 787 |
|
777 | 788 |
def _CheckNetworkConfig(): |
778 |
info = self._GetDevInfo(self._GetShowData(minor))
|
|
789 |
info = DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))
|
|
779 | 790 |
if not "local_addr" in info or not "remote_addr" in info: |
780 | 791 |
raise utils.RetryAgain() |
781 | 792 |
|
... | ... | |
797 | 808 |
self._aminor) |
798 | 809 |
if len(devices) != 2: |
799 | 810 |
base.ThrowError("drbd%d: need two devices for AddChildren", self.minor) |
800 |
info = self._GetDevInfo(self._GetShowData(self.minor))
|
|
811 |
info = DRBD8ShowInfo.GetDevInfo(self._GetShowData(self.minor))
|
|
801 | 812 |
if "local_dev" in info: |
802 | 813 |
base.ThrowError("drbd%d: already attached to a local disk", self.minor) |
803 | 814 |
backend, meta = devices |
... | ... | |
820 | 831 |
base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren", |
821 | 832 |
self._aminor) |
822 | 833 |
# early return if we don't actually have backing storage |
823 |
info = self._GetDevInfo(self._GetShowData(self.minor))
|
|
834 |
info = DRBD8ShowInfo.GetDevInfo(self._GetShowData(self.minor))
|
|
824 | 835 |
if "local_dev" not in info: |
825 | 836 |
return |
826 | 837 |
if len(self._children) != 2: |
... | ... | |
1172 | 1183 |
# pylint: disable=W0631 |
1173 | 1184 |
net_data = (self._lhost, self._lport, self._rhost, self._rport) |
1174 | 1185 |
for minor in (self._aminor,): |
1175 |
info = self._GetDevInfo(self._GetShowData(minor))
|
|
1186 |
info = DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))
|
|
1176 | 1187 |
match_l = self._MatchesLocal(info) |
1177 | 1188 |
match_r = self._MatchesNet(info) |
1178 | 1189 |
|
... | ... | |
1184 | 1195 |
# disk matches, but not attached to network, attach and recheck |
1185 | 1196 |
self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL, |
1186 | 1197 |
hmac=constants.DRBD_HMAC_ALG, secret=self._secret) |
1187 |
if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
|
|
1198 |
if self._MatchesNet(DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))):
|
|
1188 | 1199 |
break |
1189 | 1200 |
else: |
1190 | 1201 |
base.ThrowError("drbd%d: network attach successful, but 'drbdsetup" |
... | ... | |
1194 | 1205 |
# no local disk, but network attached and it matches |
1195 | 1206 |
self._AssembleLocal(minor, self._children[0].dev_path, |
1196 | 1207 |
self._children[1].dev_path, self.size) |
1197 |
if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
|
|
1208 |
if self._MatchesNet(DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))):
|
|
1198 | 1209 |
break |
1199 | 1210 |
else: |
1200 | 1211 |
base.ThrowError("drbd%d: disk attach successful, but 'drbdsetup" |
... | ... | |
1221 | 1232 |
# None) |
1222 | 1233 |
self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL, |
1223 | 1234 |
hmac=constants.DRBD_HMAC_ALG, secret=self._secret) |
1224 |
if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
|
|
1235 |
if self._MatchesNet(DRBD8ShowInfo.GetDevInfo(self._GetShowData(minor))):
|
|
1225 | 1236 |
break |
1226 | 1237 |
else: |
1227 | 1238 |
base.ThrowError("drbd%d: network attach successful, but 'drbdsetup" |
Also available in: Unified diff