Statistics
| Branch: | Tag: | Revision:

root / lib / storage / drbd_cmdgen.py @ 355d1f32

History | View | Annotate | Download (14.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""DRBD command generating classes"""
23

    
24
import logging
25
import shlex
26

    
27
from ganeti import constants
28
from ganeti import errors
29

    
30

    
31
class BaseDRBDCmdGenerator(object):
32
  """Base class for DRBD command generators.
33

34
  This class defines the interface for the command generators and holds shared
35
  code.
36

37
  """
38
  def __init__(self, version):
39
    self._version = version
40

    
41
  def GenShowCmd(self, minor):
42
    raise NotImplementedError
43

    
44
  def GenInitMetaCmd(self, minor, meta_dev):
45
    raise NotImplementedError
46

    
47
  def GenLocalInitCmds(self, minor, data_dev, meta_dev, size_mb, params):
48
    raise NotImplementedError
49

    
50
  def GenNetInitCmd(self, minor, family, lhost, lport, rhost, rport, protocol,
51
                    dual_pri, hmac, secret, params):
52
    raise NotImplementedError
53

    
54
  def GenSyncParamsCmd(self, minor, params):
55
    raise NotImplementedError
56

    
57
  def GenPauseSyncCmd(self, minor):
58
    raise NotImplementedError
59

    
60
  def GenResumeSyncCmd(self, minor):
61
    raise NotImplementedError
62

    
63
  def GenPrimaryCmd(self, minor, force):
64
    raise NotImplementedError
65

    
66
  def GenSecondaryCmd(self, minor):
67
    raise NotImplementedError
68

    
69
  def GenDetachCmd(self, minor):
70
    raise NotImplementedError
71

    
72
  def GenDisconnectCmd(self, minor, family, lhost, lport, rhost, rport):
73
    raise NotImplementedError
74

    
75
  def GenDownCmd(self, minor):
76
    raise NotImplementedError
77

    
78
  def GenResizeCmd(self, minor, size_mb):
79
    raise NotImplementedError
80

    
81
  @staticmethod
82
  def _DevPath(minor):
83
    """Return the path to a drbd device for a given minor.
84

85
    """
86
    return "/dev/drbd%d" % minor
87

    
88

    
89
class DRBD83CmdGenerator(BaseDRBDCmdGenerator):
90
  """Generates drbdsetup commands suited for the DRBD <= 8.3 syntax.
91

92
  """
93
  # command line options for barriers
94
  _DISABLE_DISK_OPTION = "--no-disk-barrier"  # -a
95
  _DISABLE_DRAIN_OPTION = "--no-disk-drain"   # -D
96
  _DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i
97
  _DISABLE_META_FLUSH_OPTION = "--no-md-flushes"  # -m
98

    
99
  def __init__(self, version):
100
    super(DRBD83CmdGenerator, self).__init__(version)
101

    
102
  def GenShowCmd(self, minor):
103
    return ["drbdsetup", self._DevPath(minor), "show"]
104

    
105
  def GenInitMetaCmd(self, minor, meta_dev):
106
    return ["drbdmeta", "--force", self._DevPath(minor),
107
            "v08", meta_dev, "0", "create-md"]
108

    
109
  def GenLocalInitCmds(self, minor, data_dev, meta_dev, size_mb, params):
110
    args = ["drbdsetup", self._DevPath(minor), "disk",
111
            data_dev, meta_dev, "0",
112
            "-e", "detach",
113
            "--create-device"]
114
    if size_mb:
115
      args.extend(["-d", "%sm" % size_mb])
116

    
117
    vmaj = self._version["k_major"]
118
    vmin = self._version["k_minor"]
119
    vrel = self._version["k_point"]
120

    
121
    barrier_args = \
122
      self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
123
                                   params[constants.LDP_BARRIERS],
124
                                   params[constants.LDP_NO_META_FLUSH])
125
    args.extend(barrier_args)
126

    
127
    if params[constants.LDP_DISK_CUSTOM]:
128
      args.extend(shlex.split(params[constants.LDP_DISK_CUSTOM]))
129

    
130
    return [args]
131

    
132
  def GenNetInitCmd(self, minor, family, lhost, lport, rhost, rport, protocol,
133
                    dual_pri, hmac, secret, params):
134
    args = ["drbdsetup", self._DevPath(minor), "net",
135
            "%s:%s:%s" % (family, lhost, lport),
136
            "%s:%s:%s" % (family, rhost, rport), protocol,
137
            "-A", "discard-zero-changes",
138
            "-B", "consensus",
139
            "--create-device",
140
            ]
141
    if dual_pri:
142
      args.append("-m")
143
    if hmac and secret:
144
      args.extend(["-a", hmac, "-x", secret])
145

    
146
    if params[constants.LDP_NET_CUSTOM]:
147
      args.extend(shlex.split(params[constants.LDP_NET_CUSTOM]))
148

    
149
    return args
150

    
151
  def GenSyncParamsCmd(self, minor, params):
152
    args = ["drbdsetup", self._DevPath(minor), "syncer"]
153
    if params[constants.LDP_DYNAMIC_RESYNC]:
154
      vmin = self._version["k_minor"]
155
      vrel = self._version["k_point"]
156

    
157
      # By definition we are using 8.x, so just check the rest of the version
158
      # number
159
      if vmin != 3 or vrel < 9:
160
        msg = ("The current DRBD version (8.%d.%d) does not support the "
161
               "dynamic resync speed controller" % (vmin, vrel))
162
        logging.error(msg)
163
        return [msg]
164

    
165
      if params[constants.LDP_PLAN_AHEAD] == 0:
166
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
167
               " controller at DRBD level. If you want to disable it, please"
168
               " set the dynamic-resync disk parameter to False.")
169
        logging.error(msg)
170
        return [msg]
171

    
172
      # add the c-* parameters to args
173
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
174
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
175
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
176
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
177
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
178
                   ])
179

    
180
    else:
181
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
182

    
183
    args.append("--create-device")
184

    
185
    return args
186

    
187
  def GenPauseSyncCmd(self, minor):
188
    return ["drbdsetup", self._DevPath(minor), "pause-sync"]
189

    
190
  def GenResumeSyncCmd(self, minor):
191
    return ["drbdsetup", self._DevPath(minor), "resume-sync"]
192

    
193
  def GenPrimaryCmd(self, minor, force):
194
    cmd = ["drbdsetup", self._DevPath(minor), "primary"]
195

    
196
    if force:
197
      cmd.append("-o")
198

    
199
    return cmd
200

    
201
  def GenSecondaryCmd(self, minor):
202
    return ["drbdsetup", self._DevPath(minor), "secondary"]
203

    
204
  def GenDetachCmd(self, minor):
205
    return ["drbdsetup", self._DevPath(minor), "detach"]
206

    
207
  def GenDisconnectCmd(self, minor, family, lhost, lport, rhost, rport):
208
    return ["drbdsetup", self._DevPath(minor), "disconnect"]
209

    
210
  def GenDownCmd(self, minor):
211
    return ["drbdsetup", self._DevPath(minor), "down"]
212

    
213
  def GenResizeCmd(self, minor, size_mb):
214
    return ["drbdsetup", self._DevPath(minor), "resize", "-s", "%dm" % size_mb]
215

    
216
  @classmethod
217
  def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
218
                              disable_meta_flush):
219
    """Compute the DRBD command line parameters for disk barriers
220

221
    Returns a list of the disk barrier parameters as requested via the
222
    disabled_barriers and disable_meta_flush arguments, and according to the
223
    supported ones in the DRBD version vmaj.vmin.vrel
224

225
    If the desired option is unsupported, raises errors.BlockDeviceError.
226

227
    """
228
    disabled_barriers_set = frozenset(disabled_barriers)
229
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
230
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
231
                                    " barriers" % disabled_barriers)
232

    
233
    args = []
234

    
235
    # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
236
    # does not exist)
237
    if not vmaj == 8 and vmin in (0, 2, 3):
238
      raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
239
                                    (vmaj, vmin, vrel))
240

    
241
    def _AppendOrRaise(option, min_version):
242
      """Helper for DRBD options"""
243
      if min_version is not None and vrel >= min_version:
244
        args.append(option)
245
      else:
246
        raise errors.BlockDeviceError("Could not use the option %s as the"
247
                                      " DRBD version %d.%d.%d does not support"
248
                                      " it." % (option, vmaj, vmin, vrel))
249

    
250
    # the minimum version for each feature is encoded via pairs of (minor
251
    # version -> x) where x is version in which support for the option was
252
    # introduced.
253
    meta_flush_supported = disk_flush_supported = {
254
      0: 12,
255
      2: 7,
256
      3: 0,
257
      }
258

    
259
    disk_drain_supported = {
260
      2: 7,
261
      3: 0,
262
      }
263

    
264
    disk_barriers_supported = {
265
      3: 0,
266
      }
267

    
268
    # meta flushes
269
    if disable_meta_flush:
270
      _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
271
                     meta_flush_supported.get(vmin, None))
272

    
273
    # disk flushes
274
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
275
      _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
276
                     disk_flush_supported.get(vmin, None))
277

    
278
    # disk drain
279
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
280
      _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
281
                     disk_drain_supported.get(vmin, None))
282

    
283
    # disk barriers
284
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
285
      _AppendOrRaise(cls._DISABLE_DISK_OPTION,
286
                     disk_barriers_supported.get(vmin, None))
287

    
288
    return args
289

    
290

    
291
class DRBD84CmdGenerator(BaseDRBDCmdGenerator):
292
  """Generates drbdsetup commands suited for the DRBD >= 8.4 syntax.
293

294
  """
295
  # command line options for barriers
296
  _DISABLE_DISK_OPTION = "--disk-barrier=no"
297
  _DISABLE_DRAIN_OPTION = "--disk-drain=no"
298
  _DISABLE_FLUSH_OPTION = "--disk-flushes=no"
299
  _DISABLE_META_FLUSH_OPTION = "--md-flushes=no"
300

    
301
  def __init__(self, version):
302
    super(DRBD84CmdGenerator, self).__init__(version)
303

    
304
  def GenShowCmd(self, minor):
305
    return ["drbdsetup", "show", minor]
306

    
307
  def GenInitMetaCmd(self, minor, meta_dev):
308
    return ["drbdmeta", "--force", self._DevPath(minor),
309
            "v08", meta_dev, "flex-external", "create-md"]
310

    
311
  def GenLocalInitCmds(self, minor, data_dev, meta_dev, size_mb, params):
312
    cmds = []
313

    
314
    cmds.append(["drbdsetup", "new-resource", self._GetResource(minor)])
315
    cmds.append(["drbdsetup", "new-minor", self._GetResource(minor),
316
                 str(minor), "0"])
317
    # We need to apply the activity log before attaching the disk else drbdsetup
318
    # will fail.
319
    cmds.append(["drbdmeta", self._DevPath(minor),
320
                 "v08", meta_dev, "flex-external", "apply-al"])
321

    
322
    attach_cmd = ["drbdsetup", "attach", minor, data_dev, meta_dev, "flexible",
323
                  "--on-io-error=detach"]
324
    if size_mb:
325
      attach_cmd.extend(["--size", "%sm" % size_mb])
326

    
327
    barrier_args = \
328
      self._ComputeDiskBarrierArgs(params[constants.LDP_BARRIERS],
329
                                   params[constants.LDP_NO_META_FLUSH])
330
    attach_cmd.extend(barrier_args)
331

    
332
    if params[constants.LDP_DISK_CUSTOM]:
333
      attach_cmd.extend(shlex.split(params[constants.LDP_DISK_CUSTOM]))
334

    
335
    cmds.append(attach_cmd)
336

    
337
    return cmds
338

    
339
  def GenNetInitCmd(self, minor, family, lhost, lport, rhost, rport, protocol,
340
                    dual_pri, hmac, secret, params):
341
    args = ["drbdsetup", "connect", self._GetResource(minor),
342
            "%s:%s:%s" % (family, lhost, lport),
343
            "%s:%s:%s" % (family, rhost, rport),
344
            "--protocol", protocol,
345
            "--after-sb-0pri", "discard-zero-changes",
346
            "--after-sb-1pri", "consensus"
347
            ]
348
    if dual_pri:
349
      args.append("--allow-two-primaries")
350
    if hmac and secret:
351
      args.extend(["--cram-hmac-alg", hmac, "--shared-secret", secret])
352

    
353
    if params[constants.LDP_NET_CUSTOM]:
354
      args.extend(shlex.split(params[constants.LDP_NET_CUSTOM]))
355

    
356
    return args
357

    
358
  def GenSyncParamsCmd(self, minor, params):
359
    args = ["drbdsetup", "disk-options", minor]
360
    if params[constants.LDP_DYNAMIC_RESYNC]:
361
      if params[constants.LDP_PLAN_AHEAD] == 0:
362
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
363
               " controller at DRBD level. If you want to disable it, please"
364
               " set the dynamic-resync disk parameter to False.")
365
        logging.error(msg)
366
        return [msg]
367

    
368
      # add the c-* parameters to args
369
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
370
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
371
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
372
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
373
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
374
                   ])
375

    
376
    else:
377
      args.extend(["--resync-rate", "%d" % params[constants.LDP_RESYNC_RATE]])
378

    
379
    return args
380

    
381
  def GenPauseSyncCmd(self, minor):
382
    return ["drbdsetup", "pause-sync", minor]
383

    
384
  def GenResumeSyncCmd(self, minor):
385
    return ["drbdsetup", "resume-sync", minor]
386

    
387
  def GenPrimaryCmd(self, minor, force):
388
    cmd = ["drbdsetup", "primary", minor]
389

    
390
    if force:
391
      cmd.append("--force")
392

    
393
    return cmd
394

    
395
  def GenSecondaryCmd(self, minor):
396
    return ["drbdsetup", "secondary", minor]
397

    
398
  def GenDetachCmd(self, minor):
399
    return ["drbdsetup", "detach", minor]
400

    
401
  def GenDisconnectCmd(self, minor, family, lhost, lport, rhost, rport):
402
    return ["drbdsetup", "disconnect",
403
            "%s:%s:%s" % (family, lhost, lport),
404
            "%s:%s:%s" % (family, rhost, rport)]
405

    
406
  def GenDownCmd(self, minor):
407
    return ["drbdsetup", "down", self._GetResource(minor)]
408

    
409
  def GenResizeCmd(self, minor, size_mb):
410
    return ["drbdsetup", "resize", minor, "--size", "%dm" % size_mb]
411

    
412
  @staticmethod
413
  def _GetResource(minor):
414
    """Return the resource name for a given minor.
415

416
    Currently we don't support DRBD volumes which share a resource, so we
417
    generate the resource name based on the minor the resulting volumes is
418
    assigned to.
419

420
    """
421
    return "resource%d" % minor
422

    
423
  @classmethod
424
  def _ComputeDiskBarrierArgs(cls, disabled_barriers, disable_meta_flush):
425
    """Compute the DRBD command line parameters for disk barriers
426

427
    """
428
    disabled_barriers_set = frozenset(disabled_barriers)
429
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
430
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
431
                                    " barriers" % disabled_barriers)
432

    
433
    args = []
434

    
435
    # meta flushes
436
    if disable_meta_flush:
437
      args.append(cls._DISABLE_META_FLUSH_OPTION)
438

    
439
    # disk flushes
440
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
441
      args.append(cls._DISABLE_FLUSH_OPTION)
442

    
443
    # disk drain
444
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
445
      args.append(cls._DISABLE_DRAIN_OPTION)
446

    
447
    # disk barriers
448
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
449
      args.append(cls._DISABLE_DISK_OPTION)
450

    
451
    return args