Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ 9b3939ea

History | View | Annotate | Download (6.7 kB)

1 cec9845c Michael Hanselmann
# Copyright (C) 2007 Google Inc.
2 cec9845c Michael Hanselmann
#
3 cec9845c Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
4 cec9845c Michael Hanselmann
# it under the terms of the GNU General Public License as published by
5 cec9845c Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
6 cec9845c Michael Hanselmann
# (at your option) any later version.
7 cec9845c Michael Hanselmann
#
8 cec9845c Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
9 cec9845c Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
10 cec9845c Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 cec9845c Michael Hanselmann
# General Public License for more details.
12 cec9845c Michael Hanselmann
#
13 cec9845c Michael Hanselmann
# You should have received a copy of the GNU General Public License
14 cec9845c Michael Hanselmann
# along with this program; if not, write to the Free Software
15 cec9845c Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 cec9845c Michael Hanselmann
# 02110-1301, USA.
17 cec9845c Michael Hanselmann
18 cec9845c Michael Hanselmann
19 cec9845c Michael Hanselmann
"""Utilities for QA tests.
20 cec9845c Michael Hanselmann

21 cec9845c Michael Hanselmann
"""
22 cec9845c Michael Hanselmann
23 cec9845c Michael Hanselmann
import os
24 23269c5b Michael Hanselmann
import sys
25 cec9845c Michael Hanselmann
import subprocess
26 cec9845c Michael Hanselmann
27 cec9845c Michael Hanselmann
from ganeti import utils
28 cec9845c Michael Hanselmann
29 cec9845c Michael Hanselmann
import qa_config
30 cec9845c Michael Hanselmann
import qa_error
31 cec9845c Michael Hanselmann
32 cec9845c Michael Hanselmann
33 23269c5b Michael Hanselmann
_INFO_SEQ = None
34 23269c5b Michael Hanselmann
_WARNING_SEQ = None
35 23269c5b Michael Hanselmann
_ERROR_SEQ = None
36 23269c5b Michael Hanselmann
_RESET_SEQ = None
37 23269c5b Michael Hanselmann
38 23269c5b Michael Hanselmann
39 1672a0d1 Michael Hanselmann
# List of all hooks
40 1672a0d1 Michael Hanselmann
_hooks = []
41 1672a0d1 Michael Hanselmann
42 1672a0d1 Michael Hanselmann
43 23269c5b Michael Hanselmann
def _SetupColours():
44 23269c5b Michael Hanselmann
  """Initializes the colour constants.
45 23269c5b Michael Hanselmann

46 23269c5b Michael Hanselmann
  """
47 23269c5b Michael Hanselmann
  global _INFO_SEQ, _WARNING_SEQ, _ERROR_SEQ, _RESET_SEQ
48 23269c5b Michael Hanselmann
49 dfe11bad Michael Hanselmann
  # Don't use colours if stdout isn't a terminal
50 dfe11bad Michael Hanselmann
  if not sys.stdout.isatty():
51 dfe11bad Michael Hanselmann
    return
52 dfe11bad Michael Hanselmann
53 23269c5b Michael Hanselmann
  try:
54 23269c5b Michael Hanselmann
    import curses
55 23269c5b Michael Hanselmann
  except ImportError:
56 23269c5b Michael Hanselmann
    # Don't use colours if curses module can't be imported
57 23269c5b Michael Hanselmann
    return
58 23269c5b Michael Hanselmann
59 23269c5b Michael Hanselmann
  curses.setupterm()
60 23269c5b Michael Hanselmann
61 23269c5b Michael Hanselmann
  _RESET_SEQ = curses.tigetstr("op")
62 23269c5b Michael Hanselmann
63 23269c5b Michael Hanselmann
  setaf = curses.tigetstr("setaf")
64 23269c5b Michael Hanselmann
  _INFO_SEQ = curses.tparm(setaf, curses.COLOR_GREEN)
65 23269c5b Michael Hanselmann
  _WARNING_SEQ = curses.tparm(setaf, curses.COLOR_YELLOW)
66 23269c5b Michael Hanselmann
  _ERROR_SEQ = curses.tparm(setaf, curses.COLOR_RED)
67 23269c5b Michael Hanselmann
68 23269c5b Michael Hanselmann
69 23269c5b Michael Hanselmann
_SetupColours()
70 23269c5b Michael Hanselmann
71 23269c5b Michael Hanselmann
72 e8ae0c20 Michael Hanselmann
def AssertEqual(first, second):
73 cec9845c Michael Hanselmann
  """Raises an error when values aren't equal.
74 cec9845c Michael Hanselmann

75 cec9845c Michael Hanselmann
  """
76 cec9845c Michael Hanselmann
  if not first == second:
77 e8ae0c20 Michael Hanselmann
    raise qa_error.Error('%r == %r' % (first, second))
78 e8ae0c20 Michael Hanselmann
79 e8ae0c20 Michael Hanselmann
80 e8ae0c20 Michael Hanselmann
def AssertNotEqual(first, second):
81 e8ae0c20 Michael Hanselmann
  """Raises an error when values are equal.
82 e8ae0c20 Michael Hanselmann

83 e8ae0c20 Michael Hanselmann
  """
84 e8ae0c20 Michael Hanselmann
  if not first != second:
85 e8ae0c20 Michael Hanselmann
    raise qa_error.Error('%r != %r' % (first, second))
86 cec9845c Michael Hanselmann
87 cec9845c Michael Hanselmann
88 cec9845c Michael Hanselmann
def GetSSHCommand(node, cmd, strict=True):
89 cec9845c Michael Hanselmann
  """Builds SSH command to be executed.
90 cec9845c Michael Hanselmann

91 cec9845c Michael Hanselmann
  """
92 cec9845c Michael Hanselmann
  args = [ 'ssh', '-oEscapeChar=none', '-oBatchMode=yes', '-l', 'root' ]
93 cec9845c Michael Hanselmann
94 cec9845c Michael Hanselmann
  if strict:
95 cec9845c Michael Hanselmann
    tmp = 'yes'
96 cec9845c Michael Hanselmann
  else:
97 cec9845c Michael Hanselmann
    tmp = 'no'
98 cec9845c Michael Hanselmann
  args.append('-oStrictHostKeyChecking=%s' % tmp)
99 cec9845c Michael Hanselmann
  args.append('-oClearAllForwardings=yes')
100 cec9845c Michael Hanselmann
  args.append('-oForwardAgent=yes')
101 cec9845c Michael Hanselmann
  args.append(node)
102 cec9845c Michael Hanselmann
103 cec9845c Michael Hanselmann
  if qa_config.options.dry_run:
104 cec9845c Michael Hanselmann
    prefix = 'exit 0; '
105 cec9845c Michael Hanselmann
  else:
106 cec9845c Michael Hanselmann
    prefix = ''
107 cec9845c Michael Hanselmann
108 cec9845c Michael Hanselmann
  args.append(prefix + cmd)
109 cec9845c Michael Hanselmann
110 cec9845c Michael Hanselmann
  print 'SSH:', utils.ShellQuoteArgs(args)
111 cec9845c Michael Hanselmann
112 cec9845c Michael Hanselmann
  return args
113 cec9845c Michael Hanselmann
114 cec9845c Michael Hanselmann
115 cec9845c Michael Hanselmann
def StartSSH(node, cmd, strict=True):
116 cec9845c Michael Hanselmann
  """Starts SSH.
117 cec9845c Michael Hanselmann

118 cec9845c Michael Hanselmann
  """
119 4b62db14 Michael Hanselmann
  return subprocess.Popen(GetSSHCommand(node, cmd, strict=strict),
120 4b62db14 Michael Hanselmann
                          shell=False)
121 4b62db14 Michael Hanselmann
122 4b62db14 Michael Hanselmann
123 4b62db14 Michael Hanselmann
def GetCommandOutput(node, cmd):
124 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
125 4b62db14 Michael Hanselmann

126 4b62db14 Michael Hanselmann
  """
127 4b62db14 Michael Hanselmann
  p = subprocess.Popen(GetSSHCommand(node, cmd),
128 4b62db14 Michael Hanselmann
                       shell=False, stdout=subprocess.PIPE)
129 4b62db14 Michael Hanselmann
  AssertEqual(p.wait(), 0)
130 4b62db14 Michael Hanselmann
  return p.stdout.read()
131 cec9845c Michael Hanselmann
132 cec9845c Michael Hanselmann
133 cec9845c Michael Hanselmann
def UploadFile(node, src):
134 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
135 cec9845c Michael Hanselmann

136 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
137 cec9845c Michael Hanselmann
  anymore.
138 cec9845c Michael Hanselmann
  """
139 cec9845c Michael Hanselmann
  # Make sure nobody else has access to it while preserving local permissions
140 cec9845c Michael Hanselmann
  mode = os.stat(src).st_mode & 0700
141 cec9845c Michael Hanselmann
142 cec9845c Michael Hanselmann
  cmd = ('tmp=$(tempfile --mode %o --prefix gnt) && '
143 cec9845c Michael Hanselmann
         '[[ -f "${tmp}" ]] && '
144 cec9845c Michael Hanselmann
         'cat > "${tmp}" && '
145 cec9845c Michael Hanselmann
         'echo "${tmp}"') % mode
146 cec9845c Michael Hanselmann
147 cec9845c Michael Hanselmann
  f = open(src, 'r')
148 cec9845c Michael Hanselmann
  try:
149 cec9845c Michael Hanselmann
    p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
150 cec9845c Michael Hanselmann
                         stdout=subprocess.PIPE)
151 cec9845c Michael Hanselmann
    AssertEqual(p.wait(), 0)
152 cec9845c Michael Hanselmann
153 cec9845c Michael Hanselmann
    # Return temporary filename
154 cec9845c Michael Hanselmann
    return p.stdout.read().strip()
155 cec9845c Michael Hanselmann
  finally:
156 cec9845c Michael Hanselmann
    f.close()
157 5d640672 Michael Hanselmann
158 5d640672 Michael Hanselmann
159 4b62db14 Michael Hanselmann
def _ResolveName(cmd, key):
160 4b62db14 Michael Hanselmann
  """Helper function.
161 4b62db14 Michael Hanselmann

162 4b62db14 Michael Hanselmann
  """
163 4b62db14 Michael Hanselmann
  master = qa_config.GetMasterNode()
164 4b62db14 Michael Hanselmann
165 4b62db14 Michael Hanselmann
  output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
166 4b62db14 Michael Hanselmann
  for line in output.splitlines():
167 4b62db14 Michael Hanselmann
    (lkey, lvalue) = line.split(':', 1)
168 4b62db14 Michael Hanselmann
    if lkey == key:
169 4b62db14 Michael Hanselmann
      return lvalue.lstrip()
170 4b62db14 Michael Hanselmann
  raise KeyError("Key not found")
171 4b62db14 Michael Hanselmann
172 4b62db14 Michael Hanselmann
173 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
174 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
175 5d640672 Michael Hanselmann

176 5d640672 Michael Hanselmann
  """
177 e8ae0c20 Michael Hanselmann
  return _ResolveName(['gnt-instance', 'info', instance['name']],
178 4b62db14 Michael Hanselmann
                      'Instance name')
179 4b62db14 Michael Hanselmann
180 4b62db14 Michael Hanselmann
181 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
182 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
183 4b62db14 Michael Hanselmann

184 4b62db14 Michael Hanselmann
  """
185 4b62db14 Michael Hanselmann
  return _ResolveName(['gnt-node', 'info', node['primary']],
186 4b62db14 Michael Hanselmann
                      'Node name')
187 4b62db14 Michael Hanselmann
188 4b62db14 Michael Hanselmann
189 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
190 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
191 4b62db14 Michael Hanselmann

192 4b62db14 Michael Hanselmann
  """
193 5d640672 Michael Hanselmann
  master = qa_config.GetMasterNode()
194 5d640672 Michael Hanselmann
195 4b62db14 Michael Hanselmann
  node_name = ResolveNodeName(node)
196 5d640672 Michael Hanselmann
197 4b62db14 Michael Hanselmann
  # Get list of all instances
198 4b62db14 Michael Hanselmann
  cmd = ['gnt-instance', 'list', '--separator=:', '--no-headers',
199 4b62db14 Michael Hanselmann
         '--output=name,pnode,snodes']
200 4b62db14 Michael Hanselmann
  output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
201 4b62db14 Michael Hanselmann
202 4b62db14 Michael Hanselmann
  instances = []
203 4b62db14 Michael Hanselmann
  for line in output.splitlines():
204 4b62db14 Michael Hanselmann
    (name, pnode, snodes) = line.split(':', 2)
205 4b62db14 Michael Hanselmann
    if ((not secondaries and pnode == node_name) or
206 4b62db14 Michael Hanselmann
        (secondaries and node_name in snodes.split(','))):
207 4b62db14 Michael Hanselmann
      instances.append(name)
208 5d640672 Michael Hanselmann
209 4b62db14 Michael Hanselmann
  return instances
210 23269c5b Michael Hanselmann
211 23269c5b Michael Hanselmann
212 dfe11bad Michael Hanselmann
def _FormatWithColor(text, seq):
213 dfe11bad Michael Hanselmann
  if not seq:
214 dfe11bad Michael Hanselmann
    return text
215 dfe11bad Michael Hanselmann
  return "%s%s%s" % (seq, text, _RESET_SEQ)
216 23269c5b Michael Hanselmann
217 23269c5b Michael Hanselmann
218 dfe11bad Michael Hanselmann
FormatWarning = lambda text: _FormatWithColor(text, _WARNING_SEQ)
219 dfe11bad Michael Hanselmann
FormatError = lambda text: _FormatWithColor(text, _ERROR_SEQ)
220 dfe11bad Michael Hanselmann
FormatInfo = lambda text: _FormatWithColor(text, _INFO_SEQ)
221 1672a0d1 Michael Hanselmann
222 1672a0d1 Michael Hanselmann
223 1672a0d1 Michael Hanselmann
def LoadHooks():
224 1672a0d1 Michael Hanselmann
  """Load all QA hooks.
225 1672a0d1 Michael Hanselmann

226 1672a0d1 Michael Hanselmann
  """
227 1672a0d1 Michael Hanselmann
  hooks_dir = qa_config.get('options', {}).get('hooks-dir', None)
228 1672a0d1 Michael Hanselmann
  if not hooks_dir:
229 1672a0d1 Michael Hanselmann
    return
230 1672a0d1 Michael Hanselmann
  if hooks_dir not in sys.path:
231 1672a0d1 Michael Hanselmann
    sys.path.insert(0, hooks_dir)
232 1672a0d1 Michael Hanselmann
  for name in utils.ListVisibleFiles(hooks_dir):
233 1672a0d1 Michael Hanselmann
    if name.endswith('.py'):
234 1672a0d1 Michael Hanselmann
      # Load and instanciate hook
235 f9fe750a Michael Hanselmann
      print "Loading hook %s" % name
236 1672a0d1 Michael Hanselmann
      _hooks.append(__import__(name[:-3], None, None, ['']).hook())
237 1672a0d1 Michael Hanselmann
238 1672a0d1 Michael Hanselmann
239 1672a0d1 Michael Hanselmann
class QaHookContext:
240 1672a0d1 Michael Hanselmann
  name = None
241 1672a0d1 Michael Hanselmann
  phase = None
242 1672a0d1 Michael Hanselmann
  success = None
243 1672a0d1 Michael Hanselmann
  args = None
244 1672a0d1 Michael Hanselmann
  kwargs = None
245 1672a0d1 Michael Hanselmann
246 1672a0d1 Michael Hanselmann
247 1672a0d1 Michael Hanselmann
def _CallHooks(ctx):
248 1672a0d1 Michael Hanselmann
  """Calls all hooks with the given context.
249 1672a0d1 Michael Hanselmann

250 1672a0d1 Michael Hanselmann
  """
251 1672a0d1 Michael Hanselmann
  if not _hooks:
252 1672a0d1 Michael Hanselmann
    return
253 1672a0d1 Michael Hanselmann
254 1672a0d1 Michael Hanselmann
  name = "%s-%s" % (ctx.phase, ctx.name)
255 1672a0d1 Michael Hanselmann
  if ctx.success is not None:
256 1672a0d1 Michael Hanselmann
    msg = "%s (success=%s)" % (name, ctx.success)
257 1672a0d1 Michael Hanselmann
  else:
258 1672a0d1 Michael Hanselmann
    msg = name
259 1672a0d1 Michael Hanselmann
  print FormatInfo("Begin %s" % msg)
260 1672a0d1 Michael Hanselmann
  for hook in _hooks:
261 1672a0d1 Michael Hanselmann
    hook.run(ctx)
262 1672a0d1 Michael Hanselmann
  print FormatInfo("End %s" % name)
263 1672a0d1 Michael Hanselmann
264 1672a0d1 Michael Hanselmann
265 1672a0d1 Michael Hanselmann
def DefineHook(name):
266 1672a0d1 Michael Hanselmann
  """Wraps a function with calls to hooks.
267 1672a0d1 Michael Hanselmann

268 1672a0d1 Michael Hanselmann
  Usage: prefix function with @qa_utils.DefineHook(...)
269 1672a0d1 Michael Hanselmann

270 1672a0d1 Michael Hanselmann
  """
271 1672a0d1 Michael Hanselmann
  def wrapper(fn):
272 1672a0d1 Michael Hanselmann
    def new_f(*args, **kwargs):
273 1672a0d1 Michael Hanselmann
      # Create context
274 1672a0d1 Michael Hanselmann
      ctx = QaHookContext()
275 1672a0d1 Michael Hanselmann
      ctx.name = name
276 1672a0d1 Michael Hanselmann
      ctx.phase = 'pre'
277 1672a0d1 Michael Hanselmann
      ctx.args = args
278 1672a0d1 Michael Hanselmann
      ctx.kwargs = kwargs
279 1672a0d1 Michael Hanselmann
280 1672a0d1 Michael Hanselmann
      _CallHooks(ctx)
281 1672a0d1 Michael Hanselmann
      try:
282 1672a0d1 Michael Hanselmann
        ctx.phase = 'post'
283 1672a0d1 Michael Hanselmann
        ctx.success = True
284 1672a0d1 Michael Hanselmann
        try:
285 1672a0d1 Michael Hanselmann
          # Call real function
286 1672a0d1 Michael Hanselmann
          return fn(*args, **kwargs)
287 1672a0d1 Michael Hanselmann
        except:
288 1672a0d1 Michael Hanselmann
          ctx.success = False
289 1672a0d1 Michael Hanselmann
          raise
290 1672a0d1 Michael Hanselmann
      finally:
291 1672a0d1 Michael Hanselmann
        _CallHooks(ctx)
292 1672a0d1 Michael Hanselmann
293 1672a0d1 Michael Hanselmann
    # Override function metadata
294 1672a0d1 Michael Hanselmann
    new_f.func_name = fn.func_name
295 1672a0d1 Michael Hanselmann
    new_f.func_doc = fn.func_doc
296 1672a0d1 Michael Hanselmann
297 1672a0d1 Michael Hanselmann
    return new_f
298 1672a0d1 Michael Hanselmann
299 1672a0d1 Michael Hanselmann
  return wrapper