Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 906a0346

History | View | Annotate | Download (22.1 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 re
27

    
28
from ganeti import utils
29
from ganeti import constants
30
from ganeti import query
31
from ganeti import pathutils
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
    qa_config.SetInstanceTemplate(instance, disk_template)
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 while instance is running must fail...
426
  AssertCommand(["gnt-instance", "modify", "--offline", instance["name"]],
427
                 fail=True)
428

    
429
  # ...while making it online is ok, and should work
430
  AssertCommand(["gnt-instance", "modify", "--online", instance["name"]])
431

    
432

    
433
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
434
def TestInstanceStoppedModify(instance):
435
  """gnt-instance modify (stopped instance)"""
436
  name = instance["name"]
437

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

    
441
  # Mark instance as offline
442
  AssertCommand(["gnt-instance", "modify", "--offline", name])
443

    
444
  # When the instance is offline shutdown should only work with --force,
445
  # while start should never work
446
  AssertCommand(["gnt-instance", "shutdown", name], fail=True)
447
  AssertCommand(["gnt-instance", "shutdown", "--force", name])
448
  AssertCommand(["gnt-instance", "start", name], fail=True)
449
  AssertCommand(["gnt-instance", "start", "--force", name], fail=True)
450

    
451
  # Also do offline to offline
452
  AssertCommand(["gnt-instance", "modify", "--offline", name])
453

    
454
  # And online again
455
  AssertCommand(["gnt-instance", "modify", "--online", name])
456

    
457

    
458
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
459
def TestInstanceConvertDisk(instance, snode):
460
  """gnt-instance modify -t"""
461
  name = instance["name"]
462
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
463
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
464
                 "-n", snode["primary"], name])
465

    
466

    
467
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
468
def TestInstanceGrowDisk(instance):
469
  """gnt-instance grow-disk"""
470
  name = instance["name"]
471
  all_size = qa_config.get("disk")
472
  all_grow = qa_config.get("disk-growth")
473
  if not all_grow:
474
    # missing disk sizes but instance grow disk has been enabled,
475
    # let's set fixed/nomimal growth
476
    all_grow = ["128M" for _ in all_size]
477
  for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
478
    # succeed in grow by amount
479
    AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
480
    # fail in grow to the old size
481
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
482
                   size], fail=True)
483
    # succeed to grow to old size + 2 * growth
484
    int_size = utils.ParseUnit(size)
485
    int_grow = utils.ParseUnit(grow)
486
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
487
                   str(int_size + 2 * int_grow)])
488

    
489

    
490
def TestInstanceList():
491
  """gnt-instance list"""
492
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
493

    
494

    
495
def TestInstanceListFields():
496
  """gnt-instance list-fields"""
497
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
498

    
499

    
500
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
501
def TestInstanceConsole(instance):
502
  """gnt-instance console"""
503
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
504

    
505

    
506
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
507
def TestReplaceDisks(instance, pnode, snode, othernode):
508
  """gnt-instance replace-disks"""
509
  # pylint: disable=W0613
510
  # due to unused pnode arg
511
  # FIXME: should be removed from the function completely
512
  def buildcmd(args):
513
    cmd = ["gnt-instance", "replace-disks"]
514
    cmd.extend(args)
515
    cmd.append(instance["name"])
516
    return cmd
517

    
518
  options = qa_config.get("options", {})
519
  use_ialloc = options.get("use-iallocators", True)
520
  for data in [
521
    ["-p"],
522
    ["-s"],
523
    # A placeholder; the actual command choice depends on use_ialloc
524
    None,
525
    # Restore the original secondary
526
    ["--new-secondary=%s" % snode["primary"]],
527
    ]:
528
    if data is None:
529
      if use_ialloc:
530
        data = ["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT]
531
      else:
532
        data = ["--new-secondary=%s" % othernode["primary"]]
533
    AssertCommand(buildcmd(data))
534

    
535
  AssertCommand(buildcmd(["-a"]))
536
  AssertCommand(["gnt-instance", "stop", instance["name"]])
537
  AssertCommand(buildcmd(["-a"]), fail=True)
538
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
539
  AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
540
                 instance["name"]])
541
  AssertCommand(buildcmd(["-a"]))
542
  AssertCommand(["gnt-instance", "start", instance["name"]])
543

    
544

    
545
def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
546
                         destroy=True):
547
  """Execute gnt-instance recreate-disks and check the result
548

549
  @param cmdargs: Arguments (instance name excluded)
550
  @param instance: Instance to operate on
551
  @param fail: True if the command is expected to fail
552
  @param check: If True and fail is False, check that the disks work
553
  @prama destroy: If True, destroy the old disks first
554

555
  """
556
  if destroy:
557
    _DestroyInstanceVolumes(instance)
558
  AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
559
                 [instance["name"]]), fail)
560
  if not fail and check:
561
    # Quick check that the disks are there
562
    AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
563
    AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
564
                   instance["name"]])
565
    AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
566

    
567

    
568
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
569
def TestRecreateDisks(instance, pnode, snode, othernodes):
570
  """gnt-instance recreate-disks
571

572
  @param instance: Instance to work on
573
  @param pnode: Primary node
574
  @param snode: Secondary node, or None for sigle-homed instances
575
  @param othernodes: list/tuple of nodes where to temporarily recreate disks
576

577
  """
578
  options = qa_config.get("options", {})
579
  use_ialloc = options.get("use-iallocators", True)
580
  other_seq = ":".join([n["primary"] for n in othernodes])
581
  orig_seq = pnode["primary"]
582
  if snode:
583
    orig_seq = orig_seq + ":" + snode["primary"]
584
  # These fail because the instance is running
585
  _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
586
  if use_ialloc:
587
    _AssertRecreateDisks(["-I", "hail"], instance, fail=True, destroy=False)
588
  else:
589
    _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
590
  AssertCommand(["gnt-instance", "stop", instance["name"]])
591
  # Disks exist: this should fail
592
  _AssertRecreateDisks([], instance, fail=True, destroy=False)
593
  # Recreate disks in place
594
  _AssertRecreateDisks([], instance)
595
  # Move disks away
596
  if use_ialloc:
597
    _AssertRecreateDisks(["-I", "hail"], instance)
598
    # Move disks somewhere else
599
    _AssertRecreateDisks(["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT],
600
                         instance)
601
  else:
602
    _AssertRecreateDisks(["-n", other_seq], instance)
603
  # Move disks back
604
  _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
605
  # This and InstanceCheck decoration check that the disks are working
606
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
607
  AssertCommand(["gnt-instance", "start", instance["name"]])
608

    
609

    
610
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
611
def TestInstanceExport(instance, node):
612
  """gnt-backup export -n ..."""
613
  name = instance["name"]
614
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
615
  return qa_utils.ResolveInstanceName(name)
616

    
617

    
618
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
619
def TestInstanceExportWithRemove(instance, node):
620
  """gnt-backup export --remove-instance"""
621
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
622
                 "--remove-instance", instance["name"]])
623
  qa_config.ReleaseInstance(instance)
624

    
625

    
626
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
627
def TestInstanceExportNoTarget(instance):
628
  """gnt-backup export (without target node, should fail)"""
629
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
630

    
631

    
632
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
633
def TestInstanceImport(newinst, node, expnode, name):
634
  """gnt-backup import"""
635
  templ = constants.DT_PLAIN
636
  cmd = (["gnt-backup", "import",
637
          "--disk-template=%s" % templ,
638
          "--no-ip-check",
639
          "--src-node=%s" % expnode["primary"],
640
          "--src-dir=%s/%s" % (pathutils.EXPORT_DIR, name),
641
          "--node=%s" % node["primary"]] +
642
         _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
643
  cmd.append(newinst["name"])
644
  AssertCommand(cmd)
645
  qa_config.SetInstanceTemplate(newinst, templ)
646

    
647

    
648
def TestBackupList(expnode):
649
  """gnt-backup list"""
650
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
651

    
652
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
653
                            namefield=None, test_unknown=False)
654

    
655

    
656
def TestBackupListFields():
657
  """gnt-backup list-fields"""
658
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())