Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 3016bc1f

History | View | Annotate | Download (15.8 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
  # Assume instance was not marked offline, so marking it online must fail
265
  AssertCommand(["gnt-instance", "modify", "--online", name], fail=True)
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 TestInstanceList():
283
  """gnt-instance list"""
284
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
285

    
286

    
287
def TestInstanceListFields():
288
  """gnt-instance list-fields"""
289
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
290

    
291

    
292
def TestInstanceConsole(instance):
293
  """gnt-instance console"""
294
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
295

    
296

    
297
def TestReplaceDisks(instance, pnode, snode, othernode):
298
  """gnt-instance replace-disks"""
299
  # pylint: disable=W0613
300
  # due to unused pnode arg
301
  # FIXME: should be removed from the function completely
302
  def buildcmd(args):
303
    cmd = ["gnt-instance", "replace-disks"]
304
    cmd.extend(args)
305
    cmd.append(instance["name"])
306
    return cmd
307

    
308
  for data in [
309
    ["-p"],
310
    ["-s"],
311
    ["--new-secondary=%s" % othernode["primary"]],
312
    # and restore
313
    ["--new-secondary=%s" % snode["primary"]],
314
    ]:
315
    AssertCommand(buildcmd(data))
316

    
317
  AssertCommand(buildcmd(["-a"]))
318
  AssertCommand(["gnt-instance", "stop", instance["name"]])
319
  AssertCommand(buildcmd(["-a"]), fail=True)
320
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
321
  AssertCommand(buildcmd(["-a"]))
322
  AssertCommand(["gnt-instance", "start", instance["name"]])
323

    
324

    
325
def TestInstanceExport(instance, node):
326
  """gnt-backup export -n ..."""
327
  name = instance["name"]
328
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
329
  return qa_utils.ResolveInstanceName(name)
330

    
331

    
332
def TestInstanceExportWithRemove(instance, node):
333
  """gnt-backup export --remove-instance"""
334
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
335
                 "--remove-instance", instance["name"]])
336

    
337

    
338
def TestInstanceExportNoTarget(instance):
339
  """gnt-backup export (without target node, should fail)"""
340
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
341

    
342

    
343
def TestInstanceImport(node, newinst, expnode, name):
344
  """gnt-backup import"""
345
  cmd = (["gnt-backup", "import",
346
          "--disk-template=plain",
347
          "--no-ip-check",
348
          "--net", "0:mac=generate",
349
          "--src-node=%s" % expnode["primary"],
350
          "--src-dir=%s/%s" % (constants.EXPORT_DIR, name),
351
          "--node=%s" % node["primary"]] +
352
         _GetGenericAddParameters())
353
  cmd.append(newinst["name"])
354
  AssertCommand(cmd)
355

    
356

    
357
def TestBackupList(expnode):
358
  """gnt-backup list"""
359
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
360

    
361

    
362
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
363
  """Testing disk failure."""
364
  master = qa_config.GetMasterNode()
365
  sq = utils.ShellQuoteArgs
366

    
367
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
368
  node_full = qa_utils.ResolveNodeName(node)
369
  node2_full = qa_utils.ResolveNodeName(node2)
370

    
371
  print qa_utils.FormatInfo("Getting physical disk names")
372
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
373
         "--output=node,phys,instance",
374
         node["primary"], node2["primary"]]
375
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
376

    
377
  # Get physical disk names
378
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
379
  node2disk = {}
380
  for line in output.splitlines():
381
    (node_name, phys, inst) = line.split("|")
382
    if inst == instance_full:
383
      if node_name not in node2disk:
384
        node2disk[node_name] = []
385

    
386
      m = re_disk.match(phys)
387
      if not m:
388
        raise qa_error.Error("Unknown disk name format: %s" % phys)
389

    
390
      name = m.group(1)
391
      if name not in node2disk[node_name]:
392
        node2disk[node_name].append(name)
393

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

    
398
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
399
                            " disks")
400
  for node_name, disks in node2disk.iteritems():
401
    cmds = []
402
    for disk in disks:
403
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
404
    AssertCommand(" && ".join(cmds), node=node_name)
405

    
406
  print qa_utils.FormatInfo("Getting device paths")
407
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
408
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
409
  devpath = []
410
  for line in output.splitlines():
411
    (_, _, tmpdevpath) = line.split(":")
412
    devpath.append(tmpdevpath)
413
  print devpath
414

    
415
  print qa_utils.FormatInfo("Getting drbd device paths")
416
  cmd = ["gnt-instance", "info", instance["name"]]
417
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
418
  pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
419
             r"\s+primary:\s+(/dev/drbd\d+)\s+")
420
  drbddevs = re.findall(pattern, output, re.M)
421
  print drbddevs
422

    
423
  halted_disks = []
424
  try:
425
    print qa_utils.FormatInfo("Deactivating disks")
426
    cmds = []
427
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
428
      halted_disks.append(name)
429
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
430
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
431

    
432
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
433
                              " to notice the problem")
434
    cmds = []
435
    for disk in devpath:
436
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
437
                      "if=%s" % disk, "of=%s" % disk]))
438
    for _ in (0, 1, 2):
439
      AssertCommand(" && ".join(cmds), node=node)
440
      time.sleep(3)
441

    
442
    print qa_utils.FormatInfo("Debugging info")
443
    for name in drbddevs:
444
      AssertCommand(["drbdsetup", name, "show"], node=node)
445

    
446
    AssertCommand(["gnt-instance", "info", instance["name"]])
447

    
448
  finally:
449
    print qa_utils.FormatInfo("Activating disks again")
450
    cmds = []
451
    for name in halted_disks:
452
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
453
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
454

    
455
  if onmaster:
456
    for name in drbddevs:
457
      AssertCommand(["drbdsetup", name, "detach"], node=node)
458
  else:
459
    for name in drbddevs:
460
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
461

    
462
  # TODO
463
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
464

    
465
  print qa_utils.FormatInfo("Making sure disks are up again")
466
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
467

    
468
  print qa_utils.FormatInfo("Restarting instance")
469
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
470
  AssertCommand(["gnt-instance", "startup", instance["name"]])
471

    
472
  AssertCommand(["gnt-cluster", "verify"])
473

    
474

    
475
def TestInstanceMasterDiskFailure(instance, node, node2):
476
  """Testing disk failure on master node."""
477
  # pylint: disable=W0613
478
  # due to unused args
479
  print qa_utils.FormatError("Disk failure on primary node cannot be"
480
                             " tested due to potential crashes.")
481
  # The following can cause crashes, thus it's disabled until fixed
482
  #return _TestInstanceDiskFailure(instance, node, node2, True)
483

    
484

    
485
def TestInstanceSecondaryDiskFailure(instance, node, node2):
486
  """Testing disk failure on secondary node."""
487
  return _TestInstanceDiskFailure(instance, node, node2, False)