Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ cc4b14f0

History | View | Annotate | Download (23.9 kB)

1
#
2
#
3

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

    
21

    
22
"""Instance related QA tests.
23

24
"""
25

    
26
import re
27
import time
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

    
80
    return instance
81
  except:
82
    qa_config.ReleaseInstance(instance)
83
    raise
84

    
85

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

89
  The pieces of information returned are:
90
    - "nodes": instance nodes, a list of strings
91
    - "volumes": instance volume IDs, a list of strings
92
  @type instance: dictionary
93
  @param instance: the instance
94
  @return: dictionary
95

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

    
127

    
128
def _DestroyInstanceVolumes(instance):
129
  """Remove all the LVM volumes of an instance.
130

131
  This is used to simulate HW errors (dead nodes, broken disks...); the
132
  configuration of the instance is not affected.
133
  @type instance: dictionary
134
  @param instance: the instance
135

136
  """
137
  info = _GetInstanceInfo(instance)
138
  vols = info["volumes"]
139
  for node in info["nodes"]:
140
    AssertCommand(["lvremove", "-f"] + vols, node=node)
141

    
142

    
143
def _GetBoolInstanceField(instance, field):
144
  """Get the Boolean value of a field of an instance.
145

146
  @type instance: string
147
  @param instance: Instance name
148
  @type field: string
149
  @param field: Name of the field
150

151
  """
152
  master = qa_config.GetMasterNode()
153
  infocmd = utils.ShellQuoteArgs(["gnt-instance", "list", "--no-headers",
154
                                  "-o", field, instance])
155
  info_out = qa_utils.GetCommandOutput(master["primary"], infocmd).strip()
156
  if info_out == "Y":
157
    return True
158
  elif info_out == "N":
159
    return False
160
  else:
161
    raise qa_error.Error("Field %s of instance %s has a non-Boolean value:"
162
                         " %s" % (field, instance, info_out))
163

    
164

    
165
@InstanceCheck(None, INST_UP, RETURN_VALUE)
166
def TestInstanceAddWithPlainDisk(node):
167
  """gnt-instance add -t plain"""
168
  return _DiskTest(node["primary"], "plain")
169

    
170

    
171
@InstanceCheck(None, INST_UP, RETURN_VALUE)
172
def TestInstanceAddWithDrbdDisk(node, node2):
173
  """gnt-instance add -t drbd"""
174
  return _DiskTest("%s:%s" % (node["primary"], node2["primary"]),
175
                   "drbd")
176

    
177

    
178
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
179
def TestInstanceRemove(instance):
180
  """gnt-instance remove"""
181
  AssertCommand(["gnt-instance", "remove", "-f", instance["name"]])
182

    
183
  qa_config.ReleaseInstance(instance)
184

    
185

    
186
@InstanceCheck(INST_DOWN, INST_UP, FIRST_ARG)
187
def TestInstanceStartup(instance):
188
  """gnt-instance startup"""
189
  AssertCommand(["gnt-instance", "startup", instance["name"]])
190

    
191

    
192
@InstanceCheck(INST_UP, INST_DOWN, FIRST_ARG)
193
def TestInstanceShutdown(instance):
194
  """gnt-instance shutdown"""
195
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
196

    
197

    
198
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
199
def TestInstanceReboot(instance):
200
  """gnt-instance reboot"""
201
  options = qa_config.get("options", {})
202
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
203
  name = instance["name"]
204
  for rtype in reboot_types:
205
    AssertCommand(["gnt-instance", "reboot", "--type=%s" % rtype, name])
206

    
207
  AssertCommand(["gnt-instance", "shutdown", name])
208
  qa_utils.RunInstanceCheck(instance, False)
209
  AssertCommand(["gnt-instance", "reboot", name])
210

    
211
  master = qa_config.GetMasterNode()
212
  cmd = ["gnt-instance", "list", "--no-headers", "-o", "status", name]
213
  result_output = qa_utils.GetCommandOutput(master["primary"],
214
                                            utils.ShellQuoteArgs(cmd))
215
  AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
216

    
217

    
218
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
219
def TestInstanceReinstall(instance):
220
  """gnt-instance reinstall"""
221
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
222

    
223

    
224
def _ReadSsconfInstanceList():
225
  """Reads ssconf_instance_list from the master node.
226

227
  """
228
  master = qa_config.GetMasterNode()
229

    
230
  cmd = ["cat", utils.PathJoin(pathutils.DATA_DIR,
231
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
232

    
233
  return qa_utils.GetCommandOutput(master["primary"],
234
                                   utils.ShellQuoteArgs(cmd)).splitlines()
235

    
236

    
237
def _CheckSsconfInstanceList(instance):
238
  """Checks if a certain instance is in the ssconf instance list.
239

240
  @type instance: string
241
  @param instance: Instance name
242

243
  """
244
  AssertIn(qa_utils.ResolveInstanceName(instance),
245
           _ReadSsconfInstanceList())
246

    
247

    
248
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
249
def TestInstanceRenameAndBack(rename_source, rename_target):
250
  """gnt-instance rename
251

252
  This must leave the instance with the original name, not the target
253
  name.
254

255
  """
256
  _CheckSsconfInstanceList(rename_source)
257

    
258
  # first do a rename to a different actual name, expecting it to fail
259
  qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
260
  try:
261
    AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
262
                  fail=True)
263
    _CheckSsconfInstanceList(rename_source)
264
  finally:
265
    qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
266

    
267
  # and now rename instance to rename_target...
268
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
269
  _CheckSsconfInstanceList(rename_target)
270
  qa_utils.RunInstanceCheck(rename_source, False)
271
  qa_utils.RunInstanceCheck(rename_target, False)
272

    
273
  # and back
274
  AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
275
  _CheckSsconfInstanceList(rename_source)
276
  qa_utils.RunInstanceCheck(rename_target, False)
277

    
278

    
279
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
280
def TestInstanceFailover(instance):
281
  """gnt-instance failover"""
282
  cmd = ["gnt-instance", "failover", "--force", instance["name"]]
283

    
284
  # failover ...
285
  AssertCommand(cmd)
286
  qa_utils.RunInstanceCheck(instance, True)
287

    
288
  # ... and back
289
  AssertCommand(cmd)
290

    
291

    
292
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
293
def TestInstanceMigrate(instance, toggle_always_failover=True):
294
  """gnt-instance migrate"""
295
  cmd = ["gnt-instance", "migrate", "--force", instance["name"]]
296
  af_par = constants.BE_ALWAYS_FAILOVER
297
  af_field = "be/" + constants.BE_ALWAYS_FAILOVER
298
  af_init_val = _GetBoolInstanceField(instance["name"], af_field)
299

    
300
  # migrate ...
301
  AssertCommand(cmd)
302
  # TODO: Verify the choice between failover and migration
303
  qa_utils.RunInstanceCheck(instance, True)
304

    
305
  # ... and back (possibly with always_failover toggled)
306
  if toggle_always_failover:
307
    AssertCommand(["gnt-instance", "modify", "-B",
308
                   ("%s=%s" % (af_par, not af_init_val)),
309
                   instance["name"]])
310
  AssertCommand(cmd)
311
  # TODO: Verify the choice between failover and migration
312
  qa_utils.RunInstanceCheck(instance, True)
313
  if toggle_always_failover:
314
    AssertCommand(["gnt-instance", "modify", "-B",
315
                   ("%s=%s" % (af_par, af_init_val)), instance["name"]])
316

    
317
  # TODO: Split into multiple tests
318
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
319
  qa_utils.RunInstanceCheck(instance, False)
320
  AssertCommand(cmd, fail=True)
321
  AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
322
                 instance["name"]])
323
  AssertCommand(["gnt-instance", "start", instance["name"]])
324
  AssertCommand(cmd)
325
  # @InstanceCheck enforces the check that the instance is running
326

    
327

    
328
def TestInstanceInfo(instance):
329
  """gnt-instance info"""
330
  AssertCommand(["gnt-instance", "info", instance["name"]])
331

    
332

    
333
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
334
def TestInstanceModify(instance):
335
  """gnt-instance modify"""
336
  default_hv = qa_config.GetDefaultHypervisor()
337

    
338
  # Assume /sbin/init exists on all systems
339
  test_kernel = "/sbin/init"
340
  test_initrd = test_kernel
341

    
342
  orig_maxmem = qa_config.get(constants.BE_MAXMEM)
343
  orig_minmem = qa_config.get(constants.BE_MINMEM)
344
  #orig_bridge = qa_config.get("bridge", "xen-br0")
345

    
346
  args = [
347
    ["-B", "%s=128" % constants.BE_MINMEM],
348
    ["-B", "%s=128" % constants.BE_MAXMEM],
349
    ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
350
                            constants.BE_MAXMEM, orig_maxmem)],
351
    ["-B", "%s=2" % constants.BE_VCPUS],
352
    ["-B", "%s=1" % constants.BE_VCPUS],
353
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
354
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
355
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
356

    
357
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
358
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
359

    
360
    # TODO: bridge tests
361
    #["--bridge", "xen-br1"],
362
    #["--bridge", orig_bridge],
363
    ]
364

    
365
  if default_hv == constants.HT_XEN_PVM:
366
    args.extend([
367
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
368
      ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
369
      ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
370
      ])
371
  elif default_hv == constants.HT_XEN_HVM:
372
    args.extend([
373
      ["-H", "%s=acn" % constants.HV_BOOT_ORDER],
374
      ["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
375
      ])
376

    
377
  for alist in args:
378
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
379

    
380
  # check no-modify
381
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
382

    
383
  # Marking offline/online while instance is running must fail
384
  for arg in ["--online", "--offline"]:
385
    AssertCommand(["gnt-instance", "modify", arg, instance["name"]], fail=True)
386

    
387

    
388
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
389
def TestInstanceStoppedModify(instance):
390
  """gnt-instance modify (stopped instance)"""
391
  name = instance["name"]
392

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

    
396
  # Mark instance as offline
397
  AssertCommand(["gnt-instance", "modify", "--offline", name])
398

    
399
  # And online again
400
  AssertCommand(["gnt-instance", "modify", "--online", name])
401

    
402

    
403
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
404
def TestInstanceConvertDisk(instance, snode):
405
  """gnt-instance modify -t"""
406
  name = instance["name"]
407
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
408
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
409
                 "-n", snode["primary"], name])
410

    
411

    
412
@InstanceCheck(INST_DOWN, INST_DOWN, FIRST_ARG)
413
def TestInstanceGrowDisk(instance):
414
  """gnt-instance grow-disk"""
415
  name = instance["name"]
416
  all_size = qa_config.get("disk")
417
  all_grow = qa_config.get("disk-growth")
418
  if not all_grow:
419
    # missing disk sizes but instance grow disk has been enabled,
420
    # let's set fixed/nomimal growth
421
    all_grow = ["128M" for _ in all_size]
422
  for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
423
    # succeed in grow by amount
424
    AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
425
    # fail in grow to the old size
426
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
427
                   size], fail=True)
428
    # succeed to grow to old size + 2 * growth
429
    int_size = utils.ParseUnit(size)
430
    int_grow = utils.ParseUnit(grow)
431
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
432
                   str(int_size + 2 * int_grow)])
433

    
434

    
435
def TestInstanceList():
436
  """gnt-instance list"""
437
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
438

    
439

    
440
def TestInstanceListFields():
441
  """gnt-instance list-fields"""
442
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
443

    
444

    
445
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
446
def TestInstanceConsole(instance):
447
  """gnt-instance console"""
448
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
449

    
450

    
451
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
452
def TestReplaceDisks(instance, pnode, snode, othernode):
453
  """gnt-instance replace-disks"""
454
  # pylint: disable=W0613
455
  # due to unused pnode arg
456
  # FIXME: should be removed from the function completely
457
  def buildcmd(args):
458
    cmd = ["gnt-instance", "replace-disks"]
459
    cmd.extend(args)
460
    cmd.append(instance["name"])
461
    return cmd
462

    
463
  for data in [
464
    ["-p"],
465
    ["-s"],
466
    ["--new-secondary=%s" % othernode["primary"]],
467
    ["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT],
468
    ]:
469
    AssertCommand(buildcmd(data))
470

    
471
  # Restore the original secondary, if needed
472
  currsec = _GetInstanceInfo(instance)["nodes"][1]
473
  if currsec != snode["primary"]:
474
    ["--new-secondary=%s" % snode["primary"]],
475

    
476
  AssertCommand(buildcmd(["-a"]))
477
  AssertCommand(["gnt-instance", "stop", instance["name"]])
478
  AssertCommand(buildcmd(["-a"]), fail=True)
479
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
480
  AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
481
                 instance["name"]])
482
  AssertCommand(buildcmd(["-a"]))
483
  AssertCommand(["gnt-instance", "start", instance["name"]])
484

    
485

    
486
def _AssertRecreateDisks(cmdargs, instance, fail=False, check=True,
487
                         destroy=True):
488
  """Execute gnt-instance recreate-disks and check the result
489

490
  @param cmdargs: Arguments (instance name excluded)
491
  @param instance: Instance to operate on
492
  @param fail: True if the command is expected to fail
493
  @param check: If True and fail is False, check that the disks work
494
  @prama destroy: If True, destroy the old disks first
495

496
  """
497
  if destroy:
498
    _DestroyInstanceVolumes(instance)
499
  AssertCommand((["gnt-instance", "recreate-disks"] + cmdargs +
500
                 [instance["name"]]), fail)
501
  if not fail and check:
502
    # Quick check that the disks are there
503
    AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
504
    AssertCommand(["gnt-instance", "activate-disks", "--wait-for-sync",
505
                   instance["name"]])
506
    AssertCommand(["gnt-instance", "deactivate-disks", instance["name"]])
507

    
508

    
509
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
510
def TestRecreateDisks(instance, pnode, snode, othernodes):
511
  """gnt-instance recreate-disks
512

513
  @param instance: Instance to work on
514
  @param pnode: Primary node
515
  @param snode: Secondary node, or None for sigle-homed instances
516
  @param othernodes: list/tuple of nodes where to temporarily recreate disks
517

518
  """
519
  other_seq = ":".join([n["primary"] for n in othernodes])
520
  orig_seq = pnode["primary"]
521
  if snode:
522
    orig_seq = orig_seq + ":" + snode["primary"]
523
  # These fail because the instance is running
524
  _AssertRecreateDisks(["-n", other_seq], instance, fail=True, destroy=False)
525
  _AssertRecreateDisks(["-I", "hail"], instance, fail=True, destroy=False)
526
  AssertCommand(["gnt-instance", "stop", instance["name"]])
527
  # Disks exist: this should fail
528
  _AssertRecreateDisks([], instance, fail=True, destroy=False)
529
  # Recreate disks in place
530
  _AssertRecreateDisks([], instance)
531
  # Move disks away
532
  _AssertRecreateDisks(["-I", "hail"], instance)
533
  # Move disks somewhere else
534
  _AssertRecreateDisks(["-I", constants.DEFAULT_IALLOCATOR_SHORTCUT], instance)
535
  # Move disks back
536
  _AssertRecreateDisks(["-n", orig_seq], instance, check=False)
537
  # This and InstanceCheck decoration check that the disks are working
538
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
539
  AssertCommand(["gnt-instance", "start", instance["name"]])
540

    
541

    
542
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
543
def TestInstanceExport(instance, node):
544
  """gnt-backup export -n ..."""
545
  name = instance["name"]
546
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
547
  return qa_utils.ResolveInstanceName(name)
548

    
549

    
550
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
551
def TestInstanceExportWithRemove(instance, node):
552
  """gnt-backup export --remove-instance"""
553
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
554
                 "--remove-instance", instance["name"]])
555

    
556

    
557
@InstanceCheck(INST_UP, INST_UP, FIRST_ARG)
558
def TestInstanceExportNoTarget(instance):
559
  """gnt-backup export (without target node, should fail)"""
560
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
561

    
562

    
563
@InstanceCheck(None, INST_DOWN, FIRST_ARG)
564
def TestInstanceImport(newinst, node, expnode, name):
565
  """gnt-backup import"""
566
  cmd = (["gnt-backup", "import",
567
          "--disk-template=plain",
568
          "--no-ip-check",
569
          "--src-node=%s" % expnode["primary"],
570
          "--src-dir=%s/%s" % (pathutils.EXPORT_DIR, name),
571
          "--node=%s" % node["primary"]] +
572
         _GetGenericAddParameters(newinst, force_mac=constants.VALUE_GENERATE))
573
  cmd.append(newinst["name"])
574
  AssertCommand(cmd)
575

    
576

    
577
def TestBackupList(expnode):
578
  """gnt-backup list"""
579
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
580

    
581
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
582
                            namefield=None, test_unknown=False)
583

    
584

    
585
def TestBackupListFields():
586
  """gnt-backup list-fields"""
587
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
588

    
589

    
590
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
591
  """Testing disk failure."""
592
  master = qa_config.GetMasterNode()
593
  sq = utils.ShellQuoteArgs
594

    
595
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
596
  node_full = qa_utils.ResolveNodeName(node)
597
  node2_full = qa_utils.ResolveNodeName(node2)
598

    
599
  print qa_utils.FormatInfo("Getting physical disk names")
600
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
601
         "--output=node,phys,instance",
602
         node["primary"], node2["primary"]]
603
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
604

    
605
  # Get physical disk names
606
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
607
  node2disk = {}
608
  for line in output.splitlines():
609
    (node_name, phys, inst) = line.split("|")
610
    if inst == instance_full:
611
      if node_name not in node2disk:
612
        node2disk[node_name] = []
613

    
614
      m = re_disk.match(phys)
615
      if not m:
616
        raise qa_error.Error("Unknown disk name format: %s" % phys)
617

    
618
      name = m.group(1)
619
      if name not in node2disk[node_name]:
620
        node2disk[node_name].append(name)
621

    
622
  if [node2_full, node_full][int(onmaster)] not in node2disk:
623
    raise qa_error.Error("Couldn't find physical disks used on"
624
                         " %s node" % ["secondary", "master"][int(onmaster)])
625

    
626
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
627
                            " disks")
628
  for node_name, disks in node2disk.iteritems():
629
    cmds = []
630
    for disk in disks:
631
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
632
    AssertCommand(" && ".join(cmds), node=node_name)
633

    
634
  print qa_utils.FormatInfo("Getting device paths")
635
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
636
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
637
  devpath = []
638
  for line in output.splitlines():
639
    (_, _, tmpdevpath) = line.split(":")
640
    devpath.append(tmpdevpath)
641
  print devpath
642

    
643
  print qa_utils.FormatInfo("Getting drbd device paths")
644
  cmd = ["gnt-instance", "info", instance["name"]]
645
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
646
  pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
647
             r"\s+primary:\s+(/dev/drbd\d+)\s+")
648
  drbddevs = re.findall(pattern, output, re.M)
649
  print drbddevs
650

    
651
  halted_disks = []
652
  try:
653
    print qa_utils.FormatInfo("Deactivating disks")
654
    cmds = []
655
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
656
      halted_disks.append(name)
657
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
658
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
659

    
660
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
661
                              " the problem")
662
    cmds = []
663
    for disk in devpath:
664
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
665
                      "if=%s" % disk, "of=%s" % disk]))
666
    for _ in (0, 1, 2):
667
      AssertCommand(" && ".join(cmds), node=node)
668
      time.sleep(3)
669

    
670
    print qa_utils.FormatInfo("Debugging info")
671
    for name in drbddevs:
672
      AssertCommand(["drbdsetup", name, "show"], node=node)
673

    
674
    AssertCommand(["gnt-instance", "info", instance["name"]])
675

    
676
  finally:
677
    print qa_utils.FormatInfo("Activating disks again")
678
    cmds = []
679
    for name in halted_disks:
680
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
681
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
682

    
683
  if onmaster:
684
    for name in drbddevs:
685
      AssertCommand(["drbdsetup", name, "detach"], node=node)
686
  else:
687
    for name in drbddevs:
688
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
689

    
690
  # TODO
691
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
692

    
693
  print qa_utils.FormatInfo("Making sure disks are up again")
694
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
695

    
696
  print qa_utils.FormatInfo("Restarting instance")
697
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
698
  AssertCommand(["gnt-instance", "startup", instance["name"]])
699

    
700
  AssertCommand(["gnt-cluster", "verify"])
701

    
702

    
703
def TestInstanceMasterDiskFailure(instance, node, node2):
704
  """Testing disk failure on master node."""
705
  # pylint: disable=W0613
706
  # due to unused args
707
  print qa_utils.FormatError("Disk failure on primary node cannot be"
708
                             " tested due to potential crashes.")
709
  # The following can cause crashes, thus it's disabled until fixed
710
  #return _TestInstanceDiskFailure(instance, node, node2, True)
711

    
712

    
713
def TestInstanceSecondaryDiskFailure(instance, node, node2):
714
  """Testing disk failure on secondary node."""
715
  return _TestInstanceDiskFailure(instance, node, node2, False)