Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 31fe5102

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

    
181

    
182
def TestInstanceInfo(instance):
183
  """gnt-instance info"""
184
  AssertCommand(["gnt-instance", "info", instance["name"]])
185

    
186

    
187
def TestInstanceModify(instance):
188
  """gnt-instance modify"""
189
  # Assume /sbin/init exists on all systems
190
  test_kernel = "/sbin/init"
191
  test_initrd = test_kernel
192

    
193
  orig_memory = qa_config.get('mem')
194
  #orig_bridge = qa_config.get("bridge", "xen-br0")
195
  args = [
196
    ["-B", "%s=128" % constants.BE_MEMORY],
197
    ["-B", "%s=%s" % (constants.BE_MEMORY, orig_memory)],
198
    ["-B", "%s=2" % constants.BE_VCPUS],
199
    ["-B", "%s=1" % constants.BE_VCPUS],
200
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
201

    
202
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
203
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
204
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
205
    ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
206
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
207

    
208
    # TODO: bridge tests
209
    #["--bridge", "xen-br1"],
210
    #["--bridge", orig_bridge],
211

    
212
    # TODO: Do these tests only with xen-hvm
213
    #["-H", "%s=acn" % constants.HV_BOOT_ORDER],
214
    #["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
215
    ]
216
  for alist in args:
217
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
218

    
219
  # check no-modify
220
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
221

    
222

    
223
def TestInstanceConvertDisk(instance, snode):
224
  """gnt-instance modify -t"""
225
  name = instance["name"]
226
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
227
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
228
                 "-n", snode["primary"], name])
229

    
230

    
231
def TestInstanceList():
232
  """gnt-instance list"""
233
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
234

    
235

    
236
def TestInstanceListFields():
237
  """gnt-instance list-fields"""
238
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
239

    
240

    
241
def TestInstanceConsole(instance):
242
  """gnt-instance console"""
243
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
244

    
245

    
246
def TestReplaceDisks(instance, pnode, snode, othernode):
247
  """gnt-instance replace-disks"""
248
  # pylint: disable-msg=W0613
249
  # due to unused pnode arg
250
  # FIXME: should be removed from the function completely
251
  def buildcmd(args):
252
    cmd = ['gnt-instance', 'replace-disks']
253
    cmd.extend(args)
254
    cmd.append(instance["name"])
255
    return cmd
256

    
257
  for data in [
258
    ["-p"],
259
    ["-s"],
260
    ["--new-secondary=%s" % othernode["primary"]],
261
    # and restore
262
    ["--new-secondary=%s" % snode["primary"]],
263
    ]:
264
    AssertCommand(buildcmd(data))
265

    
266

    
267
def TestInstanceExport(instance, node):
268
  """gnt-backup export -n ..."""
269
  name = instance["name"]
270
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
271
  return qa_utils.ResolveInstanceName(name)
272

    
273

    
274
def TestInstanceExportWithRemove(instance, node):
275
  """gnt-backup export --remove-instance"""
276
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
277
                 "--remove-instance", instance["name"]])
278

    
279

    
280
def TestInstanceExportNoTarget(instance):
281
  """gnt-backup export (without target node, should fail)"""
282
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
283

    
284

    
285
def TestInstanceImport(node, newinst, expnode, name):
286
  """gnt-backup import"""
287
  cmd = (['gnt-backup', 'import',
288
          '--disk-template=plain',
289
          '--no-ip-check',
290
          '--net', '0:mac=generate',
291
          '--src-node=%s' % expnode['primary'],
292
          '--src-dir=%s/%s' % (constants.EXPORT_DIR, name),
293
          '--node=%s' % node['primary']] +
294
         _GetGenericAddParameters())
295
  cmd.append(newinst['name'])
296
  AssertCommand(cmd)
297

    
298

    
299
def TestBackupList(expnode):
300
  """gnt-backup list"""
301
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
302

    
303

    
304
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
305
  """Testing disk failure."""
306
  master = qa_config.GetMasterNode()
307
  sq = utils.ShellQuoteArgs
308

    
309
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
310
  node_full = qa_utils.ResolveNodeName(node)
311
  node2_full = qa_utils.ResolveNodeName(node2)
312

    
313
  print qa_utils.FormatInfo("Getting physical disk names")
314
  cmd = ['gnt-node', 'volumes', '--separator=|', '--no-headers',
315
         '--output=node,phys,instance',
316
         node['primary'], node2['primary']]
317
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
318

    
319
  # Get physical disk names
320
  re_disk = re.compile(r'^/dev/([a-z]+)\d+$')
321
  node2disk = {}
322
  for line in output.splitlines():
323
    (node_name, phys, inst) = line.split('|')
324
    if inst == instance_full:
325
      if node_name not in node2disk:
326
        node2disk[node_name] = []
327

    
328
      m = re_disk.match(phys)
329
      if not m:
330
        raise qa_error.Error("Unknown disk name format: %s" % phys)
331

    
332
      name = m.group(1)
333
      if name not in node2disk[node_name]:
334
        node2disk[node_name].append(name)
335

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

    
340
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
341
                            " disks")
342
  for node_name, disks in node2disk.iteritems():
343
    cmds = []
344
    for disk in disks:
345
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
346
    AssertCommand(" && ".join(cmds), node=node_name)
347

    
348
  print qa_utils.FormatInfo("Getting device paths")
349
  cmd = ['gnt-instance', 'activate-disks', instance['name']]
350
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
351
  devpath = []
352
  for line in output.splitlines():
353
    (_, _, tmpdevpath) = line.split(':')
354
    devpath.append(tmpdevpath)
355
  print devpath
356

    
357
  print qa_utils.FormatInfo("Getting drbd device paths")
358
  cmd = ['gnt-instance', 'info', instance['name']]
359
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
360
  pattern = (r'\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$'
361
             r'\s+primary:\s+(/dev/drbd\d+)\s+')
362
  drbddevs = re.findall(pattern, output, re.M)
363
  print drbddevs
364

    
365
  halted_disks = []
366
  try:
367
    print qa_utils.FormatInfo("Deactivating disks")
368
    cmds = []
369
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
370
      halted_disks.append(name)
371
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
372
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
373

    
374
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
375
                              " to notice the problem")
376
    cmds = []
377
    for disk in devpath:
378
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
379
                      "if=%s" % disk, "of=%s" % disk]))
380
    for _ in (0, 1, 2):
381
      AssertCommand(" && ".join(cmds), node=node)
382
      time.sleep(3)
383

    
384
    print qa_utils.FormatInfo("Debugging info")
385
    for name in drbddevs:
386
      AssertCommand(["drbdsetup", name, "show"], node=node)
387

    
388
    AssertCommand(["gnt-instance", "info", instance["name"]])
389

    
390
  finally:
391
    print qa_utils.FormatInfo("Activating disks again")
392
    cmds = []
393
    for name in halted_disks:
394
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
395
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
396

    
397
  if onmaster:
398
    for name in drbddevs:
399
      AssertCommand(["drbdsetup", name, "detach"], node=node)
400
  else:
401
    for name in drbddevs:
402
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
403

    
404
  # TODO
405
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
406

    
407
  print qa_utils.FormatInfo("Making sure disks are up again")
408
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
409

    
410
  print qa_utils.FormatInfo("Restarting instance")
411
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
412
  AssertCommand(["gnt-instance", "startup", instance["name"]])
413

    
414
  AssertCommand(["gnt-cluster", "verify"])
415

    
416

    
417
def TestInstanceMasterDiskFailure(instance, node, node2):
418
  """Testing disk failure on master node."""
419
  # pylint: disable-msg=W0613
420
  # due to unused args
421
  print qa_utils.FormatError("Disk failure on primary node cannot be"
422
                             " tested due to potential crashes.")
423
  # The following can cause crashes, thus it's disabled until fixed
424
  #return _TestInstanceDiskFailure(instance, node, node2, True)
425

    
426

    
427
def TestInstanceSecondaryDiskFailure(instance, node, node2):
428
  """Testing disk failure on secondary node."""
429
  return _TestInstanceDiskFailure(instance, node, node2, False)