Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 5de31440

History | View | Annotate | Download (23.3 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
def IsFailoverSupported(instance):
165
  templ = qa_config.GetInstanceTemplate(instance)
166
  return templ in constants.DTS_MIRRORED
167

    
168

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

    
173

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

    
178

    
179
@InstanceCheck(None, INST_UP, RETURN_VALUE)
180
def TestInstanceAddWithPlainDisk(node):
181
  """gnt-instance add -t plain"""
182
  return _DiskTest(node["primary"], "plain")
183

    
184

    
185
@InstanceCheck(None, INST_UP, RETURN_VALUE)
186
def TestInstanceAddWithDrbdDisk(node, node2):
187
  """gnt-instance add -t drbd"""
188
  return _DiskTest("%s:%s" % (node["primary"], node2["primary"]),
189
                   "drbd")
190

    
191

    
192
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
193
def TestInstanceRemove(instance):
194
  """gnt-instance remove"""
195
  AssertCommand(["gnt-instance", "remove", "-f", instance["name"]])
196

    
197
  qa_config.ReleaseInstance(instance)
198

    
199

    
200
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
201
def TestInstanceStartup(instance):
202
  """gnt-instance startup"""
203
  AssertCommand(["gnt-instance", "startup", instance["name"]])
204

    
205

    
206
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
207
def TestInstanceShutdown(instance):
208
  """gnt-instance shutdown"""
209
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
210

    
211

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

    
221
  AssertCommand(["gnt-instance", "shutdown", name])
222
  qa_utils.RunInstanceCheck(instance, False)
223
  AssertCommand(["gnt-instance", "reboot", name])
224

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

    
231

    
232
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
233
def TestInstanceReinstall(instance):
234
  """gnt-instance reinstall"""
235
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
236

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

    
243

    
244
def _ReadSsconfInstanceList():
245
  """Reads ssconf_instance_list from the master node.
246

247
  """
248
  master = qa_config.GetMasterNode()
249

    
250
  cmd = ["cat", utils.PathJoin(pathutils.DATA_DIR,
251
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
252

    
253
  return qa_utils.GetCommandOutput(master["primary"],
254
                                   utils.ShellQuoteArgs(cmd)).splitlines()
255

    
256

    
257
def _CheckSsconfInstanceList(instance):
258
  """Checks if a certain instance is in the ssconf instance list.
259

260
  @type instance: string
261
  @param instance: Instance name
262

263
  """
264
  AssertIn(qa_utils.ResolveInstanceName(instance),
265
           _ReadSsconfInstanceList())
266

    
267

    
268
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
269
def TestInstanceRenameAndBack(rename_source, rename_target):
270
  """gnt-instance rename
271

272
  This must leave the instance with the original name, not the target
273
  name.
274

275
  """
276
  _CheckSsconfInstanceList(rename_source)
277

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

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

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

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

    
306
  # and back
307
  AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
308
  _CheckSsconfInstanceList(rename_source)
309
  qa_utils.RunInstanceCheck(rename_target, False)
310

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

    
316

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

    
325
  cmd = ["gnt-instance", "failover", "--force", instance["name"]]
326

    
327
  # failover ...
328
  AssertCommand(cmd)
329
  qa_utils.RunInstanceCheck(instance, True)
330

    
331
  # ... and back
332
  AssertCommand(cmd)
333

    
334

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

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

    
348
  # migrate ...
349
  AssertCommand(cmd)
350
  # TODO: Verify the choice between failover and migration
351
  qa_utils.RunInstanceCheck(instance, True)
352

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

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

    
376
  AssertCommand(["gnt-instance", "modify", "-B",
377
                 ("%s=%s" %
378
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)),
379
                 instance["name"]])
380

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

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

    
391
  AssertCommand(cmd)
392
  qa_utils.RunInstanceCheck(instance, True)
393

    
394

    
395
def TestInstanceInfo(instance):
396
  """gnt-instance info"""
397
  AssertCommand(["gnt-instance", "info", instance["name"]])
398

    
399

    
400
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
401
def TestInstanceModify(instance):
402
  """gnt-instance modify"""
403
  default_hv = qa_config.GetDefaultHypervisor()
404

    
405
  # Assume /sbin/init exists on all systems
406
  test_kernel = "/sbin/init"
407
  test_initrd = test_kernel
408

    
409
  orig_maxmem = qa_config.get(constants.BE_MAXMEM)
410
  orig_minmem = qa_config.get(constants.BE_MINMEM)
411
  #orig_bridge = qa_config.get("bridge", "xen-br0")
412

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

    
424
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
425
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
426

    
427
    # TODO: bridge tests
428
    #["--bridge", "xen-br1"],
429
    #["--bridge", orig_bridge],
430
    ]
431

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

    
444
  for alist in args:
445
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
446

    
447
  # check no-modify
448
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
449

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

    
454
  # ...while making it online is ok, and should work
455
  AssertCommand(["gnt-instance", "modify", "--online", instance["name"]])
456

    
457

    
458
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
459
def TestInstanceStoppedModify(instance):
460
  """gnt-instance modify (stopped instance)"""
461
  name = instance["name"]
462

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

    
466
  # Mark instance as offline
467
  AssertCommand(["gnt-instance", "modify", "--offline", name])
468

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

    
476
  # Also do offline to offline
477
  AssertCommand(["gnt-instance", "modify", "--offline", name])
478

    
479
  # And online again
480
  AssertCommand(["gnt-instance", "modify", "--online", name])
481

    
482

    
483
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
484
def TestInstanceConvertDisk(instance, snode):
485
  """gnt-instance modify -t"""
486
  name = instance["name"]
487
  template = qa_config.GetInstanceTemplate(instance)
488
  if template != "drbd":
489
    print qa_utils.FormatInfo("Unsupported template %s, skipping conversion"
490
                              " test" % template)
491
    return
492
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
493
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
494
                 "-n", snode["primary"], name])
495

    
496

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

    
522

    
523
def TestInstanceList():
524
  """gnt-instance list"""
525
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
526

    
527

    
528
def TestInstanceListFields():
529
  """gnt-instance list-fields"""
530
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
531

    
532

    
533
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
534
def TestInstanceConsole(instance):
535
  """gnt-instance console"""
536
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
537

    
538

    
539
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
540
def TestReplaceDisks(instance, pnode, snode, othernode):
541
  """gnt-instance replace-disks"""
542
  # pylint: disable=W0613
543
  # due to unused pnode arg
544
  # FIXME: should be removed from the function completely
545
  def buildcmd(args):
546
    cmd = ["gnt-instance", "replace-disks"]
547
    cmd.extend(args)
548
    cmd.append(instance["name"])
549
    return cmd
550

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

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

    
573
  AssertCommand(buildcmd(["-a"]))
574
  AssertCommand(["gnt-instance", "stop", instance["name"]])
575
  AssertCommand(buildcmd(["-a"]), fail=True)
576
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
577
  AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
578
                 instance["name"]])
579
  AssertCommand(buildcmd(["-a"]))
580
  AssertCommand(["gnt-instance", "start", instance["name"]])
581

    
582

    
583
def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
584
                         destroy=True):
585
  """Execute gnt-instance recreate-disks and check the result
586

587
  @param cmdargs: Arguments (instance name excluded)
588
  @param instance: Instance to operate on
589
  @param fail: True if the command is expected to fail
590
  @param check: If True and fail is False, check that the disks work
591
  @prama destroy: If True, destroy the old disks first
592

593
  """
594
  if destroy:
595
    _DestroyInstanceVolumes(instance)
596
  AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
597
                 [instance["name"]]), fail)
598
  if not fail and check:
599
    # Quick check that the disks are there
600
    AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
601
    AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
602
                   instance["name"]])
603
    AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
604

    
605

    
606
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
607
def TestRecreateDisks(instance, pnode, snode, othernodes):
608
  """gnt-instance recreate-disks
609

610
  @param instance: Instance to work on
611
  @param pnode: Primary node
612
  @param snode: Secondary node, or None for sigle-homed instances
613
  @param othernodes: list/tuple of nodes where to temporarily recreate disks
614

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

    
647

    
648
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
649
def TestInstanceExport(instance, node):
650
  """gnt-backup export -n ..."""
651
  name = instance["name"]
652
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
653
  return qa_utils.ResolveInstanceName(name)
654

    
655

    
656
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
657
def TestInstanceExportWithRemove(instance, node):
658
  """gnt-backup export --remove-instance"""
659
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
660
                 "--remove-instance", instance["name"]])
661
  qa_config.ReleaseInstance(instance)
662

    
663

    
664
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
665
def TestInstanceExportNoTarget(instance):
666
  """gnt-backup export (without target node, should fail)"""
667
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
668

    
669

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

    
685

    
686
def TestBackupList(expnode):
687
  """gnt-backup list"""
688
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
689

    
690
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
691
                            namefield=None, test_unknown=False)
692

    
693

    
694
def TestBackupListFields():
695
  """gnt-backup list-fields"""
696
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())