Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 42a769f9

History | View | Annotate | Download (15 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2011 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 TestInstanceRename(rename_source, rename_target):
151
  """gnt-instance rename"""
152
  _CheckSsconfInstanceList(rename_source)
153
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
154
  _CheckSsconfInstanceList(rename_target)
155
  AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
156
  _CheckSsconfInstanceList(rename_source)
157
  qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
158
  try:
159
    AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
160
                  fail=True)
161
    _CheckSsconfInstanceList(rename_source)
162
  finally:
163
    qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
164
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
165
  _CheckSsconfInstanceList(rename_target)
166

    
167

    
168
def TestInstanceFailover(instance):
169
  """gnt-instance failover"""
170
  cmd = ["gnt-instance", "failover", "--force", instance["name"]]
171
  # failover ...
172
  AssertCommand(cmd)
173
  # ... and back
174
  AssertCommand(cmd)
175

    
176

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

    
203

    
204
def TestInstanceInfo(instance):
205
  """gnt-instance info"""
206
  AssertCommand(["gnt-instance", "info", instance["name"]])
207

    
208

    
209
def TestInstanceModify(instance):
210
  """gnt-instance modify"""
211
  # Assume /sbin/init exists on all systems
212
  test_kernel = "/sbin/init"
213
  test_initrd = test_kernel
214

    
215
  orig_maxmem = qa_config.get(constants.BE_MAXMEM)
216
  orig_minmem = qa_config.get(constants.BE_MINMEM)
217
  #orig_bridge = qa_config.get("bridge", "xen-br0")
218
  args = [
219
    ["-B", "%s=128" % constants.BE_MINMEM],
220
    ["-B", "%s=128" % constants.BE_MAXMEM],
221
    ["-B", "%s=%s,%s=%s" % (constants.BE_MINMEM, orig_minmem,
222
                            constants.BE_MAXMEM, orig_maxmem)],
223
    ["-B", "%s=2" % constants.BE_VCPUS],
224
    ["-B", "%s=1" % constants.BE_VCPUS],
225
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
226
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_TRUE)],
227
    ["-B", "%s=%s" % (constants.BE_ALWAYS_FAILOVER, constants.VALUE_DEFAULT)],
228

    
229
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
230
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
231
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
232
    ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
233
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
234

    
235
    # TODO: bridge tests
236
    #["--bridge", "xen-br1"],
237
    #["--bridge", orig_bridge],
238

    
239
    # TODO: Do these tests only with xen-hvm
240
    #["-H", "%s=acn" % constants.HV_BOOT_ORDER],
241
    #["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
242
    ]
243
  for alist in args:
244
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
245

    
246
  # check no-modify
247
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
248

    
249

    
250
def TestInstanceConvertDisk(instance, snode):
251
  """gnt-instance modify -t"""
252
  name = instance["name"]
253
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
254
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
255
                 "-n", snode["primary"], name])
256

    
257

    
258
def TestInstanceList():
259
  """gnt-instance list"""
260
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
261

    
262

    
263
def TestInstanceListFields():
264
  """gnt-instance list-fields"""
265
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
266

    
267

    
268
def TestInstanceConsole(instance):
269
  """gnt-instance console"""
270
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
271

    
272

    
273
def TestReplaceDisks(instance, pnode, snode, othernode):
274
  """gnt-instance replace-disks"""
275
  # pylint: disable=W0613
276
  # due to unused pnode arg
277
  # FIXME: should be removed from the function completely
278
  def buildcmd(args):
279
    cmd = ["gnt-instance", "replace-disks"]
280
    cmd.extend(args)
281
    cmd.append(instance["name"])
282
    return cmd
283

    
284
  for data in [
285
    ["-p"],
286
    ["-s"],
287
    ["--new-secondary=%s" % othernode["primary"]],
288
    # and restore
289
    ["--new-secondary=%s" % snode["primary"]],
290
    ]:
291
    AssertCommand(buildcmd(data))
292

    
293
  AssertCommand(buildcmd(["-a"]))
294
  AssertCommand(["gnt-instance", "stop", instance["name"]])
295
  AssertCommand(buildcmd(["-a"]), fail=True)
296
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
297
  AssertCommand(buildcmd(["-a"]))
298
  AssertCommand(["gnt-instance", "start", instance["name"]])
299

    
300

    
301
def TestInstanceExport(instance, node):
302
  """gnt-backup export -n ..."""
303
  name = instance["name"]
304
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
305
  return qa_utils.ResolveInstanceName(name)
306

    
307

    
308
def TestInstanceExportWithRemove(instance, node):
309
  """gnt-backup export --remove-instance"""
310
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
311
                 "--remove-instance", instance["name"]])
312

    
313

    
314
def TestInstanceExportNoTarget(instance):
315
  """gnt-backup export (without target node, should fail)"""
316
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
317

    
318

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

    
332

    
333
def TestBackupList(expnode):
334
  """gnt-backup list"""
335
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
336

    
337

    
338
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
339
  """Testing disk failure."""
340
  master = qa_config.GetMasterNode()
341
  sq = utils.ShellQuoteArgs
342

    
343
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
344
  node_full = qa_utils.ResolveNodeName(node)
345
  node2_full = qa_utils.ResolveNodeName(node2)
346

    
347
  print qa_utils.FormatInfo("Getting physical disk names")
348
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
349
         "--output=node,phys,instance",
350
         node["primary"], node2["primary"]]
351
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
352

    
353
  # Get physical disk names
354
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
355
  node2disk = {}
356
  for line in output.splitlines():
357
    (node_name, phys, inst) = line.split("|")
358
    if inst == instance_full:
359
      if node_name not in node2disk:
360
        node2disk[node_name] = []
361

    
362
      m = re_disk.match(phys)
363
      if not m:
364
        raise qa_error.Error("Unknown disk name format: %s" % phys)
365

    
366
      name = m.group(1)
367
      if name not in node2disk[node_name]:
368
        node2disk[node_name].append(name)
369

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

    
374
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
375
                            " disks")
376
  for node_name, disks in node2disk.iteritems():
377
    cmds = []
378
    for disk in disks:
379
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
380
    AssertCommand(" && ".join(cmds), node=node_name)
381

    
382
  print qa_utils.FormatInfo("Getting device paths")
383
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
384
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
385
  devpath = []
386
  for line in output.splitlines():
387
    (_, _, tmpdevpath) = line.split(":")
388
    devpath.append(tmpdevpath)
389
  print devpath
390

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

    
399
  halted_disks = []
400
  try:
401
    print qa_utils.FormatInfo("Deactivating disks")
402
    cmds = []
403
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
404
      halted_disks.append(name)
405
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
406
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
407

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

    
418
    print qa_utils.FormatInfo("Debugging info")
419
    for name in drbddevs:
420
      AssertCommand(["drbdsetup", name, "show"], node=node)
421

    
422
    AssertCommand(["gnt-instance", "info", instance["name"]])
423

    
424
  finally:
425
    print qa_utils.FormatInfo("Activating disks again")
426
    cmds = []
427
    for name in halted_disks:
428
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
429
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
430

    
431
  if onmaster:
432
    for name in drbddevs:
433
      AssertCommand(["drbdsetup", name, "detach"], node=node)
434
  else:
435
    for name in drbddevs:
436
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
437

    
438
  # TODO
439
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
440

    
441
  print qa_utils.FormatInfo("Making sure disks are up again")
442
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
443

    
444
  print qa_utils.FormatInfo("Restarting instance")
445
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
446
  AssertCommand(["gnt-instance", "startup", instance["name"]])
447

    
448
  AssertCommand(["gnt-cluster", "verify"])
449

    
450

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

    
460

    
461
def TestInstanceSecondaryDiskFailure(instance, node, node2):
462
  """Testing disk failure on secondary node."""
463
  return _TestInstanceDiskFailure(instance, node, node2, False)