Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 4c1a464b

History | View | Annotate | Download (15.1 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-header", "-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

    
256
def TestInstanceConvertDisk(instance, snode):
257
  """gnt-instance modify -t"""
258
  name = instance["name"]
259
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
260
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
261
                 "-n", snode["primary"], name])
262

    
263

    
264
def TestInstanceList():
265
  """gnt-instance list"""
266
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
267

    
268

    
269
def TestInstanceListFields():
270
  """gnt-instance list-fields"""
271
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
272

    
273

    
274
def TestInstanceConsole(instance):
275
  """gnt-instance console"""
276
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
277

    
278

    
279
def TestReplaceDisks(instance, pnode, snode, othernode):
280
  """gnt-instance replace-disks"""
281
  # pylint: disable=W0613
282
  # due to unused pnode arg
283
  # FIXME: should be removed from the function completely
284
  def buildcmd(args):
285
    cmd = ["gnt-instance", "replace-disks"]
286
    cmd.extend(args)
287
    cmd.append(instance["name"])
288
    return cmd
289

    
290
  for data in [
291
    ["-p"],
292
    ["-s"],
293
    ["--new-secondary=%s" % othernode["primary"]],
294
    # and restore
295
    ["--new-secondary=%s" % snode["primary"]],
296
    ]:
297
    AssertCommand(buildcmd(data))
298

    
299
  AssertCommand(buildcmd(["-a"]))
300
  AssertCommand(["gnt-instance", "stop", instance["name"]])
301
  AssertCommand(buildcmd(["-a"]), fail=True)
302
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
303
  AssertCommand(buildcmd(["-a"]))
304
  AssertCommand(["gnt-instance", "start", instance["name"]])
305

    
306

    
307
def TestInstanceExport(instance, node):
308
  """gnt-backup export -n ..."""
309
  name = instance["name"]
310
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
311
  return qa_utils.ResolveInstanceName(name)
312

    
313

    
314
def TestInstanceExportWithRemove(instance, node):
315
  """gnt-backup export --remove-instance"""
316
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
317
                 "--remove-instance", instance["name"]])
318

    
319

    
320
def TestInstanceExportNoTarget(instance):
321
  """gnt-backup export (without target node, should fail)"""
322
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
323

    
324

    
325
def TestInstanceImport(node, newinst, expnode, name):
326
  """gnt-backup import"""
327
  cmd = (["gnt-backup", "import",
328
          "--disk-template=plain",
329
          "--no-ip-check",
330
          "--net", "0:mac=generate",
331
          "--src-node=%s" % expnode["primary"],
332
          "--src-dir=%s/%s" % (constants.EXPORT_DIR, name),
333
          "--node=%s" % node["primary"]] +
334
         _GetGenericAddParameters())
335
  cmd.append(newinst["name"])
336
  AssertCommand(cmd)
337

    
338

    
339
def TestBackupList(expnode):
340
  """gnt-backup list"""
341
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
342

    
343

    
344
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
345
  """Testing disk failure."""
346
  master = qa_config.GetMasterNode()
347
  sq = utils.ShellQuoteArgs
348

    
349
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
350
  node_full = qa_utils.ResolveNodeName(node)
351
  node2_full = qa_utils.ResolveNodeName(node2)
352

    
353
  print qa_utils.FormatInfo("Getting physical disk names")
354
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
355
         "--output=node,phys,instance",
356
         node["primary"], node2["primary"]]
357
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
358

    
359
  # Get physical disk names
360
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
361
  node2disk = {}
362
  for line in output.splitlines():
363
    (node_name, phys, inst) = line.split("|")
364
    if inst == instance_full:
365
      if node_name not in node2disk:
366
        node2disk[node_name] = []
367

    
368
      m = re_disk.match(phys)
369
      if not m:
370
        raise qa_error.Error("Unknown disk name format: %s" % phys)
371

    
372
      name = m.group(1)
373
      if name not in node2disk[node_name]:
374
        node2disk[node_name].append(name)
375

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

    
380
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
381
                            " disks")
382
  for node_name, disks in node2disk.iteritems():
383
    cmds = []
384
    for disk in disks:
385
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
386
    AssertCommand(" && ".join(cmds), node=node_name)
387

    
388
  print qa_utils.FormatInfo("Getting device paths")
389
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
390
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
391
  devpath = []
392
  for line in output.splitlines():
393
    (_, _, tmpdevpath) = line.split(":")
394
    devpath.append(tmpdevpath)
395
  print devpath
396

    
397
  print qa_utils.FormatInfo("Getting drbd device paths")
398
  cmd = ["gnt-instance", "info", instance["name"]]
399
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
400
  pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
401
             r"\s+primary:\s+(/dev/drbd\d+)\s+")
402
  drbddevs = re.findall(pattern, output, re.M)
403
  print drbddevs
404

    
405
  halted_disks = []
406
  try:
407
    print qa_utils.FormatInfo("Deactivating disks")
408
    cmds = []
409
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
410
      halted_disks.append(name)
411
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
412
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
413

    
414
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
415
                              " to notice the problem")
416
    cmds = []
417
    for disk in devpath:
418
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
419
                      "if=%s" % disk, "of=%s" % disk]))
420
    for _ in (0, 1, 2):
421
      AssertCommand(" && ".join(cmds), node=node)
422
      time.sleep(3)
423

    
424
    print qa_utils.FormatInfo("Debugging info")
425
    for name in drbddevs:
426
      AssertCommand(["drbdsetup", name, "show"], node=node)
427

    
428
    AssertCommand(["gnt-instance", "info", instance["name"]])
429

    
430
  finally:
431
    print qa_utils.FormatInfo("Activating disks again")
432
    cmds = []
433
    for name in halted_disks:
434
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
435
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
436

    
437
  if onmaster:
438
    for name in drbddevs:
439
      AssertCommand(["drbdsetup", name, "detach"], node=node)
440
  else:
441
    for name in drbddevs:
442
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
443

    
444
  # TODO
445
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
446

    
447
  print qa_utils.FormatInfo("Making sure disks are up again")
448
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
449

    
450
  print qa_utils.FormatInfo("Restarting instance")
451
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
452
  AssertCommand(["gnt-instance", "startup", instance["name"]])
453

    
454
  AssertCommand(["gnt-cluster", "verify"])
455

    
456

    
457
def TestInstanceMasterDiskFailure(instance, node, node2):
458
  """Testing disk failure on master node."""
459
  # pylint: disable=W0613
460
  # due to unused args
461
  print qa_utils.FormatError("Disk failure on primary node cannot be"
462
                             " tested due to potential crashes.")
463
  # The following can cause crashes, thus it's disabled until fixed
464
  #return _TestInstanceDiskFailure(instance, node, node2, True)
465

    
466

    
467
def TestInstanceSecondaryDiskFailure(instance, node, node2):
468
  """Testing disk failure on secondary node."""
469
  return _TestInstanceDiskFailure(instance, node, node2, False)