Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ 1abbbbe2

History | View | Annotate | Download (7 kB)

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

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

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

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

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

94 c68d1f43 Michael Hanselmann
  Args:
95 c68d1f43 Michael Hanselmann
  - node: Node the command should run on
96 c68d1f43 Michael Hanselmann
  - cmd: Command to be executed as a list with all parameters
97 c68d1f43 Michael Hanselmann
  - strict: Whether to enable strict host key checking
98 c68d1f43 Michael Hanselmann

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

126 cec9845c Michael Hanselmann
  """
127 4b62db14 Michael Hanselmann
  return subprocess.Popen(GetSSHCommand(node, cmd, strict=strict),
128 4b62db14 Michael Hanselmann
                          shell=False)
129 4b62db14 Michael Hanselmann
130 4b62db14 Michael Hanselmann
131 4b62db14 Michael Hanselmann
def GetCommandOutput(node, cmd):
132 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
133 4b62db14 Michael Hanselmann

134 4b62db14 Michael Hanselmann
  """
135 4b62db14 Michael Hanselmann
  p = subprocess.Popen(GetSSHCommand(node, cmd),
136 4b62db14 Michael Hanselmann
                       shell=False, stdout=subprocess.PIPE)
137 4b62db14 Michael Hanselmann
  AssertEqual(p.wait(), 0)
138 4b62db14 Michael Hanselmann
  return p.stdout.read()
139 cec9845c Michael Hanselmann
140 cec9845c Michael Hanselmann
141 cec9845c Michael Hanselmann
def UploadFile(node, src):
142 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
143 cec9845c Michael Hanselmann

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

170 4b62db14 Michael Hanselmann
  """
171 4b62db14 Michael Hanselmann
  master = qa_config.GetMasterNode()
172 4b62db14 Michael Hanselmann
173 4b62db14 Michael Hanselmann
  output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
174 4b62db14 Michael Hanselmann
  for line in output.splitlines():
175 4b62db14 Michael Hanselmann
    (lkey, lvalue) = line.split(':', 1)
176 4b62db14 Michael Hanselmann
    if lkey == key:
177 4b62db14 Michael Hanselmann
      return lvalue.lstrip()
178 4b62db14 Michael Hanselmann
  raise KeyError("Key not found")
179 4b62db14 Michael Hanselmann
180 4b62db14 Michael Hanselmann
181 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
182 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
183 5d640672 Michael Hanselmann

184 5d640672 Michael Hanselmann
  """
185 e8ae0c20 Michael Hanselmann
  return _ResolveName(['gnt-instance', 'info', instance['name']],
186 4b62db14 Michael Hanselmann
                      'Instance name')
187 4b62db14 Michael Hanselmann
188 4b62db14 Michael Hanselmann
189 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
190 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
191 4b62db14 Michael Hanselmann

192 4b62db14 Michael Hanselmann
  """
193 4b62db14 Michael Hanselmann
  return _ResolveName(['gnt-node', 'info', node['primary']],
194 4b62db14 Michael Hanselmann
                      'Node name')
195 4b62db14 Michael Hanselmann
196 4b62db14 Michael Hanselmann
197 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
198 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
199 4b62db14 Michael Hanselmann

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

233 1672a0d1 Michael Hanselmann
  """
234 1672a0d1 Michael Hanselmann
  hooks_dir = qa_config.get('options', {}).get('hooks-dir', None)
235 1672a0d1 Michael Hanselmann
  if not hooks_dir:
236 1672a0d1 Michael Hanselmann
    return
237 1672a0d1 Michael Hanselmann
  if hooks_dir not in sys.path:
238 1672a0d1 Michael Hanselmann
    sys.path.insert(0, hooks_dir)
239 1672a0d1 Michael Hanselmann
  for name in utils.ListVisibleFiles(hooks_dir):
240 1672a0d1 Michael Hanselmann
    if name.endswith('.py'):
241 1672a0d1 Michael Hanselmann
      # Load and instanciate hook
242 f9fe750a Michael Hanselmann
      print "Loading hook %s" % name
243 1672a0d1 Michael Hanselmann
      _hooks.append(__import__(name[:-3], None, None, ['']).hook())
244 1672a0d1 Michael Hanselmann
245 1672a0d1 Michael Hanselmann
246 1672a0d1 Michael Hanselmann
class QaHookContext:
247 c68d1f43 Michael Hanselmann
  """Definition of context passed to hooks.
248 c68d1f43 Michael Hanselmann

249 c68d1f43 Michael Hanselmann
  """
250 1672a0d1 Michael Hanselmann
  name = None
251 1672a0d1 Michael Hanselmann
  phase = None
252 1672a0d1 Michael Hanselmann
  success = None
253 1672a0d1 Michael Hanselmann
  args = None
254 1672a0d1 Michael Hanselmann
  kwargs = None
255 1672a0d1 Michael Hanselmann
256 1672a0d1 Michael Hanselmann
257 1672a0d1 Michael Hanselmann
def _CallHooks(ctx):
258 1672a0d1 Michael Hanselmann
  """Calls all hooks with the given context.
259 1672a0d1 Michael Hanselmann

260 1672a0d1 Michael Hanselmann
  """
261 1672a0d1 Michael Hanselmann
  if not _hooks:
262 1672a0d1 Michael Hanselmann
    return
263 1672a0d1 Michael Hanselmann
264 1672a0d1 Michael Hanselmann
  name = "%s-%s" % (ctx.phase, ctx.name)
265 1672a0d1 Michael Hanselmann
  if ctx.success is not None:
266 1672a0d1 Michael Hanselmann
    msg = "%s (success=%s)" % (name, ctx.success)
267 1672a0d1 Michael Hanselmann
  else:
268 1672a0d1 Michael Hanselmann
    msg = name
269 1672a0d1 Michael Hanselmann
  print FormatInfo("Begin %s" % msg)
270 1672a0d1 Michael Hanselmann
  for hook in _hooks:
271 1672a0d1 Michael Hanselmann
    hook.run(ctx)
272 1672a0d1 Michael Hanselmann
  print FormatInfo("End %s" % name)
273 1672a0d1 Michael Hanselmann
274 1672a0d1 Michael Hanselmann
275 1672a0d1 Michael Hanselmann
def DefineHook(name):
276 1672a0d1 Michael Hanselmann
  """Wraps a function with calls to hooks.
277 1672a0d1 Michael Hanselmann

278 1672a0d1 Michael Hanselmann
  Usage: prefix function with @qa_utils.DefineHook(...)
279 1672a0d1 Michael Hanselmann

280 c68d1f43 Michael Hanselmann
  This is based on PEP 318, "Decorators for Functions and Methods".
281 abe3db55 Michael Hanselmann

282 1672a0d1 Michael Hanselmann
  """
283 1672a0d1 Michael Hanselmann
  def wrapper(fn):
284 1672a0d1 Michael Hanselmann
    def new_f(*args, **kwargs):
285 1672a0d1 Michael Hanselmann
      # Create context
286 1672a0d1 Michael Hanselmann
      ctx = QaHookContext()
287 1672a0d1 Michael Hanselmann
      ctx.name = name
288 1672a0d1 Michael Hanselmann
      ctx.phase = 'pre'
289 1672a0d1 Michael Hanselmann
      ctx.args = args
290 1672a0d1 Michael Hanselmann
      ctx.kwargs = kwargs
291 1672a0d1 Michael Hanselmann
292 1672a0d1 Michael Hanselmann
      _CallHooks(ctx)
293 1672a0d1 Michael Hanselmann
      try:
294 1672a0d1 Michael Hanselmann
        ctx.phase = 'post'
295 1672a0d1 Michael Hanselmann
        ctx.success = True
296 1672a0d1 Michael Hanselmann
        try:
297 1672a0d1 Michael Hanselmann
          # Call real function
298 1672a0d1 Michael Hanselmann
          return fn(*args, **kwargs)
299 1672a0d1 Michael Hanselmann
        except:
300 1672a0d1 Michael Hanselmann
          ctx.success = False
301 1672a0d1 Michael Hanselmann
          raise
302 1672a0d1 Michael Hanselmann
      finally:
303 1672a0d1 Michael Hanselmann
        _CallHooks(ctx)
304 1672a0d1 Michael Hanselmann
305 1672a0d1 Michael Hanselmann
    # Override function metadata
306 1672a0d1 Michael Hanselmann
    new_f.func_name = fn.func_name
307 1672a0d1 Michael Hanselmann
    new_f.func_doc = fn.func_doc
308 1672a0d1 Michael Hanselmann
309 1672a0d1 Michael Hanselmann
    return new_f
310 1672a0d1 Michael Hanselmann
311 1672a0d1 Michael Hanselmann
  return wrapper