Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ b07afbb3

History | View | Annotate | Download (25.9 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
from ganeti import pathutils
33

    
34
import qa_config
35
import qa_utils
36
import qa_error
37

    
38
from qa_utils import AssertIn, AssertCommand, AssertEqual
39
from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG, RETURN_VALUE
40

    
41

    
42
def _GetDiskStatePath(disk):
43
  return "/sys/block/%s/device/state" % disk
44

    
45

    
46
def _GetGenericAddParameters(inst, force_mac=None):
47
  params = ["-B"]
48
  params.append("%s=%s,%s=%s" % (constants.BE_MINMEM,
49
                                 qa_config.get(constants.BE_MINMEM),
50
                                 constants.BE_MAXMEM,
51
                                 qa_config.get(constants.BE_MAXMEM)))
52
  for idx, size in enumerate(qa_config.get("disk")):
53
    params.extend(["--disk", "%s:size=%s" % (idx, size)])
54

    
55
  # Set static MAC address if configured
56
  if force_mac:
57
    nic0_mac = force_mac
58
  else:
59
    nic0_mac = qa_config.GetInstanceNicMac(inst)
60
  if nic0_mac:
61
    params.extend(["--net", "0:mac=%s" % nic0_mac])
62

    
63
  return params
64

    
65

    
66
def _DiskTest(node, disk_template):
67
  instance = qa_config.AcquireInstance()
68
  try:
69
    cmd = (["gnt-instance", "add",
70
            "--os-type=%s" % qa_config.get("os"),
71
            "--disk-template=%s" % disk_template,
72
            "--node=%s" % node] +
73
           _GetGenericAddParameters(instance))
74
    cmd.append(instance["name"])
75

    
76
    AssertCommand(cmd)
77

    
78
    _CheckSsconfInstanceList(instance["name"])
79

    
80
    return instance
81
  except:
82
    qa_config.ReleaseInstance(instance)
83
    raise
84

    
85

    
86
def _GetInstanceInfo(instance):
87
  """Return information about the actual state of an instance.
88

89
  @type instance: string
90
  @param instance: the instance name
91
  @return: a dictionary with two keys:
92
      - "nodes": instance nodes, a list of strings
93
      - "volumes": instance volume IDs, a list of strings
94

95
  """
96
  master = qa_config.GetMasterNode()
97
  infocmd = utils.ShellQuoteArgs(["gnt-instance", "info", instance])
98
  info_out = qa_utils.GetCommandOutput(master["primary"], infocmd)
99
  re_node = re.compile(r"^\s+-\s+(?:primary|secondaries):\s+(\S.+)$")
100
  node_elem = r"([^,()]+)(?:\s+\([^)]+\))?"
101
  # re_nodelist matches a list of nodes returned by gnt-instance info, e.g.:
102
  #  node1.fqdn
103
  #  node2.fqdn,node3.fqdn
104
  #  node4.fqdn (group mygroup, group UUID 01234567-abcd-0123-4567-0123456789ab)
105
  # FIXME This works with no more than 2 secondaries
106
  re_nodelist = re.compile(node_elem + "(?:," + node_elem + ")?$")
107
  re_vol = re.compile(r"^\s+logical_id:\s+(\S+)$")
108
  nodes = []
109
  vols = []
110
  for line in info_out.splitlines():
111
    m = re_node.match(line)
112
    if m:
113
      nodestr = m.group(1)
114
      m2 = re_nodelist.match(nodestr)
115
      if m2:
116
        nodes.extend(filter(None, m2.groups()))
117
      else:
118
        nodes.append(nodestr)
119
    m = re_vol.match(line)
120
    if m:
121
      vols.append(m.group(1))
122
  assert vols
123
  assert nodes
124
  return {"nodes": nodes, "volumes": vols}
125

    
126

    
127
def _DestroyInstanceVolumes(instance):
128
  """Remove all the LVM volumes of an instance.
129

130
  This is used to simulate HW errors (dead nodes, broken disks...); the
131
  configuration of the instance is not affected.
132
  @type instance: dictionary
133
  @param instance: the instance
134

135
  """
136
  info = _GetInstanceInfo(instance["name"])
137
  vols = info["volumes"]
138
  for node in info["nodes"]:
139
    AssertCommand(["lvremove", "-f"] + vols, node=node)
140

    
141

    
142
def _GetBoolInstanceField(instance, field):
143
  """Get the Boolean value of a field of an instance.
144

145
  @type instance: string
146
  @param instance: Instance name
147
  @type field: string
148
  @param field: Name of the field
149

150
  """
151
  master = qa_config.GetMasterNode()
152
  infocmd = utils.ShellQuoteArgs(["gnt-instance", "list", "--no-headers",
153
                                  "-o", field, instance])
154
  info_out = qa_utils.GetCommandOutput(master["primary"], infocmd).strip()
155
  if info_out == "Y":
156
    return True
157
  elif info_out == "N":
158
    return False
159
  else:
160
    raise qa_error.Error("Field %s of instance %s has a non-Boolean value:"
161
                         " %s" % (field, instance, info_out))
162

    
163

    
164
@InstanceCheck(None, INST_UP, RETURN_VALUE)
165
def TestInstanceAddWithPlainDisk(node):
166
  """gnt-instance add -t plain"""
167
  return _DiskTest(node["primary"], "plain")
168

    
169

    
170
@InstanceCheck(None, INST_UP, RETURN_VALUE)
171
def TestInstanceAddWithDrbdDisk(node, node2):
172
  """gnt-instance add -t drbd"""
173
  return _DiskTest("%s:%s" % (node["primary"], node2["primary"]),
174
                   "drbd")
175

    
176

    
177
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
178
def TestInstanceRemove(instance):
179
  """gnt-instance remove"""
180
  AssertCommand(["gnt-instance", "remove", "-f", instance["name"]])
181

    
182
  qa_config.ReleaseInstance(instance)
183

    
184

    
185
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
186
def TestInstanceStartup(instance):
187
  """gnt-instance startup"""
188
  AssertCommand(["gnt-instance", "startup", instance["name"]])
189

    
190

    
191
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
192
def TestInstanceShutdown(instance):
193
  """gnt-instance shutdown"""
194
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
195

    
196

    
197
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
198
def TestInstanceReboot(instance):
199
  """gnt-instance reboot"""
200
  options = qa_config.get("options", {})
201
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
202
  name = instance["name"]
203
  for rtype in reboot_types:
204
    AssertCommand(["gnt-instance", "reboot", "--type=%s" % rtype, name])
205

    
206
  AssertCommand(["gnt-instance", "shutdown", name])
207
  qa_utils.RunInstanceCheck(instance, False)
208
  AssertCommand(["gnt-instance", "reboot", name])
209

    
210
  master = qa_config.GetMasterNode()
211
  cmd = ["gnt-instance", "list", "--no-headers", "-o", "status", name]
212
  result_output = qa_utils.GetCommandOutput(master["primary"],
213
                                            utils.ShellQuoteArgs(cmd))
214
  AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
215

    
216

    
217
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
218
def TestInstanceReinstall(instance):
219
  """gnt-instance reinstall"""
220
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
221

    
222
  # Test with non-existant OS definition
223
  AssertCommand(["gnt-instance", "reinstall", "-f",
224
                 "--os-type=NonExistantOsForQa",
225
                 instance["name"]],
226
                fail=True)
227

    
228

    
229
def _ReadSsconfInstanceList():
230
  """Reads ssconf_instance_list from the master node.
231

232
  """
233
  master = qa_config.GetMasterNode()
234

    
235
  cmd = ["cat", utils.PathJoin(pathutils.DATA_DIR,
236
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
237

    
238
  return qa_utils.GetCommandOutput(master["primary"],
239
                                   utils.ShellQuoteArgs(cmd)).splitlines()
240

    
241

    
242
def _CheckSsconfInstanceList(instance):
243
  """Checks if a certain instance is in the ssconf instance list.
244

245
  @type instance: string
246
  @param instance: Instance name
247

248
  """
249
  AssertIn(qa_utils.ResolveInstanceName(instance),
250
           _ReadSsconfInstanceList())
251

    
252

    
253
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
254
def TestInstanceRenameAndBack(rename_source, rename_target):
255
  """gnt-instance rename
256

257
  This must leave the instance with the original name, not the target
258
  name.
259

260
  """
261
  _CheckSsconfInstanceList(rename_source)
262

    
263
  # first do a rename to a different actual name, expecting it to fail
264
  qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
265
  try:
266
    AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
267
                  fail=True)
268
    _CheckSsconfInstanceList(rename_source)
269
  finally:
270
    qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
271

    
272
  # Check instance volume tags correctly updated
273
  # FIXME: this is LVM specific!
274
  info = _GetInstanceInfo(rename_source)
275
  tags_cmd = ("lvs -o tags --noheadings %s | grep " %
276
              (" ".join(info["volumes"]), ))
277

    
278
  # and now rename instance to rename_target...
279
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
280
  _CheckSsconfInstanceList(rename_target)
281
  qa_utils.RunInstanceCheck(rename_source, False)
282
  qa_utils.RunInstanceCheck(rename_target, False)
283

    
284
  # NOTE: tags might not be the exactly as the instance name, due to
285
  # charset restrictions; hence the test might be flaky
286
  if rename_source != rename_target:
287
    for node in info["nodes"]:
288
      AssertCommand(tags_cmd + rename_source, node=node, fail=True)
289
      AssertCommand(tags_cmd + rename_target, node=node, fail=False)
290

    
291
  # and back
292
  AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
293
  _CheckSsconfInstanceList(rename_source)
294
  qa_utils.RunInstanceCheck(rename_target, False)
295

    
296
  if rename_source != rename_target:
297
    for node in info["nodes"]:
298
      AssertCommand(tags_cmd + rename_source, node=node, fail=False)
299
      AssertCommand(tags_cmd + rename_target, node=node, fail=True)
300

    
301

    
302
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
303
def TestInstanceFailover(instance):
304
  """gnt-instance failover"""
305
  cmd = ["gnt-instance", "failover", "--force", instance["name"]]
306

    
307
  # failover ...
308
  AssertCommand(cmd)
309
  qa_utils.RunInstanceCheck(instance, True)
310

    
311
  # ... and back
312
  AssertCommand(cmd)
313

    
314

    
315
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
316
def TestInstanceMigrate(instance, toggle_always_failover=True):
317
  """gnt-instance migrate"""
318
  cmd = ["gnt-instance", "migrate", "--force", instance["name"]]
319
  af_par = constants.BE_ALWAYS_FAILOVER
320
  af_field = "be/" + constants.BE_ALWAYS_FAILOVER
321
  af_init_val = _GetBoolInstanceField(instance["name"], af_field)
322

    
323
  # migrate ...
324
  AssertCommand(cmd)
325
  # TODO: Verify the choice between failover and migration
326
  qa_utils.RunInstanceCheck(instance, True)
327

    
328
  # ... and back (possibly with always_failover toggled)
329
  if toggle_always_failover:
330
    AssertCommand(["gnt-instance", "modify", "-B",
331
                   ("%s=%s" % (af_par, not af_init_val)),
332
                   instance["name"]])
333
  AssertCommand(cmd)
334
  # TODO: Verify the choice between failover and migration
335
  qa_utils.RunInstanceCheck(instance, True)
336
  if toggle_always_failover:
337
    AssertCommand(["gnt-instance", "modify", "-B",
338
                   ("%s=%s" % (af_par, af_init_val)), instance["name"]])
339

    
340
  # TODO: Split into multiple tests
341
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
342
  qa_utils.RunInstanceCheck(instance, False)
343
  AssertCommand(cmd, fail=True)
344
  AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
345
                 instance["name"]])
346
  AssertCommand(["gnt-instance", "start", instance["name"]])
347
  AssertCommand(cmd)
348
  # @InstanceCheck enforces the check that the instance is running
349
  qa_utils.RunInstanceCheck(instance, True)
350

    
351
  AssertCommand(["gnt-instance", "modify", "-B",
352
                 ("%s=%s" %
353
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)),
354
                 instance["name"]])
355

    
356
  AssertCommand(cmd)
357
  qa_utils.RunInstanceCheck(instance, True)
358
  # TODO: Verify that a failover has been done instead of a migration
359

    
360
  # TODO: Verify whether the default value is restored here (not hardcoded)
361
  AssertCommand(["gnt-instance", "modify", "-B",
362
                 ("%s=%s" %
363
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_FALSE)),
364
                 instance["name"]])
365

    
366
  AssertCommand(cmd)
367
  qa_utils.RunInstanceCheck(instance, True)
368

    
369

    
370
def TestInstanceInfo(instance):
371
  """gnt-instance info"""
372
  AssertCommand(["gnt-instance", "info", instance["name"]])
373

    
374

    
375
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
376
def TestInstanceModify(instance):
377
  """gnt-instance modify"""
378
  default_hv = qa_config.GetDefaultHypervisor()
379

    
380
  # Assume /sbin/init exists on all systems
381
  test_kernel = "/sbin/init"
382
  test_initrd = test_kernel
383

    
384
  orig_maxmem = qa_config.get(constants.BE_MAXMEM)
385
  orig_minmem = qa_config.get(constants.BE_MINMEM)
386
  #orig_bridge = qa_config.get("bridge", "xen-br0")
387

    
388
  args = [
389
    ["-B", "%s=128" % constants.BE_MINMEM],
390
    ["-B", "%s=128" % constants.BE_MAXMEM],
391
    ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
392
                            constants.BE_MAXMEM, orig_maxmem)],
393
    ["-B", "%s=2" % constants.BE_VCPUS],
394
    ["-B", "%s=1" % constants.BE_VCPUS],
395
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
396
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
397
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
398

    
399
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
400
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
401

    
402
    # TODO: bridge tests
403
    #["--bridge", "xen-br1"],
404
    #["--bridge", orig_bridge],
405
    ]
406

    
407
  if default_hv == constants.HT_XEN_PVM:
408
    args.extend([
409
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
410
      ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
411
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
412
      ])
413
  elif default_hv == constants.HT_XEN_HVM:
414
    args.extend([
415
      ["-H", "%s=acn" % constants.HV_BOOT_ORDER],
416
      ["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
417
      ])
418

    
419
  for alist in args:
420
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
421

    
422
  # check no-modify
423
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
424

    
425
  # Marking offline/online while instance is running must fail
426
  for arg in ["--online", "--offline"]:
427
    AssertCommand(["gnt-instance", "modify", arg, instance["name"]], fail=True)
428

    
429

    
430
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
431
def TestInstanceStoppedModify(instance):
432
  """gnt-instance modify (stopped instance)"""
433
  name = instance["name"]
434

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

    
438
  # Mark instance as offline
439
  AssertCommand(["gnt-instance", "modify", "--offline", name])
440

    
441
  # And online again
442
  AssertCommand(["gnt-instance", "modify", "--online", name])
443

    
444

    
445
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
446
def TestInstanceConvertDisk(instance, snode):
447
  """gnt-instance modify -t"""
448
  name = instance["name"]
449
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
450
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
451
                 "-n", snode["primary"], name])
452

    
453

    
454
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
455
def TestInstanceGrowDisk(instance):
456
  """gnt-instance grow-disk"""
457
  name = instance["name"]
458
  all_size = qa_config.get("disk")
459
  all_grow = qa_config.get("disk-growth")
460
  if not all_grow:
461
    # missing disk sizes but instance grow disk has been enabled,
462
    # let's set fixed/nomimal growth
463
    all_grow = ["128M" for _ in all_size]
464
  for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
465
    # succeed in grow by amount
466
    AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
467
    # fail in grow to the old size
468
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
469
                   size], fail=True)
470
    # succeed to grow to old size + 2 * growth
471
    int_size = utils.ParseUnit(size)
472
    int_grow = utils.ParseUnit(grow)
473
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
474
                   str(int_size + 2 * int_grow)])
475

    
476

    
477
def TestInstanceList():
478
  """gnt-instance list"""
479
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
480

    
481

    
482
def TestInstanceListFields():
483
  """gnt-instance list-fields"""
484
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
485

    
486

    
487
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
488
def TestInstanceConsole(instance):
489
  """gnt-instance console"""
490
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
491

    
492

    
493
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
494
def TestReplaceDisks(instance, pnode, snode, othernode):
495
  """gnt-instance replace-disks"""
496
  # pylint: disable=W0613
497
  # due to unused pnode arg
498
  # FIXME: should be removed from the function completely
499
  def buildcmd(args):
500
    cmd = ["gnt-instance", "replace-disks"]
501
    cmd.extend(args)
502
    cmd.append(instance["name"])
503
    return cmd
504

    
505
  options = qa_config.get("options", {})
506
  use_ialloc = options.get("use-iallocators", True)
507
  for data in [
508
    ["-p"],
509
    ["-s"],
510
    # A placeholder; the actual command choice depends on use_ialloc
511
    None,
512
    # Restore the original secondary
513
    ["--new-secondary=%s" % snode["primary"]],
514
    ]:
515
    if data is None:
516
      if use_ialloc:
517
        data = ["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT]
518
      else:
519
        data = ["--new-secondary=%s" % othernode["primary"]]
520
    AssertCommand(buildcmd(data))
521

    
522
  AssertCommand(buildcmd(["-a"]))
523
  AssertCommand(["gnt-instance", "stop", instance["name"]])
524
  AssertCommand(buildcmd(["-a"]), fail=True)
525
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
526
  AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
527
                 instance["name"]])
528
  AssertCommand(buildcmd(["-a"]))
529
  AssertCommand(["gnt-instance", "start", instance["name"]])
530

    
531

    
532
def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
533
                         destroy=True):
534
  """Execute gnt-instance recreate-disks and check the result
535

536
  @param cmdargs: Arguments (instance name excluded)
537
  @param instance: Instance to operate on
538
  @param fail: True if the command is expected to fail
539
  @param check: If True and fail is False, check that the disks work
540
  @prama destroy: If True, destroy the old disks first
541

542
  """
543
  if destroy:
544
    _DestroyInstanceVolumes(instance)
545
  AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
546
                 [instance["name"]]), fail)
547
  if not fail and check:
548
    # Quick check that the disks are there
549
    AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
550
    AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
551
                   instance["name"]])
552
    AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
553

    
554

    
555
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
556
def TestRecreateDisks(instance, pnode, snode, othernodes):
557
  """gnt-instance recreate-disks
558

559
  @param instance: Instance to work on
560
  @param pnode: Primary node
561
  @param snode: Secondary node, or None for sigle-homed instances
562
  @param othernodes: list/tuple of nodes where to temporarily recreate disks
563

564
  """
565
  options = qa_config.get("options", {})
566
  use_ialloc = options.get("use-iallocators", True)
567
  other_seq = ":".join([n["primary"] for n in othernodes])
568
  orig_seq = pnode["primary"]
569
  if snode:
570
    orig_seq = orig_seq + ":" + snode["primary"]
571
  # These fail because the instance is running
572
  _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
573
  if use_ialloc:
574
    _AssertRecreateDisks(["-I", "hail"], instance, fail=True, destroy=False)
575
  else:
576
    _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
577
  AssertCommand(["gnt-instance", "stop", instance["name"]])
578
  # Disks exist: this should fail
579
  _AssertRecreateDisks([], instance, fail=True, destroy=False)
580
  # Recreate disks in place
581
  _AssertRecreateDisks([], instance)
582
  # Move disks away
583
  if use_ialloc:
584
    _AssertRecreateDisks(["-I", "hail"], instance)
585
    # Move disks somewhere else
586
    _AssertRecreateDisks(["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT],
587
                         instance)
588
  else:
589
    _AssertRecreateDisks(["-n", other_seq], instance)
590
  # Move disks back
591
  _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
592
  # This and InstanceCheck decoration check that the disks are working
593
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
594
  AssertCommand(["gnt-instance", "start", instance["name"]])
595

    
596

    
597
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
598
def TestInstanceExport(instance, node):
599
  """gnt-backup export -n ..."""
600
  name = instance["name"]
601
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
602
  return qa_utils.ResolveInstanceName(name)
603

    
604

    
605
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
606
def TestInstanceExportWithRemove(instance, node):
607
  """gnt-backup export --remove-instance"""
608
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
609
                 "--remove-instance", instance["name"]])
610

    
611

    
612
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
613
def TestInstanceExportNoTarget(instance):
614
  """gnt-backup export (without target node, should fail)"""
615
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
616

    
617

    
618
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
619
def TestInstanceImport(newinst, node, expnode, name):
620
  """gnt-backup import"""
621
  cmd = (["gnt-backup", "import",
622
          "--disk-template=plain",
623
          "--no-ip-check",
624
          "--src-node=%s" % expnode["primary"],
625
          "--src-dir=%s/%s" % (pathutils.EXPORT_DIR, name),
626
          "--node=%s" % node["primary"]] +
627
         _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
628
  cmd.append(newinst["name"])
629
  AssertCommand(cmd)
630

    
631

    
632
def TestBackupList(expnode):
633
  """gnt-backup list"""
634
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
635

    
636
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
637
                            namefield=None, test_unknown=False)
638

    
639

    
640
def TestBackupListFields():
641
  """gnt-backup list-fields"""
642
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
643

    
644

    
645
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
646
  """Testing disk failure."""
647
  master = qa_config.GetMasterNode()
648
  sq = utils.ShellQuoteArgs
649

    
650
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
651
  node_full = qa_utils.ResolveNodeName(node)
652
  node2_full = qa_utils.ResolveNodeName(node2)
653

    
654
  print qa_utils.FormatInfo("Getting physical disk names")
655
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
656
         "--output=node,phys,instance",
657
         node["primary"], node2["primary"]]
658
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
659

    
660
  # Get physical disk names
661
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
662
  node2disk = {}
663
  for line in output.splitlines():
664
    (node_name, phys, inst) = line.split("|")
665
    if inst == instance_full:
666
      if node_name not in node2disk:
667
        node2disk[node_name] = []
668

    
669
      m = re_disk.match(phys)
670
      if not m:
671
        raise qa_error.Error("Unknown disk name format: %s" % phys)
672

    
673
      name = m.group(1)
674
      if name not in node2disk[node_name]:
675
        node2disk[node_name].append(name)
676

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

    
681
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
682
                            " disks")
683
  for node_name, disks in node2disk.iteritems():
684
    cmds = []
685
    for disk in disks:
686
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
687
    AssertCommand(" && ".join(cmds), node=node_name)
688

    
689
  print qa_utils.FormatInfo("Getting device paths")
690
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
691
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
692
  devpath = []
693
  for line in output.splitlines():
694
    (_, _, tmpdevpath) = line.split(":")
695
    devpath.append(tmpdevpath)
696
  print devpath
697

    
698
  print qa_utils.FormatInfo("Getting drbd device paths")
699
  cmd = ["gnt-instance", "info", instance["name"]]
700
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
701
  pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
702
             r"\s+primary:\s+(/dev/drbd\d+)\s+")
703
  drbddevs = re.findall(pattern, output, re.M)
704
  print drbddevs
705

    
706
  halted_disks = []
707
  try:
708
    print qa_utils.FormatInfo("Deactivating disks")
709
    cmds = []
710
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
711
      halted_disks.append(name)
712
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
713
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
714

    
715
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
716
                              " the problem")
717
    cmds = []
718
    for disk in devpath:
719
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
720
                      "if=%s" % disk, "of=%s" % disk]))
721
    for _ in (0, 1, 2):
722
      AssertCommand(" && ".join(cmds), node=node)
723
      time.sleep(3)
724

    
725
    print qa_utils.FormatInfo("Debugging info")
726
    for name in drbddevs:
727
      AssertCommand(["drbdsetup", name, "show"], node=node)
728

    
729
    AssertCommand(["gnt-instance", "info", instance["name"]])
730

    
731
  finally:
732
    print qa_utils.FormatInfo("Activating disks again")
733
    cmds = []
734
    for name in halted_disks:
735
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
736
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
737

    
738
  if onmaster:
739
    for name in drbddevs:
740
      AssertCommand(["drbdsetup", name, "detach"], node=node)
741
  else:
742
    for name in drbddevs:
743
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
744

    
745
  # TODO
746
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
747

    
748
  print qa_utils.FormatInfo("Making sure disks are up again")
749
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
750

    
751
  print qa_utils.FormatInfo("Restarting instance")
752
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
753
  AssertCommand(["gnt-instance", "startup", instance["name"]])
754

    
755
  AssertCommand(["gnt-cluster", "verify"])
756

    
757

    
758
def TestInstanceMasterDiskFailure(instance, node, node2):
759
  """Testing disk failure on master node."""
760
  # pylint: disable=W0613
761
  # due to unused args
762
  print qa_utils.FormatError("Disk failure on primary node cannot be"
763
                             " tested due to potential crashes.")
764
  # The following can cause crashes, thus it's disabled until fixed
765
  #return _TestInstanceDiskFailure(instance, node, node2, True)
766

    
767

    
768
def TestInstanceSecondaryDiskFailure(instance, node, node2):
769
  """Testing disk failure on secondary node."""
770
  return _TestInstanceDiskFailure(instance, node, node2, False)