Statistics
| Branch: | Tag: | Revision:

root / qa / qa_instance.py @ 2237687b

History | View | Annotate | Download (12.5 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
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
  # pylint: disable-msg=W0613
229
  # due to unused pnode arg
230
  # FIXME: should be removed from the function completely
231
  def buildcmd(args):
232
    cmd = ['gnt-instance', 'replace-disks']
233
    cmd.extend(args)
234
    cmd.append(instance["name"])
235
    return cmd
236

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

    
246

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

    
253

    
254
def TestInstanceExportWithRemove(instance, node):
255
  """gnt-backup export --remove-instance"""
256
  AssertCommand(["gnt-backup", "export", "-n", node["primary"],
257
                 "--remove-instance", instance["name"]])
258

    
259

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

    
264

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

    
278

    
279
def TestBackupList(expnode):
280
  """gnt-backup list"""
281
  AssertCommand(["gnt-backup", "list", "--node=%s" % expnode["primary"]])
282

    
283

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

    
289
  instance_full = qa_utils.ResolveInstanceName(instance["name"])
290
  node_full = qa_utils.ResolveNodeName(node)
291
  node2_full = qa_utils.ResolveNodeName(node2)
292

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

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

    
308
      m = re_disk.match(phys)
309
      if not m:
310
        raise qa_error.Error("Unknown disk name format: %s" % phys)
311

    
312
      name = m.group(1)
313
      if name not in node2disk[node_name]:
314
        node2disk[node_name].append(name)
315

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

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

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

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

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

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

    
364
    print qa_utils.FormatInfo("Debugging info")
365
    for name in drbddevs:
366
      AssertCommand(["drbdsetup", name, "show"], node=node)
367

    
368
    AssertCommand(["gnt-instance", "info", instance["name"]])
369

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

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

    
384
  # TODO
385
  #AssertCommand(["vgs"], [node2, node][int(onmaster)])
386

    
387
  print qa_utils.FormatInfo("Making sure disks are up again")
388
  AssertCommand(["gnt-instance", "replace-disks", instance["name"]])
389

    
390
  print qa_utils.FormatInfo("Restarting instance")
391
  AssertCommand(["gnt-instance", "shutdown", instance["name"]])
392
  AssertCommand(["gnt-instance", "startup", instance["name"]])
393

    
394
  AssertCommand(["gnt-cluster", "verify"])
395

    
396

    
397
def TestInstanceMasterDiskFailure(instance, node, node2):
398
  """Testing disk failure on master node."""
399
  # pylint: disable-msg=W0613
400
  # due to unused args
401
  print qa_utils.FormatError("Disk failure on primary node cannot be"
402
                             " tested due to potential crashes.")
403
  # The following can cause crashes, thus it's disabled until fixed
404
  #return _TestInstanceDiskFailure(instance, node, node2, True)
405

    
406

    
407
def TestInstanceSecondaryDiskFailure(instance, node, node2):
408
  """Testing disk failure on secondary node."""
409
  return _TestInstanceDiskFailure(instance, node, node2, False)