Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ 946e553b

History | View | Annotate | Download (16.8 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 cec9845c Michael Hanselmann
43 cec9845c Michael Hanselmann
import qa_config
44 cec9845c Michael Hanselmann
import qa_error
45 cec9845c Michael Hanselmann
46 cec9845c Michael Hanselmann
47 23269c5b Michael Hanselmann
_INFO_SEQ = None
48 23269c5b Michael Hanselmann
_WARNING_SEQ = None
49 23269c5b Michael Hanselmann
_ERROR_SEQ = None
50 23269c5b Michael Hanselmann
_RESET_SEQ = None
51 23269c5b Michael Hanselmann
52 f7e6f3c8 Iustin Pop
_MULTIPLEXERS = {}
53 f7e6f3c8 Iustin Pop
54 c9e05005 Michael Hanselmann
#: Unique ID per QA run
55 c9e05005 Michael Hanselmann
_RUN_UUID = utils.NewUUID()
56 c9e05005 Michael Hanselmann
57 c9e05005 Michael Hanselmann
58 c9e05005 Michael Hanselmann
(INST_DOWN,
59 c9e05005 Michael Hanselmann
 INST_UP) = range(500, 502)
60 c9e05005 Michael Hanselmann
61 c9e05005 Michael Hanselmann
(FIRST_ARG,
62 c9e05005 Michael Hanselmann
 RETURN_VALUE) = range(1000, 1002)
63 c9e05005 Michael Hanselmann
64 23269c5b Michael Hanselmann
65 23269c5b Michael Hanselmann
def _SetupColours():
66 23269c5b Michael Hanselmann
  """Initializes the colour constants.
67 23269c5b Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

534 31fe5102 René Nussbaumer
  """
535 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
536 31fe5102 René Nussbaumer
  tmp_hosts = UploadData(master["primary"], "", mode=0644)
537 31fe5102 René Nussbaumer
538 31fe5102 René Nussbaumer
  quoted_tmp_hosts = utils.ShellQuote(tmp_hosts)
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 31fe5102 René Nussbaumer
    AssertCommand(("cat /etc/hosts > %s && echo -e '%s' >> %s && mv %s"
545 31fe5102 René Nussbaumer
                   " /etc/hosts") % (quoted_tmp_hosts, "\\n".join(data),
546 31fe5102 René Nussbaumer
                                     quoted_tmp_hosts, quoted_tmp_hosts))
547 31fe5102 René Nussbaumer
  except qa_error.Error:
548 31fe5102 René Nussbaumer
    AssertCommand(["rm", tmp_hosts])
549 31fe5102 René Nussbaumer
550 31fe5102 René Nussbaumer
551 31fe5102 René Nussbaumer
def RemoveFromEtcHosts(hostnames):
552 31fe5102 René Nussbaumer
  """Remove hostnames from /etc/hosts.
553 31fe5102 René Nussbaumer

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

556 31fe5102 René Nussbaumer
  """
557 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
558 31fe5102 René Nussbaumer
  tmp_hosts = UploadData(master["primary"], "", mode=0644)
559 31fe5102 René Nussbaumer
  quoted_tmp_hosts = utils.ShellQuote(tmp_hosts)
560 31fe5102 René Nussbaumer
561 31fe5102 René Nussbaumer
  sed_data = " ".join(hostnames)
562 31fe5102 René Nussbaumer
  try:
563 31fe5102 René Nussbaumer
    AssertCommand(("sed -e '/^\(::1\|127\.0\.0\.1\)\s\+%s/d' /etc/hosts > %s"
564 31fe5102 René Nussbaumer
                   " && mv %s /etc/hosts") % (sed_data, quoted_tmp_hosts,
565 31fe5102 René Nussbaumer
                                              quoted_tmp_hosts))
566 31fe5102 René Nussbaumer
  except qa_error.Error:
567 31fe5102 René Nussbaumer
    AssertCommand(["rm", tmp_hosts])
568 c9e05005 Michael Hanselmann
569 c9e05005 Michael Hanselmann
570 c9e05005 Michael Hanselmann
def RunInstanceCheck(instance, running):
571 c9e05005 Michael Hanselmann
  """Check if instance is running or not.
572 c9e05005 Michael Hanselmann

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

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

629 c9e05005 Michael Hanselmann
  @param before: L{INST_DOWN} if instance must be stopped before test,
630 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running before test, L{None} to not check.
631 c9e05005 Michael Hanselmann
  @param after: L{INST_DOWN} if instance must be stopped after test,
632 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running after test, L{None} to not check.
633 c9e05005 Michael Hanselmann
  @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
634 c9e05005 Michael Hanselmann
    dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
635 c9e05005 Michael Hanselmann

636 c9e05005 Michael Hanselmann
  """
637 c9e05005 Michael Hanselmann
  def decorator(fn):
638 c9e05005 Michael Hanselmann
    @functools.wraps(fn)
639 c9e05005 Michael Hanselmann
    def wrapper(*args, **kwargs):
640 c9e05005 Michael Hanselmann
      _InstanceCheckInner(before, instarg, args, NotImplemented)
641 c9e05005 Michael Hanselmann
642 c9e05005 Michael Hanselmann
      result = fn(*args, **kwargs)
643 c9e05005 Michael Hanselmann
644 c9e05005 Michael Hanselmann
      _InstanceCheckInner(after, instarg, args, result)
645 c9e05005 Michael Hanselmann
646 c9e05005 Michael Hanselmann
      return result
647 c9e05005 Michael Hanselmann
    return wrapper
648 c9e05005 Michael Hanselmann
  return decorator