Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ e37f47d3

History | View | Annotate | Download (16.9 kB)

1 c68d1f43 Michael Hanselmann
#
2 c68d1f43 Michael Hanselmann
#
3 c68d1f43 Michael Hanselmann
4 710bc88c Iustin Pop
# Copyright (C) 2007, 2011, 2012 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 e6ce18ac René Nussbaumer
import re
28 23269c5b Michael Hanselmann
import sys
29 cec9845c Michael Hanselmann
import subprocess
30 288d6440 Michael Hanselmann
import random
31 f7e6f3c8 Iustin Pop
import tempfile
32 cec9845c Michael Hanselmann
33 c9e05005 Michael Hanselmann
try:
34 c9e05005 Michael Hanselmann
  import functools
35 c9e05005 Michael Hanselmann
except ImportError, err:
36 c9e05005 Michael Hanselmann
  raise ImportError("Python 2.5 or higher is required: %s" % err)
37 c9e05005 Michael Hanselmann
38 cec9845c Michael Hanselmann
from ganeti import utils
39 288d6440 Michael Hanselmann
from ganeti import compat
40 2214cf14 Michael Hanselmann
from ganeti import constants
41 c9e05005 Michael Hanselmann
from ganeti import ht
42 48967eb0 Michael Hanselmann
from ganeti import pathutils
43 cec9845c Michael Hanselmann
44 cec9845c Michael Hanselmann
import qa_config
45 cec9845c Michael Hanselmann
import qa_error
46 cec9845c Michael Hanselmann
47 cec9845c Michael Hanselmann
48 23269c5b Michael Hanselmann
_INFO_SEQ = None
49 23269c5b Michael Hanselmann
_WARNING_SEQ = None
50 23269c5b Michael Hanselmann
_ERROR_SEQ = None
51 23269c5b Michael Hanselmann
_RESET_SEQ = None
52 23269c5b Michael Hanselmann
53 f7e6f3c8 Iustin Pop
_MULTIPLEXERS = {}
54 f7e6f3c8 Iustin Pop
55 c9e05005 Michael Hanselmann
#: Unique ID per QA run
56 c9e05005 Michael Hanselmann
_RUN_UUID = utils.NewUUID()
57 c9e05005 Michael Hanselmann
58 c9e05005 Michael Hanselmann
59 c9e05005 Michael Hanselmann
(INST_DOWN,
60 c9e05005 Michael Hanselmann
 INST_UP) = range(500, 502)
61 c9e05005 Michael Hanselmann
62 c9e05005 Michael Hanselmann
(FIRST_ARG,
63 c9e05005 Michael Hanselmann
 RETURN_VALUE) = range(1000, 1002)
64 c9e05005 Michael Hanselmann
65 23269c5b Michael Hanselmann
66 23269c5b Michael Hanselmann
def _SetupColours():
67 23269c5b Michael Hanselmann
  """Initializes the colour constants.
68 23269c5b Michael Hanselmann

69 23269c5b Michael Hanselmann
  """
70 b459a848 Andrea Spadaccini
  # pylint: disable=W0603
71 3582eef6 Iustin Pop
  # due to global usage
72 23269c5b Michael Hanselmann
  global _INFO_SEQ, _WARNING_SEQ, _ERROR_SEQ, _RESET_SEQ
73 23269c5b Michael Hanselmann
74 dfe11bad Michael Hanselmann
  # Don't use colours if stdout isn't a terminal
75 dfe11bad Michael Hanselmann
  if not sys.stdout.isatty():
76 dfe11bad Michael Hanselmann
    return
77 dfe11bad Michael Hanselmann
78 23269c5b Michael Hanselmann
  try:
79 23269c5b Michael Hanselmann
    import curses
80 23269c5b Michael Hanselmann
  except ImportError:
81 23269c5b Michael Hanselmann
    # Don't use colours if curses module can't be imported
82 23269c5b Michael Hanselmann
    return
83 23269c5b Michael Hanselmann
84 23269c5b Michael Hanselmann
  curses.setupterm()
85 23269c5b Michael Hanselmann
86 23269c5b Michael Hanselmann
  _RESET_SEQ = curses.tigetstr("op")
87 23269c5b Michael Hanselmann
88 23269c5b Michael Hanselmann
  setaf = curses.tigetstr("setaf")
89 23269c5b Michael Hanselmann
  _INFO_SEQ = curses.tparm(setaf, curses.COLOR_GREEN)
90 23269c5b Michael Hanselmann
  _WARNING_SEQ = curses.tparm(setaf, curses.COLOR_YELLOW)
91 23269c5b Michael Hanselmann
  _ERROR_SEQ = curses.tparm(setaf, curses.COLOR_RED)
92 23269c5b Michael Hanselmann
93 23269c5b Michael Hanselmann
94 23269c5b Michael Hanselmann
_SetupColours()
95 23269c5b Michael Hanselmann
96 23269c5b Michael Hanselmann
97 eaef8a05 Michael Hanselmann
def AssertIn(item, sequence):
98 eaef8a05 Michael Hanselmann
  """Raises an error when item is not in sequence.
99 eaef8a05 Michael Hanselmann

100 eaef8a05 Michael Hanselmann
  """
101 eaef8a05 Michael Hanselmann
  if item not in sequence:
102 d0c8c01d Iustin Pop
    raise qa_error.Error("%r not in %r" % (item, sequence))
103 eaef8a05 Michael Hanselmann
104 eaef8a05 Michael Hanselmann
105 79eac09b Michael Hanselmann
def AssertNotIn(item, sequence):
106 79eac09b Michael Hanselmann
  """Raises an error when item is in sequence.
107 79eac09b Michael Hanselmann

108 79eac09b Michael Hanselmann
  """
109 79eac09b Michael Hanselmann
  if item in sequence:
110 d0c8c01d Iustin Pop
    raise qa_error.Error("%r in %r" % (item, sequence))
111 79eac09b Michael Hanselmann
112 79eac09b Michael Hanselmann
113 e8ae0c20 Michael Hanselmann
def AssertEqual(first, second):
114 cec9845c Michael Hanselmann
  """Raises an error when values aren't equal.
115 cec9845c Michael Hanselmann

116 cec9845c Michael Hanselmann
  """
117 cec9845c Michael Hanselmann
  if not first == second:
118 d0c8c01d Iustin Pop
    raise qa_error.Error("%r == %r" % (first, second))
119 e8ae0c20 Michael Hanselmann
120 e8ae0c20 Michael Hanselmann
121 e8ae0c20 Michael Hanselmann
def AssertNotEqual(first, second):
122 e8ae0c20 Michael Hanselmann
  """Raises an error when values are equal.
123 e8ae0c20 Michael Hanselmann

124 e8ae0c20 Michael Hanselmann
  """
125 e8ae0c20 Michael Hanselmann
  if not first != second:
126 d0c8c01d Iustin Pop
    raise qa_error.Error("%r != %r" % (first, second))
127 cec9845c Michael Hanselmann
128 cec9845c Michael Hanselmann
129 e6ce18ac René Nussbaumer
def AssertMatch(string, pattern):
130 e6ce18ac René Nussbaumer
  """Raises an error when string doesn't match regexp pattern.
131 e6ce18ac René Nussbaumer

132 e6ce18ac René Nussbaumer
  """
133 e6ce18ac René Nussbaumer
  if not re.match(pattern, string):
134 e6ce18ac René Nussbaumer
    raise qa_error.Error("%r doesn't match /%r/" % (string, pattern))
135 e6ce18ac René Nussbaumer
136 e6ce18ac René Nussbaumer
137 889bed16 Michael Hanselmann
def _GetName(entity, key):
138 889bed16 Michael Hanselmann
  """Tries to get name of an entity.
139 889bed16 Michael Hanselmann

140 889bed16 Michael Hanselmann
  @type entity: string or dict
141 889bed16 Michael Hanselmann
  @type key: string
142 889bed16 Michael Hanselmann
  @param key: Dictionary key containing name
143 889bed16 Michael Hanselmann

144 889bed16 Michael Hanselmann
  """
145 889bed16 Michael Hanselmann
  if isinstance(entity, basestring):
146 889bed16 Michael Hanselmann
    result = entity
147 889bed16 Michael Hanselmann
  elif isinstance(entity, dict):
148 889bed16 Michael Hanselmann
    result = entity[key]
149 889bed16 Michael Hanselmann
  else:
150 889bed16 Michael Hanselmann
    raise qa_error.Error("Expected string or dictionary, got %s: %s" %
151 889bed16 Michael Hanselmann
                         (type(entity), entity))
152 889bed16 Michael Hanselmann
153 889bed16 Michael Hanselmann
  if not ht.TNonEmptyString(result):
154 889bed16 Michael Hanselmann
    raise Exception("Invalid name '%s'" % result)
155 889bed16 Michael Hanselmann
156 889bed16 Michael Hanselmann
  return result
157 889bed16 Michael Hanselmann
158 889bed16 Michael Hanselmann
159 2f4b4f78 Iustin Pop
def AssertCommand(cmd, fail=False, node=None):
160 2f4b4f78 Iustin Pop
  """Checks that a remote command succeeds.
161 2f4b4f78 Iustin Pop

162 2f4b4f78 Iustin Pop
  @param cmd: either a string (the command to execute) or a list (to
163 2f4b4f78 Iustin Pop
      be converted using L{utils.ShellQuoteArgs} into a string)
164 2f4b4f78 Iustin Pop
  @type fail: boolean
165 2f4b4f78 Iustin Pop
  @param fail: if the command is expected to fail instead of succeeding
166 2f4b4f78 Iustin Pop
  @param node: if passed, it should be the node on which the command
167 2f4b4f78 Iustin Pop
      should be executed, instead of the master node (can be either a
168 2f4b4f78 Iustin Pop
      dict or a string)
169 2f4b4f78 Iustin Pop

170 2f4b4f78 Iustin Pop
  """
171 2f4b4f78 Iustin Pop
  if node is None:
172 2f4b4f78 Iustin Pop
    node = qa_config.GetMasterNode()
173 2f4b4f78 Iustin Pop
174 889bed16 Michael Hanselmann
  nodename = _GetName(node, "primary")
175 2f4b4f78 Iustin Pop
176 2f4b4f78 Iustin Pop
  if isinstance(cmd, basestring):
177 2f4b4f78 Iustin Pop
    cmdstr = cmd
178 2f4b4f78 Iustin Pop
  else:
179 2f4b4f78 Iustin Pop
    cmdstr = utils.ShellQuoteArgs(cmd)
180 2f4b4f78 Iustin Pop
181 2f4b4f78 Iustin Pop
  rcode = StartSSH(nodename, cmdstr).wait()
182 2f4b4f78 Iustin Pop
183 2f4b4f78 Iustin Pop
  if fail:
184 2f4b4f78 Iustin Pop
    if rcode == 0:
185 2f4b4f78 Iustin Pop
      raise qa_error.Error("Command '%s' on node %s was expected to fail but"
186 2f4b4f78 Iustin Pop
                           " didn't" % (cmdstr, nodename))
187 2f4b4f78 Iustin Pop
  else:
188 2f4b4f78 Iustin Pop
    if rcode != 0:
189 2f4b4f78 Iustin Pop
      raise qa_error.Error("Command '%s' on node %s failed, exit code %s" %
190 2f4b4f78 Iustin Pop
                           (cmdstr, nodename, rcode))
191 2f4b4f78 Iustin Pop
192 2214cf14 Michael Hanselmann
  return rcode
193 2214cf14 Michael Hanselmann
194 2f4b4f78 Iustin Pop
195 f14a8b15 Iustin Pop
def GetSSHCommand(node, cmd, strict=True, opts=None, tty=None):
196 cec9845c Michael Hanselmann
  """Builds SSH command to be executed.
197 cec9845c Michael Hanselmann

198 0a05f959 Adeodato Simo
  @type node: string
199 0a05f959 Adeodato Simo
  @param node: node the command should run on
200 0a05f959 Adeodato Simo
  @type cmd: string
201 f7e6f3c8 Iustin Pop
  @param cmd: command to be executed in the node; if None or empty
202 f7e6f3c8 Iustin Pop
      string, no command will be executed
203 0a05f959 Adeodato Simo
  @type strict: boolean
204 0a05f959 Adeodato Simo
  @param strict: whether to enable strict host key checking
205 f7e6f3c8 Iustin Pop
  @type opts: list
206 f7e6f3c8 Iustin Pop
  @param opts: list of additional options
207 f14a8b15 Iustin Pop
  @type tty: boolean or None
208 f14a8b15 Iustin Pop
  @param tty: if we should use tty; if None, will be auto-detected
209 c68d1f43 Michael Hanselmann

210 cec9845c Michael Hanselmann
  """
211 710bc88c Iustin Pop
  args = ["ssh", "-oEscapeChar=none", "-oBatchMode=yes", "-lroot"]
212 50265802 René Nussbaumer
213 f14a8b15 Iustin Pop
  if tty is None:
214 f14a8b15 Iustin Pop
    tty = sys.stdout.isatty()
215 f14a8b15 Iustin Pop
216 50265802 René Nussbaumer
  if tty:
217 50265802 René Nussbaumer
    args.append("-t")
218 cec9845c Michael Hanselmann
219 cec9845c Michael Hanselmann
  if strict:
220 d0c8c01d Iustin Pop
    tmp = "yes"
221 cec9845c Michael Hanselmann
  else:
222 d0c8c01d Iustin Pop
    tmp = "no"
223 d0c8c01d Iustin Pop
  args.append("-oStrictHostKeyChecking=%s" % tmp)
224 d0c8c01d Iustin Pop
  args.append("-oClearAllForwardings=yes")
225 d0c8c01d Iustin Pop
  args.append("-oForwardAgent=yes")
226 f7e6f3c8 Iustin Pop
  if opts:
227 f7e6f3c8 Iustin Pop
    args.extend(opts)
228 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
229 f7e6f3c8 Iustin Pop
    spath = _MULTIPLEXERS[node][0]
230 d0c8c01d Iustin Pop
    args.append("-oControlPath=%s" % spath)
231 d0c8c01d Iustin Pop
    args.append("-oControlMaster=no")
232 cec9845c Michael Hanselmann
  args.append(node)
233 f7e6f3c8 Iustin Pop
  if cmd:
234 f7e6f3c8 Iustin Pop
    args.append(cmd)
235 cec9845c Michael Hanselmann
236 cec9845c Michael Hanselmann
  return args
237 cec9845c Michael Hanselmann
238 cec9845c Michael Hanselmann
239 710bc88c Iustin Pop
def StartLocalCommand(cmd, _nolog_opts=False, **kwargs):
240 5d831182 Michael Hanselmann
  """Starts a local command.
241 5d831182 Michael Hanselmann

242 5d831182 Michael Hanselmann
  """
243 710bc88c Iustin Pop
  if _nolog_opts:
244 710bc88c Iustin Pop
    pcmd = [i for i in cmd if not i.startswith("-")]
245 710bc88c Iustin Pop
  else:
246 710bc88c Iustin Pop
    pcmd = cmd
247 710bc88c Iustin Pop
  print "Command: %s" % utils.ShellQuoteArgs(pcmd)
248 5d831182 Michael Hanselmann
  return subprocess.Popen(cmd, shell=False, **kwargs)
249 5d831182 Michael Hanselmann
250 5d831182 Michael Hanselmann
251 cec9845c Michael Hanselmann
def StartSSH(node, cmd, strict=True):
252 cec9845c Michael Hanselmann
  """Starts SSH.
253 cec9845c Michael Hanselmann

254 cec9845c Michael Hanselmann
  """
255 710bc88c Iustin Pop
  return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict),
256 710bc88c Iustin Pop
                           _nolog_opts=True)
257 4b62db14 Michael Hanselmann
258 4b62db14 Michael Hanselmann
259 f7e6f3c8 Iustin Pop
def StartMultiplexer(node):
260 f7e6f3c8 Iustin Pop
  """Starts a multiplexer command.
261 f7e6f3c8 Iustin Pop

262 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
263 f7e6f3c8 Iustin Pop

264 f7e6f3c8 Iustin Pop
  """
265 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
266 f7e6f3c8 Iustin Pop
    return
267 f7e6f3c8 Iustin Pop
268 f7e6f3c8 Iustin Pop
  # Note: yes, we only need mktemp, since we'll remove the file anyway
269 f7e6f3c8 Iustin Pop
  sname = tempfile.mktemp(prefix="ganeti-qa-multiplexer.")
270 f7e6f3c8 Iustin Pop
  utils.RemoveFile(sname)
271 f7e6f3c8 Iustin Pop
  opts = ["-N", "-oControlPath=%s" % sname, "-oControlMaster=yes"]
272 f7e6f3c8 Iustin Pop
  print "Created socket at %s" % sname
273 f7e6f3c8 Iustin Pop
  child = StartLocalCommand(GetSSHCommand(node, None, opts=opts))
274 f7e6f3c8 Iustin Pop
  _MULTIPLEXERS[node] = (sname, child)
275 f7e6f3c8 Iustin Pop
276 f7e6f3c8 Iustin Pop
277 f7e6f3c8 Iustin Pop
def CloseMultiplexers():
278 f7e6f3c8 Iustin Pop
  """Closes all current multiplexers and cleans up.
279 f7e6f3c8 Iustin Pop

280 f7e6f3c8 Iustin Pop
  """
281 f7e6f3c8 Iustin Pop
  for node in _MULTIPLEXERS.keys():
282 f7e6f3c8 Iustin Pop
    (sname, child) = _MULTIPLEXERS.pop(node)
283 f7e6f3c8 Iustin Pop
    utils.KillProcess(child.pid, timeout=10, waitpid=True)
284 f7e6f3c8 Iustin Pop
    utils.RemoveFile(sname)
285 f7e6f3c8 Iustin Pop
286 f7e6f3c8 Iustin Pop
287 f14a8b15 Iustin Pop
def GetCommandOutput(node, cmd, tty=None):
288 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
289 4b62db14 Michael Hanselmann

290 4b62db14 Michael Hanselmann
  """
291 50265802 René Nussbaumer
  p = StartLocalCommand(GetSSHCommand(node, cmd, tty=tty),
292 50265802 René Nussbaumer
                        stdout=subprocess.PIPE)
293 4b62db14 Michael Hanselmann
  AssertEqual(p.wait(), 0)
294 4b62db14 Michael Hanselmann
  return p.stdout.read()
295 cec9845c Michael Hanselmann
296 cec9845c Michael Hanselmann
297 cec9845c Michael Hanselmann
def UploadFile(node, src):
298 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
299 cec9845c Michael Hanselmann

300 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
301 cec9845c Michael Hanselmann
  anymore.
302 49d50e52 Michael Hanselmann

303 cec9845c Michael Hanselmann
  """
304 cec9845c Michael Hanselmann
  # Make sure nobody else has access to it while preserving local permissions
305 cec9845c Michael Hanselmann
  mode = os.stat(src).st_mode & 0700
306 cec9845c Michael Hanselmann
307 cec9845c Michael Hanselmann
  cmd = ('tmp=$(tempfile --mode %o --prefix gnt) && '
308 cec9845c Michael Hanselmann
         '[[ -f "${tmp}" ]] && '
309 cec9845c Michael Hanselmann
         'cat > "${tmp}" && '
310 cec9845c Michael Hanselmann
         'echo "${tmp}"') % mode
311 cec9845c Michael Hanselmann
312 d0c8c01d Iustin Pop
  f = open(src, "r")
313 cec9845c Michael Hanselmann
  try:
314 cec9845c Michael Hanselmann
    p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
315 cec9845c Michael Hanselmann
                         stdout=subprocess.PIPE)
316 cec9845c Michael Hanselmann
    AssertEqual(p.wait(), 0)
317 cec9845c Michael Hanselmann
318 cec9845c Michael Hanselmann
    # Return temporary filename
319 cec9845c Michael Hanselmann
    return p.stdout.read().strip()
320 cec9845c Michael Hanselmann
  finally:
321 cec9845c Michael Hanselmann
    f.close()
322 5d640672 Michael Hanselmann
323 5d640672 Michael Hanselmann
324 b9955569 René Nussbaumer
def UploadData(node, data, mode=0600, filename=None):
325 b9955569 René Nussbaumer
  """Uploads data to a node and returns the filename.
326 b9955569 René Nussbaumer

327 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
328 b9955569 René Nussbaumer
  anymore.
329 b9955569 René Nussbaumer

330 b9955569 René Nussbaumer
  """
331 b9955569 René Nussbaumer
  if filename:
332 b9955569 René Nussbaumer
    tmp = "tmp=%s" % utils.ShellQuote(filename)
333 b9955569 René Nussbaumer
  else:
334 b9955569 René Nussbaumer
    tmp = "tmp=$(tempfile --mode %o --prefix gnt)" % mode
335 b9955569 René Nussbaumer
  cmd = ("%s && "
336 b9955569 René Nussbaumer
         "[[ -f \"${tmp}\" ]] && "
337 b9955569 René Nussbaumer
         "cat > \"${tmp}\" && "
338 b9955569 René Nussbaumer
         "echo \"${tmp}\"") % tmp
339 b9955569 René Nussbaumer
340 b9955569 René Nussbaumer
  p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False,
341 b9955569 René Nussbaumer
                       stdin=subprocess.PIPE, stdout=subprocess.PIPE)
342 b9955569 René Nussbaumer
  p.stdin.write(data)
343 b9955569 René Nussbaumer
  p.stdin.close()
344 b9955569 René Nussbaumer
  AssertEqual(p.wait(), 0)
345 b9955569 René Nussbaumer
346 b9955569 René Nussbaumer
  # Return temporary filename
347 b9955569 René Nussbaumer
  return p.stdout.read().strip()
348 b9955569 René Nussbaumer
349 b9955569 René Nussbaumer
350 49d50e52 Michael Hanselmann
def BackupFile(node, path):
351 49d50e52 Michael Hanselmann
  """Creates a backup of a file on the node and returns the filename.
352 49d50e52 Michael Hanselmann

353 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
354 49d50e52 Michael Hanselmann
  anymore.
355 49d50e52 Michael Hanselmann

356 49d50e52 Michael Hanselmann
  """
357 49d50e52 Michael Hanselmann
  cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
358 49d50e52 Michael Hanselmann
         "[[ -f \"$tmp\" ]] && "
359 49d50e52 Michael Hanselmann
         "cp %s $tmp && "
360 49d50e52 Michael Hanselmann
         "echo $tmp") % (utils.ShellQuote(path), utils.ShellQuote(path))
361 49d50e52 Michael Hanselmann
362 49d50e52 Michael Hanselmann
  # Return temporary filename
363 49d50e52 Michael Hanselmann
  return GetCommandOutput(node, cmd).strip()
364 49d50e52 Michael Hanselmann
365 49d50e52 Michael Hanselmann
366 4b62db14 Michael Hanselmann
def _ResolveName(cmd, key):
367 4b62db14 Michael Hanselmann
  """Helper function.
368 4b62db14 Michael Hanselmann

369 4b62db14 Michael Hanselmann
  """
370 4b62db14 Michael Hanselmann
  master = qa_config.GetMasterNode()
371 4b62db14 Michael Hanselmann
372 d0c8c01d Iustin Pop
  output = GetCommandOutput(master["primary"], utils.ShellQuoteArgs(cmd))
373 4b62db14 Michael Hanselmann
  for line in output.splitlines():
374 d0c8c01d Iustin Pop
    (lkey, lvalue) = line.split(":", 1)
375 4b62db14 Michael Hanselmann
    if lkey == key:
376 4b62db14 Michael Hanselmann
      return lvalue.lstrip()
377 4b62db14 Michael Hanselmann
  raise KeyError("Key not found")
378 4b62db14 Michael Hanselmann
379 4b62db14 Michael Hanselmann
380 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
381 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
382 5d640672 Michael Hanselmann

383 46f9a948 Michael Hanselmann
  @type instance: string
384 46f9a948 Michael Hanselmann
  @param instance: Instance name
385 46f9a948 Michael Hanselmann

386 5d640672 Michael Hanselmann
  """
387 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-instance", "info", instance],
388 d0c8c01d Iustin Pop
                      "Instance name")
389 4b62db14 Michael Hanselmann
390 4b62db14 Michael Hanselmann
391 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
392 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
393 4b62db14 Michael Hanselmann

394 4b62db14 Michael Hanselmann
  """
395 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-node", "info", node["primary"]],
396 d0c8c01d Iustin Pop
                      "Node name")
397 4b62db14 Michael Hanselmann
398 4b62db14 Michael Hanselmann
399 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
400 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
401 4b62db14 Michael Hanselmann

402 4b62db14 Michael Hanselmann
  """
403 5d640672 Michael Hanselmann
  master = qa_config.GetMasterNode()
404 4b62db14 Michael Hanselmann
  node_name = ResolveNodeName(node)
405 5d640672 Michael Hanselmann
406 4b62db14 Michael Hanselmann
  # Get list of all instances
407 d0c8c01d Iustin Pop
  cmd = ["gnt-instance", "list", "--separator=:", "--no-headers",
408 d0c8c01d Iustin Pop
         "--output=name,pnode,snodes"]
409 d0c8c01d Iustin Pop
  output = GetCommandOutput(master["primary"], utils.ShellQuoteArgs(cmd))
410 4b62db14 Michael Hanselmann
411 4b62db14 Michael Hanselmann
  instances = []
412 4b62db14 Michael Hanselmann
  for line in output.splitlines():
413 d0c8c01d Iustin Pop
    (name, pnode, snodes) = line.split(":", 2)
414 4b62db14 Michael Hanselmann
    if ((not secondaries and pnode == node_name) or
415 d0c8c01d Iustin Pop
        (secondaries and node_name in snodes.split(","))):
416 4b62db14 Michael Hanselmann
      instances.append(name)
417 5d640672 Michael Hanselmann
418 4b62db14 Michael Hanselmann
  return instances
419 23269c5b Michael Hanselmann
420 23269c5b Michael Hanselmann
421 288d6440 Michael Hanselmann
def _SelectQueryFields(rnd, fields):
422 288d6440 Michael Hanselmann
  """Generates a list of fields for query tests.
423 288d6440 Michael Hanselmann

424 288d6440 Michael Hanselmann
  """
425 288d6440 Michael Hanselmann
  # Create copy for shuffling
426 288d6440 Michael Hanselmann
  fields = list(fields)
427 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
428 288d6440 Michael Hanselmann
429 288d6440 Michael Hanselmann
  # Check all fields
430 288d6440 Michael Hanselmann
  yield fields
431 288d6440 Michael Hanselmann
  yield sorted(fields)
432 288d6440 Michael Hanselmann
433 288d6440 Michael Hanselmann
  # Duplicate fields
434 288d6440 Michael Hanselmann
  yield fields + fields
435 288d6440 Michael Hanselmann
436 288d6440 Michael Hanselmann
  # Check small groups of fields
437 288d6440 Michael Hanselmann
  while fields:
438 288d6440 Michael Hanselmann
    yield [fields.pop() for _ in range(rnd.randint(2, 10)) if fields]
439 288d6440 Michael Hanselmann
440 288d6440 Michael Hanselmann
441 288d6440 Michael Hanselmann
def _List(listcmd, fields, names):
442 288d6440 Michael Hanselmann
  """Runs a list command.
443 288d6440 Michael Hanselmann

444 288d6440 Michael Hanselmann
  """
445 288d6440 Michael Hanselmann
  master = qa_config.GetMasterNode()
446 288d6440 Michael Hanselmann
447 58ea8d17 Michael Hanselmann
  cmd = [listcmd, "list", "--separator=|", "--no-headers",
448 288d6440 Michael Hanselmann
         "--output", ",".join(fields)]
449 288d6440 Michael Hanselmann
450 288d6440 Michael Hanselmann
  if names:
451 288d6440 Michael Hanselmann
    cmd.extend(names)
452 288d6440 Michael Hanselmann
453 288d6440 Michael Hanselmann
  return GetCommandOutput(master["primary"],
454 288d6440 Michael Hanselmann
                          utils.ShellQuoteArgs(cmd)).splitlines()
455 288d6440 Michael Hanselmann
456 288d6440 Michael Hanselmann
457 6d1e4845 Michael Hanselmann
def GenericQueryTest(cmd, fields, namefield="name", test_unknown=True):
458 288d6440 Michael Hanselmann
  """Runs a number of tests on query commands.
459 288d6440 Michael Hanselmann

460 288d6440 Michael Hanselmann
  @param cmd: Command name
461 288d6440 Michael Hanselmann
  @param fields: List of field names
462 288d6440 Michael Hanselmann

463 288d6440 Michael Hanselmann
  """
464 288d6440 Michael Hanselmann
  rnd = random.Random(hash(cmd))
465 288d6440 Michael Hanselmann
466 3582eef6 Iustin Pop
  fields = list(fields)
467 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
468 288d6440 Michael Hanselmann
469 288d6440 Michael Hanselmann
  # Test a number of field combinations
470 288d6440 Michael Hanselmann
  for testfields in _SelectQueryFields(rnd, fields):
471 288d6440 Michael Hanselmann
    AssertCommand([cmd, "list", "--output", ",".join(testfields)])
472 288d6440 Michael Hanselmann
473 0fdf247d Michael Hanselmann
  if namefield is not None:
474 0fdf247d Michael Hanselmann
    namelist_fn = compat.partial(_List, cmd, [namefield])
475 288d6440 Michael Hanselmann
476 0fdf247d Michael Hanselmann
    # When no names were requested, the list must be sorted
477 0fdf247d Michael Hanselmann
    names = namelist_fn(None)
478 0fdf247d Michael Hanselmann
    AssertEqual(names, utils.NiceSort(names))
479 288d6440 Michael Hanselmann
480 0fdf247d Michael Hanselmann
    # When requesting specific names, the order must be kept
481 0fdf247d Michael Hanselmann
    revnames = list(reversed(names))
482 0fdf247d Michael Hanselmann
    AssertEqual(namelist_fn(revnames), revnames)
483 288d6440 Michael Hanselmann
484 0fdf247d Michael Hanselmann
    randnames = list(names)
485 0fdf247d Michael Hanselmann
    rnd.shuffle(randnames)
486 0fdf247d Michael Hanselmann
    AssertEqual(namelist_fn(randnames), randnames)
487 288d6440 Michael Hanselmann
488 6d1e4845 Michael Hanselmann
  if test_unknown:
489 6d1e4845 Michael Hanselmann
    # Listing unknown items must fail
490 6d1e4845 Michael Hanselmann
    AssertCommand([cmd, "list", "this.name.certainly.does.not.exist"],
491 6d1e4845 Michael Hanselmann
                  fail=True)
492 2214cf14 Michael Hanselmann
493 2214cf14 Michael Hanselmann
  # Check exit code for listing unknown field
494 2214cf14 Michael Hanselmann
  AssertEqual(AssertCommand([cmd, "list", "--output=field/does/not/exist"],
495 2214cf14 Michael Hanselmann
                            fail=True),
496 2214cf14 Michael Hanselmann
              constants.EXIT_UNKNOWN_FIELD)
497 2214cf14 Michael Hanselmann
498 2214cf14 Michael Hanselmann
499 2214cf14 Michael Hanselmann
def GenericQueryFieldsTest(cmd, fields):
500 2214cf14 Michael Hanselmann
  master = qa_config.GetMasterNode()
501 2214cf14 Michael Hanselmann
502 2214cf14 Michael Hanselmann
  # Listing fields
503 2214cf14 Michael Hanselmann
  AssertCommand([cmd, "list-fields"])
504 2214cf14 Michael Hanselmann
  AssertCommand([cmd, "list-fields"] + fields)
505 2214cf14 Michael Hanselmann
506 2214cf14 Michael Hanselmann
  # Check listed fields (all, must be sorted)
507 2214cf14 Michael Hanselmann
  realcmd = [cmd, "list-fields", "--separator=|", "--no-headers"]
508 2214cf14 Michael Hanselmann
  output = GetCommandOutput(master["primary"],
509 2214cf14 Michael Hanselmann
                            utils.ShellQuoteArgs(realcmd)).splitlines()
510 2214cf14 Michael Hanselmann
  AssertEqual([line.split("|", 1)[0] for line in output],
511 c694367b Michael Hanselmann
              utils.NiceSort(fields))
512 2214cf14 Michael Hanselmann
513 2214cf14 Michael Hanselmann
  # Check exit code for listing unknown field
514 2214cf14 Michael Hanselmann
  AssertEqual(AssertCommand([cmd, "list-fields", "field/does/not/exist"],
515 2214cf14 Michael Hanselmann
                            fail=True),
516 2214cf14 Michael Hanselmann
              constants.EXIT_UNKNOWN_FIELD)
517 2214cf14 Michael Hanselmann
518 288d6440 Michael Hanselmann
519 dfe11bad Michael Hanselmann
def _FormatWithColor(text, seq):
520 dfe11bad Michael Hanselmann
  if not seq:
521 dfe11bad Michael Hanselmann
    return text
522 dfe11bad Michael Hanselmann
  return "%s%s%s" % (seq, text, _RESET_SEQ)
523 23269c5b Michael Hanselmann
524 23269c5b Michael Hanselmann
525 dfe11bad Michael Hanselmann
FormatWarning = lambda text: _FormatWithColor(text, _WARNING_SEQ)
526 dfe11bad Michael Hanselmann
FormatError = lambda text: _FormatWithColor(text, _ERROR_SEQ)
527 dfe11bad Michael Hanselmann
FormatInfo = lambda text: _FormatWithColor(text, _INFO_SEQ)
528 31fe5102 René Nussbaumer
529 31fe5102 René Nussbaumer
530 31fe5102 René Nussbaumer
def AddToEtcHosts(hostnames):
531 31fe5102 René Nussbaumer
  """Adds hostnames to /etc/hosts.
532 31fe5102 René Nussbaumer

533 31fe5102 René Nussbaumer
  @param hostnames: List of hostnames first used A records, all other CNAMEs
534 31fe5102 René Nussbaumer

535 31fe5102 René Nussbaumer
  """
536 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
537 31fe5102 René Nussbaumer
  tmp_hosts = UploadData(master["primary"], "", mode=0644)
538 31fe5102 René Nussbaumer
539 31fe5102 René Nussbaumer
  data = []
540 31fe5102 René Nussbaumer
  for localhost in ("::1", "127.0.0.1"):
541 31fe5102 René Nussbaumer
    data.append("%s %s" % (localhost, " ".join(hostnames)))
542 31fe5102 René Nussbaumer
543 31fe5102 René Nussbaumer
  try:
544 48967eb0 Michael Hanselmann
    AssertCommand("{ cat %s && echo -e '%s'; } > %s && mv %s %s" %
545 48967eb0 Michael Hanselmann
                  (utils.ShellQuote(pathutils.ETC_HOSTS),
546 48967eb0 Michael Hanselmann
                   "\\n".join(data),
547 48967eb0 Michael Hanselmann
                   utils.ShellQuote(tmp_hosts),
548 48967eb0 Michael Hanselmann
                   utils.ShellQuote(tmp_hosts),
549 48967eb0 Michael Hanselmann
                   utils.ShellQuote(pathutils.ETC_HOSTS)))
550 48967eb0 Michael Hanselmann
  except Exception:
551 48967eb0 Michael Hanselmann
    AssertCommand(["rm", "-f", tmp_hosts])
552 48967eb0 Michael Hanselmann
    raise
553 31fe5102 René Nussbaumer
554 31fe5102 René Nussbaumer
555 31fe5102 René Nussbaumer
def RemoveFromEtcHosts(hostnames):
556 31fe5102 René Nussbaumer
  """Remove hostnames from /etc/hosts.
557 31fe5102 René Nussbaumer

558 31fe5102 René Nussbaumer
  @param hostnames: List of hostnames first used A records, all other CNAMEs
559 31fe5102 René Nussbaumer

560 31fe5102 René Nussbaumer
  """
561 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
562 31fe5102 René Nussbaumer
  tmp_hosts = UploadData(master["primary"], "", mode=0644)
563 31fe5102 René Nussbaumer
  quoted_tmp_hosts = utils.ShellQuote(tmp_hosts)
564 31fe5102 René Nussbaumer
565 31fe5102 René Nussbaumer
  sed_data = " ".join(hostnames)
566 31fe5102 René Nussbaumer
  try:
567 48967eb0 Michael Hanselmann
    AssertCommand(("sed -e '/^\(::1\|127\.0\.0\.1\)\s\+%s/d' %s > %s"
568 48967eb0 Michael Hanselmann
                   " && mv %s %s") %
569 48967eb0 Michael Hanselmann
                   (sed_data, utils.ShellQuote(pathutils.ETC_HOSTS),
570 48967eb0 Michael Hanselmann
                    quoted_tmp_hosts, quoted_tmp_hosts,
571 48967eb0 Michael Hanselmann
                    utils.ShellQuote(pathutils.ETC_HOSTS)))
572 48967eb0 Michael Hanselmann
  except Exception:
573 48967eb0 Michael Hanselmann
    AssertCommand(["rm", "-f", tmp_hosts])
574 48967eb0 Michael Hanselmann
    raise
575 c9e05005 Michael Hanselmann
576 c9e05005 Michael Hanselmann
577 c9e05005 Michael Hanselmann
def RunInstanceCheck(instance, running):
578 c9e05005 Michael Hanselmann
  """Check if instance is running or not.
579 c9e05005 Michael Hanselmann

580 c9e05005 Michael Hanselmann
  """
581 889bed16 Michael Hanselmann
  instance_name = _GetName(instance, "name")
582 2ac35588 Michael Hanselmann
583 c9e05005 Michael Hanselmann
  script = qa_config.GetInstanceCheckScript()
584 c9e05005 Michael Hanselmann
  if not script:
585 c9e05005 Michael Hanselmann
    return
586 c9e05005 Michael Hanselmann
587 c9e05005 Michael Hanselmann
  master_node = qa_config.GetMasterNode()
588 c9e05005 Michael Hanselmann
589 c9e05005 Michael Hanselmann
  # Build command to connect to master node
590 c9e05005 Michael Hanselmann
  master_ssh = GetSSHCommand(master_node["primary"], "--")
591 c9e05005 Michael Hanselmann
592 c9e05005 Michael Hanselmann
  if running:
593 c9e05005 Michael Hanselmann
    running_shellval = "1"
594 c9e05005 Michael Hanselmann
    running_text = ""
595 c9e05005 Michael Hanselmann
  else:
596 c9e05005 Michael Hanselmann
    running_shellval = ""
597 c9e05005 Michael Hanselmann
    running_text = "not "
598 c9e05005 Michael Hanselmann
599 c9e05005 Michael Hanselmann
  print FormatInfo("Checking if instance '%s' is %srunning" %
600 c9e05005 Michael Hanselmann
                   (instance_name, running_text))
601 c9e05005 Michael Hanselmann
602 c9e05005 Michael Hanselmann
  args = [script, instance_name]
603 c9e05005 Michael Hanselmann
  env = {
604 c9e05005 Michael Hanselmann
    "PATH": constants.HOOKS_PATH,
605 c9e05005 Michael Hanselmann
    "RUN_UUID": _RUN_UUID,
606 c9e05005 Michael Hanselmann
    "MASTER_SSH": utils.ShellQuoteArgs(master_ssh),
607 c9e05005 Michael Hanselmann
    "INSTANCE_NAME": instance_name,
608 c9e05005 Michael Hanselmann
    "INSTANCE_RUNNING": running_shellval,
609 c9e05005 Michael Hanselmann
    }
610 c9e05005 Michael Hanselmann
611 c9e05005 Michael Hanselmann
  result = os.spawnve(os.P_WAIT, script, args, env)
612 c9e05005 Michael Hanselmann
  if result != 0:
613 c9e05005 Michael Hanselmann
    raise qa_error.Error("Instance check failed with result %s" % result)
614 c9e05005 Michael Hanselmann
615 c9e05005 Michael Hanselmann
616 c9e05005 Michael Hanselmann
def _InstanceCheckInner(expected, instarg, args, result):
617 c9e05005 Michael Hanselmann
  """Helper function used by L{InstanceCheck}.
618 c9e05005 Michael Hanselmann

619 c9e05005 Michael Hanselmann
  """
620 c9e05005 Michael Hanselmann
  if instarg == FIRST_ARG:
621 c9e05005 Michael Hanselmann
    instance = args[0]
622 c9e05005 Michael Hanselmann
  elif instarg == RETURN_VALUE:
623 c9e05005 Michael Hanselmann
    instance = result
624 c9e05005 Michael Hanselmann
  else:
625 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s' for instance argument" % instarg)
626 c9e05005 Michael Hanselmann
627 c9e05005 Michael Hanselmann
  if expected in (INST_DOWN, INST_UP):
628 c9e05005 Michael Hanselmann
    RunInstanceCheck(instance, (expected == INST_UP))
629 c9e05005 Michael Hanselmann
  elif expected is not None:
630 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s'" % expected)
631 c9e05005 Michael Hanselmann
632 c9e05005 Michael Hanselmann
633 c9e05005 Michael Hanselmann
def InstanceCheck(before, after, instarg):
634 c9e05005 Michael Hanselmann
  """Decorator to check instance status before and after test.
635 c9e05005 Michael Hanselmann

636 c9e05005 Michael Hanselmann
  @param before: L{INST_DOWN} if instance must be stopped before test,
637 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running before test, L{None} to not check.
638 c9e05005 Michael Hanselmann
  @param after: L{INST_DOWN} if instance must be stopped after test,
639 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running after test, L{None} to not check.
640 c9e05005 Michael Hanselmann
  @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
641 c9e05005 Michael Hanselmann
    dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
642 c9e05005 Michael Hanselmann

643 c9e05005 Michael Hanselmann
  """
644 c9e05005 Michael Hanselmann
  def decorator(fn):
645 c9e05005 Michael Hanselmann
    @functools.wraps(fn)
646 c9e05005 Michael Hanselmann
    def wrapper(*args, **kwargs):
647 c9e05005 Michael Hanselmann
      _InstanceCheckInner(before, instarg, args, NotImplemented)
648 c9e05005 Michael Hanselmann
649 c9e05005 Michael Hanselmann
      result = fn(*args, **kwargs)
650 c9e05005 Michael Hanselmann
651 c9e05005 Michael Hanselmann
      _InstanceCheckInner(after, instarg, args, result)
652 c9e05005 Michael Hanselmann
653 c9e05005 Michael Hanselmann
      return result
654 c9e05005 Michael Hanselmann
    return wrapper
655 c9e05005 Michael Hanselmann
  return decorator