Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 0afce24e

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 = 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
    qa_config.SetInstanceTemplate(instance, 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
  templ = qa_config.GetInstanceTemplate(instance)
181
  return templ in constants.DTS_MIRRORED
182

    
183

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

    
188

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

    
193

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

    
200

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

    
208

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

    
214

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

    
220

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

    
226

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

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

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

    
246

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

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

    
258

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

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

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

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

    
271

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

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

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

    
282

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

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

290
  """
291
  _CheckSsconfInstanceList(rename_source)
292

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

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

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

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

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

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

    
331

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

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

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

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

    
349

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

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

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

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

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

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

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

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

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

    
409

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

    
414

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

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

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

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

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

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

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

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

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

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

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

    
472

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

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

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

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

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

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

    
497

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

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

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

    
514

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

    
540

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

    
545

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

    
550

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

    
556

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

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

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

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

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

    
603

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

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

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

    
626

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

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

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

    
665

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

    
673

    
674
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
675
def TestInstanceExportWithRemove(instance, node):
676
  """gnt-backup export --remove-instance"""
677
  AssertCommand(["gnt-backup", "export", "-n", node.primary,
678
                 "--remove-instance", instance.name])
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)