root / lib / storage / drbd_cmdgen.py @ 32449822
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
|