Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 26a5056d

History | View | Annotate | Download (16.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

    
33
import qa_config
34
import qa_utils
35
import qa_error
36

    
37
from qa_utils import AssertIn, AssertCommand, AssertEqual
38

    
39

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

    
43

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

    
54

    
55
def _DiskTest(node, disk_template):
56
  instance = qa_config.AcquireInstance()
57
  try:
58
    cmd = (["gnt-instance", "add",
59
            "--os-type=%s" % qa_config.get("os"),
60
            "--disk-template=%s" % disk_template,
61
            "--node=%s" % node] +
62
           _GetGenericAddParameters())
63
    cmd.append(instance["name"])
64

    
65
    AssertCommand(cmd)
66

    
67
    _CheckSsconfInstanceList(instance["name"])
68

    
69
    return instance
70
  except:
71
    qa_config.ReleaseInstance(instance)
72
    raise
73

    
74

    
75
def TestInstanceAddWithPlainDisk(node):
76
  """gnt-instance add -t plain"""
77
  return _DiskTest(node["primary"], "plain")
78

    
79

    
80
def TestInstanceAddWithDrbdDisk(node, node2):
81
  """gnt-instance add -t drbd"""
82
  return _DiskTest("%s:%s" % (node["primary"], node2["primary"]),
83
                   "drbd")
84

    
85

    
86
def TestInstanceRemove(instance):
87
  """gnt-instance remove"""
88
  AssertCommand(["gnt-instance", "remove", "-f", instance["name"]])
89

    
90
  qa_config.ReleaseInstance(instance)
91

    
92

    
93
def TestInstanceStartup(instance):
94
  """gnt-instance startup"""
95
  AssertCommand(["gnt-instance", "startup", instance["name"]])
96

    
97

    
98
def TestInstanceShutdown(instance):
99
  """gnt-instance shutdown"""
100
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
101

    
102

    
103
def TestInstanceReboot(instance):
104
  """gnt-instance reboot"""
105
  options = qa_config.get("options", {})
106
  reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
107
  name = instance["name"]
108
  for rtype in reboot_types:
109
    AssertCommand(["gnt-instance", "reboot", "--type=%s" % rtype, name])
110

    
111
  AssertCommand(["gnt-instance", "shutdown", name])
112
  AssertCommand(["gnt-instance", "reboot", name])
113

    
114
  master = qa_config.GetMasterNode()
115
  cmd = ["gnt-instance", "list", "--no-headers", "-o", "status", name]
116
  result_output = qa_utils.GetCommandOutput(master["primary"],
117
                                            utils.ShellQuoteArgs(cmd))
118
  AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
119

    
120

    
121
def TestInstanceReinstall(instance):
122
  """gnt-instance reinstall"""
123
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
124

    
125

    
126
def _ReadSsconfInstanceList():
127
  """Reads ssconf_instance_list from the master node.
128

129
  """
130
  master = qa_config.GetMasterNode()
131

    
132
  cmd = ["cat", utils.PathJoin(constants.DATA_DIR,
133
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
134

    
135
  return qa_utils.GetCommandOutput(master["primary"],
136
                                   utils.ShellQuoteArgs(cmd)).splitlines()
137

    
138

    
139
def _CheckSsconfInstanceList(instance):
140
  """Checks if a certain instance is in the ssconf instance list.
141

142
  @type instance: string
143
  @param instance: Instance name
144

145
  """
146
  AssertIn(qa_utils.ResolveInstanceName(instance),
147
           _ReadSsconfInstanceList())
148

    
149

    
150
def TestInstanceRenameAndBack(rename_source, rename_target):
151
  """gnt-instance rename
152

153
  This must leave the instance with the original name, not the target
154
  name.
155

156
  """
157
  _CheckSsconfInstanceList(rename_source)
158
  # first do a rename to a different actual name, expecting it to fail
159
  qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
160
  try:
161
    AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
162
                  fail=True)
163
    _CheckSsconfInstanceList(rename_source)
164
  finally:
165
    qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
166
  # and now rename instance to rename_target...
167
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
168
  _CheckSsconfInstanceList(rename_target)
169
  # and back
170
  AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
171
  _CheckSsconfInstanceList(rename_source)
172

    
173

    
174
def TestInstanceFailover(instance):
175
  """gnt-instance failover"""
176
  cmd = ["gnt-instance", "failover", "--force", instance["name"]]
177
  # failover ...
178
  AssertCommand(cmd)
179
  # ... and back
180
  AssertCommand(cmd)
181

    
182

    
183
def TestInstanceMigrate(instance):
184
  """gnt-instance migrate"""
185
  cmd = ["gnt-instance", "migrate", "--force", instance["name"]]
186
  # migrate ...
187
  AssertCommand(cmd)
188
  # ... and back
189
  AssertCommand(cmd)
190
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
191
  AssertCommand(cmd, fail=True)
192
  AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
193
                 instance["name"]])
194
  AssertCommand(["gnt-instance", "start", instance["name"]])
195
  AssertCommand(cmd)
196
  AssertCommand(["gnt-instance", "modify", "-B",
197
                 ("%s=%s" %
198
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)),
199
                 instance["name"]])
200
  AssertCommand(cmd, fail=True)
201
  AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
202
                 instance["name"]])
203
  AssertCommand(["gnt-instance", "modify", "-B",
204
                 ("%s=%s" %
205
                  (constants.BE_ALWAYS_FAILOVER, constants.VALUE_FALSE)),
206
                 instance["name"]])
207
  AssertCommand(cmd)
208

    
209

    
210
def TestInstanceInfo(instance):
211
  """gnt-instance info"""
212
  AssertCommand(["gnt-instance", "info", instance["name"]])
213

    
214

    
215
def TestInstanceModify(instance):
216
  """gnt-instance modify"""
217
  # Assume /sbin/init exists on all systems
218
  test_kernel = "/sbin/init"
219
  test_initrd = test_kernel
220

    
221
  orig_maxmem = qa_config.get(constants.BE_MAXMEM)
222
  orig_minmem = qa_config.get(constants.BE_MINMEM)
223
  #orig_bridge = qa_config.get("bridge", "xen-br0")
224
  args = [
225
    ["-B", "%s=128" % constants.BE_MINMEM],
226
    ["-B", "%s=128" % constants.BE_MAXMEM],
227
    ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
228
                            constants.BE_MAXMEM, orig_maxmem)],
229
    ["-B", "%s=2" % constants.BE_VCPUS],
230
    ["-B", "%s=1" % constants.BE_VCPUS],
231
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
232
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
233
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
234

    
235
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
236
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
237
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
238
    ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
239
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
240

    
241
    # TODO: bridge tests
242
    #["--bridge", "xen-br1"],
243
    #["--bridge", orig_bridge],
244

    
245
    # TODO: Do these tests only with xen-hvm
246
    #["-H", "%s=acn" % constants.HV_BOOT_ORDER],
247
    #["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
248
    ]
249
  for alist in args:
250
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
251

    
252
  # check no-modify
253
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
254

    
255
  # Marking offline/online while instance is running must fail
256
  for arg in ["--online", "--offline"]:
257
    AssertCommand(["gnt-instance", "modify", arg, instance["name"]], fail=True)
258

    
259

    
260
def TestInstanceStoppedModify(instance):
261
  """gnt-instance modify (stopped instance)"""
262
  name = instance["name"]
263

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

    
267
  # Mark instance as offline
268
  AssertCommand(["gnt-instance", "modify", "--offline", name])
269

    
270
  # And online again
271
  AssertCommand(["gnt-instance", "modify", "--online", name])
272

    
273

    
274
def TestInstanceConvertDisk(instance, snode):
275
  """gnt-instance modify -t"""
276
  name = instance["name"]
277
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
278
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
279
                 "-n", snode["primary"], name])
280

    
281

    
282
def TestInstanceGrowDisk(instance):
283
  """gnt-instance grow-disk"""
284
  name = instance["name"]
285
  all_size = qa_config.get("disk")
286
  all_grow = qa_config.get("disk-growth")
287
  if not all_grow:
288
    # missing disk sizes but instance grow disk has been enabled,
289
    # let's set fixed/nomimal growth
290
    all_grow = ["128M" for _ in all_size]
291
  for idx, (size, grow) in enumerate(zip(all_size, all_grow)):
292
    # succeed in grow by amount
293
    AssertCommand(["gnt-instance", "grow-disk", name, str(idx), grow])
294
    # fail in grow to the old size
295
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
296
                   size], fail=True)
297
    # succeed to grow to old size + 2 * growth
298
    int_size = utils.ParseUnit(size)
299
    int_grow = utils.ParseUnit(grow)
300
    AssertCommand(["gnt-instance", "grow-disk", "--absolute", name, str(idx),
301
                   str(int_size + 2 * int_grow)])
302

    
303

    
304
def TestInstanceList():
305
  """gnt-instance list"""
306
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
307

    
308

    
309
def TestInstanceListFields():
310
  """gnt-instance list-fields"""
311
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
312

    
313

    
314
def TestInstanceConsole(instance):
315
  """gnt-instance console"""
316
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
317

    
318

    
319
def TestReplaceDisks(instance, pnode, snode, othernode):
320
  """gnt-instance replace-disks"""
321
  # pylint: disable=W0613
322
  # due to unused pnode arg
323
  # FIXME: should be removed from the function completely
324
  def buildcmd(args):
325
    cmd = ["gnt-instance", "replace-disks"]
326
    cmd.extend(args)
327
    cmd.append(instance["name"])
328
    return cmd
329

    
330
  for data in [
331
    ["-p"],
332
    ["-s"],
333
    ["--new-secondary=%s" % othernode["primary"]],
334
    # and restore
335
    ["--new-secondary=%s" % snode["primary"]],
336
    ]:
337
    AssertCommand(buildcmd(data))
338

    
339
  AssertCommand(buildcmd(["-a"]))
340
  AssertCommand(["gnt-instance", "stop", instance["name"]])
341
  AssertCommand(buildcmd(["-a"]), fail=True)
342
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
343
  AssertCommand(buildcmd(["-a"]))
344
  AssertCommand(["gnt-instance", "start", instance["name"]])
345

    
346

    
347
def TestInstanceExport(instance, node):
348
  """gnt-backup export -n ..."""
349
  name = instance["name"]
350
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
351
  return qa_utils.ResolveInstanceName(name)
352

    
353

    
354
def TestInstanceExportWithRemove(instance, node):
355
  """gnt-backup export --remove-instance"""
356
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
357
                 "--remove-instance", instance["name"]])
358

    
359

    
360
def TestInstanceExportNoTarget(instance):
361
  """gnt-backup export (without target node, should fail)"""
362
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
363

    
364

    
365
def TestInstanceImport(node, newinst, expnode, name):
366
  """gnt-backup import"""
367
  cmd = (["gnt-backup", "import",
368
          "--disk-template=plain",
369
          "--no-ip-check",
370
          "--net", "0:mac=generate",
371
          "--src-node=%s" % expnode["primary"],
372
          "--src-dir=%s/%s" % (constants.EXPORT_DIR, name),
373
          "--node=%s" % node["primary"]] +
374
         _GetGenericAddParameters())
375
  cmd.append(newinst["name"])
376
  AssertCommand(cmd)
377

    
378

    
379
def TestBackupList(expnode):
380
  """gnt-backup list"""
381
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
382

    
383
  qa_utils.GenericQueryTest("gnt-backup", query.EXPORT_FIELDS.keys(),
384
                            namefield=None, test_unknown=False)
385

    
386

    
387
def TestBackupListFields():
388
  """gnt-backup list-fields"""
389
  qa_utils.GenericQueryFieldsTest("gnt-backup", query.EXPORT_FIELDS.keys())
390

    
391

    
392
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
393
  """Testing disk failure."""
394
  master = qa_config.GetMasterNode()
395
  sq = utils.ShellQuoteArgs
396

    
397
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
398
  node_full = qa_utils.ResolveNodeName(node)
399
  node2_full = qa_utils.ResolveNodeName(node2)
400

    
401
  print qa_utils.FormatInfo("Getting physical disk names")
402
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
403
         "--output=node,phys,instance",
404
         node["primary"], node2["primary"]]
405
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
406

    
407
  # Get physical disk names
408
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
409
  node2disk = {}
410
  for line in output.splitlines():
411
    (node_name, phys, inst) = line.split("|")
412
    if inst == instance_full:
413
      if node_name not in node2disk:
414
        node2disk[node_name] = []
415

    
416
      m = re_disk.match(phys)
417
      if not m:
418
        raise qa_error.Error("Unknown disk name format: %s" % phys)
419

    
420
      name = m.group(1)
421
      if name not in node2disk[node_name]:
422
        node2disk[node_name].append(name)
423

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

    
428
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
429
                            " disks")
430
  for node_name, disks in node2disk.iteritems():
431
    cmds = []
432
    for disk in disks:
433
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
434
    AssertCommand(" && ".join(cmds), node=node_name)
435

    
436
  print qa_utils.FormatInfo("Getting device paths")
437
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
438
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
439
  devpath = []
440
  for line in output.splitlines():
441
    (_, _, tmpdevpath) = line.split(":")
442
    devpath.append(tmpdevpath)
443
  print devpath
444

    
445
  print qa_utils.FormatInfo("Getting drbd device paths")
446
  cmd = ["gnt-instance", "info", instance["name"]]
447
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
448
  pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
449
             r"\s+primary:\s+(/dev/drbd\d+)\s+")
450
  drbddevs = re.findall(pattern, output, re.M)
451
  print drbddevs
452

    
453
  halted_disks = []
454
  try:
455
    print qa_utils.FormatInfo("Deactivating disks")
456
    cmds = []
457
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
458
      halted_disks.append(name)
459
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
460
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
461

    
462
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
463
                              " to notice the problem")
464
    cmds = []
465
    for disk in devpath:
466
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
467
                      "if=%s" % disk, "of=%s" % disk]))
468
    for _ in (0, 1, 2):
469
      AssertCommand(" && ".join(cmds), node=node)
470
      time.sleep(3)
471

    
472
    print qa_utils.FormatInfo("Debugging info")
473
    for name in drbddevs:
474
      AssertCommand(["drbdsetup", name, "show"], node=node)
475

    
476
    AssertCommand(["gnt-instance", "info", instance["name"]])
477

    
478
  finally:
479
    print qa_utils.FormatInfo("Activating disks again")
480
    cmds = []
481
    for name in halted_disks:
482
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
483
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
484

    
485
  if onmaster:
486
    for name in drbddevs:
487
      AssertCommand(["drbdsetup", name, "detach"], node=node)
488
  else:
489
    for name in drbddevs:
490
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
491

    
492
  # TODO
493
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
494

    
495
  print qa_utils.FormatInfo("Making sure disks are up again")
496
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
497

    
498
  print qa_utils.FormatInfo("Restarting instance")
499
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
500
  AssertCommand(["gnt-instance", "startup", instance["name"]])
501

    
502
  AssertCommand(["gnt-cluster", "verify"])
503

    
504

    
505
def TestInstanceMasterDiskFailure(instance, node, node2):
506
  """Testing disk failure on master node."""
507
  # pylint: disable=W0613
508
  # due to unused args
509
  print qa_utils.FormatError("Disk failure on primary node cannot be"
510
                             " tested due to potential crashes.")
511
  # The following can cause crashes, thus it's disabled until fixed
512
  #return _TestInstanceDiskFailure(instance, node, node2, True)
513

    
514

    
515
def TestInstanceSecondaryDiskFailure(instance, node, node2):
516
  """Testing disk failure on secondary node."""
517
  return _TestInstanceDiskFailure(instance, node, node2, False)