Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 68c8c3df

History | View | Annotate | Download (24.4 kB)

1
#
2
#
3

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

    
21

    
22
"""Instance related QA tests.
23

24
"""
25

    
26
import operator
27
import re
28

    
29
from ganeti import utils
30
from ganeti import constants
31
from ganeti import query
32
from ganeti import pathutils
33

    
34
import qa_config
35
import qa_utils
36
import qa_error
37

    
38
from qa_utils import AssertIn, AssertCommand, AssertEqual
39
from qa_utils import InstanceCheck, INST_DOWN, INST_UP, FIRST_ARG, RETURN_VALUE
40

    
41

    
42
def _GetDiskStatePath(disk):
43
  return "/sys/block/%s/device/state" % disk
44

    
45

    
46
def _GetGenericAddParameters(inst, force_mac=None):
47
  params = ["-B"]
48
  params.append("%s=%s,%s=%s" % (constants.BE_MINMEM,
49
                                 qa_config.get(constants.BE_MINMEM),
50
                                 constants.BE_MAXMEM,
51
                                 qa_config.get(constants.BE_MAXMEM)))
52
  for idx, size in enumerate(qa_config.get("disk")):
53
    params.extend(["--disk", "%s:size=%s" % (idx, size)])
54

    
55
  # Set static MAC address if configured
56
  if force_mac:
57
    nic0_mac = force_mac
58
  else:
59
    nic0_mac = qa_config.GetInstanceNicMac(inst)
60
  if nic0_mac:
61
    params.extend(["--net", "0:mac=%s" % nic0_mac])
62

    
63
  return params
64

    
65

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

    
76
    AssertCommand(cmd)
77

    
78
    _CheckSsconfInstanceList(instance.name)
79
    qa_config.SetInstanceTemplate(instance, disk_template)
80

    
81
    return instance
82
  except:
83
    instance.Release()
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, constants.DT_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.attrgetter("primary"), nodes)),
205
                   constants.DT_DRBD8)
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

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

    
219

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

    
225

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

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

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

    
245

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

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

    
257

    
258
def _ReadSsconfInstanceList():
259
  """Reads ssconf_instance_list from the master node.
260

261
  """
262
  master = qa_config.GetMasterNode()
263

    
264
  cmd = ["cat", utils.PathJoin(pathutils.DATA_DIR,
265
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
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 = qa_config.GetInstanceTemplate(instance)
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
  qa_config.SetInstanceTemplate(newinst, 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)