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