Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ a085d96d

History | View | Annotate | Download (22.4 kB)

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
  default_hv = qa_config.GetDefaultHypervisor()
302

    
303
  # Assume /sbin/init exists on all systems
304
  test_kernel = "/sbin/init"
305
  test_initrd = test_kernel
306

    
307
  orig_maxmem = qa_config.get(constants.BE_MAXMEM)
308
  orig_minmem = qa_config.get(constants.BE_MINMEM)
309
  #orig_bridge = qa_config.get("bridge", "xen-br0")
310

    
311
  args = [
312
    ["-B", "%s=128" % constants.BE_MINMEM],
313
    ["-B", "%s=128" % constants.BE_MAXMEM],
314
    ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
315
                            constants.BE_MAXMEM, orig_maxmem)],
316
    ["-B", "%s=2" % constants.BE_VCPUS],
317
    ["-B", "%s=1" % constants.BE_VCPUS],
318
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
319
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
320
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
321

    
322
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
323
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
324

    
325
    # TODO: bridge tests
326
    #["--bridge", "xen-br1"],
327
    #["--bridge", orig_bridge],
328
    ]
329

    
330
  if default_hv == constants.HT_XEN_PVM:
331
    args.extend([
332
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
333
      ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
334
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
335
      ])
336
  elif default_hv == constants.HT_XEN_HVM:
337
    args.extend([
338
      ["-H", "%s=acn" % constants.HV_BOOT_ORDER],
339
      ["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
340
      ])
341

    
342
  for alist in args:
343
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
344

    
345
  # check no-modify
346
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
347

    
348
  # Marking offline/online while instance is running must fail
349
  for arg in ["--online", "--offline"]:
350
    AssertCommand(["gnt-instance", "modify", arg, instance["name"]], fail=True)
351

    
352

    
353
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
354
def TestInstanceStoppedModify(instance):
355
  """gnt-instance modify (stopped instance)"""
356
  name = instance["name"]
357

    
358
  # Instance was not marked offline; try marking it online once more
359
  AssertCommand(["gnt-instance", "modify", "--online", name])
360

    
361
  # Mark instance as offline
362
  AssertCommand(["gnt-instance", "modify", "--offline", name])
363

    
364
  # And online again
365
  AssertCommand(["gnt-instance", "modify", "--online", name])
366

    
367

    
368
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
369
def TestInstanceConvertDisk(instance, snode):
370
  """gnt-instance modify -t"""
371
  name = instance["name"]
372
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
373
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
374
                 "-n", snode["primary"], name])
375

    
376

    
377
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
378
def TestInstanceGrowDisk(instance):
379
  """gnt-instance grow-disk"""
380
  name = instance["name"]
381
  all_size = qa_config.get("disk")
382
  all_grow = qa_config.get("disk-growth")
383
  if not all_grow:
384
    # missing disk sizes but instance grow disk has been enabled,
385
    # let's set fixed/nomimal growth
386
    all_grow = ["128M" for _ in all_size]
387
  for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
388
    # succeed in grow by amount
389
    AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
390
    # fail in grow to the old size
391
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
392
                   size], fail=True)
393
    # succeed to grow to old size + 2 * growth
394
    int_size = utils.ParseUnit(size)
395
    int_grow = utils.ParseUnit(grow)
396
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
397
                   str(int_size + 2 * int_grow)])
398

    
399

    
400
def TestInstanceList():
401
  """gnt-instance list"""
402
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
403

    
404

    
405
def TestInstanceListFields():
406
  """gnt-instance list-fields"""
407
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
408

    
409

    
410
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
411
def TestInstanceConsole(instance):
412
  """gnt-instance console"""
413
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
414

    
415

    
416
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
417
def TestReplaceDisks(instance, pnode, snode, othernode):
418
  """gnt-instance replace-disks"""
419
  # pylint: disable=W0613
420
  # due to unused pnode arg
421
  # FIXME: should be removed from the function completely
422
  def buildcmd(args):
423
    cmd = ["gnt-instance", "replace-disks"]
424
    cmd.extend(args)
425
    cmd.append(instance["name"])
426
    return cmd
427

    
428
  for data in [
429
    ["-p"],
430
    ["-s"],
431
    ["--new-secondary=%s" % othernode["primary"]],
432
    # and restore
433
    ["--new-secondary=%s" % snode["primary"]],
434
    ]:
435
    AssertCommand(buildcmd(data))
436

    
437
  AssertCommand(buildcmd(["-a"]))
438
  AssertCommand(["gnt-instance", "stop", instance["name"]])
439
  AssertCommand(buildcmd(["-a"]), fail=True)
440
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
441
  AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
442
                 instance["name"]])
443
  AssertCommand(buildcmd(["-a"]))
444
  AssertCommand(["gnt-instance", "start", instance["name"]])
445

    
446

    
447
def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
448
                         destroy=True):
449
  """Execute gnt-instance recreate-disks and check the result
450

451
  @param cmdargs: Arguments (instance name excluded)
452
  @param instance: Instance to operate on
453
  @param fail: True if the command is expected to fail
454
  @param check: If True and fail is False, check that the disks work
455
  @prama destroy: If True, destroy the old disks first
456

457
  """
458
  if destroy:
459
    _DestroyInstanceVolumes(instance)
460
  AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
461
                 [instance["name"]]), fail)
462
  if not fail and check:
463
    # Quick check that the disks are there
464
    AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
465
    AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
466
                   instance["name"]])
467
    AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
468

    
469

    
470
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
471
def TestRecreateDisks(instance, pnode, snode, othernodes):
472
  """gnt-instance recreate-disks
473

474
  @param instance: Instance to work on
475
  @param pnode: Primary node
476
  @param snode: Secondary node, or None for sigle-homed instances
477
  @param othernodes: list/tuple of nodes where to temporarily recreate disks
478

479
  """
480
  other_seq = ":".join([n["primary"] for n in othernodes])
481
  orig_seq = pnode["primary"]
482
  if snode:
483
    orig_seq = orig_seq + ":" + snode["primary"]
484
  # These fail because the instance is running
485
  _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
486
  _AssertRecreateDisks(["-I", "hail"], instance, fail=True, destroy=False)
487
  AssertCommand(["gnt-instance", "stop", instance["name"]])
488
  # Disks exist: this should fail
489
  _AssertRecreateDisks([], instance, fail=True, destroy=False)
490
  # Recreate disks in place
491
  _AssertRecreateDisks([], instance)
492
  # Move disks away
493
  _AssertRecreateDisks(["-I", "hail"], instance)
494
  # Move disks back
495
  _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
496
  # This and InstanceCheck decoration check that the disks are working
497
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
498
  AssertCommand(["gnt-instance", "start", instance["name"]])
499

    
500

    
501
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
502
def TestInstanceExport(instance, node):
503
  """gnt-backup export -n ..."""
504
  name = instance["name"]
505
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
506
  return qa_utils.ResolveInstanceName(name)
507

    
508

    
509
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
510
def TestInstanceExportWithRemove(instance, node):
511
  """gnt-backup export --remove-instance"""
512
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
513
                 "--remove-instance", instance["name"]])
514

    
515

    
516
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
517
def TestInstanceExportNoTarget(instance):
518
  """gnt-backup export (without target node, should fail)"""
519
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
520

    
521

    
522
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
523
def TestInstanceImport(newinst, node, expnode, name):
524
  """gnt-backup import"""
525
  cmd = (["gnt-backup", "import",
526
          "--disk-template=plain",
527
          "--no-ip-check",
528
          "--src-node=%s" % expnode["primary"],
529
          "--src-dir=%s/%s" % (constants.EXPORT_DIR, name),
530
          "--node=%s" % node["primary"]] +
531
         _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
532
  cmd.append(newinst["name"])
533
  AssertCommand(cmd)
534

    
535

    
536
def TestBackupList(expnode):
537
  """gnt-backup list"""
538
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
539

    
540
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
541
                            namefield=None, test_unknown=False)
542

    
543

    
544
def TestBackupListFields():
545
  """gnt-backup list-fields"""
546
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
547

    
548

    
549
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
550
  """Testing disk failure."""
551
  master = qa_config.GetMasterNode()
552
  sq = utils.ShellQuoteArgs
553

    
554
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
555
  node_full = qa_utils.ResolveNodeName(node)
556
  node2_full = qa_utils.ResolveNodeName(node2)
557

    
558
  print qa_utils.FormatInfo("Getting physical disk names")
559
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
560
         "--output=node,phys,instance",
561
         node["primary"], node2["primary"]]
562
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
563

    
564
  # Get physical disk names
565
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
566
  node2disk = {}
567
  for line in output.splitlines():
568
    (node_name, phys, inst) = line.split("|")
569
    if inst == instance_full:
570
      if node_name not in node2disk:
571
        node2disk[node_name] = []
572

    
573
      m = re_disk.match(phys)
574
      if not m:
575
        raise qa_error.Error("Unknown disk name format: %s" % phys)
576

    
577
      name = m.group(1)
578
      if name not in node2disk[node_name]:
579
        node2disk[node_name].append(name)
580

    
581
  if [node2_full, node_full][int(onmaster)] not in node2disk:
582
    raise qa_error.Error("Couldn't find physical disks used on"
583
                         " %s node" % ["secondary", "master"][int(onmaster)])
584

    
585
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
586
                            " disks")
587
  for node_name, disks in node2disk.iteritems():
588
    cmds = []
589
    for disk in disks:
590
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
591
    AssertCommand(" && ".join(cmds), node=node_name)
592

    
593
  print qa_utils.FormatInfo("Getting device paths")
594
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
595
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
596
  devpath = []
597
  for line in output.splitlines():
598
    (_, _, tmpdevpath) = line.split(":")
599
    devpath.append(tmpdevpath)
600
  print devpath
601

    
602
  print qa_utils.FormatInfo("Getting drbd device paths")
603
  cmd = ["gnt-instance", "info", instance["name"]]
604
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
605
  pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
606
             r"\s+primary:\s+(/dev/drbd\d+)\s+")
607
  drbddevs = re.findall(pattern, output, re.M)
608
  print drbddevs
609

    
610
  halted_disks = []
611
  try:
612
    print qa_utils.FormatInfo("Deactivating disks")
613
    cmds = []
614
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
615
      halted_disks.append(name)
616
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
617
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
618

    
619
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
620
                              " the problem")
621
    cmds = []
622
    for disk in devpath:
623
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
624
                      "if=%s" % disk, "of=%s" % disk]))
625
    for _ in (0, 1, 2):
626
      AssertCommand(" && ".join(cmds), node=node)
627
      time.sleep(3)
628

    
629
    print qa_utils.FormatInfo("Debugging info")
630
    for name in drbddevs:
631
      AssertCommand(["drbdsetup", name, "show"], node=node)
632

    
633
    AssertCommand(["gnt-instance", "info", instance["name"]])
634

    
635
  finally:
636
    print qa_utils.FormatInfo("Activating disks again")
637
    cmds = []
638
    for name in halted_disks:
639
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
640
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
641

    
642
  if onmaster:
643
    for name in drbddevs:
644
      AssertCommand(["drbdsetup", name, "detach"], node=node)
645
  else:
646
    for name in drbddevs:
647
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
648

    
649
  # TODO
650
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
651

    
652
  print qa_utils.FormatInfo("Making sure disks are up again")
653
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
654

    
655
  print qa_utils.FormatInfo("Restarting instance")
656
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
657
  AssertCommand(["gnt-instance", "startup", instance["name"]])
658

    
659
  AssertCommand(["gnt-cluster", "verify"])
660

    
661

    
662
def TestInstanceMasterDiskFailure(instance, node, node2):
663
  """Testing disk failure on master node."""
664
  # pylint: disable=W0613
665
  # due to unused args
666
  print qa_utils.FormatError("Disk failure on primary node cannot be"
667
                             " tested due to potential crashes.")
668
  # The following can cause crashes, thus it's disabled until fixed
669
  #return _TestInstanceDiskFailure(instance, node, node2, True)
670

    
671

    
672
def TestInstanceSecondaryDiskFailure(instance, node, node2):
673
  """Testing disk failure on secondary node."""
674
  return _TestInstanceDiskFailure(instance, node, node2, False)