Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ e35c341e

History | View | Annotate | Download (24.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 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 = inst.GetNicMacAddr(0, None)
60

    
61
  if nic0_mac:
62
    params.extend(["--net", "0:mac=%s" % nic0_mac])
63

    
64
  return params
65

    
66

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

    
77
    AssertCommand(cmd)
78

    
79
    _CheckSsconfInstanceList(instance.name)
80
    instance.SetDiskTemplate(disk_template)
81

    
82
    return instance
83
  except:
84
    instance.Release()
85
    raise
86

    
87

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

91
  @type instance: string
92
  @param instance: the instance name
93
  @return: a dictionary with the following keys:
94
      - "nodes": instance nodes, a list of strings
95
      - "volumes": instance volume IDs, a list of strings
96
      - "drbd-minors": DRBD minors used by the instance, a dictionary where
97
        keys are nodes, and values are lists of integers (or an empty
98
        dictionary for non-DRBD instances)
99

100
  """
101
  master = qa_config.GetMasterNode()
102
  infocmd = utils.ShellQuoteArgs(["gnt-instance", "info", instance])
103
  info_out = qa_utils.GetCommandOutput(master.primary, infocmd)
104
  re_node = re.compile(r"^\s+-\s+(?:primary|secondaries):\s+(\S.+)$")
105
  node_elem = r"([^,()]+)(?:\s+\([^)]+\))?"
106
  # re_nodelist matches a list of nodes returned by gnt-instance info, e.g.:
107
  #  node1.fqdn
108
  #  node2.fqdn,node3.fqdn
109
  #  node4.fqdn (group mygroup, group UUID 01234567-abcd-0123-4567-0123456789ab)
110
  # FIXME This works with no more than 2 secondaries
111
  re_nodelist = re.compile(node_elem + "(?:," + node_elem + ")?$")
112
  re_vol = re.compile(r"^\s+logical_id:\s+(\S+)$")
113
  re_drbdnode = re.compile(r"^\s+node[AB]:\s+([^\s,]+),\s+minor=([0-9]+)$")
114
  nodes = []
115
  vols = []
116
  drbd_min = {}
117
  for line in info_out.splitlines():
118
    m = re_node.match(line)
119
    if m:
120
      nodestr = m.group(1)
121
      m2 = re_nodelist.match(nodestr)
122
      if m2:
123
        nodes.extend(filter(None, m2.groups()))
124
      else:
125
        nodes.append(nodestr)
126
    m = re_vol.match(line)
127
    if m:
128
      vols.append(m.group(1))
129
    m = re_drbdnode.match(line)
130
    if m:
131
      node = m.group(1)
132
      minor = int(m.group(2))
133
      if drbd_min.get(node) is not None:
134
        drbd_min[node].append(minor)
135
      else:
136
        drbd_min[node] = [minor]
137
  assert vols
138
  assert nodes
139
  return {"nodes": nodes, "volumes": vols, "drbd-minors": drbd_min}
140

    
141

    
142
def _DestroyInstanceVolumes(instance):
143
  """Remove all the LVM volumes of an instance.
144

145
  This is used to simulate HW errors (dead nodes, broken disks...); the
146
  configuration of the instance is not affected.
147
  @type instance: dictionary
148
  @param instance: the instance
149

150
  """
151
  info = _GetInstanceInfo(instance.name)
152
  vols = info["volumes"]
153
  for node in info["nodes"]:
154
    AssertCommand(["lvremove", "-f"] + vols, node=node)
155

    
156

    
157
def _GetBoolInstanceField(instance, field):
158
  """Get the Boolean value of a field of an instance.
159

160
  @type instance: string
161
  @param instance: Instance name
162
  @type field: string
163
  @param field: Name of the field
164

165
  """
166
  master = qa_config.GetMasterNode()
167
  infocmd = utils.ShellQuoteArgs(["gnt-instance", "list", "--no-headers",
168
                                  "-o", field, instance])
169
  info_out = qa_utils.GetCommandOutput(master.primary, infocmd).strip()
170
  if info_out == "Y":
171
    return True
172
  elif info_out == "N":
173
    return False
174
  else:
175
    raise qa_error.Error("Field %s of instance %s has a non-Boolean value:"
176
                         " %s" % (field, instance, info_out))
177

    
178

    
179
def IsFailoverSupported(instance):
180
  return instance.disk_template in constants.DTS_MIRRORED
181

    
182

    
183
def IsMigrationSupported(instance):
184
  return instance.disk_template in constants.DTS_MIRRORED
185

    
186

    
187
def IsDiskReplacingSupported(instance):
188
  return instance.disk_template == constants.DT_DRBD8
189

    
190

    
191
@InstanceCheck(None, INST_UP, RETURN_VALUE)
192
def TestInstanceAddWithPlainDisk(nodes):
193
  """gnt-instance add -t plain"""
194
  assert len(nodes) == 1
195
  return _DiskTest(nodes[0].primary, constants.DT_PLAIN)
196

    
197

    
198
@InstanceCheck(None, INST_UP, RETURN_VALUE)
199
def TestInstanceAddWithDrbdDisk(nodes):
200
  """gnt-instance add -t drbd"""
201
  assert len(nodes) == 2
202
  return _DiskTest(":".join(map(operator.attrgetter("primary"), nodes)),
203
                   constants.DT_DRBD8)
204

    
205

    
206
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
207
def TestInstanceRemove(instance):
208
  """gnt-instance remove"""
209
  AssertCommand(["gnt-instance", "remove", "-f", instance.name])
210

    
211

    
212
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
213
def TestInstanceStartup(instance):
214
  """gnt-instance startup"""
215
  AssertCommand(["gnt-instance", "startup", instance.name])
216

    
217

    
218
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
219
def TestInstanceShutdown(instance):
220
  """gnt-instance shutdown"""
221
  AssertCommand(["gnt-instance", "shutdown", instance.name])
222

    
223

    
224
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
225
def TestInstanceReboot(instance):
226
  """gnt-instance reboot"""
227
  options = qa_config.get("options", {})
228
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
229
  name = instance.name
230
  for rtype in reboot_types:
231
    AssertCommand(["gnt-instance", "reboot", "--type=%s" % rtype, name])
232

    
233
  AssertCommand(["gnt-instance", "shutdown", name])
234
  qa_utils.RunInstanceCheck(instance, False)
235
  AssertCommand(["gnt-instance", "reboot", name])
236

    
237
  master = qa_config.GetMasterNode()
238
  cmd = ["gnt-instance", "list", "--no-headers", "-o", "status", name]
239
  result_output = qa_utils.GetCommandOutput(master.primary,
240
                                            utils.ShellQuoteArgs(cmd))
241
  AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
242

    
243

    
244
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
245
def TestInstanceReinstall(instance):
246
  """gnt-instance reinstall"""
247
  AssertCommand(["gnt-instance", "reinstall", "-f", instance.name])
248

    
249
  # Test with non-existant OS definition
250
  AssertCommand(["gnt-instance", "reinstall", "-f",
251
                 "--os-type=NonExistantOsForQa",
252
                 instance.name],
253
                fail=True)
254

    
255

    
256
def _ReadSsconfInstanceList():
257
  """Reads ssconf_instance_list from the master node.
258

259
  """
260
  master = qa_config.GetMasterNode()
261

    
262
  ssconf_path = utils.PathJoin(pathutils.DATA_DIR,
263
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)
264

    
265
  cmd = ["cat", qa_utils.MakeNodePath(master, ssconf_path)]
266

    
267
  return qa_utils.GetCommandOutput(master.primary,
268
                                   utils.ShellQuoteArgs(cmd)).splitlines()
269

    
270

    
271
def _CheckSsconfInstanceList(instance):
272
  """Checks if a certain instance is in the ssconf instance list.
273

274
  @type instance: string
275
  @param instance: Instance name
276

277
  """
278
  AssertIn(qa_utils.ResolveInstanceName(instance),
279
           _ReadSsconfInstanceList())
280

    
281

    
282
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
283
def TestInstanceRenameAndBack(rename_source, rename_target):
284
  """gnt-instance rename
285

286
  This must leave the instance with the original name, not the target
287
  name.
288

289
  """
290
  _CheckSsconfInstanceList(rename_source)
291

    
292
  # first do a rename to a different actual name, expecting it to fail
293
  qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
294
  try:
295
    AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
296
                  fail=True)
297
    _CheckSsconfInstanceList(rename_source)
298
  finally:
299
    qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
300

    
301
  # Check instance volume tags correctly updated
302
  # FIXME: this is LVM specific!
303
  info = _GetInstanceInfo(rename_source)
304
  tags_cmd = ("lvs -o tags --noheadings %s | grep " %
305
              (" ".join(info["volumes"]), ))
306

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

    
313
  # NOTE: tags might not be the exactly as the instance name, due to
314
  # charset restrictions; hence the test might be flaky
315
  if rename_source != rename_target:
316
    for node in info["nodes"]:
317
      AssertCommand(tags_cmd + rename_source, node=node, fail=True)
318
      AssertCommand(tags_cmd + rename_target, node=node, fail=False)
319

    
320
  # and back
321
  AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
322
  _CheckSsconfInstanceList(rename_source)
323
  qa_utils.RunInstanceCheck(rename_target, False)
324

    
325
  if rename_source != rename_target:
326
    for node in info["nodes"]:
327
      AssertCommand(tags_cmd + rename_source, node=node, fail=False)
328
      AssertCommand(tags_cmd + rename_target, node=node, fail=True)
329

    
330

    
331
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
332
def TestInstanceFailover(instance):
333
  """gnt-instance failover"""
334
  if not IsFailoverSupported(instance):
335
    print qa_utils.FormatInfo("Instance doesn't support failover, skipping"
336
                              " test")
337
    return
338

    
339
  cmd = ["gnt-instance", "failover", "--force", instance.name]
340

    
341
  # failover ...
342
  AssertCommand(cmd)
343
  qa_utils.RunInstanceCheck(instance, True)
344

    
345
  # ... and back
346
  AssertCommand(cmd)
347

    
348

    
349
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
350
def TestInstanceMigrate(instance, toggle_always_failover=True):
351
  """gnt-instance migrate"""
352
  if not IsMigrationSupported(instance):
353
    print qa_utils.FormatInfo("Instance doesn't support migration, skipping"
354
                              " test")
355
    return
356

    
357
  cmd = ["gnt-instance", "migrate", "--force", instance.name]
358
  af_par = constants.BE_ALWAYS_FAILOVER
359
  af_field = "be/" + constants.BE_ALWAYS_FAILOVER
360
  af_init_val = _GetBoolInstanceField(instance.name, af_field)
361

    
362
  # migrate ...
363
  AssertCommand(cmd)
364
  # TODO: Verify the choice between failover and migration
365
  qa_utils.RunInstanceCheck(instance, True)
366

    
367
  # ... and back (possibly with always_failover toggled)
368
  if toggle_always_failover:
369
    AssertCommand(["gnt-instance", "modify", "-B",
370
                   ("%s=%s" % (af_par, not af_init_val)),
371
                   instance.name])
372
  AssertCommand(cmd)
373
  # TODO: Verify the choice between failover and migration
374
  qa_utils.RunInstanceCheck(instance, True)
375
  if toggle_always_failover:
376
    AssertCommand(["gnt-instance", "modify", "-B",
377
                   ("%s=%s" % (af_par, af_init_val)), instance.name])
378

    
379
  # TODO: Split into multiple tests
380
  AssertCommand(["gnt-instance", "shutdown", instance.name])
381
  qa_utils.RunInstanceCheck(instance, False)
382
  AssertCommand(cmd, fail=True)
383
  AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
384
                 instance.name])
385
  AssertCommand(["gnt-instance", "start", instance.name])
386
  AssertCommand(cmd)
387
  # @InstanceCheck enforces the check that the instance is running
388
  qa_utils.RunInstanceCheck(instance, True)
389

    
390
  AssertCommand(["gnt-instance", "modify", "-B",
391
                 ("%s=%s" %
392
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)),
393
                 instance.name])
394

    
395
  AssertCommand(cmd)
396
  qa_utils.RunInstanceCheck(instance, True)
397
  # TODO: Verify that a failover has been done instead of a migration
398

    
399
  # TODO: Verify whether the default value is restored here (not hardcoded)
400
  AssertCommand(["gnt-instance", "modify", "-B",
401
                 ("%s=%s" %
402
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_FALSE)),
403
                 instance.name])
404

    
405
  AssertCommand(cmd)
406
  qa_utils.RunInstanceCheck(instance, True)
407

    
408

    
409
def TestInstanceInfo(instance):
410
  """gnt-instance info"""
411
  AssertCommand(["gnt-instance", "info", instance.name])
412

    
413

    
414
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
415
def TestInstanceModify(instance):
416
  """gnt-instance modify"""
417
  default_hv = qa_config.GetDefaultHypervisor()
418

    
419
  # Assume /sbin/init exists on all systems
420
  test_kernel = "/sbin/init"
421
  test_initrd = test_kernel
422

    
423
  orig_maxmem = qa_config.get(constants.BE_MAXMEM)
424
  orig_minmem = qa_config.get(constants.BE_MINMEM)
425
  #orig_bridge = qa_config.get("bridge", "xen-br0")
426

    
427
  args = [
428
    ["-B", "%s=128" % constants.BE_MINMEM],
429
    ["-B", "%s=128" % constants.BE_MAXMEM],
430
    ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
431
                            constants.BE_MAXMEM, orig_maxmem)],
432
    ["-B", "%s=2" % constants.BE_VCPUS],
433
    ["-B", "%s=1" % constants.BE_VCPUS],
434
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
435
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
436
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
437

    
438
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
439
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
440

    
441
    # TODO: bridge tests
442
    #["--bridge", "xen-br1"],
443
    #["--bridge", orig_bridge],
444
    ]
445

    
446
  if default_hv == constants.HT_XEN_PVM:
447
    args.extend([
448
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
449
      ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
450
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
451
      ])
452
  elif default_hv == constants.HT_XEN_HVM:
453
    args.extend([
454
      ["-H", "%s=acn" % constants.HV_BOOT_ORDER],
455
      ["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
456
      ])
457

    
458
  for alist in args:
459
    AssertCommand(["gnt-instance", "modify"] + alist + [instance.name])
460

    
461
  # check no-modify
462
  AssertCommand(["gnt-instance", "modify", instance.name], fail=True)
463

    
464
  # Marking offline while instance is running must fail...
465
  AssertCommand(["gnt-instance", "modify", "--offline", instance.name],
466
                 fail=True)
467

    
468
  # ...while making it online is ok, and should work
469
  AssertCommand(["gnt-instance", "modify", "--online", instance.name])
470

    
471

    
472
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
473
def TestInstanceStoppedModify(instance):
474
  """gnt-instance modify (stopped instance)"""
475
  name = instance.name
476

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

    
480
  # Mark instance as offline
481
  AssertCommand(["gnt-instance", "modify", "--offline", name])
482

    
483
  # When the instance is offline shutdown should only work with --force,
484
  # while start should never work
485
  AssertCommand(["gnt-instance", "shutdown", name], fail=True)
486
  AssertCommand(["gnt-instance", "shutdown", "--force", name])
487
  AssertCommand(["gnt-instance", "start", name], fail=True)
488
  AssertCommand(["gnt-instance", "start", "--force", name], fail=True)
489

    
490
  # Also do offline to offline
491
  AssertCommand(["gnt-instance", "modify", "--offline", name])
492

    
493
  # And online again
494
  AssertCommand(["gnt-instance", "modify", "--online", name])
495

    
496

    
497
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
498
def TestInstanceConvertDiskToPlain(instance, inodes):
499
  """gnt-instance modify -t"""
500
  name = instance.name
501

    
502
  template = instance.disk_template
503
  if template != constants.DT_DRBD8:
504
    print qa_utils.FormatInfo("Unsupported template %s, skipping conversion"
505
                              " test" % template)
506
    return
507

    
508
  assert len(inodes) == 2
509
  AssertCommand(["gnt-instance", "modify", "-t", constants.DT_PLAIN, name])
510
  AssertCommand(["gnt-instance", "modify", "-t", constants.DT_DRBD8,
511
                 "-n", inodes[1].primary, name])
512

    
513

    
514
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
515
def TestInstanceGrowDisk(instance):
516
  """gnt-instance grow-disk"""
517
  if qa_config.GetExclusiveStorage():
518
    print qa_utils.FormatInfo("Test not supported with exclusive_storage")
519
    return
520
  name = instance.name
521
  all_size = qa_config.get("disk")
522
  all_grow = qa_config.get("disk-growth")
523
  if not all_grow:
524
    # missing disk sizes but instance grow disk has been enabled,
525
    # let's set fixed/nomimal growth
526
    all_grow = ["128M" for _ in all_size]
527
  for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
528
    # succeed in grow by amount
529
    AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
530
    # fail in grow to the old size
531
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
532
                   size], fail=True)
533
    # succeed to grow to old size + 2 * growth
534
    int_size = utils.ParseUnit(size)
535
    int_grow = utils.ParseUnit(grow)
536
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
537
                   str(int_size + 2 * int_grow)])
538

    
539

    
540
def TestInstanceList():
541
  """gnt-instance list"""
542
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
543

    
544

    
545
def TestInstanceListFields():
546
  """gnt-instance list-fields"""
547
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
548

    
549

    
550
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
551
def TestInstanceConsole(instance):
552
  """gnt-instance console"""
553
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance.name])
554

    
555

    
556
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
557
def TestReplaceDisks(instance, curr_nodes, other_nodes):
558
  """gnt-instance replace-disks"""
559
  def buildcmd(args):
560
    cmd = ["gnt-instance", "replace-disks"]
561
    cmd.extend(args)
562
    cmd.append(instance.name)
563
    return cmd
564

    
565
  if not IsDiskReplacingSupported(instance):
566
    print qa_utils.FormatInfo("Instance doesn't support disk replacing,"
567
                              " skipping test")
568
    return
569

    
570
  # Currently all supported templates have one primary and one secondary node
571
  assert len(curr_nodes) == 2
572
  snode = curr_nodes[1]
573
  assert len(other_nodes) == 1
574
  othernode = other_nodes[0]
575

    
576
  options = qa_config.get("options", {})
577
  use_ialloc = options.get("use-iallocators", True)
578
  for data in [
579
    ["-p"],
580
    ["-s"],
581
    # A placeholder; the actual command choice depends on use_ialloc
582
    None,
583
    # Restore the original secondary
584
    ["--new-secondary=%s" % snode.primary],
585
    ]:
586
    if data is None:
587
      if use_ialloc:
588
        data = ["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT]
589
      else:
590
        data = ["--new-secondary=%s" % othernode.primary]
591
    AssertCommand(buildcmd(data))
592

    
593
  AssertCommand(buildcmd(["-a"]))
594
  AssertCommand(["gnt-instance", "stop", instance.name])
595
  AssertCommand(buildcmd(["-a"]), fail=True)
596
  AssertCommand(["gnt-instance", "activate-disks", instance.name])
597
  AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
598
                 instance.name])
599
  AssertCommand(buildcmd(["-a"]))
600
  AssertCommand(["gnt-instance", "start", instance.name])
601

    
602

    
603
def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
604
                         destroy=True):
605
  """Execute gnt-instance recreate-disks and check the result
606

607
  @param cmdargs: Arguments (instance name excluded)
608
  @param instance: Instance to operate on
609
  @param fail: True if the command is expected to fail
610
  @param check: If True and fail is False, check that the disks work
611
  @prama destroy: If True, destroy the old disks first
612

613
  """
614
  if destroy:
615
    _DestroyInstanceVolumes(instance)
616
  AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
617
                 [instance.name]), fail)
618
  if not fail and check:
619
    # Quick check that the disks are there
620
    AssertCommand(["gnt-instance", "activate-disks", instance.name])
621
    AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
622
                   instance.name])
623
    AssertCommand(["gnt-instance", "deactivate-disks", instance.name])
624

    
625

    
626
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
627
def TestRecreateDisks(instance, inodes, othernodes):
628
  """gnt-instance recreate-disks
629

630
  @param instance: Instance to work on
631
  @param inodes: List of the current nodes of the instance
632
  @param othernodes: list/tuple of nodes where to temporarily recreate disks
633

634
  """
635
  options = qa_config.get("options", {})
636
  use_ialloc = options.get("use-iallocators", True)
637
  other_seq = ":".join([n.primary for n in othernodes])
638
  orig_seq = ":".join([n.primary for n in inodes])
639
  # These fail because the instance is running
640
  _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
641
  if use_ialloc:
642
    _AssertRecreateDisks(["-I", "hail"], instance, fail=True, destroy=False)
643
  else:
644
    _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
645
  AssertCommand(["gnt-instance", "stop", instance.name])
646
  # Disks exist: this should fail
647
  _AssertRecreateDisks([], instance, fail=True, destroy=False)
648
  # Recreate disks in place
649
  _AssertRecreateDisks([], instance)
650
  # Move disks away
651
  if use_ialloc:
652
    _AssertRecreateDisks(["-I", "hail"], instance)
653
    # Move disks somewhere else
654
    _AssertRecreateDisks(["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT],
655
                         instance)
656
  else:
657
    _AssertRecreateDisks(["-n", other_seq], instance)
658
  # Move disks back
659
  _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
660
  # This and InstanceCheck decoration check that the disks are working
661
  AssertCommand(["gnt-instance", "reinstall", "-f", instance.name])
662
  AssertCommand(["gnt-instance", "start", instance.name])
663

    
664

    
665
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
666
def TestInstanceExport(instance, node):
667
  """gnt-backup export -n ..."""
668
  name = instance.name
669
  AssertCommand(["gnt-backup", "export", "-n", node.primary, name])
670
  return qa_utils.ResolveInstanceName(name)
671

    
672

    
673
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
674
def TestInstanceExportWithRemove(instance, node):
675
  """gnt-backup export --remove-instance"""
676
  AssertCommand(["gnt-backup", "export", "-n", node.primary,
677
                 "--remove-instance", instance.name])
678

    
679

    
680
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
681
def TestInstanceExportNoTarget(instance):
682
  """gnt-backup export (without target node, should fail)"""
683
  AssertCommand(["gnt-backup", "export", instance.name], fail=True)
684

    
685

    
686
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
687
def TestInstanceImport(newinst, node, expnode, name):
688
  """gnt-backup import"""
689
  templ = constants.DT_PLAIN
690
  cmd = (["gnt-backup", "import",
691
          "--disk-template=%s" % templ,
692
          "--no-ip-check",
693
          "--src-node=%s" % expnode.primary,
694
          "--src-dir=%s/%s" % (pathutils.EXPORT_DIR, name),
695
          "--node=%s" % node.primary] +
696
         _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
697
  cmd.append(newinst.name)
698
  AssertCommand(cmd)
699
  newinst.SetDiskTemplate(templ)
700

    
701

    
702
def TestBackupList(expnode):
703
  """gnt-backup list"""
704
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode.primary])
705

    
706
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
707
                            namefield=None, test_unknown=False)
708

    
709

    
710
def TestBackupListFields():
711
  """gnt-backup list-fields"""
712
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
713

    
714

    
715
def TestRemoveInstanceOfflineNode(instance, snode, set_offline, set_online):
716
  """gtn-instance remove with an off-line node
717

718
  @param instance: instance
719
  @param snode: secondary node, to be set offline
720
  @param set_offline: function to call to set the node off-line
721
  @param set_online: function to call to set the node on-line
722

723
  """
724
  info = _GetInstanceInfo(instance.name)
725
  set_offline(snode)
726
  try:
727
    TestInstanceRemove(instance)
728
  finally:
729
    set_online(snode)
730
  # Clean up the disks on the offline node
731
  for minor in info["drbd-minors"][snode.primary]:
732
    AssertCommand(["drbdsetup", str(minor), "down"], node=snode)
733
  AssertCommand(["lvremove", "-f"] + info["volumes"], node=snode)