4 # Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
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.
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.
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
22 """DRBD command generating classes"""
27 from ganeti import constants
28 from ganeti import errors
31 class BaseDRBDCmdGenerator(object):
32 """Base class for DRBD command generators.
34 This class defines the interface for the command generators and holds shared
38 def __init__(self, version):
39 self._version = version
41 def GenShowCmd(self, minor):
42 raise NotImplementedError
44 def GenInitMetaCmd(self, minor, meta_dev):
45 raise NotImplementedError
47 def GenLocalInitCmds(self, minor, data_dev, meta_dev, size_mb, params):
48 raise NotImplementedError
50 def GenNetInitCmd(self, minor, family, lhost, lport, rhost, rport, protocol,
51 dual_pri, hmac, secret, params):
52 raise NotImplementedError
54 def GenSyncParamsCmd(self, minor, params):
55 raise NotImplementedError
57 def GenPauseSyncCmd(self, minor):
58 raise NotImplementedError
60 def GenResumeSyncCmd(self, minor):
61 raise NotImplementedError
63 def GenPrimaryCmd(self, minor, force):
64 raise NotImplementedError
66 def GenSecondaryCmd(self, minor):
67 raise NotImplementedError
69 def GenDetachCmd(self, minor):
70 raise NotImplementedError
72 def GenDisconnectCmd(self, minor, family, lhost, lport, rhost, rport):
73 raise NotImplementedError
75 def GenDownCmd(self, minor):
76 raise NotImplementedError
78 def GenResizeCmd(self, minor, size_mb):
79 raise NotImplementedError
83 """Return the path to a drbd device for a given minor.
86 return "/dev/drbd%d" % minor
89 class DRBD83CmdGenerator(BaseDRBDCmdGenerator):
90 """Generates drbdsetup commands suited for the DRBD <= 8.3 syntax.
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
99 def __init__(self, version):
100 super(DRBD83CmdGenerator, self).__init__(version)
102 def GenShowCmd(self, minor):
103 return ["drbdsetup", self._DevPath(minor), "show"]
105 def GenInitMetaCmd(self, minor, meta_dev):
106 return ["drbdmeta", "--force", self._DevPath(minor),
107 "v08", meta_dev, "0", "create-md"]
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",
115 args.extend(["-d", "%sm" % size_mb])
117 vmaj = self._version["k_major"]
118 vmin = self._version["k_minor"]
119 vrel = self._version["k_point"]
122 self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
123 params[constants.LDP_BARRIERS],
124 params[constants.LDP_NO_META_FLUSH])
125 args.extend(barrier_args)
127 if params[constants.LDP_DISK_CUSTOM]:
128 args.extend(shlex.split(params[constants.LDP_DISK_CUSTOM]))
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",
144 args.extend(["-a", hmac, "-x", secret])
146 if params[constants.LDP_NET_CUSTOM]:
147 args.extend(shlex.split(params[constants.LDP_NET_CUSTOM]))
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"]
157 # By definition we are using 8.x, so just check the rest of the version
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))
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.")
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],
181 args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
183 args.append("--create-device")
187 def GenPauseSyncCmd(self, minor):
188 return ["drbdsetup", self._DevPath(minor), "pause-sync"]
190 def GenResumeSyncCmd(self, minor):
191 return ["drbdsetup", self._DevPath(minor), "resume-sync"]
193 def GenPrimaryCmd(self, minor, force):
194 cmd = ["drbdsetup", self._DevPath(minor), "primary"]
201 def GenSecondaryCmd(self, minor):
202 return ["drbdsetup", self._DevPath(minor), "secondary"]
204 def GenDetachCmd(self, minor):
205 return ["drbdsetup", self._DevPath(minor), "detach"]
207 def GenDisconnectCmd(self, minor, family, lhost, lport, rhost, rport):
208 return ["drbdsetup", self._DevPath(minor), "disconnect"]
210 def GenDownCmd(self, minor):
211 return ["drbdsetup", self._DevPath(minor), "down"]
213 def GenResizeCmd(self, minor, size_mb):
214 return ["drbdsetup", self._DevPath(minor), "resize", "-s", "%dm" % size_mb]
217 def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
219 """Compute the DRBD command line parameters for disk barriers
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
225 If the desired option is unsupported, raises errors.BlockDeviceError.
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)
235 # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
237 if not vmaj == 8 and vmin in (0, 2, 3):
238 raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
241 def _AppendOrRaise(option, min_version):
242 """Helper for DRBD options"""
243 if min_version is not None and vrel >= min_version:
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))
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
253 meta_flush_supported = disk_flush_supported = {
259 disk_drain_supported = {
264 disk_barriers_supported = {
269 if disable_meta_flush:
270 _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
271 meta_flush_supported.get(vmin, None))
274 if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
275 _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
276 disk_flush_supported.get(vmin, None))
279 if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
280 _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
281 disk_drain_supported.get(vmin, None))
284 if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
285 _AppendOrRaise(cls._DISABLE_DISK_OPTION,
286 disk_barriers_supported.get(vmin, None))
291 class DRBD84CmdGenerator(BaseDRBDCmdGenerator):
292 """Generates drbdsetup commands suited for the DRBD >= 8.4 syntax.
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"
301 def __init__(self, version):
302 super(DRBD84CmdGenerator, self).__init__(version)
304 def GenShowCmd(self, minor):
305 return ["drbdsetup", "show", minor]
307 def GenInitMetaCmd(self, minor, meta_dev):
308 return ["drbdmeta", "--force", self._DevPath(minor),
309 "v08", meta_dev, "flex-external", "create-md"]
311 def GenLocalInitCmds(self, minor, data_dev, meta_dev, size_mb, params):
314 cmds.append(["drbdsetup", "new-resource", self._GetResource(minor)])
315 cmds.append(["drbdsetup", "new-minor", self._GetResource(minor),
317 # We need to apply the activity log before attaching the disk else drbdsetup
319 cmds.append(["drbdmeta", self._DevPath(minor),
320 "v08", meta_dev, "flex-external", "apply-al"])
322 attach_cmd = ["drbdsetup", "attach", minor, data_dev, meta_dev, "flexible",
323 "--on-io-error=detach"]
325 attach_cmd.extend(["--size", "%sm" % size_mb])
328 self._ComputeDiskBarrierArgs(params[constants.LDP_BARRIERS],
329 params[constants.LDP_NO_META_FLUSH])
330 attach_cmd.extend(barrier_args)
332 if params[constants.LDP_DISK_CUSTOM]:
333 attach_cmd.extend(shlex.split(params[constants.LDP_DISK_CUSTOM]))
335 cmds.append(attach_cmd)
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"
349 args.append("--allow-two-primaries")
351 args.extend(["--cram-hmac-alg", hmac, "--shared-secret", secret])
353 if params[constants.LDP_NET_CUSTOM]:
354 args.extend(shlex.split(params[constants.LDP_NET_CUSTOM]))
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.")
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],
377 args.extend(["--resync-rate", "%d" % params[constants.LDP_RESYNC_RATE]])
381 def GenPauseSyncCmd(self, minor):
382 return ["drbdsetup", "pause-sync", minor]
384 def GenResumeSyncCmd(self, minor):
385 return ["drbdsetup", "resume-sync", minor]
387 def GenPrimaryCmd(self, minor, force):
388 cmd = ["drbdsetup", "primary", minor]
391 cmd.append("--force")
395 def GenSecondaryCmd(self, minor):
396 return ["drbdsetup", "secondary", minor]
398 def GenDetachCmd(self, minor):
399 return ["drbdsetup", "detach", minor]
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)]
406 def GenDownCmd(self, minor):
407 return ["drbdsetup", "down", self._GetResource(minor)]
409 def GenResizeCmd(self, minor, size_mb):
410 return ["drbdsetup", "resize", minor, "--size", "%dm" % size_mb]
413 def _GetResource(minor):
414 """Return the resource name for a given minor.
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
421 return "resource%d" % minor
424 def _ComputeDiskBarrierArgs(cls, disabled_barriers, disable_meta_flush):
425 """Compute the DRBD command line parameters for disk barriers
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)
436 if disable_meta_flush:
437 args.append(cls._DISABLE_META_FLUSH_OPTION)
440 if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
441 args.append(cls._DISABLE_FLUSH_OPTION)
444 if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
445 args.append(cls._DISABLE_DRAIN_OPTION)
448 if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
449 args.append(cls._DISABLE_DISK_OPTION)