Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ cf62af3a

History | View | Annotate | Download (24.6 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 the following keys:
93
      - "nodes": instance nodes, a list of strings
94
      - "volumes": instance volume IDs, a list of strings
95
      - "drbd-minors": DRBD minors used by the instance, a dictionary where
96
        keys are nodes, and values are lists of integers (or an empty
97
        dictionary for non-DRBD instances)
98

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

    
140

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

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

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

    
155

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

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

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

    
177

    
178
def IsFailoverSupported(instance):
179
  templ = qa_config.GetInstanceTemplate(instance)
180
  return templ in constants.DTS_MIRRORED
181

    
182

    
183
def IsMigrationSupported(instance):
184
  templ = qa_config.GetInstanceTemplate(instance)
185
  return templ in constants.DTS_MIRRORED
186

    
187

    
188
def IsDiskReplacingSupported(instance):
189
  templ = qa_config.GetInstanceTemplate(instance)
190
  return templ == constants.DT_DRBD8
191

    
192

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

    
199

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

    
207

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

    
213
  qa_config.ReleaseInstance(instance)
214

    
215

    
216
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
217
def TestInstanceStartup(instance):
218
  """gnt-instance startup"""
219
  AssertCommand(["gnt-instance", "startup", instance["name"]])
220

    
221

    
222
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
223
def TestInstanceShutdown(instance):
224
  """gnt-instance shutdown"""
225
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
226

    
227

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

    
237
  AssertCommand(["gnt-instance", "shutdown", name])
238
  qa_utils.RunInstanceCheck(instance, False)
239
  AssertCommand(["gnt-instance", "reboot", name])
240

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

    
247

    
248
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
249
def TestInstanceReinstall(instance):
250
  """gnt-instance reinstall"""
251
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
252

    
253
  # Test with non-existant OS definition
254
  AssertCommand(["gnt-instance", "reinstall", "-f",
255
                 "--os-type=NonExistantOsForQa",
256
                 instance["name"]],
257
                fail=True)
258

    
259

    
260
def _ReadSsconfInstanceList():
261
  """Reads ssconf_instance_list from the master node.
262

263
  """
264
  master = qa_config.GetMasterNode()
265

    
266
  cmd = ["cat", utils.PathJoin(pathutils.DATA_DIR,
267
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
268

    
269
  return qa_utils.GetCommandOutput(master["primary"],
270
                                   utils.ShellQuoteArgs(cmd)).splitlines()
271

    
272

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

276
  @type instance: string
277
  @param instance: Instance name
278

279
  """
280
  AssertIn(qa_utils.ResolveInstanceName(instance),
281
           _ReadSsconfInstanceList())
282

    
283

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

288
  This must leave the instance with the original name, not the target
289
  name.
290

291
  """
292
  _CheckSsconfInstanceList(rename_source)
293

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

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

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

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

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

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

    
332

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

    
341
  cmd = ["gnt-instance", "failover", "--force", instance["name"]]
342

    
343
  # failover ...
344
  AssertCommand(cmd)
345
  qa_utils.RunInstanceCheck(instance, True)
346

    
347
  # ... and back
348
  AssertCommand(cmd)
349

    
350

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

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

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

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

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

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

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

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

    
407
  AssertCommand(cmd)
408
  qa_utils.RunInstanceCheck(instance, True)
409

    
410

    
411
def TestInstanceInfo(instance):
412
  """gnt-instance info"""
413
  AssertCommand(["gnt-instance", "info", instance["name"]])
414

    
415

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

    
421
  # Assume /sbin/init exists on all systems
422
  test_kernel = "/sbin/init"
423
  test_initrd = test_kernel
424

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

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

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

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

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

    
460
  for alist in args:
461
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
462

    
463
  # check no-modify
464
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
465

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

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

    
473

    
474
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
475
def TestInstanceStoppedModify(instance):
476
  """gnt-instance modify (stopped instance)"""
477
  name = instance["name"]
478

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

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

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

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

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

    
498

    
499
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
500
def TestInstanceConvertDiskToPlain(instance, inodes):
501
  """gnt-instance modify -t"""
502
  name = instance["name"]
503
  template = qa_config.GetInstanceTemplate(instance)
504
  if template != "drbd":
505
    print qa_utils.FormatInfo("Unsupported template %s, skipping conversion"
506
                              " test" % template)
507
    return
508
  assert len(inodes) == 2
509
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
510
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
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
  qa_config.ReleaseInstance(instance)
679

    
680

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

    
686

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

    
702

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

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

    
710

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

    
715

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

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

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