Revision 09a78e1c lib/block/drbd.py
b/lib/block/drbd.py | ||
---|---|---|
23 | 23 |
|
24 | 24 |
import errno |
25 | 25 |
import logging |
26 |
import shlex |
|
27 | 26 |
import time |
28 | 27 |
|
29 | 28 |
from ganeti import constants |
... | ... | |
35 | 34 |
from ganeti.block.drbd_info import DRBD8Info |
36 | 35 |
from ganeti.block.drbd_info import DRBD83ShowInfo |
37 | 36 |
from ganeti.block.drbd_info import DRBD84ShowInfo |
37 |
from ganeti.block import drbd_cmdgen |
|
38 | 38 |
|
39 | 39 |
|
40 | 40 |
# Size of reads in _CanReadDevice |
... | ... | |
64 | 64 |
# timeout constants |
65 | 65 |
_NET_RECONFIG_TIMEOUT = 60 |
66 | 66 |
|
67 |
# command line options for barriers |
|
68 |
_DISABLE_DISK_OPTION = "--no-disk-barrier" # -a |
|
69 |
_DISABLE_DRAIN_OPTION = "--no-disk-drain" # -D |
|
70 |
_DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i |
|
71 |
_DISABLE_META_FLUSH_OPTION = "--no-md-flushes" # -m |
|
72 |
|
|
73 | 67 |
def __init__(self, unique_id, children, size, params): |
74 | 68 |
if children and children.count(None) > 0: |
75 | 69 |
children = [] |
... | ... | |
96 | 90 |
|
97 | 91 |
if version["k_minor"] <= 3: |
98 | 92 |
self._show_info_cls = DRBD83ShowInfo |
93 |
self._cmd_gen = drbd_cmdgen.DRBD83CmdGenerator(drbd_info) |
|
99 | 94 |
else: |
100 | 95 |
self._show_info_cls = DRBD84ShowInfo |
96 |
# FIXME: use proper command generator! |
|
97 |
self._cmd_gen = None |
|
101 | 98 |
|
102 | 99 |
if (self._lhost is not None and self._lhost == self._rhost and |
103 | 100 |
self._lport == self._rport): |
... | ... | |
230 | 227 |
|
231 | 228 |
return highest + 1 |
232 | 229 |
|
233 |
@classmethod |
|
234 |
def _GetShowData(cls, minor): |
|
230 |
def _GetShowData(self, minor): |
|
235 | 231 |
"""Return the `drbdsetup show` data for a minor. |
236 | 232 |
|
237 | 233 |
""" |
238 |
result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
|
|
234 |
result = utils.RunCmd(self._cmd_gen.GenShowCmd(minor))
|
|
239 | 235 |
if result.failed: |
240 | 236 |
logging.error("Can't display the drbd config: %s - %s", |
241 | 237 |
result.fail_reason, result.output) |
... | ... | |
303 | 299 |
"""Configure the local part of a DRBD device. |
304 | 300 |
|
305 | 301 |
""" |
306 |
args = ["drbdsetup", self._DevPath(minor), "disk", |
|
307 |
backend, meta, "0", |
|
308 |
"-e", "detach", |
|
309 |
"--create-device"] |
|
310 |
if size: |
|
311 |
args.extend(["-d", "%sm" % size]) |
|
312 |
|
|
313 |
drbd_info = DRBD8Info.CreateFromFile() |
|
314 |
version = drbd_info.GetVersion() |
|
315 |
vmaj = version["k_major"] |
|
316 |
vmin = version["k_minor"] |
|
317 |
vrel = version["k_point"] |
|
318 |
|
|
319 |
barrier_args = \ |
|
320 |
self._ComputeDiskBarrierArgs(vmaj, vmin, vrel, |
|
321 |
self.params[constants.LDP_BARRIERS], |
|
322 |
self.params[constants.LDP_NO_META_FLUSH]) |
|
323 |
args.extend(barrier_args) |
|
324 |
|
|
325 |
if self.params[constants.LDP_DISK_CUSTOM]: |
|
326 |
args.extend(shlex.split(self.params[constants.LDP_DISK_CUSTOM])) |
|
327 |
|
|
328 |
result = utils.RunCmd(args) |
|
329 |
if result.failed: |
|
330 |
base.ThrowError("drbd%d: can't attach local disk: %s", |
|
331 |
minor, result.output) |
|
332 |
|
|
333 |
@classmethod |
|
334 |
def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers, |
|
335 |
disable_meta_flush): |
|
336 |
"""Compute the DRBD command line parameters for disk barriers |
|
302 |
cmds = self._cmd_gen.GenLocalInitCmds(minor, backend, meta, |
|
303 |
size, self.params) |
|
337 | 304 |
|
338 |
Returns a list of the disk barrier parameters as requested via the |
|
339 |
disabled_barriers and disable_meta_flush arguments, and according to the |
|
340 |
supported ones in the DRBD version vmaj.vmin.vrel |
|
341 |
|
|
342 |
If the desired option is unsupported, raises errors.BlockDeviceError. |
|
343 |
|
|
344 |
""" |
|
345 |
disabled_barriers_set = frozenset(disabled_barriers) |
|
346 |
if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT: |
|
347 |
raise errors.BlockDeviceError("%s is not a valid option set for DRBD" |
|
348 |
" barriers" % disabled_barriers) |
|
349 |
|
|
350 |
args = [] |
|
351 |
|
|
352 |
# The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x |
|
353 |
# does not exist) |
|
354 |
if not vmaj == 8 and vmin in (0, 2, 3): |
|
355 |
raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" % |
|
356 |
(vmaj, vmin, vrel)) |
|
357 |
|
|
358 |
def _AppendOrRaise(option, min_version): |
|
359 |
"""Helper for DRBD options""" |
|
360 |
if min_version is not None and vrel >= min_version: |
|
361 |
args.append(option) |
|
362 |
else: |
|
363 |
raise errors.BlockDeviceError("Could not use the option %s as the" |
|
364 |
" DRBD version %d.%d.%d does not support" |
|
365 |
" it." % (option, vmaj, vmin, vrel)) |
|
366 |
|
|
367 |
# the minimum version for each feature is encoded via pairs of (minor |
|
368 |
# version -> x) where x is version in which support for the option was |
|
369 |
# introduced. |
|
370 |
meta_flush_supported = disk_flush_supported = { |
|
371 |
0: 12, |
|
372 |
2: 7, |
|
373 |
3: 0, |
|
374 |
} |
|
375 |
|
|
376 |
disk_drain_supported = { |
|
377 |
2: 7, |
|
378 |
3: 0, |
|
379 |
} |
|
380 |
|
|
381 |
disk_barriers_supported = { |
|
382 |
3: 0, |
|
383 |
} |
|
384 |
|
|
385 |
# meta flushes |
|
386 |
if disable_meta_flush: |
|
387 |
_AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION, |
|
388 |
meta_flush_supported.get(vmin, None)) |
|
389 |
|
|
390 |
# disk flushes |
|
391 |
if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set: |
|
392 |
_AppendOrRaise(cls._DISABLE_FLUSH_OPTION, |
|
393 |
disk_flush_supported.get(vmin, None)) |
|
394 |
|
|
395 |
# disk drain |
|
396 |
if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set: |
|
397 |
_AppendOrRaise(cls._DISABLE_DRAIN_OPTION, |
|
398 |
disk_drain_supported.get(vmin, None)) |
|
399 |
|
|
400 |
# disk barriers |
|
401 |
if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set: |
|
402 |
_AppendOrRaise(cls._DISABLE_DISK_OPTION, |
|
403 |
disk_barriers_supported.get(vmin, None)) |
|
404 |
|
|
405 |
return args |
|
305 |
for cmd in cmds: |
|
306 |
result = utils.RunCmd(cmd) |
|
307 |
if result.failed: |
|
308 |
base.ThrowError("drbd%d: can't attach local disk: %s", |
|
309 |
minor, result.output) |
|
406 | 310 |
|
407 | 311 |
def _AssembleNet(self, minor, net_info, protocol, |
408 | 312 |
dual_pri=False, hmac=None, secret=None): |
... | ... | |
440 | 344 |
else: |
441 | 345 |
base.ThrowError("drbd%d: Invalid ip %s" % (minor, lhost)) |
442 | 346 |
|
443 |
args = ["drbdsetup", self._DevPath(minor), "net", |
|
444 |
"%s:%s:%s" % (family, lhost, lport), |
|
445 |
"%s:%s:%s" % (family, rhost, rport), protocol, |
|
446 |
"-A", "discard-zero-changes", |
|
447 |
"-B", "consensus", |
|
448 |
"--create-device", |
|
449 |
] |
|
450 |
if dual_pri: |
|
451 |
args.append("-m") |
|
452 |
if hmac and secret: |
|
453 |
args.extend(["-a", hmac, "-x", secret]) |
|
454 |
|
|
455 |
if self.params[constants.LDP_NET_CUSTOM]: |
|
456 |
args.extend(shlex.split(self.params[constants.LDP_NET_CUSTOM])) |
|
457 |
|
|
458 |
result = utils.RunCmd(args) |
|
347 |
cmd = self._cmd_gen.GenNetInitCmd(minor, family, lhost, lport, |
|
348 |
rhost, rport, protocol, |
|
349 |
dual_pri, hmac, secret, self.params) |
|
350 |
|
|
351 |
result = utils.RunCmd(cmd) |
|
459 | 352 |
if result.failed: |
460 | 353 |
base.ThrowError("drbd%d: can't setup network: %s - %s", |
461 | 354 |
minor, result.fail_reason, result.output) |
... | ... | |
539 | 432 |
@return: a list of error messages |
540 | 433 |
|
541 | 434 |
""" |
542 |
|
|
543 |
args = ["drbdsetup", self._DevPath(minor), "syncer"] |
|
544 |
if params[constants.LDP_DYNAMIC_RESYNC]: |
|
545 |
drbd_info = DRBD8Info.CreateFromFile() |
|
546 |
version = drbd_info.GetVersion() |
|
547 |
vmin = version["k_minor"] |
|
548 |
vrel = version["k_point"] |
|
549 |
|
|
550 |
# By definition we are using 8.x, so just check the rest of the version |
|
551 |
# number |
|
552 |
if vmin != 3 or vrel < 9: |
|
553 |
msg = ("The current DRBD version (8.%d.%d) does not support the " |
|
554 |
"dynamic resync speed controller" % (vmin, vrel)) |
|
555 |
logging.error(msg) |
|
556 |
return [msg] |
|
557 |
|
|
558 |
if params[constants.LDP_PLAN_AHEAD] == 0: |
|
559 |
msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed" |
|
560 |
" controller at DRBD level. If you want to disable it, please" |
|
561 |
" set the dynamic-resync disk parameter to False.") |
|
562 |
logging.error(msg) |
|
563 |
return [msg] |
|
564 |
|
|
565 |
# add the c-* parameters to args |
|
566 |
args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD], |
|
567 |
"--c-fill-target", params[constants.LDP_FILL_TARGET], |
|
568 |
"--c-delay-target", params[constants.LDP_DELAY_TARGET], |
|
569 |
"--c-max-rate", params[constants.LDP_MAX_RATE], |
|
570 |
"--c-min-rate", params[constants.LDP_MIN_RATE], |
|
571 |
]) |
|
572 |
|
|
573 |
else: |
|
574 |
args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]]) |
|
575 |
|
|
576 |
args.append("--create-device") |
|
577 |
result = utils.RunCmd(args) |
|
435 |
cmd = self._cmd_gen.GenSyncParamsCmd(minor, params) |
|
436 |
result = utils.RunCmd(cmd) |
|
578 | 437 |
if result.failed: |
579 | 438 |
msg = ("Can't change syncer rate: %s - %s" % |
580 | 439 |
(result.fail_reason, result.output)) |
... | ... | |
616 | 475 |
children_result = super(DRBD8, self).PauseResumeSync(pause) |
617 | 476 |
|
618 | 477 |
if pause: |
619 |
cmd = "pause-sync"
|
|
478 |
cmd = self._cmd_gen.GenPauseSyncCmd(self.minor)
|
|
620 | 479 |
else: |
621 |
cmd = "resume-sync"
|
|
480 |
cmd = self._cmd_gen.GenResumeSyncCmd(self.minor)
|
|
622 | 481 |
|
623 |
result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
|
|
482 |
result = utils.RunCmd(cmd)
|
|
624 | 483 |
if result.failed: |
625 | 484 |
logging.error("Can't %s: %s - %s", cmd, |
626 | 485 |
result.fail_reason, result.output) |
... | ... | |
689 | 548 |
if self.minor is None and not self.Attach(): |
690 | 549 |
logging.error("DRBD cannot attach to a device during open") |
691 | 550 |
return False |
692 |
cmd = ["drbdsetup", self.dev_path, "primary"] |
|
693 |
if force:
|
|
694 |
cmd.append("-o") |
|
551 |
|
|
552 |
cmd = self._cmd_gen.GenPrimaryCmd(self.minor, force)
|
|
553 |
|
|
695 | 554 |
result = utils.RunCmd(cmd) |
696 | 555 |
if result.failed: |
697 | 556 |
base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor, |
... | ... | |
705 | 564 |
""" |
706 | 565 |
if self.minor is None and not self.Attach(): |
707 | 566 |
base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor) |
708 |
result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"]) |
|
567 |
cmd = self._cmd_gen.GenSecondaryCmd(self.minor) |
|
568 |
result = utils.RunCmd(cmd) |
|
709 | 569 |
if result.failed: |
710 | 570 |
base.ThrowError("drbd%d: can't switch drbd device to secondary: %s", |
711 | 571 |
self.minor, result.output) |
... | ... | |
938 | 798 |
hmac=constants.DRBD_HMAC_ALG, secret=self._secret) |
939 | 799 |
self._SetFromMinor(minor) |
940 | 800 |
|
941 |
@classmethod |
|
942 |
def _ShutdownLocal(cls, minor): |
|
801 |
def _ShutdownLocal(self, minor): |
|
943 | 802 |
"""Detach from the local device. |
944 | 803 |
|
945 | 804 |
I/Os will continue to be served from the remote device. If we |
946 | 805 |
don't have a remote device, this operation will fail. |
947 | 806 |
|
948 | 807 |
""" |
949 |
result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"]) |
|
808 |
cmd = self._cmd_gen.GenDetachCmd(minor) |
|
809 |
result = utils.RunCmd(cmd) |
|
950 | 810 |
if result.failed: |
951 | 811 |
base.ThrowError("drbd%d: can't detach local disk: %s", |
952 | 812 |
minor, result.output) |
953 | 813 |
|
954 |
@classmethod |
|
955 |
def _ShutdownNet(cls, minor): |
|
814 |
def _ShutdownNet(self, minor): |
|
956 | 815 |
"""Disconnect from the remote peer. |
957 | 816 |
|
958 | 817 |
This fails if we don't have a local device. |
959 | 818 |
|
960 | 819 |
""" |
961 |
result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"]) |
|
820 |
cmd = self._cmd_gen.GenDisconnectCmd(minor) |
|
821 |
result = utils.RunCmd(cmd) |
|
962 | 822 |
if result.failed: |
963 | 823 |
base.ThrowError("drbd%d: can't shutdown network: %s", |
964 | 824 |
minor, result.output) |
... | ... | |
970 | 830 |
This will, of course, fail if the device is in use. |
971 | 831 |
|
972 | 832 |
""" |
973 |
result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"]) |
|
833 |
# FIXME: _ShutdownAll, despite being private, is used in nodemaint.py. |
|
834 |
# That's why we can't make it an instance method, which in turn requires |
|
835 |
# us to duplicate code here (from __init__). This should be proberly fixed. |
|
836 |
drbd_info = DRBD8Info.CreateFromFile() |
|
837 |
if drbd_info.GetVersion()["k_minor"] <= 3: |
|
838 |
cmd_gen = drbd_cmdgen.DRBD83CmdGenerator(drbd_info) |
|
839 |
else: |
|
840 |
# FIXME: use proper command generator! |
|
841 |
cmd_gen = None |
|
842 |
|
|
843 |
cmd = cmd_gen.GenDownCmd(minor) |
|
844 |
result = utils.RunCmd(cmd) |
|
974 | 845 |
if result.failed: |
975 | 846 |
base.ThrowError("drbd%d: can't shutdown drbd device: %s", |
976 | 847 |
minor, result.output) |
... | ... | |
1048 | 919 |
# DRBD does not support dry-run mode and is not backing storage, |
1049 | 920 |
# so we'll return here |
1050 | 921 |
return |
1051 |
result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
|
|
1052 |
"%dm" % (self.size + amount)])
|
|
922 |
cmd = self._cmd_gen.GenResizeCmd(self.minor, self.size + amount)
|
|
923 |
result = utils.RunCmd(cmd)
|
|
1053 | 924 |
if result.failed: |
1054 | 925 |
base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output) |
1055 | 926 |
|
Also available in: Unified diff