Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 54f834df

History | View | Annotate | Download (14 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", "%s=%s" % (constants.BE_MEMORY, qa_config.get("mem"))]
46
  for idx, size in enumerate(qa_config.get("disk")):
47
    params.extend(["--disk", "%s:size=%s" % (idx, size)])
48
  return params
49

    
50

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

    
61
    AssertCommand(cmd)
62

    
63
    _CheckSsconfInstanceList(instance["name"])
64

    
65
    return instance
66
  except:
67
    qa_config.ReleaseInstance(instance)
68
    raise
69

    
70

    
71
def TestInstanceAddWithPlainDisk(node):
72
  """gnt-instance add -t plain"""
73
  return _DiskTest(node["primary"], "plain")
74

    
75

    
76
def TestInstanceAddWithDrbdDisk(node, node2):
77
  """gnt-instance add -t drbd"""
78
  return _DiskTest("%s:%s" % (node["primary"], node2["primary"]),
79
                   "drbd")
80

    
81

    
82
def TestInstanceRemove(instance):
83
  """gnt-instance remove"""
84
  AssertCommand(["gnt-instance", "remove", "-f", instance["name"]])
85

    
86
  qa_config.ReleaseInstance(instance)
87

    
88

    
89
def TestInstanceStartup(instance):
90
  """gnt-instance startup"""
91
  AssertCommand(["gnt-instance", "startup", instance["name"]])
92

    
93

    
94
def TestInstanceShutdown(instance):
95
  """gnt-instance shutdown"""
96
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
97

    
98

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

    
107
  AssertCommand(["gnt-instance", "shutdown", name])
108
  AssertCommand(["gnt-instance", "reboot", name])
109

    
110
  master = qa_config.GetMasterNode()
111
  cmd = ["gnt-instance", "list", "--no-header", "-o", "status", name]
112
  result_output = qa_utils.GetCommandOutput(master["primary"],
113
                                            utils.ShellQuoteArgs(cmd))
114
  AssertEqual(result_output.strip(), constants.INSTST_RUNNING)
115

    
116

    
117
def TestInstanceReinstall(instance):
118
  """gnt-instance reinstall"""
119
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
120

    
121

    
122
def _ReadSsconfInstanceList():
123
  """Reads ssconf_instance_list from the master node.
124

125
  """
126
  master = qa_config.GetMasterNode()
127

    
128
  cmd = ["cat", utils.PathJoin(constants.DATA_DIR,
129
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
130

    
131
  return qa_utils.GetCommandOutput(master["primary"],
132
                                   utils.ShellQuoteArgs(cmd)).splitlines()
133

    
134

    
135
def _CheckSsconfInstanceList(instance):
136
  """Checks if a certain instance is in the ssconf instance list.
137

138
  @type instance: string
139
  @param instance: Instance name
140

141
  """
142
  AssertIn(qa_utils.ResolveInstanceName(instance),
143
           _ReadSsconfInstanceList())
144

    
145

    
146
def TestInstanceRename(rename_source, rename_target):
147
  """gnt-instance rename"""
148
  _CheckSsconfInstanceList(rename_source)
149
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
150
  _CheckSsconfInstanceList(rename_target)
151
  AssertCommand(["gnt-instance", "rename", rename_target, rename_source])
152
  _CheckSsconfInstanceList(rename_source)
153
  qa_utils.AddToEtcHosts(["meeeeh-not-exists", rename_target])
154
  try:
155
    AssertCommand(["gnt-instance", "rename", rename_source, rename_target],
156
                  fail=True)
157
    _CheckSsconfInstanceList(rename_source)
158
  finally:
159
    qa_utils.RemoveFromEtcHosts(["meeeeh-not-exists", rename_target])
160
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
161
  _CheckSsconfInstanceList(rename_target)
162

    
163

    
164
def TestInstanceFailover(instance):
165
  """gnt-instance failover"""
166
  cmd = ["gnt-instance", "failover", "--force", instance["name"]]
167
  # failover ...
168
  AssertCommand(cmd)
169
  # ... and back
170
  AssertCommand(cmd)
171

    
172

    
173
def TestInstanceMigrate(instance):
174
  """gnt-instance migrate"""
175
  cmd = ["gnt-instance", "migrate", "--force", instance["name"]]
176
  # migrate ...
177
  AssertCommand(cmd)
178
  # ... and back
179
  AssertCommand(cmd)
180
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
181
  AssertCommand(cmd, fail=True)
182
  AssertCommand(["gnt-instance", "migrate", "--force", "--allow-failover",
183
                 instance["name"]])
184
  AssertCommand(["gnt-instance", "start", instance["name"]])
185
  AssertCommand(cmd)
186

    
187

    
188
def TestInstanceInfo(instance):
189
  """gnt-instance info"""
190
  AssertCommand(["gnt-instance", "info", instance["name"]])
191

    
192

    
193
def TestInstanceModify(instance):
194
  """gnt-instance modify"""
195
  # Assume /sbin/init exists on all systems
196
  test_kernel = "/sbin/init"
197
  test_initrd = test_kernel
198

    
199
  orig_memory = qa_config.get("mem")
200
  #orig_bridge = qa_config.get("bridge", "xen-br0")
201
  args = [
202
    ["-B", "%s=128" % constants.BE_MEMORY],
203
    ["-B", "%s=%s" % (constants.BE_MEMORY, orig_memory)],
204
    ["-B", "%s=2" % constants.BE_VCPUS],
205
    ["-B", "%s=1" % constants.BE_VCPUS],
206
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
207

    
208
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
209
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
210
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
211
    ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
212
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
213

    
214
    # TODO: bridge tests
215
    #["--bridge", "xen-br1"],
216
    #["--bridge", orig_bridge],
217

    
218
    # TODO: Do these tests only with xen-hvm
219
    #["-H", "%s=acn" % constants.HV_BOOT_ORDER],
220
    #["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
221
    ]
222
  for alist in args:
223
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
224

    
225
  # check no-modify
226
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
227

    
228

    
229
def TestInstanceConvertDisk(instance, snode):
230
  """gnt-instance modify -t"""
231
  name = instance["name"]
232
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
233
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
234
                 "-n", snode["primary"], name])
235

    
236

    
237
def TestInstanceList():
238
  """gnt-instance list"""
239
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
240

    
241

    
242
def TestInstanceListFields():
243
  """gnt-instance list-fields"""
244
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
245

    
246

    
247
def TestInstanceConsole(instance):
248
  """gnt-instance console"""
249
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
250

    
251

    
252
def TestReplaceDisks(instance, pnode, snode, othernode):
253
  """gnt-instance replace-disks"""
254
  # pylint: disable=W0613
255
  # due to unused pnode arg
256
  # FIXME: should be removed from the function completely
257
  def buildcmd(args):
258
    cmd = ["gnt-instance", "replace-disks"]
259
    cmd.extend(args)
260
    cmd.append(instance["name"])
261
    return cmd
262

    
263
  for data in [
264
    ["-p"],
265
    ["-s"],
266
    ["--new-secondary=%s" % othernode["primary"]],
267
    # and restore
268
    ["--new-secondary=%s" % snode["primary"]],
269
    ]:
270
    AssertCommand(buildcmd(data))
271

    
272
  AssertCommand(buildcmd(["-a"]))
273
  AssertCommand(["gnt-instance", "stop", instance["name"]])
274
  AssertCommand(buildcmd(["-a"]), fail=True)
275
  AssertCommand(["gnt-instance", "activate-disks", instance["name"]])
276
  AssertCommand(buildcmd(["-a"]))
277
  AssertCommand(["gnt-instance", "start", instance["name"]])
278

    
279

    
280
def TestInstanceExport(instance, node):
281
  """gnt-backup export -n ..."""
282
  name = instance["name"]
283
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
284
  return qa_utils.ResolveInstanceName(name)
285

    
286

    
287
def TestInstanceExportWithRemove(instance, node):
288
  """gnt-backup export --remove-instance"""
289
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
290
                 "--remove-instance", instance["name"]])
291

    
292

    
293
def TestInstanceExportNoTarget(instance):
294
  """gnt-backup export (without target node, should fail)"""
295
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
296

    
297

    
298
def TestInstanceImport(node, newinst, expnode, name):
299
  """gnt-backup import"""
300
  cmd = (["gnt-backup", "import",
301
          "--disk-template=plain",
302
          "--no-ip-check",
303
          "--net", "0:mac=generate",
304
          "--src-node=%s" % expnode["primary"],
305
          "--src-dir=%s/%s" % (constants.EXPORT_DIR, name),
306
          "--node=%s" % node["primary"]] +
307
         _GetGenericAddParameters())
308
  cmd.append(newinst["name"])
309
  AssertCommand(cmd)
310

    
311

    
312
def TestBackupList(expnode):
313
  """gnt-backup list"""
314
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
315

    
316

    
317
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
318
  """Testing disk failure."""
319
  master = qa_config.GetMasterNode()
320
  sq = utils.ShellQuoteArgs
321

    
322
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
323
  node_full = qa_utils.ResolveNodeName(node)
324
  node2_full = qa_utils.ResolveNodeName(node2)
325

    
326
  print qa_utils.FormatInfo("Getting physical disk names")
327
  cmd = ["gnt-node", "volumes", "--separator=|", "--no-headers",
328
         "--output=node,phys,instance",
329
         node["primary"], node2["primary"]]
330
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
331

    
332
  # Get physical disk names
333
  re_disk = re.compile(r"^/dev/([a-z]+)\d+$")
334
  node2disk = {}
335
  for line in output.splitlines():
336
    (node_name, phys, inst) = line.split("|")
337
    if inst == instance_full:
338
      if node_name not in node2disk:
339
        node2disk[node_name] = []
340

    
341
      m = re_disk.match(phys)
342
      if not m:
343
        raise qa_error.Error("Unknown disk name format: %s" % phys)
344

    
345
      name = m.group(1)
346
      if name not in node2disk[node_name]:
347
        node2disk[node_name].append(name)
348

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

    
353
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
354
                            " disks")
355
  for node_name, disks in node2disk.iteritems():
356
    cmds = []
357
    for disk in disks:
358
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
359
    AssertCommand(" && ".join(cmds), node=node_name)
360

    
361
  print qa_utils.FormatInfo("Getting device paths")
362
  cmd = ["gnt-instance", "activate-disks", instance["name"]]
363
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
364
  devpath = []
365
  for line in output.splitlines():
366
    (_, _, tmpdevpath) = line.split(":")
367
    devpath.append(tmpdevpath)
368
  print devpath
369

    
370
  print qa_utils.FormatInfo("Getting drbd device paths")
371
  cmd = ["gnt-instance", "info", instance["name"]]
372
  output = qa_utils.GetCommandOutput(master["primary"], sq(cmd))
373
  pattern = (r"\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$"
374
             r"\s+primary:\s+(/dev/drbd\d+)\s+")
375
  drbddevs = re.findall(pattern, output, re.M)
376
  print drbddevs
377

    
378
  halted_disks = []
379
  try:
380
    print qa_utils.FormatInfo("Deactivating disks")
381
    cmds = []
382
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
383
      halted_disks.append(name)
384
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
385
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
386

    
387
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
388
                              " to notice the problem")
389
    cmds = []
390
    for disk in devpath:
391
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
392
                      "if=%s" % disk, "of=%s" % disk]))
393
    for _ in (0, 1, 2):
394
      AssertCommand(" && ".join(cmds), node=node)
395
      time.sleep(3)
396

    
397
    print qa_utils.FormatInfo("Debugging info")
398
    for name in drbddevs:
399
      AssertCommand(["drbdsetup", name, "show"], node=node)
400

    
401
    AssertCommand(["gnt-instance", "info", instance["name"]])
402

    
403
  finally:
404
    print qa_utils.FormatInfo("Activating disks again")
405
    cmds = []
406
    for name in halted_disks:
407
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
408
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
409

    
410
  if onmaster:
411
    for name in drbddevs:
412
      AssertCommand(["drbdsetup", name, "detach"], node=node)
413
  else:
414
    for name in drbddevs:
415
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
416

    
417
  # TODO
418
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
419

    
420
  print qa_utils.FormatInfo("Making sure disks are up again")
421
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
422

    
423
  print qa_utils.FormatInfo("Restarting instance")
424
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
425
  AssertCommand(["gnt-instance", "startup", instance["name"]])
426

    
427
  AssertCommand(["gnt-cluster", "verify"])
428

    
429

    
430
def TestInstanceMasterDiskFailure(instance, node, node2):
431
  """Testing disk failure on master node."""
432
  # pylint: disable=W0613
433
  # due to unused args
434
  print qa_utils.FormatError("Disk failure on primary node cannot be"
435
                             " tested due to potential crashes.")
436
  # The following can cause crashes, thus it's disabled until fixed
437
  #return _TestInstanceDiskFailure(instance, node, node2, True)
438

    
439

    
440
def TestInstanceSecondaryDiskFailure(instance, node, node2):
441
  """Testing disk failure on secondary node."""
442
  return _TestInstanceDiskFailure(instance, node, node2, False)