QA: Added test for gnt-instance recreate-disks
[ganeti-local] / qa / qa_instance.py
1 #
2 #
3
4 # Copyright (C) 2007, 2011, 2012 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 """Instance related QA tests.
23
24 """
25
26 import re
27 import time
28
29 from ganeti import utils
30 from ganeti import constants
31 from ganeti import query
32
33 import qa_config
34 import qa_utils
35 import qa_error
36
37 from qa_utils import AssertIn, AssertCommand, AssertEqual
38 from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG, RETURN_VALUE
39
40
41 def _GetDiskStatePath(disk):
42   return "/sys/block/%s/device/state" % disk
43
44
45 def _GetGenericAddParameters(inst, force_mac=None):
46   params = ["-B"]
47   params.append("%s=%s,%s=%s" % (constants.BE_MINMEM,
48                                  qa_config.get(constants.BE_MINMEM),
49                                  constants.BE_MAXMEM,
50                                  qa_config.get(constants.BE_MAXMEM)))
51   for idx, size in enumerate(qa_config.get("disk")):
52     params.extend(["--disk", "%s:size=%s" % (idx, size)])
53
54   # Set static MAC address if configured
55   if force_mac:
56     nic0_mac = force_mac
57   else:
58     nic0_mac = qa_config.GetInstanceNicMac(inst)
59   if nic0_mac:
60     params.extend(["--net", "0:mac=%s" % nic0_mac])
61
62   return params
63
64
65 def _DiskTest(node, disk_template):
66   instance = qa_config.AcquireInstance()
67   try:
68     cmd = (["gnt-instance", "add",
69             "--os-type=%s" % qa_config.get("os"),
70             "--disk-template=%s" % disk_template,
71             "--node=%s" % node] +
72            _GetGenericAddParameters(instance))
73     cmd.append(instance["name"])
74
75     AssertCommand(cmd)
76
77     _CheckSsconfInstanceList(instance["name"])
78
79     return instance
80   except:
81     qa_config.ReleaseInstance(instance)
82     raise
83
84
85 def _DestroyInstanceVolumes(instance):
86   """Remove all the LVM volumes of an instance.
87
88   This is used to simulate HW errors (dead nodes, broken disks...); the
89   configuration of the instance is not affected.
90
91   """
92   master = qa_config.GetMasterNode()
93   infocmd = utils.ShellQuoteArgs(["gnt-instance", "info", instance["name"]])
94   info_out = qa_utils.GetCommandOutput(master["primary"], infocmd)
95   re_node = re.compile(r"^\s+-\s+(?:primary|secondaries):\s+(\S.+)$")
96   node_elem = r"([^,()]+)(?:\s+\([^)]+\))?"
97   # re_nodelist matches a list of nodes returned by gnt-instance info, e.g.:
98   #  node1.fqdn
99   #  node2.fqdn,node3.fqdn
100   #  node4.fqdn (group mygroup, group UUID 01234567-abcd-0123-4567-0123456789ab)
101   # FIXME This works with no more than 2 secondaries
102   re_nodelist = re.compile(node_elem + "(?:," + node_elem + ")?$")
103   re_vol = re.compile(r"^\s+logical_id:\s+(\S+)$")
104   nodes = []
105   vols = []
106   for line in info_out.splitlines():
107     m = re_node.match(line)
108     if m:
109       nodestr = m.group(1)
110       m2 = re_nodelist.match(nodestr)
111       if m2:
112         nodes.extend(filter(None, m2.groups()))
113       else:
114         nodes.append(nodestr)
115     m = re_vol.match(line)
116     if m:
117       vols.append(m.group(1))
118   assert vols
119   assert nodes
120   for node in nodes:
121     AssertCommand(["lvremove", "-f"] + vols, node=node)
122
123
124 @InstanceCheck(None, INST_UP, RETURN_VALUE)
125 def TestInstanceAddWithPlainDisk(node):
126   """gnt-instance add -t plain"""
127   return _DiskTest(node["primary"], "plain")
128
129
130 @InstanceCheck(None, INST_UP, RETURN_VALUE)
131 def TestInstanceAddWithDrbdDisk(node, node2):
132   """gnt-instance add -t drbd"""
133   return _DiskTest("%s:%s" % (node["primary"], node2["primary"]),
134                    "drbd")
135
136
137 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
138 def TestInstanceRemove(instance):
139   """gnt-instance remove"""
140   AssertCommand(["gnt-instance", "remove", "-f", instance["name"]])
141
142   qa_config.ReleaseInstance(instance)
143
144
145 @InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
146 def TestInstanceStartup(instance):
147   """gnt-instance startup"""
148   AssertCommand(["gnt-instance", "startup", instance["name"]])
149
150
151 @InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
152 def TestInstanceShutdown(instance):
153   """gnt-instance shutdown"""
154   AssertCommand(["gnt-instance", "shutdown", instance["name"]])
155
156
157 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
158 def TestInstanceReboot(instance):
159   """gnt-instance reboot"""
160   options = qa_config.get("options", {})
161   reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
162   name = instance["name"]
163   for rtype in reboot_types:
164     AssertCommand(["gnt-instance", "reboot", "--type=%s" % rtype, name])
165
166   AssertCommand(["gnt-instance", "shutdown", name])
167   qa_utils.RunInstanceCheck(instance, False)
168   AssertCommand(["gnt-instance", "reboot", name])
169
170   master = qa_config.GetMasterNode()
171   cmd = ["gnt-instance", "list", "--no-headers", "-o", "status", name]
172   result_output = qa_utils.GetCommandOutput(master["primary"],
173                                             utils.ShellQuoteArgs(cmd))
174   AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
175
176
177 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
178 def TestInstanceReinstall(instance):
179   """gnt-instance reinstall"""
180   AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
181
182
183 def _ReadSsconfInstanceList():
184   """Reads ssconf_instance_list from the master node.
185
186   """
187   master = qa_config.GetMasterNode()
188
189   cmd = ["cat", utils.PathJoin(constants.DATA_DIR,
190                                "ssconf_%s" % constants.SS_INSTANCE_LIST)]
191
192   return qa_utils.GetCommandOutput(master["primary"],
193                                    utils.ShellQuoteArgs(cmd)).splitlines()
194
195
196 def _CheckSsconfInstanceList(instance):
197   """Checks if a certain instance is in the ssconf instance list.
198
199   @type instance: string
200   @param instance: Instance name
201
202   """
203   AssertIn(qa_utils.ResolveInstanceName(instance),
204            _ReadSsconfInstanceList())
205
206
207 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
208 def TestInstanceRenameAndBack(rename_source, rename_target):
209   """gnt-instance rename
210
211   This must leave the instance with the original name, not the target
212   name.
213
214   """
215   _CheckSsconfInstanceList(rename_source)
216
217   # first do a rename to a different actual name, expecting it to fail
218   qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
219   try:
220     AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
221                   fail=True)
222     _CheckSsconfInstanceList(rename_source)
223   finally:
224     qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
225
226   # and now rename instance to rename_target...
227   AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
228   _CheckSsconfInstanceList(rename_target)
229   qa_utils.RunInstanceCheck(rename_source, False)
230   qa_utils.RunInstanceCheck(rename_target, False)
231
232   # and back
233   AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
234   _CheckSsconfInstanceList(rename_source)
235   qa_utils.RunInstanceCheck(rename_target, False)
236
237
238 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
239 def TestInstanceFailover(instance):
240   """gnt-instance failover"""
241   cmd = ["gnt-instance", "failover", "--force", instance["name"]]
242
243   # failover ...
244   AssertCommand(cmd)
245   qa_utils.RunInstanceCheck(instance, True)
246
247   # ... and back
248   AssertCommand(cmd)
249
250
251 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
252 def TestInstanceMigrate(instance):
253   """gnt-instance migrate"""
254   cmd = ["gnt-instance", "migrate", "--force", instance["name"]]
255
256   # migrate ...
257   AssertCommand(cmd)
258   qa_utils.RunInstanceCheck(instance, True)
259
260   # ... and back
261   AssertCommand(cmd)
262
263   # TODO: Split into multiple tests
264   AssertCommand(["gnt-instance", "shutdown", instance["name"]])
265   qa_utils.RunInstanceCheck(instance, False)
266   AssertCommand(cmd, fail=True)
267   AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
268                  instance["name"]])
269   AssertCommand(["gnt-instance", "start", instance["name"]])
270   AssertCommand(cmd)
271   qa_utils.RunInstanceCheck(instance, True)
272
273   AssertCommand(["gnt-instance", "modify", "-B",
274                  ("%s=%s" %
275                   (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)),
276                  instance["name"]])
277
278   AssertCommand(cmd, fail=True)
279   qa_utils.RunInstanceCheck(instance, True)
280   AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
281                  instance["name"]])
282
283   # TODO: Verify whether the default value is restored here (not hardcoded)
284   AssertCommand(["gnt-instance", "modify", "-B",
285                  ("%s=%s" %
286                   (constants.BE_ALWAYS_FAILOVER, constants.VALUE_FALSE)),
287                  instance["name"]])
288
289   AssertCommand(cmd)
290   qa_utils.RunInstanceCheck(instance, True)
291
292
293 def TestInstanceInfo(instance):
294   """gnt-instance info"""
295   AssertCommand(["gnt-instance", "info", instance["name"]])
296
297
298 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
299 def TestInstanceModify(instance):
300   """gnt-instance modify"""
301   # Assume /sbin/init exists on all systems
302   test_kernel = "/sbin/init"
303   test_initrd = test_kernel
304
305   orig_maxmem = qa_config.get(constants.BE_MAXMEM)
306   orig_minmem = qa_config.get(constants.BE_MINMEM)
307   #orig_bridge = qa_config.get("bridge", "xen-br0")
308   args = [
309     ["-B", "%s=128" % constants.BE_MINMEM],
310     ["-B", "%s=128" % constants.BE_MAXMEM],
311     ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
312                             constants.BE_MAXMEM, orig_maxmem)],
313     ["-B", "%s=2" % constants.BE_VCPUS],
314     ["-B", "%s=1" % constants.BE_VCPUS],
315     ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
316     ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
317     ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
318
319     ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
320     ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
321     ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
322     ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
323     ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
324
325     # TODO: bridge tests
326     #["--bridge", "xen-br1"],
327     #["--bridge", orig_bridge],
328
329     # TODO: Do these tests only with xen-hvm
330     #["-H", "%s=acn" % constants.HV_BOOT_ORDER],
331     #["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
332     ]
333   for alist in args:
334     AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
335
336   # check no-modify
337   AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
338
339   # Marking offline/online while instance is running must fail
340   for arg in ["--online", "--offline"]:
341     AssertCommand(["gnt-instance", "modify", arg, instance["name"]], fail=True)
342
343
344 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
345 def TestInstanceStoppedModify(instance):
346   """gnt-instance modify (stopped instance)"""
347   name = instance["name"]
348
349   # Instance was not marked offline; try marking it online once more
350   AssertCommand(["gnt-instance", "modify", "--online", name])
351
352   # Mark instance as offline
353   AssertCommand(["gnt-instance", "modify", "--offline", name])
354
355   # And online again
356   AssertCommand(["gnt-instance", "modify", "--online", name])
357
358
359 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
360 def TestInstanceConvertDisk(instance, snode):
361   """gnt-instance modify -t"""
362   name = instance["name"]
363   AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
364   AssertCommand(["gnt-instance", "modify", "-t", "drbd",
365                  "-n", snode["primary"], name])
366
367
368 @InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
369 def TestInstanceGrowDisk(instance):
370   """gnt-instance grow-disk"""
371   name = instance["name"]
372   all_size = qa_config.get("disk")
373   all_grow = qa_config.get("disk-growth")
374   if not all_grow:
375     # missing disk sizes but instance grow disk has been enabled,
376     # let's set fixed/nomimal growth
377     all_grow = ["128M" for _ in all_size]
378   for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
379     # succeed in grow by amount
380     AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
381     # fail in grow to the old size
382     AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
383                    size], fail=True)
384     # succeed to grow to old size + 2 * growth
385     int_size = utils.ParseUnit(size)
386     int_grow = utils.ParseUnit(grow)
387     AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
388                    str(int_size + 2 * int_grow)])
389
390
391 def TestInstanceList():
392   """gnt-instance list"""
393   qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
394
395
396 def TestInstanceListFields():
397   """gnt-instance list-fields"""
398   qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
399
400
401 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
402 def TestInstanceConsole(instance):
403   """gnt-instance console"""
404   AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
405
406
407 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
408 def TestReplaceDisks(instance, pnode, snode, othernode):
409   """gnt-instance replace-disks"""
410   # pylint: disable=W0613
411   # due to unused pnode arg
412   # FIXME: should be removed from the function completely
413   def buildcmd(args):
414     cmd = ["gnt-instance", "replace-disks"]
415     cmd.extend(args)
416     cmd.append(instance["name"])
417     return cmd
418
419   for data in [
420     ["-p"],
421     ["-s"],
422     ["--new-secondary=%s" % othernode["primary"]],
423     # and restore
424     ["--new-secondary=%s" % snode["primary"]],
425     ]:
426     AssertCommand(buildcmd(data))
427
428   AssertCommand(buildcmd(["-a"]))
429   AssertCommand(["gnt-instance", "stop", instance["name"]])
430   AssertCommand(buildcmd(["-a"]), fail=True)
431   AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
432   AssertCommand(buildcmd(["-a"]))
433   AssertCommand(["gnt-instance", "start", instance["name"]])
434
435
436 def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
437                          destroy=True):
438   """Execute gnt-instance recreate-disks and check the result
439
440   @param cmdargs: Arguments (instance name excluded)
441   @param instance: Instance to operate on
442   @param fail: True if the command is expected to fail
443   @param check: If True and fail is False, check that the disks work
444   @prama destroy: If True, destroy the old disks first
445
446   """
447   if destroy:
448     _DestroyInstanceVolumes(instance)
449   AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
450                  [instance["name"]]), fail)
451   if not fail and check:
452     # Quick check that the disks are there
453     AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
454     AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
455
456 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
457 def TestRecreateDisks(instance, pnode, snode, othernodes):
458   """gnt-instance recreate-disks
459
460   @param instance: Instance to work on
461   @param pnode: Primary node
462   @param snode: Secondary node, or None for sigle-homed instances
463   @param othernodes: list/tuple of nodes where to temporarily recreate disks
464
465   """
466   other_seq = ":".join([n["primary"] for n in othernodes])
467   orig_seq = pnode["primary"]
468   if snode:
469     orig_seq = orig_seq + ":" + snode["primary"]
470   # This fails beacuse the instance is running
471   _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
472   AssertCommand(["gnt-instance", "stop", instance["name"]])
473   # Disks exist: this should fail
474   _AssertRecreateDisks([], instance, fail=True, destroy=False)
475   # Recreate disks in place
476   _AssertRecreateDisks([], instance)
477   # Move disks away
478   _AssertRecreateDisks(["-n", other_seq], instance)
479   # Move disks back
480   _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
481   # This and InstanceCheck decoration check that the disks are working
482   AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
483   AssertCommand(["gnt-instance", "start", instance["name"]])
484
485
486 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
487 def TestInstanceExport(instance, node):
488   """gnt-backup export -n ..."""
489   name = instance["name"]
490   AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
491   return qa_utils.ResolveInstanceName(name)
492
493
494 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
495 def TestInstanceExportWithRemove(instance, node):
496   """gnt-backup export --remove-instance"""
497   AssertCommand(["gnt-backup", "export", "-n", node["primary"],
498                  "--remove-instance", instance["name"]])
499
500
501 @InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
502 def TestInstanceExportNoTarget(instance):
503   """gnt-backup export (without target node, should fail)"""
504   AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
505
506
507 @InstanceCheck(None, INST_DOWN, FIRST_ARG)
508 def TestInstanceImport(newinst, node, expnode, name):
509   """gnt-backup import"""
510   cmd = (["gnt-backup", "import",
511           "--disk-template=plain",
512           "--no-ip-check",
513           "--src-node=%s" % expnode["primary"],
514           "--src-dir=%s/%s" % (constants.EXPORT_DIR, name),
515           "--node=%s" % node["primary"]] +
516          _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
517   cmd.append(newinst["name"])
518   AssertCommand(cmd)
519
520
521 def TestBackupList(expnode):
522   """gnt-backup list"""
523   AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
524
525   qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
526                             namefield=None, test_unknown=False)
527
528
529 def TestBackupListFields():
530   """gnt-backup list-fields"""
531   qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
532
533
534 def _TestInstanceDiskFailure(instance, node, node2, onmaster):
535   """Testing disk failure."""
536   master = qa_config.GetMasterNode()
537   sq = utils.ShellQuoteArgs
538
539   instance_full = qa_utils.ResolveInstanceName(instance["name"])
540   node_full = qa_utils.ResolveNodeName(node)
541   node2_full = qa_utils.ResolveNodeName(node2)
542
543   print qa_utils.FormatInfo("Getting physical disk names")
544   cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
545          "--output=node,phys,instance",
546          node["primary"], node2["primary"]]
547   output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
548
549   # Get physical disk names
550   re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
551   node2disk = {}
552   for line in output.splitlines():
553     (node_name, phys, inst) = line.split("|")
554     if inst == instance_full:
555       if node_name not in node2disk:
556         node2disk[node_name] = []
557
558       m = re_disk.match(phys)
559       if not m:
560         raise qa_error.Error("Unknown disk name format: %s" % phys)
561
562       name = m.group(1)
563       if name not in node2disk[node_name]:
564         node2disk[node_name].append(name)
565
566   if [node2_full, node_full][int(onmaster)] not in node2disk:
567     raise qa_error.Error("Couldn't find physical disks used on"
568                          " %s node" % ["secondary", "master"][int(onmaster)])
569
570   print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
571                             " disks")
572   for node_name, disks in node2disk.iteritems():
573     cmds = []
574     for disk in disks:
575       cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
576     AssertCommand(" && ".join(cmds), node=node_name)
577
578   print qa_utils.FormatInfo("Getting device paths")
579   cmd = ["gnt-instance", "activate-disks", instance["name"]]
580   output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
581   devpath = []
582   for line in output.splitlines():
583     (_, _, tmpdevpath) = line.split(":")
584     devpath.append(tmpdevpath)
585   print devpath
586
587   print qa_utils.FormatInfo("Getting drbd device paths")
588   cmd = ["gnt-instance", "info", instance["name"]]
589   output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
590   pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
591              r"\s+primary:\s+(/dev/drbd\d+)\s+")
592   drbddevs = re.findall(pattern, output, re.M)
593   print drbddevs
594
595   halted_disks = []
596   try:
597     print qa_utils.FormatInfo("Deactivating disks")
598     cmds = []
599     for name in node2disk[[node2_full, node_full][int(onmaster)]]:
600       halted_disks.append(name)
601       cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
602     AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
603
604     print qa_utils.FormatInfo("Write to disks and give some time to notice"
605                               " the problem")
606     cmds = []
607     for disk in devpath:
608       cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
609                       "if=%s" % disk, "of=%s" % disk]))
610     for _ in (0, 1, 2):
611       AssertCommand(" && ".join(cmds), node=node)
612       time.sleep(3)
613
614     print qa_utils.FormatInfo("Debugging info")
615     for name in drbddevs:
616       AssertCommand(["drbdsetup", name, "show"], node=node)
617
618     AssertCommand(["gnt-instance", "info", instance["name"]])
619
620   finally:
621     print qa_utils.FormatInfo("Activating disks again")
622     cmds = []
623     for name in halted_disks:
624       cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
625     AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
626
627   if onmaster:
628     for name in drbddevs:
629       AssertCommand(["drbdsetup", name, "detach"], node=node)
630   else:
631     for name in drbddevs:
632       AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
633
634   # TODO
635   #AssertCommand(["vgs"], [node2, node][int(onmaster)])
636
637   print qa_utils.FormatInfo("Making sure disks are up again")
638   AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
639
640   print qa_utils.FormatInfo("Restarting instance")
641   AssertCommand(["gnt-instance", "shutdown", instance["name"]])
642   AssertCommand(["gnt-instance", "startup", instance["name"]])
643
644   AssertCommand(["gnt-cluster", "verify"])
645
646
647 def TestInstanceMasterDiskFailure(instance, node, node2):
648   """Testing disk failure on master node."""
649   # pylint: disable=W0613
650   # due to unused args
651   print qa_utils.FormatError("Disk failure on primary node cannot be"
652                              " tested due to potential crashes.")
653   # The following can cause crashes, thus it's disabled until fixed
654   #return _TestInstanceDiskFailure(instance, node, node2, True)
655
656
657 def TestInstanceSecondaryDiskFailure(instance, node, node2):
658   """Testing disk failure on secondary node."""
659   return _TestInstanceDiskFailure(instance, node, node2, False)