Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 2214cf14

History | View | Annotate | Download (12.3 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007 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
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

    
108
def TestInstanceReinstall(instance):
109
  """gnt-instance reinstall"""
110
  AssertCommand(["gnt-instance", "reinstall", "-f", instance["name"]])
111

    
112

    
113
def _ReadSsconfInstanceList():
114
  """Reads ssconf_instance_list from the master node.
115

116
  """
117
  master = qa_config.GetMasterNode()
118

    
119
  cmd = ["cat", utils.PathJoin(constants.DATA_DIR,
120
                               "ssconf_%s" % constants.SS_INSTANCE_LIST)]
121

    
122
  return qa_utils.GetCommandOutput(master["primary"],
123
                                   utils.ShellQuoteArgs(cmd)).splitlines()
124

    
125

    
126
def _CheckSsconfInstanceList(instance):
127
  """Checks if a certain instance is in the ssconf instance list.
128

129
  @type instance: string
130
  @param instance: Instance name
131

132
  """
133
  AssertIn(qa_utils.ResolveInstanceName(instance),
134
           _ReadSsconfInstanceList())
135

    
136

    
137
def TestInstanceRename(rename_source, rename_target):
138
  """gnt-instance rename"""
139
  _CheckSsconfInstanceList(rename_source)
140
  AssertCommand(["gnt-instance", "rename", rename_source, rename_target])
141
  _CheckSsconfInstanceList(rename_target)
142

    
143

    
144
def TestInstanceFailover(instance):
145
  """gnt-instance failover"""
146
  cmd = ['gnt-instance', 'failover', '--force', instance['name']]
147
  # failover ...
148
  AssertCommand(cmd)
149
  # ... and back
150
  AssertCommand(cmd)
151

    
152

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

    
161

    
162
def TestInstanceInfo(instance):
163
  """gnt-instance info"""
164
  AssertCommand(["gnt-instance", "info", instance["name"]])
165

    
166

    
167
def TestInstanceModify(instance):
168
  """gnt-instance modify"""
169
  # Assume /sbin/init exists on all systems
170
  test_kernel = "/sbin/init"
171
  test_initrd = test_kernel
172

    
173
  orig_memory = qa_config.get('mem')
174
  #orig_bridge = qa_config.get("bridge", "xen-br0")
175
  args = [
176
    ["-B", "%s=128" % constants.BE_MEMORY],
177
    ["-B", "%s=%s" % (constants.BE_MEMORY, orig_memory)],
178
    ["-B", "%s=2" % constants.BE_VCPUS],
179
    ["-B", "%s=1" % constants.BE_VCPUS],
180
    ["-B", "%s=%s" % (constants.BE_VCPUS, constants.VALUE_DEFAULT)],
181

    
182
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, test_kernel)],
183
    ["-H", "%s=%s" % (constants.HV_KERNEL_PATH, constants.VALUE_DEFAULT)],
184
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, test_initrd)],
185
    ["-H", "no_%s" % (constants.HV_INITRD_PATH, )],
186
    ["-H", "%s=%s" % (constants.HV_INITRD_PATH, constants.VALUE_DEFAULT)],
187

    
188
    # TODO: bridge tests
189
    #["--bridge", "xen-br1"],
190
    #["--bridge", orig_bridge],
191

    
192
    # TODO: Do these tests only with xen-hvm
193
    #["-H", "%s=acn" % constants.HV_BOOT_ORDER],
194
    #["-H", "%s=%s" % (constants.HV_BOOT_ORDER, constants.VALUE_DEFAULT)],
195
    ]
196
  for alist in args:
197
    AssertCommand(["gnt-instance", "modify"] + alist + [instance["name"]])
198

    
199
  # check no-modify
200
  AssertCommand(["gnt-instance", "modify", instance["name"]], fail=True)
201

    
202

    
203
def TestInstanceConvertDisk(instance, snode):
204
  """gnt-instance modify -t"""
205
  name = instance["name"]
206
  AssertCommand(["gnt-instance", "modify", "-t", "plain", name])
207
  AssertCommand(["gnt-instance", "modify", "-t", "drbd",
208
                 "-n", snode["primary"], name])
209

    
210

    
211
def TestInstanceList():
212
  """gnt-instance list"""
213
  qa_utils.GenericQueryTest("gnt-instance", query.INSTANCE_FIELDS.keys())
214

    
215

    
216
def TestInstanceListFields():
217
  """gnt-instance list-fields"""
218
  qa_utils.GenericQueryFieldsTest("gnt-instance", query.INSTANCE_FIELDS.keys())
219

    
220

    
221
def TestInstanceConsole(instance):
222
  """gnt-instance console"""
223
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
224

    
225

    
226
def TestReplaceDisks(instance, pnode, snode, othernode):
227
  """gnt-instance replace-disks"""
228
  def buildcmd(args):
229
    cmd = ['gnt-instance', 'replace-disks']
230
    cmd.extend(args)
231
    cmd.append(instance["name"])
232
    return cmd
233

    
234
  for data in [
235
    ["-p"],
236
    ["-s"],
237
    ["--new-secondary=%s" % othernode["primary"]],
238
    # and restore
239
    ["--new-secondary=%s" % snode["primary"]],
240
    ]:
241
    AssertCommand(buildcmd(data))
242

    
243

    
244
def TestInstanceExport(instance, node):
245
  """gnt-backup export -n ..."""
246
  name = instance["name"]
247
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
248
  return qa_utils.ResolveInstanceName(name)
249

    
250

    
251
def TestInstanceExportWithRemove(instance, node):
252
  """gnt-backup export --remove-instance"""
253
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
254
                 "--remove-instance", instance["name"]])
255

    
256

    
257
def TestInstanceExportNoTarget(instance):
258
  """gnt-backup export (without target node, should fail)"""
259
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
260

    
261

    
262
def TestInstanceImport(node, newinst, expnode, name):
263
  """gnt-backup import"""
264
  cmd = (['gnt-backup', 'import',
265
          '--disk-template=plain',
266
          '--no-ip-check',
267
          '--net', '0:mac=generate',
268
          '--src-node=%s' % expnode['primary'],
269
          '--src-dir=%s/%s' % (constants.EXPORT_DIR, name),
270
          '--node=%s' % node['primary']] +
271
         _GetGenericAddParameters())
272
  cmd.append(newinst['name'])
273
  AssertCommand(cmd)
274

    
275

    
276
def TestBackupList(expnode):
277
  """gnt-backup list"""
278
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
279

    
280

    
281
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
282
  """Testing disk failure."""
283
  master = qa_config.GetMasterNode()
284
  sq = utils.ShellQuoteArgs
285

    
286
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
287
  node_full = qa_utils.ResolveNodeName(node)
288
  node2_full = qa_utils.ResolveNodeName(node2)
289

    
290
  print qa_utils.FormatInfo("Getting physical disk names")
291
  cmd = ['gnt-node', 'volumes', '--separator=|', '--no-headers',
292
         '--output=node,phys,instance',
293
         node['primary'], node2['primary']]
294
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
295

    
296
  # Get physical disk names
297
  re_disk = re.compile(r'^/dev/([a-z]+)\d+$')
298
  node2disk = {}
299
  for line in output.splitlines():
300
    (node_name, phys, inst) = line.split('|')
301
    if inst == instance_full:
302
      if node_name not in node2disk:
303
        node2disk[node_name] = []
304

    
305
      m = re_disk.match(phys)
306
      if not m:
307
        raise qa_error.Error("Unknown disk name format: %s" % phys)
308

    
309
      name = m.group(1)
310
      if name not in node2disk[node_name]:
311
        node2disk[node_name].append(name)
312

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

    
317
  print qa_utils.FormatInfo("Checking whether nodes have ability to stop"
318
                            " disks")
319
  for node_name, disks in node2disk.iteritems():
320
    cmds = []
321
    for disk in disks:
322
      cmds.append(sq(["test", "-f", _GetDiskStatePath(disk)]))
323
    AssertCommand(" && ".join(cmds), node=node_name)
324

    
325
  print qa_utils.FormatInfo("Getting device paths")
326
  cmd = ['gnt-instance', 'activate-disks', instance['name']]
327
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
328
  devpath = []
329
  for line in output.splitlines():
330
    (_, _, tmpdevpath) = line.split(':')
331
    devpath.append(tmpdevpath)
332
  print devpath
333

    
334
  print qa_utils.FormatInfo("Getting drbd device paths")
335
  cmd = ['gnt-instance', 'info', instance['name']]
336
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
337
  pattern = (r'\s+-\s+sd[a-z]+,\s+type:\s+drbd8?,\s+.*$'
338
             r'\s+primary:\s+(/dev/drbd\d+)\s+')
339
  drbddevs = re.findall(pattern, output, re.M)
340
  print drbddevs
341

    
342
  halted_disks = []
343
  try:
344
    print qa_utils.FormatInfo("Deactivating disks")
345
    cmds = []
346
    for name in node2disk[[node2_full, node_full][int(onmaster)]]:
347
      halted_disks.append(name)
348
      cmds.append(sq(["echo", "offline"]) + " >%s" % _GetDiskStatePath(name))
349
    AssertCommand(" && ".join(cmds), node=[node2, node][int(onmaster)])
350

    
351
    print qa_utils.FormatInfo("Write to disks and give some time to notice"
352
                              " to notice the problem")
353
    cmds = []
354
    for disk in devpath:
355
      cmds.append(sq(["dd", "count=1", "bs=512", "conv=notrunc",
356
                      "if=%s" % disk, "of=%s" % disk]))
357
    for _ in (0, 1, 2):
358
      AssertCommand(" && ".join(cmds), node=node)
359
      time.sleep(3)
360

    
361
    print qa_utils.FormatInfo("Debugging info")
362
    for name in drbddevs:
363
      AssertCommand(["drbdsetup", name, "show"], node=node)
364

    
365
    AssertCommand(["gnt-instance", "info", instance["name"]])
366

    
367
  finally:
368
    print qa_utils.FormatInfo("Activating disks again")
369
    cmds = []
370
    for name in halted_disks:
371
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
372
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
373

    
374
  if onmaster:
375
    for name in drbddevs:
376
      AssertCommand(["drbdsetup", name, "detach"], node=node)
377
  else:
378
    for name in drbddevs:
379
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
380

    
381
  # TODO
382
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
383

    
384
  print qa_utils.FormatInfo("Making sure disks are up again")
385
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
386

    
387
  print qa_utils.FormatInfo("Restarting instance")
388
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
389
  AssertCommand(["gnt-instance", "startup", instance["name"]])
390

    
391
  AssertCommand(["gnt-cluster", "verify"])
392

    
393

    
394
def TestInstanceMasterDiskFailure(instance, node, node2):
395
  """Testing disk failure on master node."""
396
  print qa_utils.FormatError("Disk failure on primary node cannot be"
397
                             " tested due to potential crashes.")
398
  # The following can cause crashes, thus it's disabled until fixed
399
  #return _TestInstanceDiskFailure(instance, node, node2, True)
400

    
401

    
402
def TestInstanceSecondaryDiskFailure(instance, node, node2):
403
  """Testing disk failure on secondary node."""
404
  return _TestInstanceDiskFailure(instance, node, node2, False)