Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ c99200a3

History | View | Annotate | Download (23.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Instance related QA tests.
23

24
"""
25

    
26
import operator
27
import re
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
    qa_config.SetInstanceTemplate(instance, disk_template)
80

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

    
86

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

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

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

    
127

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

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

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

    
142

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

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

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

    
164

    
165
def IsFailoverSupported(instance):
166
  templ = qa_config.GetInstanceTemplate(instance)
167
  return templ in constants.DTS_MIRRORED
168

    
169

    
170
def IsMigrationSupported(instance):
171
  templ = qa_config.GetInstanceTemplate(instance)
172
  return templ in constants.DTS_MIRRORED
173

    
174

    
175
def IsDiskReplacingSupported(instance):
176
  templ = qa_config.GetInstanceTemplate(instance)
177
  return templ == constants.DT_DRBD8
178

    
179

    
180
@InstanceCheck(None, INST_UP, RETURN_VALUE)
181
def TestInstanceAddWithPlainDisk(nodes):
182
  """gnt-instance add -t plain"""
183
  assert len(nodes) == 1
184
  return _DiskTest(nodes[0]["primary"], "plain")
185

    
186

    
187
@InstanceCheck(None, INST_UP, RETURN_VALUE)
188
def TestInstanceAddWithDrbdDisk(nodes):
189
  """gnt-instance add -t drbd"""
190
  assert len(nodes) == 2
191
  return _DiskTest(":".join(map(operator.itemgetter("primary"), nodes)),
192
                   "drbd")
193

    
194

    
195
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
196
def TestInstanceRemove(instance):
197
  """gnt-instance remove"""
198
  AssertCommand(["gnt-instance", "remove", "-f", instance["name"]])
199

    
200
  qa_config.ReleaseInstance(instance)
201

    
202

    
203
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
204
def TestInstanceStartup(instance):
205
  """gnt-instance startup"""
206
  AssertCommand(["gnt-instance", "startup", instance["name"]])
207

    
208

    
209
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
210
def TestInstanceShutdown(instance):
211
  """gnt-instance shutdown"""
212
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
213

    
214

    
215
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
216
def TestInstanceReboot(instance):
217
  """gnt-instance reboot"""
218
  options = qa_config.get("options", {})
219
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
220
  name = instance["name"]
221
  for rtype in reboot_types:
222
    AssertCommand(["gnt-instance", "reboot", "--type=%s" % rtype, name])
223

    
224
  AssertCommand(["gnt-instance", "shutdown", name])
225
  qa_utils.RunInstanceCheck(instance, False)
226
  AssertCommand(["gnt-instance", "reboot", name])
227

    
228
  master = qa_config.GetMasterNode()
229
  cmd = ["gnt-instance", "list", "--no-headers", "-o", "status", name]
230
  result_output = qa_utils.GetCommandOutput(master["primary"],
231
                                            utils.ShellQuoteArgs(cmd))
232
  AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
233

    
234

    
235
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
236
def TestInstanceReinstall(instance):
237
  """gnt-instance reinstall"""
238
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
239

    
240
  # Test with non-existant OS definition
241
  AssertCommand(["gnt-instance", "reinstall", "-f",
242
                 "--os-type=NonExistantOsForQa",
243
                 instance["name"]],
244
                fail=True)
245

    
246

    
247
def _ReadSsconfInstanceList():
248
  """Reads ssconf_instance_list from the master node.
249

250
  """
251
  master = qa_config.GetMasterNode()
252

    
253
  cmd = ["cat", utils.PathJoin(pathutils.DATA_DIR,
254
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
255

    
256
  return qa_utils.GetCommandOutput(master["primary"],
257
                                   utils.ShellQuoteArgs(cmd)).splitlines()
258

    
259

    
260
def _CheckSsconfInstanceList(instance):
261
  """Checks if a certain instance is in the ssconf instance list.
262

263
  @type instance: string
264
  @param instance: Instance name
265

266
  """
267
  AssertIn(qa_utils.ResolveInstanceName(instance),
268
           _ReadSsconfInstanceList())
269

    
270

    
271
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
272
def TestInstanceRenameAndBack(rename_source, rename_target):
273
  """gnt-instance rename
274

275
  This must leave the instance with the original name, not the target
276
  name.
277

278
  """
279
  _CheckSsconfInstanceList(rename_source)
280

    
281
  # first do a rename to a different actual name, expecting it to fail
282
  qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
283
  try:
284
    AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
285
                  fail=True)
286
    _CheckSsconfInstanceList(rename_source)
287
  finally:
288
    qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
289

    
290
  # Check instance volume tags correctly updated
291
  # FIXME: this is LVM specific!
292
  info = _GetInstanceInfo(rename_source)
293
  tags_cmd = ("lvs -o tags --noheadings %s | grep " %
294
              (" ".join(info["volumes"]), ))
295

    
296
  # and now rename instance to rename_target...
297
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
298
  _CheckSsconfInstanceList(rename_target)
299
  qa_utils.RunInstanceCheck(rename_source, False)
300
  qa_utils.RunInstanceCheck(rename_target, False)
301

    
302
  # NOTE: tags might not be the exactly as the instance name, due to
303
  # charset restrictions; hence the test might be flaky
304
  if rename_source != rename_target:
305
    for node in info["nodes"]:
306
      AssertCommand(tags_cmd + rename_source, node=node, fail=True)
307
      AssertCommand(tags_cmd + rename_target, node=node, fail=False)
308

    
309
  # and back
310
  AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
311
  _CheckSsconfInstanceList(rename_source)
312
  qa_utils.RunInstanceCheck(rename_target, False)
313

    
314
  if rename_source != rename_target:
315
    for node in info["nodes"]:
316
      AssertCommand(tags_cmd + rename_source, node=node, fail=False)
317
      AssertCommand(tags_cmd + rename_target, node=node, fail=True)
318

    
319

    
320
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
321
def TestInstanceFailover(instance):
322
  """gnt-instance failover"""
323
  if not IsFailoverSupported(instance):
324
    print qa_utils.FormatInfo("Instance doesn't support failover, skipping"
325
                              " test")
326
    return
327

    
328
  cmd = ["gnt-instance", "failover", "--force", instance["name"]]
329

    
330
  # failover ...
331
  AssertCommand(cmd)
332
  qa_utils.RunInstanceCheck(instance, True)
333

    
334
  # ... and back
335
  AssertCommand(cmd)
336

    
337

    
338
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
339
def TestInstanceMigrate(instance, toggle_always_failover=True):
340
  """gnt-instance migrate"""
341
  if not IsMigrationSupported(instance):
342
    print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
343
                              " test")
344
    return
345

    
346
  cmd = ["gnt-instance", "migrate", "--force", instance["name"]]
347
  af_par = constants.BE_ALWAYS_FAILOVER
348
  af_field = "be/" + constants.BE_ALWAYS_FAILOVER
349
  af_init_val = _GetBoolInstanceField(instance["name"], af_field)
350

    
351
  # migrate ...
352
  AssertCommand(cmd)
353
  # TODO: Verify the choice between failover and migration
354
  qa_utils.RunInstanceCheck(instance, True)
355

    
356
  # ... and back (possibly with always_failover toggled)
357
  if toggle_always_failover:
358
    AssertCommand(["gnt-instance", "modify", "-B",
359
                   ("%s=%s" % (af_par, not af_init_val)),
360
                   instance["name"]])
361
  AssertCommand(cmd)
362
  # TODO: Verify the choice between failover and migration
363
  qa_utils.RunInstanceCheck(instance, True)
364
  if toggle_always_failover:
365
    AssertCommand(["gnt-instance", "modify", "-B",
366
                   ("%s=%s" % (af_par, af_init_val)), instance["name"]])
367

    
368
  # TODO: Split into multiple tests
369
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
370
  qa_utils.RunInstanceCheck(instance, False)
371
  AssertCommand(cmd, fail=True)
372
  AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
373
                 instance["name"]])
374
  AssertCommand(["gnt-instance", "start", instance["name"]])
375
  AssertCommand(cmd)
376
  # @InstanceCheck enforces the check that the instance is running
377
  qa_utils.RunInstanceCheck(instance, True)
378

    
379
  AssertCommand(["gnt-instance", "modify", "-B",
380
                 ("%s=%s" %
381
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)),
382
                 instance["name"]])
383

    
384
  AssertCommand(cmd)
385
  qa_utils.RunInstanceCheck(instance, True)
386
  # TODO: Verify that a failover has been done instead of a migration
387

    
388
  # TODO: Verify whether the default value is restored here (not hardcoded)
389
  AssertCommand(["gnt-instance", "modify", "-B",
390
                 ("%s=%s" %
391
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_FALSE)),
392
                 instance["name"]])
393

    
394
  AssertCommand(cmd)
395
  qa_utils.RunInstanceCheck(instance, True)
396

    
397

    
398
def TestInstanceInfo(instance):
399
  """gnt-instance info"""
400
  AssertCommand(["gnt-instance", "info", instance["name"]])
401

    
402

    
403
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
404
def TestInstanceModify(instance):
405
  """gnt-instance modify"""
406
  default_hv = qa_config.GetDefaultHypervisor()
407

    
408
  # Assume /sbin/init exists on all systems
409
  test_kernel = "/sbin/init"
410
  test_initrd = test_kernel
411

    
412
  orig_maxmem = qa_config.get(constants.BE_MAXMEM)
413
  orig_minmem = qa_config.get(constants.BE_MINMEM)
414
  #orig_bridge = qa_config.get("bridge", "xen-br0")
415

    
416
  args = [
417
    ["-B", "%s=128" % constants.BE_MINMEM],
418
    ["-B", "%s=128" % constants.BE_MAXMEM],
419
    ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
420
                            constants.BE_MAXMEM, orig_maxmem)],
421
    ["-B", "%s=2" % constants.BE_VCPUS],
422
    ["-B", "%s=1" % constants.BE_VCPUS],
423
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
424
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
425
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
426

    
427
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
428
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
429

    
430
    # TODO: bridge tests
431
    #["--bridge", "xen-br1"],
432
    #["--bridge", orig_bridge],
433
    ]
434

    
435
  if default_hv == constants.HT_XEN_PVM:
436
    args.extend([
437
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
438
      ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
439
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
440
      ])
441
  elif default_hv == constants.HT_XEN_HVM:
442
    args.extend([
443
      ["-H", "%s=acn" % constants.HV_BOOT_ORDER],
444
      ["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
445
      ])
446

    
447
  for alist in args:
448
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
449

    
450
  # check no-modify
451
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
452

    
453
  # Marking offline while instance is running must fail...
454
  AssertCommand(["gnt-instance", "modify", "--offline", instance["name"]],
455
                 fail=True)
456

    
457
  # ...while making it online is ok, and should work
458
  AssertCommand(["gnt-instance", "modify", "--online", instance["name"]])
459

    
460

    
461
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
462
def TestInstanceStoppedModify(instance):
463
  """gnt-instance modify (stopped instance)"""
464
  name = instance["name"]
465

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

    
469
  # Mark instance as offline
470
  AssertCommand(["gnt-instance", "modify", "--offline", name])
471

    
472
  # When the instance is offline shutdown should only work with --force,
473
  # while start should never work
474
  AssertCommand(["gnt-instance", "shutdown", name], fail=True)
475
  AssertCommand(["gnt-instance", "shutdown", "--force", name])
476
  AssertCommand(["gnt-instance", "start", name], fail=True)
477
  AssertCommand(["gnt-instance", "start", "--force", name], fail=True)
478

    
479
  # Also do offline to offline
480
  AssertCommand(["gnt-instance", "modify", "--offline", name])
481

    
482
  # And online again
483
  AssertCommand(["gnt-instance", "modify", "--online", name])
484

    
485

    
486
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
487
def TestInstanceConvertDiskToPlain(instance, inodes):
488
  """gnt-instance modify -t"""
489
  name = instance["name"]
490
  template = qa_config.GetInstanceTemplate(instance)
491
  if template != "drbd":
492
    print qa_utils.FormatInfo("Unsupported template %s, skipping conversion"
493
                              " test" % template)
494
    return
495
  assert len(inodes) == 2
496
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
497
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
498
                 "-n", inodes[1]["primary"], name])
499

    
500

    
501
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
502
def TestInstanceGrowDisk(instance):
503
  """gnt-instance grow-disk"""
504
  if qa_config.GetExclusiveStorage():
505
    print qa_utils.FormatInfo("Test not supported with exclusive_storage")
506
    return
507
  name = instance["name"]
508
  all_size = qa_config.get("disk")
509
  all_grow = qa_config.get("disk-growth")
510
  if not all_grow:
511
    # missing disk sizes but instance grow disk has been enabled,
512
    # let's set fixed/nomimal growth
513
    all_grow = ["128M" for _ in all_size]
514
  for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
515
    # succeed in grow by amount
516
    AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
517
    # fail in grow to the old size
518
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
519
                   size], fail=True)
520
    # succeed to grow to old size + 2 * growth
521
    int_size = utils.ParseUnit(size)
522
    int_grow = utils.ParseUnit(grow)
523
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
524
                   str(int_size + 2 * int_grow)])
525

    
526

    
527
def TestInstanceList():
528
  """gnt-instance list"""
529
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
530

    
531

    
532
def TestInstanceListFields():
533
  """gnt-instance list-fields"""
534
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
535

    
536

    
537
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
538
def TestInstanceConsole(instance):
539
  """gnt-instance console"""
540
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
541

    
542

    
543
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
544
def TestReplaceDisks(instance, curr_nodes, other_nodes):
545
  """gnt-instance replace-disks"""
546
  def buildcmd(args):
547
    cmd = ["gnt-instance", "replace-disks"]
548
    cmd.extend(args)
549
    cmd.append(instance["name"])
550
    return cmd
551

    
552
  if not IsDiskReplacingSupported(instance):
553
    print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
554
                              " skipping test")
555
    return
556

    
557
  # Currently all supported templates have one primary and one secondary node
558
  assert len(curr_nodes) == 2
559
  snode = curr_nodes[1]
560
  assert len(other_nodes) == 1
561
  othernode = other_nodes[0]
562

    
563
  options = qa_config.get("options", {})
564
  use_ialloc = options.get("use-iallocators", True)
565
  for data in [
566
    ["-p"],
567
    ["-s"],
568
    # A placeholder; the actual command choice depends on use_ialloc
569
    None,
570
    # Restore the original secondary
571
    ["--new-secondary=%s" % snode["primary"]],
572
    ]:
573
    if data is None:
574
      if use_ialloc:
575
        data = ["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT]
576
      else:
577
        data = ["--new-secondary=%s" % othernode["primary"]]
578
    AssertCommand(buildcmd(data))
579

    
580
  AssertCommand(buildcmd(["-a"]))
581
  AssertCommand(["gnt-instance", "stop", instance["name"]])
582
  AssertCommand(buildcmd(["-a"]), fail=True)
583
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
584
  AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
585
                 instance["name"]])
586
  AssertCommand(buildcmd(["-a"]))
587
  AssertCommand(["gnt-instance", "start", instance["name"]])
588

    
589

    
590
def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
591
                         destroy=True):
592
  """Execute gnt-instance recreate-disks and check the result
593

594
  @param cmdargs: Arguments (instance name excluded)
595
  @param instance: Instance to operate on
596
  @param fail: True if the command is expected to fail
597
  @param check: If True and fail is False, check that the disks work
598
  @prama destroy: If True, destroy the old disks first
599

600
  """
601
  if destroy:
602
    _DestroyInstanceVolumes(instance)
603
  AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
604
                 [instance["name"]]), fail)
605
  if not fail and check:
606
    # Quick check that the disks are there
607
    AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
608
    AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
609
                   instance["name"]])
610
    AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
611

    
612

    
613
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
614
def TestRecreateDisks(instance, inodes, othernodes):
615
  """gnt-instance recreate-disks
616

617
  @param instance: Instance to work on
618
  @param inodes: List of the current nodes of the instance
619
  @param othernodes: list/tuple of nodes where to temporarily recreate disks
620

621
  """
622
  options = qa_config.get("options", {})
623
  use_ialloc = options.get("use-iallocators", True)
624
  other_seq = ":".join([n["primary"] for n in othernodes])
625
  orig_seq = ":".join([n["primary"] for n in inodes])
626
  # These fail because the instance is running
627
  _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
628
  if use_ialloc:
629
    _AssertRecreateDisks(["-I", "hail"], instance, fail=True, destroy=False)
630
  else:
631
    _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
632
  AssertCommand(["gnt-instance", "stop", instance["name"]])
633
  # Disks exist: this should fail
634
  _AssertRecreateDisks([], instance, fail=True, destroy=False)
635
  # Recreate disks in place
636
  _AssertRecreateDisks([], instance)
637
  # Move disks away
638
  if use_ialloc:
639
    _AssertRecreateDisks(["-I", "hail"], instance)
640
    # Move disks somewhere else
641
    _AssertRecreateDisks(["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT],
642
                         instance)
643
  else:
644
    _AssertRecreateDisks(["-n", other_seq], instance)
645
  # Move disks back
646
  _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
647
  # This and InstanceCheck decoration check that the disks are working
648
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
649
  AssertCommand(["gnt-instance", "start", instance["name"]])
650

    
651

    
652
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
653
def TestInstanceExport(instance, node):
654
  """gnt-backup export -n ..."""
655
  name = instance["name"]
656
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
657
  return qa_utils.ResolveInstanceName(name)
658

    
659

    
660
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
661
def TestInstanceExportWithRemove(instance, node):
662
  """gnt-backup export --remove-instance"""
663
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
664
                 "--remove-instance", instance["name"]])
665
  qa_config.ReleaseInstance(instance)
666

    
667

    
668
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
669
def TestInstanceExportNoTarget(instance):
670
  """gnt-backup export (without target node, should fail)"""
671
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
672

    
673

    
674
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
675
def TestInstanceImport(newinst, node, expnode, name):
676
  """gnt-backup import"""
677
  templ = constants.DT_PLAIN
678
  cmd = (["gnt-backup", "import",
679
          "--disk-template=%s" % templ,
680
          "--no-ip-check",
681
          "--src-node=%s" % expnode["primary"],
682
          "--src-dir=%s/%s" % (pathutils.EXPORT_DIR, name),
683
          "--node=%s" % node["primary"]] +
684
         _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
685
  cmd.append(newinst["name"])
686
  AssertCommand(cmd)
687
  qa_config.SetInstanceTemplate(newinst, templ)
688

    
689

    
690
def TestBackupList(expnode):
691
  """gnt-backup list"""
692
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
693

    
694
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
695
                            namefield=None, test_unknown=False)
696

    
697

    
698
def TestBackupListFields():
699
  """gnt-backup list-fields"""
700
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())