Statistics
| Branch: | Tag: | Revision:

root / testing / ganeti.qa.py @ 53b78ba4

History | View | Annotate | Download (18.2 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Script for doing Q&A on Ganeti"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import os
25 a8083063 Iustin Pop
import re
26 a8083063 Iustin Pop
import sys
27 a8083063 Iustin Pop
import yaml
28 a8083063 Iustin Pop
import time
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
from datetime import datetime
31 a8083063 Iustin Pop
from optparse import OptionParser
32 a8083063 Iustin Pop
33 a8083063 Iustin Pop
# I want more flexibility for testing over SSH, therefore I'm not using
34 a8083063 Iustin Pop
# Ganeti's ssh module.
35 a8083063 Iustin Pop
import subprocess
36 a8083063 Iustin Pop
37 a8083063 Iustin Pop
from ganeti import utils
38 a8083063 Iustin Pop
from ganeti import constants
39 a8083063 Iustin Pop
40 a8083063 Iustin Pop
# {{{ Global variables
41 a8083063 Iustin Pop
cfg = None
42 a8083063 Iustin Pop
options = None
43 a8083063 Iustin Pop
# }}}
44 a8083063 Iustin Pop
45 a8083063 Iustin Pop
# {{{ Errors
46 a8083063 Iustin Pop
class Error(Exception):
47 a8083063 Iustin Pop
  """An error occurred during Q&A testing.
48 a8083063 Iustin Pop

49 a8083063 Iustin Pop
  """
50 a8083063 Iustin Pop
  pass
51 a8083063 Iustin Pop
52 a8083063 Iustin Pop
53 a8083063 Iustin Pop
class OutOfNodesError(Error):
54 a8083063 Iustin Pop
  """Out of nodes.
55 a8083063 Iustin Pop

56 a8083063 Iustin Pop
  """
57 a8083063 Iustin Pop
  pass
58 a8083063 Iustin Pop
59 a8083063 Iustin Pop
60 a8083063 Iustin Pop
class OutOfInstancesError(Error):
61 a8083063 Iustin Pop
  """Out of instances.
62 a8083063 Iustin Pop

63 a8083063 Iustin Pop
  """
64 a8083063 Iustin Pop
  pass
65 a8083063 Iustin Pop
# }}}
66 a8083063 Iustin Pop
67 a8083063 Iustin Pop
# {{{ Utilities
68 a8083063 Iustin Pop
def TestEnabled(test):
69 a8083063 Iustin Pop
  """Returns True if the given test is enabled."""
70 a8083063 Iustin Pop
  return cfg.get('tests', {}).get(test, False)
71 a8083063 Iustin Pop
72 a8083063 Iustin Pop
73 a8083063 Iustin Pop
def RunTest(callable, *args):
74 a8083063 Iustin Pop
  """Runs a test after printing a header.
75 a8083063 Iustin Pop

76 a8083063 Iustin Pop
  """
77 a8083063 Iustin Pop
  if callable.__doc__:
78 a8083063 Iustin Pop
    desc = callable.__doc__.splitlines()[0].strip()
79 a8083063 Iustin Pop
  else:
80 a8083063 Iustin Pop
    desc = '%r' % callable
81 a8083063 Iustin Pop
82 a8083063 Iustin Pop
  now = str(datetime.now())
83 a8083063 Iustin Pop
84 a8083063 Iustin Pop
  print
85 a8083063 Iustin Pop
  print '---', now, ('-' * (55 - len(now)))
86 a8083063 Iustin Pop
  print desc
87 a8083063 Iustin Pop
  print '-' * 60
88 a8083063 Iustin Pop
89 a8083063 Iustin Pop
  return callable(*args)
90 a8083063 Iustin Pop
91 a8083063 Iustin Pop
92 a8083063 Iustin Pop
def AssertEqual(first, second, msg=None):
93 a8083063 Iustin Pop
  """Raises an error when values aren't equal.
94 a8083063 Iustin Pop

95 a8083063 Iustin Pop
  """
96 a8083063 Iustin Pop
  if not first == second:
97 a8083063 Iustin Pop
    raise Error, (msg or '%r == %r' % (first, second))
98 a8083063 Iustin Pop
99 a8083063 Iustin Pop
100 a8083063 Iustin Pop
def GetSSHCommand(node, cmd, strict=True):
101 a8083063 Iustin Pop
  """Builds SSH command to be executed.
102 a8083063 Iustin Pop

103 a8083063 Iustin Pop
  """
104 a8083063 Iustin Pop
  args = [ 'ssh', '-oEscapeChar=none', '-oBatchMode=yes', '-l', 'root' ]
105 a8083063 Iustin Pop
106 a8083063 Iustin Pop
  if strict:
107 a8083063 Iustin Pop
    tmp = 'yes'
108 a8083063 Iustin Pop
  else:
109 a8083063 Iustin Pop
    tmp = 'no'
110 a8083063 Iustin Pop
  args.append('-oStrictHostKeyChecking=%s' % tmp)
111 a8083063 Iustin Pop
  args.append(node)
112 a8083063 Iustin Pop
113 a8083063 Iustin Pop
  if options.dry_run:
114 a8083063 Iustin Pop
    prefix = 'exit 0; '
115 a8083063 Iustin Pop
  else:
116 a8083063 Iustin Pop
    prefix = ''
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
  args.append(prefix + cmd)
119 a8083063 Iustin Pop
120 a8083063 Iustin Pop
  if options.verbose:
121 a8083063 Iustin Pop
    print 'SSH:', utils.ShellQuoteArgs(args)
122 a8083063 Iustin Pop
123 a8083063 Iustin Pop
  return args
124 a8083063 Iustin Pop
125 a8083063 Iustin Pop
126 a8083063 Iustin Pop
def StartSSH(node, cmd, strict=True):
127 a8083063 Iustin Pop
  """Starts SSH.
128 a8083063 Iustin Pop

129 a8083063 Iustin Pop
  """
130 a8083063 Iustin Pop
  args = GetSSHCommand(node, cmd, strict=strict)
131 a8083063 Iustin Pop
  return subprocess.Popen(args, shell=False)
132 a8083063 Iustin Pop
133 a8083063 Iustin Pop
134 a8083063 Iustin Pop
def UploadFile(node, file):
135 a8083063 Iustin Pop
  """Uploads a file to a node and returns the filename.
136 a8083063 Iustin Pop

137 a8083063 Iustin Pop
  Caller needs to remove the file when it's not needed anymore.
138 a8083063 Iustin Pop
  """
139 a8083063 Iustin Pop
  if os.stat(file).st_mode & 0100:
140 a8083063 Iustin Pop
    mode = '0700'
141 a8083063 Iustin Pop
  else:
142 a8083063 Iustin Pop
    mode = '0600'
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
  cmd = ('tmp=$(tempfile --mode %s --prefix gnt) && '
145 a8083063 Iustin Pop
         '[[ -f "${tmp}" ]] && '
146 a8083063 Iustin Pop
         'cat > "${tmp}" && '
147 a8083063 Iustin Pop
         'echo "${tmp}"') % mode
148 a8083063 Iustin Pop
149 a8083063 Iustin Pop
  f = open(file, 'r')
150 a8083063 Iustin Pop
  try:
151 a8083063 Iustin Pop
    p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
152 a8083063 Iustin Pop
                         stdout=subprocess.PIPE)
153 a8083063 Iustin Pop
    AssertEqual(p.wait(), 0)
154 a8083063 Iustin Pop
155 a8083063 Iustin Pop
    name = p.stdout.read().strip()
156 a8083063 Iustin Pop
157 a8083063 Iustin Pop
    return name
158 a8083063 Iustin Pop
  finally:
159 a8083063 Iustin Pop
    f.close()
160 a8083063 Iustin Pop
# }}}
161 a8083063 Iustin Pop
162 a8083063 Iustin Pop
# {{{ Config helpers
163 a8083063 Iustin Pop
def GetMasterNode():
164 a8083063 Iustin Pop
  return cfg['nodes'][0]
165 a8083063 Iustin Pop
166 a8083063 Iustin Pop
167 a8083063 Iustin Pop
def AcquireInstance():
168 a8083063 Iustin Pop
  """Returns an instance which isn't in use.
169 a8083063 Iustin Pop

170 a8083063 Iustin Pop
  """
171 a8083063 Iustin Pop
  # Filter out unwanted instances
172 a8083063 Iustin Pop
  tmp_flt = lambda inst: not inst.get('_used', False)
173 a8083063 Iustin Pop
  instances = filter(tmp_flt, cfg['instances'])
174 a8083063 Iustin Pop
  del tmp_flt
175 a8083063 Iustin Pop
176 a8083063 Iustin Pop
  if len(instances) == 0:
177 a8083063 Iustin Pop
    raise OutOfInstancesError, ("No instances left")
178 a8083063 Iustin Pop
179 a8083063 Iustin Pop
  inst = instances[0]
180 a8083063 Iustin Pop
  inst['_used'] = True
181 a8083063 Iustin Pop
  return inst
182 a8083063 Iustin Pop
183 a8083063 Iustin Pop
184 a8083063 Iustin Pop
def ReleaseInstance(inst):
185 a8083063 Iustin Pop
  inst['_used'] = False
186 a8083063 Iustin Pop
187 a8083063 Iustin Pop
188 a8083063 Iustin Pop
def AcquireNode(exclude=None):
189 a8083063 Iustin Pop
  """Returns the least used node.
190 a8083063 Iustin Pop

191 a8083063 Iustin Pop
  """
192 a8083063 Iustin Pop
  master = GetMasterNode()
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
  # Filter out unwanted nodes
195 a8083063 Iustin Pop
  # TODO: Maybe combine filters
196 a8083063 Iustin Pop
  if exclude is None:
197 a8083063 Iustin Pop
    nodes = cfg['nodes'][:]
198 a8083063 Iustin Pop
  else:
199 a8083063 Iustin Pop
    nodes = filter(lambda node: node != exclude, cfg['nodes'])
200 a8083063 Iustin Pop
201 a8083063 Iustin Pop
  tmp_flt = lambda node: node.get('_added', False) or node == master
202 a8083063 Iustin Pop
  nodes = filter(tmp_flt, nodes)
203 a8083063 Iustin Pop
  del tmp_flt
204 a8083063 Iustin Pop
205 a8083063 Iustin Pop
  if len(nodes) == 0:
206 a8083063 Iustin Pop
    raise OutOfNodesError, ("No nodes left")
207 a8083063 Iustin Pop
208 a8083063 Iustin Pop
  # Get node with least number of uses
209 a8083063 Iustin Pop
  def compare(a, b):
210 a8083063 Iustin Pop
    result = cmp(a.get('_count', 0), b.get('_count', 0))
211 a8083063 Iustin Pop
    if result == 0:
212 a8083063 Iustin Pop
      result = cmp(a['primary'], b['primary'])
213 a8083063 Iustin Pop
    return result
214 a8083063 Iustin Pop
215 a8083063 Iustin Pop
  nodes.sort(cmp=compare)
216 a8083063 Iustin Pop
217 a8083063 Iustin Pop
  node = nodes[0]
218 a8083063 Iustin Pop
  node['_count'] = node.get('_count', 0) + 1
219 a8083063 Iustin Pop
  return node
220 a8083063 Iustin Pop
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
def ReleaseNode(node):
223 a8083063 Iustin Pop
  node['_count'] = node.get('_count', 0) - 1
224 a8083063 Iustin Pop
# }}}
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
# {{{ Environment tests
227 a8083063 Iustin Pop
def TestConfig():
228 a8083063 Iustin Pop
  """Test configuration for sanity.
229 a8083063 Iustin Pop

230 a8083063 Iustin Pop
  """
231 a8083063 Iustin Pop
  if len(cfg['nodes']) < 1:
232 a8083063 Iustin Pop
    raise Error, ("Need at least one node")
233 a8083063 Iustin Pop
  if len(cfg['instances']) < 1:
234 a8083063 Iustin Pop
    raise Error, ("Need at least one instance")
235 a8083063 Iustin Pop
  # TODO: Add more checks
236 a8083063 Iustin Pop
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
def TestSshConnection():
239 a8083063 Iustin Pop
  """Test SSH connection.
240 a8083063 Iustin Pop

241 a8083063 Iustin Pop
  """
242 a8083063 Iustin Pop
  for node in cfg['nodes']:
243 a8083063 Iustin Pop
    AssertEqual(StartSSH(node['primary'], 'exit').wait(), 0)
244 a8083063 Iustin Pop
245 a8083063 Iustin Pop
246 a8083063 Iustin Pop
def TestGanetiCommands():
247 a8083063 Iustin Pop
  """Test availibility of Ganeti commands.
248 a8083063 Iustin Pop

249 a8083063 Iustin Pop
  """
250 a8083063 Iustin Pop
  cmds = ( ['gnt-cluster', '--version'],
251 a8083063 Iustin Pop
           ['gnt-os', '--version'],
252 a8083063 Iustin Pop
           ['gnt-node', '--version'],
253 a8083063 Iustin Pop
           ['gnt-instance', '--version'],
254 a8083063 Iustin Pop
           ['gnt-backup', '--version'],
255 a8083063 Iustin Pop
           ['ganeti-noded', '--version'],
256 a8083063 Iustin Pop
           ['ganeti-watcher', '--version'] )
257 a8083063 Iustin Pop
258 e9e35aaa Michael Hanselmann
  cmd = ' && '.join([utils.ShellQuoteArgs(i) for i in cmds])
259 a8083063 Iustin Pop
260 a8083063 Iustin Pop
  for node in cfg['nodes']:
261 a8083063 Iustin Pop
    AssertEqual(StartSSH(node['primary'], cmd).wait(), 0)
262 a8083063 Iustin Pop
263 a8083063 Iustin Pop
264 a8083063 Iustin Pop
def TestIcmpPing():
265 a8083063 Iustin Pop
  """ICMP ping each node.
266 a8083063 Iustin Pop

267 a8083063 Iustin Pop
  """
268 a8083063 Iustin Pop
  for node in cfg['nodes']:
269 a8083063 Iustin Pop
    check = []
270 a8083063 Iustin Pop
    for i in cfg['nodes']:
271 a8083063 Iustin Pop
      check.append(i['primary'])
272 a8083063 Iustin Pop
      if i.has_key('secondary'):
273 a8083063 Iustin Pop
        check.append(i['secondary'])
274 a8083063 Iustin Pop
275 a8083063 Iustin Pop
    ping = lambda ip: utils.ShellQuoteArgs(['ping', '-w', '3', '-c', '1', ip])
276 e9e35aaa Michael Hanselmann
    cmd = ' && '.join([ping(i) for i in check])
277 a8083063 Iustin Pop
278 a8083063 Iustin Pop
    AssertEqual(StartSSH(node['primary'], cmd).wait(), 0)
279 a8083063 Iustin Pop
# }}}
280 a8083063 Iustin Pop
281 a8083063 Iustin Pop
# {{{ Cluster tests
282 a8083063 Iustin Pop
def TestClusterInit():
283 a8083063 Iustin Pop
  """gnt-cluster init"""
284 a8083063 Iustin Pop
  master = GetMasterNode()
285 a8083063 Iustin Pop
286 a8083063 Iustin Pop
  cmd = ['gnt-cluster', 'init']
287 a8083063 Iustin Pop
  if master.get('secondary', None):
288 a8083063 Iustin Pop
    cmd.append('--secondary-ip=%s' % master['secondary'])
289 6f11f250 Michael Hanselmann
  if cfg.get('bridge', None):
290 6f11f250 Michael Hanselmann
    cmd.append('--bridge=%s' % cfg['bridge'])
291 e9e35aaa Michael Hanselmann
    cmd.append('--master-netdev=%s' % cfg['bridge'])
292 a8083063 Iustin Pop
  cmd.append(cfg['name'])
293 a8083063 Iustin Pop
294 a8083063 Iustin Pop
  AssertEqual(StartSSH(master['primary'],
295 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
296 a8083063 Iustin Pop
297 a8083063 Iustin Pop
298 a8083063 Iustin Pop
def TestClusterVerify():
299 a8083063 Iustin Pop
  """gnt-cluster verify"""
300 a8083063 Iustin Pop
  cmd = ['gnt-cluster', 'verify']
301 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
302 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
303 a8083063 Iustin Pop
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
def TestClusterInfo():
306 a8083063 Iustin Pop
  """gnt-cluster info"""
307 a8083063 Iustin Pop
  cmd = ['gnt-cluster', 'info']
308 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
309 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
310 a8083063 Iustin Pop
311 a8083063 Iustin Pop
312 a8083063 Iustin Pop
def TestClusterBurnin():
313 a8083063 Iustin Pop
  """Burnin"""
314 a8083063 Iustin Pop
  master = GetMasterNode()
315 a8083063 Iustin Pop
316 a8083063 Iustin Pop
  # Get as many instances as we need
317 a8083063 Iustin Pop
  instances = []
318 a8083063 Iustin Pop
  try:
319 a8083063 Iustin Pop
    for _ in xrange(0, cfg.get('options', {}).get('burnin-instances', 1)):
320 a8083063 Iustin Pop
      instances.append(AcquireInstance())
321 a8083063 Iustin Pop
  except OutOfInstancesError:
322 a8083063 Iustin Pop
    print "Not enough instances, continuing anyway."
323 a8083063 Iustin Pop
324 a8083063 Iustin Pop
  if len(instances) < 1:
325 a8083063 Iustin Pop
    raise Error, ("Burnin needs at least one instance")
326 a8083063 Iustin Pop
327 a8083063 Iustin Pop
  # Run burnin
328 a8083063 Iustin Pop
  try:
329 a8083063 Iustin Pop
    script = UploadFile(master['primary'], '../tools/burnin')
330 a8083063 Iustin Pop
    try:
331 a8083063 Iustin Pop
      cmd = [script, '--os=%s' % cfg['os']]
332 e9e35aaa Michael Hanselmann
      cmd += [inst['name'] for inst in instances]
333 a8083063 Iustin Pop
      AssertEqual(StartSSH(master['primary'],
334 a8083063 Iustin Pop
                           utils.ShellQuoteArgs(cmd)).wait(), 0)
335 a8083063 Iustin Pop
    finally:
336 a8083063 Iustin Pop
      cmd = ['rm', '-f', script]
337 a8083063 Iustin Pop
      AssertEqual(StartSSH(master['primary'],
338 a8083063 Iustin Pop
                           utils.ShellQuoteArgs(cmd)).wait(), 0)
339 a8083063 Iustin Pop
  finally:
340 a8083063 Iustin Pop
    for inst in instances:
341 a8083063 Iustin Pop
      ReleaseInstance(inst)
342 a8083063 Iustin Pop
343 a8083063 Iustin Pop
344 a8083063 Iustin Pop
def TestClusterMasterFailover():
345 a8083063 Iustin Pop
  """gnt-cluster masterfailover"""
346 a8083063 Iustin Pop
  master = GetMasterNode()
347 a8083063 Iustin Pop
348 a8083063 Iustin Pop
  failovermaster = AcquireNode(exclude=master)
349 a8083063 Iustin Pop
  try:
350 a8083063 Iustin Pop
    cmd = ['gnt-cluster', 'masterfailover']
351 a8083063 Iustin Pop
    AssertEqual(StartSSH(failovermaster['primary'],
352 a8083063 Iustin Pop
                         utils.ShellQuoteArgs(cmd)).wait(), 0)
353 a8083063 Iustin Pop
354 a8083063 Iustin Pop
    cmd = ['gnt-cluster', 'masterfailover']
355 a8083063 Iustin Pop
    AssertEqual(StartSSH(master['primary'],
356 a8083063 Iustin Pop
                         utils.ShellQuoteArgs(cmd)).wait(), 0)
357 a8083063 Iustin Pop
  finally:
358 a8083063 Iustin Pop
    ReleaseNode(failovermaster)
359 a8083063 Iustin Pop
360 a8083063 Iustin Pop
361 a8083063 Iustin Pop
def TestClusterDestroy():
362 a8083063 Iustin Pop
  """gnt-cluster destroy"""
363 a8083063 Iustin Pop
  cmd = ['gnt-cluster', 'destroy', '--yes-do-it']
364 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
365 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
366 a8083063 Iustin Pop
# }}}
367 a8083063 Iustin Pop
368 a8083063 Iustin Pop
# {{{ Node tests
369 a8083063 Iustin Pop
def _NodeAdd(node):
370 a8083063 Iustin Pop
  if node.get('_added', False):
371 a8083063 Iustin Pop
    raise Error, ("Node %s already in cluster" % node['primary'])
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
  cmd = ['gnt-node', 'add']
374 a8083063 Iustin Pop
  if node.get('secondary', None):
375 a8083063 Iustin Pop
    cmd.append('--secondary-ip=%s' % node['secondary'])
376 a8083063 Iustin Pop
  cmd.append(node['primary'])
377 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
378 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
379 a8083063 Iustin Pop
380 a8083063 Iustin Pop
  node['_added'] = True
381 a8083063 Iustin Pop
382 a8083063 Iustin Pop
383 a8083063 Iustin Pop
def TestNodeAddAll():
384 a8083063 Iustin Pop
  """Adding all nodes to cluster."""
385 a8083063 Iustin Pop
  master = GetMasterNode()
386 a8083063 Iustin Pop
  for node in cfg['nodes']:
387 a8083063 Iustin Pop
    if node != master:
388 a8083063 Iustin Pop
      _NodeAdd(node)
389 a8083063 Iustin Pop
390 a8083063 Iustin Pop
391 a8083063 Iustin Pop
def _NodeRemove(node):
392 a8083063 Iustin Pop
  cmd = ['gnt-node', 'remove', node['primary']]
393 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
394 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
395 a8083063 Iustin Pop
  node['_added'] = False
396 a8083063 Iustin Pop
397 a8083063 Iustin Pop
398 a8083063 Iustin Pop
def TestNodeRemoveAll():
399 a8083063 Iustin Pop
  """Removing all nodes from cluster."""
400 a8083063 Iustin Pop
  master = GetMasterNode()
401 a8083063 Iustin Pop
  for node in cfg['nodes']:
402 a8083063 Iustin Pop
    if node != master:
403 a8083063 Iustin Pop
      _NodeRemove(node)
404 e9e35aaa Michael Hanselmann
405 e9e35aaa Michael Hanselmann
406 e9e35aaa Michael Hanselmann
def TestNodeInfo():
407 e9e35aaa Michael Hanselmann
  """gnt-node info"""
408 e9e35aaa Michael Hanselmann
  cmd = ['gnt-node', 'info']
409 e9e35aaa Michael Hanselmann
  AssertEqual(StartSSH(GetMasterNode()['primary'],
410 e9e35aaa Michael Hanselmann
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
411 a8083063 Iustin Pop
# }}}
412 a8083063 Iustin Pop
413 a8083063 Iustin Pop
# {{{ Instance tests
414 a8083063 Iustin Pop
def _DiskTest(node, instance, args):
415 a8083063 Iustin Pop
  cmd = ['gnt-instance', 'add',
416 a8083063 Iustin Pop
         '--os-type=%s' % cfg['os'],
417 a8083063 Iustin Pop
         '--os-size=%s' % cfg['os-size'],
418 a8083063 Iustin Pop
         '--swap-size=%s' % cfg['swap-size'],
419 a8083063 Iustin Pop
         '--memory=%s' % cfg['mem'],
420 a8083063 Iustin Pop
         '--node=%s' % node['primary']]
421 a8083063 Iustin Pop
  if args:
422 a8083063 Iustin Pop
    cmd += args
423 a8083063 Iustin Pop
  cmd.append(instance['name'])
424 a8083063 Iustin Pop
425 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
426 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
427 a8083063 Iustin Pop
  return instance
428 a8083063 Iustin Pop
429 a8083063 Iustin Pop
430 a8083063 Iustin Pop
def TestInstanceAddWithPlainDisk(node):
431 a8083063 Iustin Pop
  """gnt-instance add -t plain"""
432 a8083063 Iustin Pop
  return _DiskTest(node, AcquireInstance(), ['--disk-template=plain'])
433 a8083063 Iustin Pop
434 a8083063 Iustin Pop
435 a8083063 Iustin Pop
def TestInstanceAddWithLocalMirrorDisk(node):
436 a8083063 Iustin Pop
  """gnt-instance add -t local_raid1"""
437 a8083063 Iustin Pop
  return _DiskTest(node, AcquireInstance(), ['--disk-template=local_raid1'])
438 a8083063 Iustin Pop
439 a8083063 Iustin Pop
440 a8083063 Iustin Pop
def TestInstanceAddWithRemoteRaidDisk(node, node2):
441 a8083063 Iustin Pop
  """gnt-instance add -t remote_raid1"""
442 a8083063 Iustin Pop
  return _DiskTest(node, AcquireInstance(),
443 a8083063 Iustin Pop
                   ['--disk-template=remote_raid1',
444 a8083063 Iustin Pop
                    '--secondary-node=%s' % node2['primary']])
445 a8083063 Iustin Pop
446 a8083063 Iustin Pop
447 a8083063 Iustin Pop
def TestInstanceRemove(instance):
448 a8083063 Iustin Pop
  """gnt-instance remove"""
449 a8083063 Iustin Pop
  cmd = ['gnt-instance', 'remove', '-f', instance['name']]
450 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
451 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
452 a8083063 Iustin Pop
453 a8083063 Iustin Pop
  ReleaseInstance(instance)
454 a8083063 Iustin Pop
455 a8083063 Iustin Pop
456 a8083063 Iustin Pop
def TestInstanceStartup(instance):
457 a8083063 Iustin Pop
  """gnt-instance startup"""
458 a8083063 Iustin Pop
  cmd = ['gnt-instance', 'startup', instance['name']]
459 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
460 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
461 a8083063 Iustin Pop
462 a8083063 Iustin Pop
463 a8083063 Iustin Pop
def TestInstanceShutdown(instance):
464 a8083063 Iustin Pop
  """gnt-instance shutdown"""
465 a8083063 Iustin Pop
  cmd = ['gnt-instance', 'shutdown', instance['name']]
466 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
467 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
468 a8083063 Iustin Pop
469 a8083063 Iustin Pop
470 a8083063 Iustin Pop
def TestInstanceFailover(instance):
471 a8083063 Iustin Pop
  """gnt-instance failover"""
472 a8083063 Iustin Pop
  cmd = ['gnt-instance', 'failover', '--force', instance['name']]
473 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
474 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
475 e9e35aaa Michael Hanselmann
476 e9e35aaa Michael Hanselmann
477 e9e35aaa Michael Hanselmann
def TestInstanceInfo(instance):
478 e9e35aaa Michael Hanselmann
  """gnt-instance info"""
479 e9e35aaa Michael Hanselmann
  cmd = ['gnt-instance', 'info', instance['name']]
480 e9e35aaa Michael Hanselmann
  AssertEqual(StartSSH(GetMasterNode()['primary'],
481 e9e35aaa Michael Hanselmann
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
482 a8083063 Iustin Pop
# }}}
483 a8083063 Iustin Pop
484 a8083063 Iustin Pop
# {{{ Daemon tests
485 a8083063 Iustin Pop
def _ResolveInstanceName(instance):
486 a8083063 Iustin Pop
  """Gets the full Xen name of an instance.
487 a8083063 Iustin Pop

488 a8083063 Iustin Pop
  """
489 a8083063 Iustin Pop
  master = GetMasterNode()
490 a8083063 Iustin Pop
491 a8083063 Iustin Pop
  info_cmd = utils.ShellQuoteArgs(['gnt-instance', 'info', instance['name']])
492 a8083063 Iustin Pop
  sed_cmd = utils.ShellQuoteArgs(['sed', '-n', '-e', 's/^Instance name: *//p'])
493 a8083063 Iustin Pop
494 a8083063 Iustin Pop
  cmd = '%s | %s' % (info_cmd, sed_cmd)
495 a8083063 Iustin Pop
  p = subprocess.Popen(GetSSHCommand(master['primary'], cmd), shell=False,
496 a8083063 Iustin Pop
                       stdout=subprocess.PIPE)
497 a8083063 Iustin Pop
  AssertEqual(p.wait(), 0)
498 a8083063 Iustin Pop
499 a8083063 Iustin Pop
  return p.stdout.read().strip()
500 a8083063 Iustin Pop
501 a8083063 Iustin Pop
502 a8083063 Iustin Pop
def _InstanceRunning(node, name):
503 a8083063 Iustin Pop
  """Checks whether an instance is running.
504 a8083063 Iustin Pop

505 a8083063 Iustin Pop
  Args:
506 a8083063 Iustin Pop
    node: Node the instance runs on
507 a8083063 Iustin Pop
    name: Full name of Xen instance
508 a8083063 Iustin Pop
  """
509 a8083063 Iustin Pop
  cmd = utils.ShellQuoteArgs(['xm', 'list', name]) + ' >/dev/null'
510 a8083063 Iustin Pop
  ret = StartSSH(node['primary'], cmd).wait()
511 a8083063 Iustin Pop
  return ret == 0
512 a8083063 Iustin Pop
513 a8083063 Iustin Pop
514 a8083063 Iustin Pop
def _XmShutdownInstance(node, name):
515 a8083063 Iustin Pop
  """Shuts down instance using "xm" and waits for completion.
516 a8083063 Iustin Pop

517 a8083063 Iustin Pop
  Args:
518 a8083063 Iustin Pop
    node: Node the instance runs on
519 a8083063 Iustin Pop
    name: Full name of Xen instance
520 a8083063 Iustin Pop
  """
521 a8083063 Iustin Pop
  cmd = ['xm', 'shutdown', name]
522 a8083063 Iustin Pop
  AssertEqual(StartSSH(GetMasterNode()['primary'],
523 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
524 a8083063 Iustin Pop
525 a8083063 Iustin Pop
  # Wait up to a minute
526 a8083063 Iustin Pop
  end = time.time() + 60
527 a8083063 Iustin Pop
  while time.time() <= end:
528 a8083063 Iustin Pop
    if not _InstanceRunning(node, name):
529 a8083063 Iustin Pop
      break
530 a8083063 Iustin Pop
    time.sleep(5)
531 a8083063 Iustin Pop
  else:
532 a8083063 Iustin Pop
    raise Error, ("xm shutdown failed")
533 a8083063 Iustin Pop
534 a8083063 Iustin Pop
535 a8083063 Iustin Pop
def _ResetWatcherDaemon(node):
536 a8083063 Iustin Pop
  """Removes the watcher daemon's state file.
537 a8083063 Iustin Pop

538 a8083063 Iustin Pop
  Args:
539 a8083063 Iustin Pop
    node: Node to be reset
540 a8083063 Iustin Pop
  """
541 a8083063 Iustin Pop
  cmd = ['rm', '-f', constants.WATCHER_STATEFILE]
542 a8083063 Iustin Pop
  AssertEqual(StartSSH(node['primary'],
543 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
544 a8083063 Iustin Pop
545 a8083063 Iustin Pop
546 a8083063 Iustin Pop
def TestInstanceAutomaticRestart(node, instance):
547 a8083063 Iustin Pop
  """Test automatic restart of instance by ganeti-watcher.
548 a8083063 Iustin Pop

549 a8083063 Iustin Pop
  Note: takes up to 6 minutes to complete.
550 a8083063 Iustin Pop
  """
551 a8083063 Iustin Pop
  master = GetMasterNode()
552 a8083063 Iustin Pop
  inst_name = _ResolveInstanceName(instance)
553 a8083063 Iustin Pop
554 a8083063 Iustin Pop
  _ResetWatcherDaemon(node)
555 a8083063 Iustin Pop
  _XmShutdownInstance(node, inst_name)
556 a8083063 Iustin Pop
557 a8083063 Iustin Pop
  # Give it a bit more than five minutes to start again
558 a8083063 Iustin Pop
  restart_at = time.time() + 330
559 a8083063 Iustin Pop
560 a8083063 Iustin Pop
  # Wait until it's running again
561 a8083063 Iustin Pop
  while time.time() <= restart_at:
562 a8083063 Iustin Pop
    if _InstanceRunning(node, inst_name):
563 a8083063 Iustin Pop
      break
564 a8083063 Iustin Pop
    time.sleep(15)
565 a8083063 Iustin Pop
  else:
566 a8083063 Iustin Pop
    raise Error, ("Daemon didn't restart instance in time")
567 a8083063 Iustin Pop
568 a8083063 Iustin Pop
  cmd = ['gnt-instance', 'info', inst_name]
569 a8083063 Iustin Pop
  AssertEqual(StartSSH(master['primary'],
570 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
571 a8083063 Iustin Pop
572 a8083063 Iustin Pop
573 a8083063 Iustin Pop
def TestInstanceConsecutiveFailures(node, instance):
574 a8083063 Iustin Pop
  """Test five consecutive instance failures.
575 a8083063 Iustin Pop

576 a8083063 Iustin Pop
  Note: takes at least 35 minutes to complete.
577 a8083063 Iustin Pop
  """
578 a8083063 Iustin Pop
  master = GetMasterNode()
579 a8083063 Iustin Pop
  inst_name = _ResolveInstanceName(instance)
580 a8083063 Iustin Pop
581 a8083063 Iustin Pop
  _ResetWatcherDaemon(node)
582 a8083063 Iustin Pop
  _XmShutdownInstance(node, inst_name)
583 a8083063 Iustin Pop
584 a8083063 Iustin Pop
  # Do shutdowns for 30 minutes
585 a8083063 Iustin Pop
  finished_at = time.time() + (35 * 60)
586 a8083063 Iustin Pop
587 a8083063 Iustin Pop
  while time.time() <= finished_at:
588 a8083063 Iustin Pop
    if _InstanceRunning(node, inst_name):
589 a8083063 Iustin Pop
      _XmShutdownInstance(node, inst_name)
590 a8083063 Iustin Pop
    time.sleep(30)
591 a8083063 Iustin Pop
592 a8083063 Iustin Pop
  # Check for some time whether the instance doesn't start again
593 a8083063 Iustin Pop
  check_until = time.time() + 330
594 a8083063 Iustin Pop
  while time.time() <= check_until:
595 a8083063 Iustin Pop
    if _InstanceRunning(node, inst_name):
596 a8083063 Iustin Pop
      raise Error, ("Instance started when it shouldn't")
597 a8083063 Iustin Pop
    time.sleep(30)
598 a8083063 Iustin Pop
599 a8083063 Iustin Pop
  cmd = ['gnt-instance', 'info', inst_name]
600 a8083063 Iustin Pop
  AssertEqual(StartSSH(master['primary'],
601 a8083063 Iustin Pop
                       utils.ShellQuoteArgs(cmd)).wait(), 0)
602 a8083063 Iustin Pop
# }}}
603 a8083063 Iustin Pop
604 a8083063 Iustin Pop
# {{{ Main program
605 a8083063 Iustin Pop
if __name__ == '__main__':
606 a8083063 Iustin Pop
  # {{{ Option parsing
607 a8083063 Iustin Pop
  parser = OptionParser(usage="%prog [options] <configfile>")
608 a8083063 Iustin Pop
  parser.add_option('--cleanup', dest='cleanup',
609 a8083063 Iustin Pop
      action="store_true",
610 a8083063 Iustin Pop
      help="Clean up cluster after testing?")
611 a8083063 Iustin Pop
  parser.add_option('--dry-run', dest='dry_run',
612 a8083063 Iustin Pop
      action="store_true",
613 a8083063 Iustin Pop
      help="Show what would be done")
614 a8083063 Iustin Pop
  parser.add_option('--verbose', dest='verbose',
615 a8083063 Iustin Pop
      action="store_true",
616 a8083063 Iustin Pop
      help="Verbose output")
617 a8083063 Iustin Pop
  parser.add_option('--yes-do-it', dest='yes_do_it',
618 a8083063 Iustin Pop
      action="store_true",
619 a8083063 Iustin Pop
      help="Really execute the tests")
620 a8083063 Iustin Pop
  (options, args) = parser.parse_args()
621 a8083063 Iustin Pop
  # }}}
622 a8083063 Iustin Pop
623 a8083063 Iustin Pop
  if len(args) == 1:
624 a8083063 Iustin Pop
    config_file = args[0]
625 a8083063 Iustin Pop
  else:
626 a8083063 Iustin Pop
    raise SyntaxError, ("Exactly one configuration file is expected")
627 a8083063 Iustin Pop
628 a8083063 Iustin Pop
  if not options.yes_do_it:
629 a8083063 Iustin Pop
    print ("Executing this script irreversibly destroys any Ganeti\n"
630 a8083063 Iustin Pop
           "configuration on all nodes involved. If you really want\n"
631 a8083063 Iustin Pop
           "to start testing, supply the --yes-do-it option.")
632 a8083063 Iustin Pop
    sys.exit(1)
633 a8083063 Iustin Pop
634 a8083063 Iustin Pop
  f = open(config_file, 'r')
635 a8083063 Iustin Pop
  try:
636 a8083063 Iustin Pop
    cfg = yaml.load(f.read())
637 a8083063 Iustin Pop
  finally:
638 a8083063 Iustin Pop
    f.close()
639 a8083063 Iustin Pop
640 a8083063 Iustin Pop
  RunTest(TestConfig)
641 a8083063 Iustin Pop
642 a8083063 Iustin Pop
  if TestEnabled('env'):
643 a8083063 Iustin Pop
    RunTest(TestSshConnection)
644 a8083063 Iustin Pop
    RunTest(TestIcmpPing)
645 a8083063 Iustin Pop
    RunTest(TestGanetiCommands)
646 a8083063 Iustin Pop
647 a8083063 Iustin Pop
  RunTest(TestClusterInit)
648 a8083063 Iustin Pop
649 a8083063 Iustin Pop
  if TestEnabled('cluster-verify'):
650 a8083063 Iustin Pop
    RunTest(TestClusterVerify)
651 e9e35aaa Michael Hanselmann
652 e9e35aaa Michael Hanselmann
  if TestEnabled('cluster-info'):
653 a8083063 Iustin Pop
    RunTest(TestClusterInfo)
654 a8083063 Iustin Pop
655 a8083063 Iustin Pop
  RunTest(TestNodeAddAll)
656 a8083063 Iustin Pop
657 e9e35aaa Michael Hanselmann
  if TestEnabled('node-info'):
658 e9e35aaa Michael Hanselmann
    RunTest(TestNodeInfo)
659 e9e35aaa Michael Hanselmann
660 a8083063 Iustin Pop
  if TestEnabled('cluster-burnin'):
661 a8083063 Iustin Pop
    RunTest(TestClusterBurnin)
662 a8083063 Iustin Pop
663 a8083063 Iustin Pop
  if TestEnabled('cluster-master-failover'):
664 a8083063 Iustin Pop
    RunTest(TestClusterMasterFailover)
665 a8083063 Iustin Pop
666 a8083063 Iustin Pop
  node = AcquireNode()
667 a8083063 Iustin Pop
  try:
668 a8083063 Iustin Pop
    if TestEnabled('instance-add-plain-disk'):
669 a8083063 Iustin Pop
      instance = RunTest(TestInstanceAddWithPlainDisk, node)
670 a8083063 Iustin Pop
      RunTest(TestInstanceShutdown, instance)
671 a8083063 Iustin Pop
      RunTest(TestInstanceStartup, instance)
672 a8083063 Iustin Pop
673 e9e35aaa Michael Hanselmann
      if TestEnabled('instance-info'):
674 e9e35aaa Michael Hanselmann
        RunTest(TestInstanceInfo, instance)
675 e9e35aaa Michael Hanselmann
676 a8083063 Iustin Pop
      if TestEnabled('instance-automatic-restart'):
677 a8083063 Iustin Pop
        RunTest(TestInstanceAutomaticRestart, node, instance)
678 a8083063 Iustin Pop
679 a8083063 Iustin Pop
      if TestEnabled('instance-consecutive-failures'):
680 a8083063 Iustin Pop
        RunTest(TestInstanceConsecutiveFailures, node, instance)
681 a8083063 Iustin Pop
682 a8083063 Iustin Pop
      RunTest(TestInstanceRemove, instance)
683 a8083063 Iustin Pop
      del instance
684 a8083063 Iustin Pop
685 a8083063 Iustin Pop
    if TestEnabled('instance-add-local-mirror-disk'):
686 a8083063 Iustin Pop
      instance = RunTest(TestInstanceAddWithLocalMirrorDisk, node)
687 a8083063 Iustin Pop
      RunTest(TestInstanceShutdown, instance)
688 a8083063 Iustin Pop
      RunTest(TestInstanceStartup, instance)
689 e9e35aaa Michael Hanselmann
690 e9e35aaa Michael Hanselmann
      if TestEnabled('instance-info'):
691 e9e35aaa Michael Hanselmann
        RunTest(TestInstanceInfo, instance)
692 e9e35aaa Michael Hanselmann
693 a8083063 Iustin Pop
      RunTest(TestInstanceRemove, instance)
694 a8083063 Iustin Pop
      del instance
695 a8083063 Iustin Pop
696 a8083063 Iustin Pop
    if TestEnabled('instance-add-remote-raid-disk'):
697 a8083063 Iustin Pop
      node2 = AcquireNode(exclude=node)
698 a8083063 Iustin Pop
      try:
699 a8083063 Iustin Pop
        instance = RunTest(TestInstanceAddWithRemoteRaidDisk, node, node2)
700 a8083063 Iustin Pop
        RunTest(TestInstanceShutdown, instance)
701 a8083063 Iustin Pop
        RunTest(TestInstanceStartup, instance)
702 a8083063 Iustin Pop
703 e9e35aaa Michael Hanselmann
        if TestEnabled('instance-info'):
704 e9e35aaa Michael Hanselmann
          RunTest(TestInstanceInfo, instance)
705 e9e35aaa Michael Hanselmann
706 a8083063 Iustin Pop
        if TestEnabled('instance-failover'):
707 a8083063 Iustin Pop
          RunTest(TestInstanceFailover, instance)
708 a8083063 Iustin Pop
709 a8083063 Iustin Pop
        RunTest(TestInstanceRemove, instance)
710 a8083063 Iustin Pop
        del instance
711 a8083063 Iustin Pop
      finally:
712 a8083063 Iustin Pop
        ReleaseNode(node2)
713 a8083063 Iustin Pop
714 a8083063 Iustin Pop
  finally:
715 a8083063 Iustin Pop
    ReleaseNode(node)
716 a8083063 Iustin Pop
717 a8083063 Iustin Pop
  RunTest(TestNodeRemoveAll)
718 a8083063 Iustin Pop
719 a8083063 Iustin Pop
  if TestEnabled('cluster-destroy'):
720 a8083063 Iustin Pop
    RunTest(TestClusterDestroy)
721 a8083063 Iustin Pop
# }}}
722 a8083063 Iustin Pop
723 a8083063 Iustin Pop
# vim: foldmethod=marker :