Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ ff699aa9

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

    
152

    
153
def TestInstanceFailover(instance):
154
  """gnt-instance failover"""
155
  cmd = ['gnt-instance', 'failover', '--force', instance['name']]
156
  # failover ...
157
  AssertCommand(cmd)
158
  # ... and back
159
  AssertCommand(cmd)
160

    
161

    
162
def TestInstanceMigrate(instance):
163
  """gnt-instance migrate"""
164
  cmd = ["gnt-instance", "migrate", "--force", instance["name"]]
165
  # migrate ...
166
  AssertCommand(cmd)
167
  # ... and back
168
  AssertCommand(cmd)
169

    
170

    
171
def TestInstanceInfo(instance):
172
  """gnt-instance info"""
173
  AssertCommand(["gnt-instance", "info", instance["name"]])
174

    
175

    
176
def TestInstanceModify(instance):
177
  """gnt-instance modify"""
178
  # Assume /sbin/init exists on all systems
179
  test_kernel = "/sbin/init"
180
  test_initrd = test_kernel
181

    
182
  orig_memory = qa_config.get('mem')
183
  #orig_bridge = qa_config.get("bridge", "xen-br0")
184
  args = [
185
    ["-B", "%s=128" % constants.BE_MEMORY],
186
    ["-B", "%s=%s" % (constants.BE_MEMORY, orig_memory)],
187
    ["-B", "%s=2" % constants.BE_VCPUS],
188
    ["-B", "%s=1" % constants.BE_VCPUS],
189
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
190

    
191
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
192
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
193
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
194
    ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
195
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
196

    
197
    # TODO: bridge tests
198
    #["--bridge", "xen-br1"],
199
    #["--bridge", orig_bridge],
200

    
201
    # TODO: Do these tests only with xen-hvm
202
    #["-H", "%s=acn" % constants.HV_BOOT_ORDER],
203
    #["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
204
    ]
205
  for alist in args:
206
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
207

    
208
  # check no-modify
209
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
210

    
211

    
212
def TestInstanceConvertDisk(instance, snode):
213
  """gnt-instance modify -t"""
214
  name = instance["name"]
215
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
216
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
217
                 "-n", snode["primary"], name])
218

    
219

    
220
def TestInstanceList():
221
  """gnt-instance list"""
222
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
223

    
224

    
225
def TestInstanceListFields():
226
  """gnt-instance list-fields"""
227
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
228

    
229

    
230
def TestInstanceConsole(instance):
231
  """gnt-instance console"""
232
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
233

    
234

    
235
def TestReplaceDisks(instance, pnode, snode, othernode):
236
  """gnt-instance replace-disks"""
237
  # pylint: disable-msg=W0613
238
  # due to unused pnode arg
239
  # FIXME: should be removed from the function completely
240
  def buildcmd(args):
241
    cmd = ['gnt-instance', 'replace-disks']
242
    cmd.extend(args)
243
    cmd.append(instance["name"])
244
    return cmd
245

    
246
  for data in [
247
    ["-p"],
248
    ["-s"],
249
    ["--new-secondary=%s" % othernode["primary"]],
250
    # and restore
251
    ["--new-secondary=%s" % snode["primary"]],
252
    ]:
253
    AssertCommand(buildcmd(data))
254

    
255

    
256
def TestInstanceExport(instance, node):
257
  """gnt-backup export -n ..."""
258
  name = instance["name"]
259
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
260
  return qa_utils.ResolveInstanceName(name)
261

    
262

    
263
def TestInstanceExportWithRemove(instance, node):
264
  """gnt-backup export --remove-instance"""
265
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
266
                 "--remove-instance", instance["name"]])
267

    
268

    
269
def TestInstanceExportNoTarget(instance):
270
  """gnt-backup export (without target node, should fail)"""
271
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
272

    
273

    
274
def TestInstanceImport(node, newinst, expnode, name):
275
  """gnt-backup import"""
276
  cmd = (['gnt-backup', 'import',
277
          '--disk-template=plain',
278
          '--no-ip-check',
279
          '--net', '0:mac=generate',
280
          '--src-node=%s' % expnode['primary'],
281
          '--src-dir=%s/%s' % (constants.EXPORT_DIR, name),
282
          '--node=%s' % node['primary']] +
283
         _GetGenericAddParameters())
284
  cmd.append(newinst['name'])
285
  AssertCommand(cmd)
286

    
287

    
288
def TestBackupList(expnode):
289
  """gnt-backup list"""
290
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
291

    
292

    
293
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
294
  """Testing disk failure."""
295
  master = qa_config.GetMasterNode()
296
  sq = utils.ShellQuoteArgs
297

    
298
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
299
  node_full = qa_utils.ResolveNodeName(node)
300
  node2_full = qa_utils.ResolveNodeName(node2)
301

    
302
  print qa_utils.FormatInfo("Getting physical disk names")
303
  cmd = ['gnt-node', 'volumes', '--separator=|', '--no-headers',
304
         '--output=node,phys,instance',
305
         node['primary'], node2['primary']]
306
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
307

    
308
  # Get physical disk names
309
  re_disk = re.compile(r'^/dev/([a-z]+)\d+$')
310
  node2disk = {}
311
  for line in output.splitlines():
312
    (node_name, phys, inst) = line.split('|')
313
    if inst == instance_full:
314
      if node_name not in node2disk:
315
        node2disk[node_name] = []
316

    
317
      m = re_disk.match(phys)
318
      if not m:
319
        raise qa_error.Error("Unknown disk name format: %s" % phys)
320

    
321
      name = m.group(1)
322
      if name not in node2disk[node_name]:
323
        node2disk[node_name].append(name)
324

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

    
329
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
330
                            " disks")
331
  for node_name, disks in node2disk.iteritems():
332
    cmds = []
333
    for disk in disks:
334
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
335
    AssertCommand(" && ".join(cmds), node=node_name)
336

    
337
  print qa_utils.FormatInfo("Getting device paths")
338
  cmd = ['gnt-instance', 'activate-disks', instance['name']]
339
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
340
  devpath = []
341
  for line in output.splitlines():
342
    (_, _, tmpdevpath) = line.split(':')
343
    devpath.append(tmpdevpath)
344
  print devpath
345

    
346
  print qa_utils.FormatInfo("Getting drbd device paths")
347
  cmd = ['gnt-instance', 'info', instance['name']]
348
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
349
  pattern = (r'\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$'
350
             r'\s+primary:\s+(/dev/drbd\d+)\s+')
351
  drbddevs = re.findall(pattern, output, re.M)
352
  print drbddevs
353

    
354
  halted_disks = []
355
  try:
356
    print qa_utils.FormatInfo("Deactivating disks")
357
    cmds = []
358
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
359
      halted_disks.append(name)
360
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
361
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
362

    
363
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
364
                              " to notice the problem")
365
    cmds = []
366
    for disk in devpath:
367
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
368
                      "if=%s" % disk, "of=%s" % disk]))
369
    for _ in (0, 1, 2):
370
      AssertCommand(" && ".join(cmds), node=node)
371
      time.sleep(3)
372

    
373
    print qa_utils.FormatInfo("Debugging info")
374
    for name in drbddevs:
375
      AssertCommand(["drbdsetup", name, "show"], node=node)
376

    
377
    AssertCommand(["gnt-instance", "info", instance["name"]])
378

    
379
  finally:
380
    print qa_utils.FormatInfo("Activating disks again")
381
    cmds = []
382
    for name in halted_disks:
383
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
384
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
385

    
386
  if onmaster:
387
    for name in drbddevs:
388
      AssertCommand(["drbdsetup", name, "detach"], node=node)
389
  else:
390
    for name in drbddevs:
391
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
392

    
393
  # TODO
394
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
395

    
396
  print qa_utils.FormatInfo("Making sure disks are up again")
397
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
398

    
399
  print qa_utils.FormatInfo("Restarting instance")
400
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
401
  AssertCommand(["gnt-instance", "startup", instance["name"]])
402

    
403
  AssertCommand(["gnt-cluster", "verify"])
404

    
405

    
406
def TestInstanceMasterDiskFailure(instance, node, node2):
407
  """Testing disk failure on master node."""
408
  # pylint: disable-msg=W0613
409
  # due to unused args
410
  print qa_utils.FormatError("Disk failure on primary node cannot be"
411
                             " tested due to potential crashes.")
412
  # The following can cause crashes, thus it's disabled until fixed
413
  #return _TestInstanceDiskFailure(instance, node, node2, True)
414

    
415

    
416
def TestInstanceSecondaryDiskFailure(instance, node, node2):
417
  """Testing disk failure on secondary node."""
418
  return _TestInstanceDiskFailure(instance, node, node2, False)