Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 288d6440

History | View | Annotate | Download (12.2 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 TestInstanceConsole(instance):
217
  """gnt-instance console"""
218
  AssertCommand(["gnt-instance", "console", "--show-cmd", instance["name"]])
219

    
220

    
221
def TestReplaceDisks(instance, pnode, snode, othernode):
222
  """gnt-instance replace-disks"""
223
  def buildcmd(args):
224
    cmd = ['gnt-instance', 'replace-disks']
225
    cmd.extend(args)
226
    cmd.append(instance["name"])
227
    return cmd
228

    
229
  for data in [
230
    ["-p"],
231
    ["-s"],
232
    ["--new-secondary=%s" % othernode["primary"]],
233
    # and restore
234
    ["--new-secondary=%s" % snode["primary"]],
235
    ]:
236
    AssertCommand(buildcmd(data))
237

    
238

    
239
def TestInstanceExport(instance, node):
240
  """gnt-backup export -n ..."""
241
  name = instance["name"]
242
  AssertCommand(["gnt-backup", "export", "-n", node["primary"], name])
243
  return qa_utils.ResolveInstanceName(name)
244

    
245

    
246
def TestInstanceExportWithRemove(instance, node):
247
  """gnt-backup export --remove-instance"""
248
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
249
                 "--remove-instance", instance["name"]])
250

    
251

    
252
def TestInstanceExportNoTarget(instance):
253
  """gnt-backup export (without target node, should fail)"""
254
  AssertCommand(["gnt-backup", "export", instance["name"]], fail=True)
255

    
256

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

    
270

    
271
def TestBackupList(expnode):
272
  """gnt-backup list"""
273
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
274

    
275

    
276
def _TestInstanceDiskFailure(instance, node, node2, onmaster):
277
  """Testing disk failure."""
278
  master = qa_config.GetMasterNode()
279
  sq = utils.ShellQuoteArgs
280

    
281
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
282
  node_full = qa_utils.ResolveNodeName(node)
283
  node2_full = qa_utils.ResolveNodeName(node2)
284

    
285
  print qa_utils.FormatInfo("Getting physical disk names")
286
  cmd = ['gnt-node', 'volumes', '--separator=|', '--no-headers',
287
         '--output=node,phys,instance',
288
         node['primary'], node2['primary']]
289
  output = qa_utils.GetCommandOutput(master['primary'], sq(cmd))
290

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

    
300
      m = re_disk.match(phys)
301
      if not m:
302
        raise qa_error.Error("Unknown disk name format: %s" % phys)
303

    
304
      name = m.group(1)
305
      if name not in node2disk[node_name]:
306
        node2disk[node_name].append(name)
307

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

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

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

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

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

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

    
356
    print qa_utils.FormatInfo("Debugging info")
357
    for name in drbddevs:
358
      AssertCommand(["drbdsetup", name, "show"], node=node)
359

    
360
    AssertCommand(["gnt-instance", "info", instance["name"]])
361

    
362
  finally:
363
    print qa_utils.FormatInfo("Activating disks again")
364
    cmds = []
365
    for name in halted_disks:
366
      cmds.append(sq(["echo", "running"]) + " >%s" % _GetDiskStatePath(name))
367
    AssertCommand("; ".join(cmds), node=[node2, node][int(onmaster)])
368

    
369
  if onmaster:
370
    for name in drbddevs:
371
      AssertCommand(["drbdsetup", name, "detach"], node=node)
372
  else:
373
    for name in drbddevs:
374
      AssertCommand(["drbdsetup", name, "disconnect"], node=node2)
375

    
376
  # TODO
377
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
378

    
379
  print qa_utils.FormatInfo("Making sure disks are up again")
380
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
381

    
382
  print qa_utils.FormatInfo("Restarting instance")
383
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
384
  AssertCommand(["gnt-instance", "startup", instance["name"]])
385

    
386
  AssertCommand(["gnt-cluster", "verify"])
387

    
388

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

    
396

    
397
def TestInstanceSecondaryDiskFailure(instance, node, node2):
398
  """Testing disk failure on secondary node."""
399
  return _TestInstanceDiskFailure(instance, node, node2, False)