Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 32da72f3

History | View | Annotate | Download (22.1 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
  # 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(["gnt-instance", "activate-disks", "--wait-for-sync",
433
                 instance["name"]])
434
  AssertCommand(buildcmd(["-a"]))
435
  AssertCommand(["gnt-instance", "start", instance["name"]])
436

    
437

    
438
def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
439
                         destroy=True):
440
  """Execute gnt-instance recreate-disks and check the result
441

442
  @param cmdargs: Arguments (instance name excluded)
443
  @param instance: Instance to operate on
444
  @param fail: True if the command is expected to fail
445
  @param check: If True and fail is False, check that the disks work
446
  @prama destroy: If True, destroy the old disks first
447

448
  """
449
  if destroy:
450
    _DestroyInstanceVolumes(instance)
451
  AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
452
                 [instance["name"]]), fail)
453
  if not fail and check:
454
    # Quick check that the disks are there
455
    AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
456
    AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
457
                   instance["name"]])
458
    AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
459

    
460

    
461
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
462
def TestRecreateDisks(instance, pnode, snode, othernodes):
463
  """gnt-instance recreate-disks
464

465
  @param instance: Instance to work on
466
  @param pnode: Primary node
467
  @param snode: Secondary node, or None for sigle-homed instances
468
  @param othernodes: list/tuple of nodes where to temporarily recreate disks
469

470
  """
471
  other_seq = ":".join([n["primary"] for n in othernodes])
472
  orig_seq = pnode["primary"]
473
  if snode:
474
    orig_seq = orig_seq + ":" + snode["primary"]
475
  # This fails beacuse the instance is running
476
  _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
477
  AssertCommand(["gnt-instance", "stop", instance["name"]])
478
  # Disks exist: this should fail
479
  _AssertRecreateDisks([], instance, fail=True, destroy=False)
480
  # Recreate disks in place
481
  _AssertRecreateDisks([], instance)
482
  # Move disks away
483
  _AssertRecreateDisks(["-n", other_seq], instance)
484
  # Move disks back
485
  _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
486
  # This and InstanceCheck decoration check that the disks are working
487
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
488
  AssertCommand(["gnt-instance", "start", instance["name"]])
489

    
490

    
491
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
492
def TestInstanceExport(instance, node):
493
  """gnt-backup export -n ..."""
494
  name = instance["name"]
495
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
496
  return qa_utils.ResolveInstanceName(name)
497

    
498

    
499
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
500
def TestInstanceExportWithRemove(instance, node):
501
  """gnt-backup export --remove-instance"""
502
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
503
                 "--remove-instance", instance["name"]])
504

    
505

    
506
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
507
def TestInstanceExportNoTarget(instance):
508
  """gnt-backup export (without target node, should fail)"""
509
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
510

    
511

    
512
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
513
def TestInstanceImport(newinst, node, expnode, name):
514
  """gnt-backup import"""
515
  cmd = (["gnt-backup", "import",
516
          "--disk-template=plain",
517
          "--no-ip-check",
518
          "--src-node=%s" % expnode["primary"],
519
          "--src-dir=%s/%s" % (constants.EXPORT_DIR, name),
520
          "--node=%s" % node["primary"]] +
521
         _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
522
  cmd.append(newinst["name"])
523
  AssertCommand(cmd)
524

    
525

    
526
def TestBackupList(expnode):
527
  """gnt-backup list"""
528
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
529

    
530
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
531
                            namefield=None, test_unknown=False)
532

    
533

    
534
def TestBackupListFields():
535
  """gnt-backup list-fields"""
536
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
537

    
538

    
539
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
540
  """Testing disk failure."""
541
  master = qa_config.GetMasterNode()
542
  sq = utils.ShellQuoteArgs
543

    
544
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
545
  node_full = qa_utils.ResolveNodeName(node)
546
  node2_full = qa_utils.ResolveNodeName(node2)
547

    
548
  print qa_utils.FormatInfo("Getting physical disk names")
549
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
550
         "--output=node,phys,instance",
551
         node["primary"], node2["primary"]]
552
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
553

    
554
  # Get physical disk names
555
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
556
  node2disk = {}
557
  for line in output.splitlines():
558
    (node_name, phys, inst) = line.split("|")
559
    if inst == instance_full:
560
      if node_name not in node2disk:
561
        node2disk[node_name] = []
562

    
563
      m = re_disk.match(phys)
564
      if not m:
565
        raise qa_error.Error("Unknown disk name format: %s" % phys)
566

    
567
      name = m.group(1)
568
      if name not in node2disk[node_name]:
569
        node2disk[node_name].append(name)
570

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

    
575
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
576
                            " disks")
577
  for node_name, disks in node2disk.iteritems():
578
    cmds = []
579
    for disk in disks:
580
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
581
    AssertCommand(" && ".join(cmds), node=node_name)
582

    
583
  print qa_utils.FormatInfo("Getting device paths")
584
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
585
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
586
  devpath = []
587
  for line in output.splitlines():
588
    (_, _, tmpdevpath) = line.split(":")
589
    devpath.append(tmpdevpath)
590
  print devpath
591

    
592
  print qa_utils.FormatInfo("Getting drbd device paths")
593
  cmd = ["gnt-instance", "info", instance["name"]]
594
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
595
  pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
596
             r"\s+primary:\s+(/dev/drbd\d+)\s+")
597
  drbddevs = re.findall(pattern, output, re.M)
598
  print drbddevs
599

    
600
  halted_disks = []
601
  try:
602
    print qa_utils.FormatInfo("Deactivating disks")
603
    cmds = []
604
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
605
      halted_disks.append(name)
606
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
607
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
608

    
609
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
610
                              " the problem")
611
    cmds = []
612
    for disk in devpath:
613
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
614
                      "if=%s" % disk, "of=%s" % disk]))
615
    for _ in (0, 1, 2):
616
      AssertCommand(" && ".join(cmds), node=node)
617
      time.sleep(3)
618

    
619
    print qa_utils.FormatInfo("Debugging info")
620
    for name in drbddevs:
621
      AssertCommand(["drbdsetup", name, "show"], node=node)
622

    
623
    AssertCommand(["gnt-instance", "info", instance["name"]])
624

    
625
  finally:
626
    print qa_utils.FormatInfo("Activating disks again")
627
    cmds = []
628
    for name in halted_disks:
629
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
630
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
631

    
632
  if onmaster:
633
    for name in drbddevs:
634
      AssertCommand(["drbdsetup", name, "detach"], node=node)
635
  else:
636
    for name in drbddevs:
637
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
638

    
639
  # TODO
640
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
641

    
642
  print qa_utils.FormatInfo("Making sure disks are up again")
643
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
644

    
645
  print qa_utils.FormatInfo("Restarting instance")
646
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
647
  AssertCommand(["gnt-instance", "startup", instance["name"]])
648

    
649
  AssertCommand(["gnt-cluster", "verify"])
650

    
651

    
652
def TestInstanceMasterDiskFailure(instance, node, node2):
653
  """Testing disk failure on master node."""
654
  # pylint: disable=W0613
655
  # due to unused args
656
  print qa_utils.FormatError("Disk failure on primary node cannot be"
657
                             " tested due to potential crashes.")
658
  # The following can cause crashes, thus it's disabled until fixed
659
  #return _TestInstanceDiskFailure(instance, node, node2, True)
660

    
661

    
662
def TestInstanceSecondaryDiskFailure(instance, node, node2):
663
  """Testing disk failure on secondary node."""
664
  return _TestInstanceDiskFailure(instance, node, node2, False)