Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ 2ac35588

History | View | Annotate | Download (16.2 kB)

1 c68d1f43 Michael Hanselmann
#
2 c68d1f43 Michael Hanselmann
#
3 c68d1f43 Michael Hanselmann
4 f7e6f3c8 Iustin Pop
# Copyright (C) 2007, 2011 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 2f4b4f78 Iustin Pop
def AssertCommand(cmd, fail=False, node=None):
137 2f4b4f78 Iustin Pop
  """Checks that a remote command succeeds.
138 2f4b4f78 Iustin Pop

139 2f4b4f78 Iustin Pop
  @param cmd: either a string (the command to execute) or a list (to
140 2f4b4f78 Iustin Pop
      be converted using L{utils.ShellQuoteArgs} into a string)
141 2f4b4f78 Iustin Pop
  @type fail: boolean
142 2f4b4f78 Iustin Pop
  @param fail: if the command is expected to fail instead of succeeding
143 2f4b4f78 Iustin Pop
  @param node: if passed, it should be the node on which the command
144 2f4b4f78 Iustin Pop
      should be executed, instead of the master node (can be either a
145 2f4b4f78 Iustin Pop
      dict or a string)
146 2f4b4f78 Iustin Pop

147 2f4b4f78 Iustin Pop
  """
148 2f4b4f78 Iustin Pop
  if node is None:
149 2f4b4f78 Iustin Pop
    node = qa_config.GetMasterNode()
150 2f4b4f78 Iustin Pop
151 2f4b4f78 Iustin Pop
  if isinstance(node, basestring):
152 2f4b4f78 Iustin Pop
    nodename = node
153 2f4b4f78 Iustin Pop
  else:
154 2f4b4f78 Iustin Pop
    nodename = node["primary"]
155 2f4b4f78 Iustin Pop
156 2f4b4f78 Iustin Pop
  if isinstance(cmd, basestring):
157 2f4b4f78 Iustin Pop
    cmdstr = cmd
158 2f4b4f78 Iustin Pop
  else:
159 2f4b4f78 Iustin Pop
    cmdstr = utils.ShellQuoteArgs(cmd)
160 2f4b4f78 Iustin Pop
161 2f4b4f78 Iustin Pop
  rcode = StartSSH(nodename, cmdstr).wait()
162 2f4b4f78 Iustin Pop
163 2f4b4f78 Iustin Pop
  if fail:
164 2f4b4f78 Iustin Pop
    if rcode == 0:
165 2f4b4f78 Iustin Pop
      raise qa_error.Error("Command '%s' on node %s was expected to fail but"
166 2f4b4f78 Iustin Pop
                           " didn't" % (cmdstr, nodename))
167 2f4b4f78 Iustin Pop
  else:
168 2f4b4f78 Iustin Pop
    if rcode != 0:
169 2f4b4f78 Iustin Pop
      raise qa_error.Error("Command '%s' on node %s failed, exit code %s" %
170 2f4b4f78 Iustin Pop
                           (cmdstr, nodename, rcode))
171 2f4b4f78 Iustin Pop
172 2214cf14 Michael Hanselmann
  return rcode
173 2214cf14 Michael Hanselmann
174 2f4b4f78 Iustin Pop
175 50265802 René Nussbaumer
def GetSSHCommand(node, cmd, strict=True, opts=None, tty=True):
176 cec9845c Michael Hanselmann
  """Builds SSH command to be executed.
177 cec9845c Michael Hanselmann

178 0a05f959 Adeodato Simo
  @type node: string
179 0a05f959 Adeodato Simo
  @param node: node the command should run on
180 0a05f959 Adeodato Simo
  @type cmd: string
181 f7e6f3c8 Iustin Pop
  @param cmd: command to be executed in the node; if None or empty
182 f7e6f3c8 Iustin Pop
      string, no command will be executed
183 0a05f959 Adeodato Simo
  @type strict: boolean
184 0a05f959 Adeodato Simo
  @param strict: whether to enable strict host key checking
185 f7e6f3c8 Iustin Pop
  @type opts: list
186 f7e6f3c8 Iustin Pop
  @param opts: list of additional options
187 50265802 René Nussbaumer
  @type tty: Bool
188 50265802 René Nussbaumer
  @param tty: If we should use tty
189 c68d1f43 Michael Hanselmann

190 cec9845c Michael Hanselmann
  """
191 50265802 René Nussbaumer
  args = ["ssh", "-oEscapeChar=none", "-oBatchMode=yes", "-l", "root"]
192 50265802 René Nussbaumer
193 50265802 René Nussbaumer
  if tty:
194 50265802 René Nussbaumer
    args.append("-t")
195 cec9845c Michael Hanselmann
196 cec9845c Michael Hanselmann
  if strict:
197 d0c8c01d Iustin Pop
    tmp = "yes"
198 cec9845c Michael Hanselmann
  else:
199 d0c8c01d Iustin Pop
    tmp = "no"
200 d0c8c01d Iustin Pop
  args.append("-oStrictHostKeyChecking=%s" % tmp)
201 d0c8c01d Iustin Pop
  args.append("-oClearAllForwardings=yes")
202 d0c8c01d Iustin Pop
  args.append("-oForwardAgent=yes")
203 f7e6f3c8 Iustin Pop
  if opts:
204 f7e6f3c8 Iustin Pop
    args.extend(opts)
205 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
206 f7e6f3c8 Iustin Pop
    spath = _MULTIPLEXERS[node][0]
207 d0c8c01d Iustin Pop
    args.append("-oControlPath=%s" % spath)
208 d0c8c01d Iustin Pop
    args.append("-oControlMaster=no")
209 cec9845c Michael Hanselmann
  args.append(node)
210 f7e6f3c8 Iustin Pop
  if cmd:
211 f7e6f3c8 Iustin Pop
    args.append(cmd)
212 cec9845c Michael Hanselmann
213 cec9845c Michael Hanselmann
  return args
214 cec9845c Michael Hanselmann
215 cec9845c Michael Hanselmann
216 5d831182 Michael Hanselmann
def StartLocalCommand(cmd, **kwargs):
217 5d831182 Michael Hanselmann
  """Starts a local command.
218 5d831182 Michael Hanselmann

219 5d831182 Michael Hanselmann
  """
220 5d831182 Michael Hanselmann
  print "Command: %s" % utils.ShellQuoteArgs(cmd)
221 5d831182 Michael Hanselmann
  return subprocess.Popen(cmd, shell=False, **kwargs)
222 5d831182 Michael Hanselmann
223 5d831182 Michael Hanselmann
224 cec9845c Michael Hanselmann
def StartSSH(node, cmd, strict=True):
225 cec9845c Michael Hanselmann
  """Starts SSH.
226 cec9845c Michael Hanselmann

227 cec9845c Michael Hanselmann
  """
228 5d831182 Michael Hanselmann
  return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict))
229 4b62db14 Michael Hanselmann
230 4b62db14 Michael Hanselmann
231 f7e6f3c8 Iustin Pop
def StartMultiplexer(node):
232 f7e6f3c8 Iustin Pop
  """Starts a multiplexer command.
233 f7e6f3c8 Iustin Pop

234 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
235 f7e6f3c8 Iustin Pop

236 f7e6f3c8 Iustin Pop
  """
237 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
238 f7e6f3c8 Iustin Pop
    return
239 f7e6f3c8 Iustin Pop
240 f7e6f3c8 Iustin Pop
  # Note: yes, we only need mktemp, since we'll remove the file anyway
241 f7e6f3c8 Iustin Pop
  sname = tempfile.mktemp(prefix="ganeti-qa-multiplexer.")
242 f7e6f3c8 Iustin Pop
  utils.RemoveFile(sname)
243 f7e6f3c8 Iustin Pop
  opts = ["-N", "-oControlPath=%s" % sname, "-oControlMaster=yes"]
244 f7e6f3c8 Iustin Pop
  print "Created socket at %s" % sname
245 f7e6f3c8 Iustin Pop
  child = StartLocalCommand(GetSSHCommand(node, None, opts=opts))
246 f7e6f3c8 Iustin Pop
  _MULTIPLEXERS[node] = (sname, child)
247 f7e6f3c8 Iustin Pop
248 f7e6f3c8 Iustin Pop
249 f7e6f3c8 Iustin Pop
def CloseMultiplexers():
250 f7e6f3c8 Iustin Pop
  """Closes all current multiplexers and cleans up.
251 f7e6f3c8 Iustin Pop

252 f7e6f3c8 Iustin Pop
  """
253 f7e6f3c8 Iustin Pop
  for node in _MULTIPLEXERS.keys():
254 f7e6f3c8 Iustin Pop
    (sname, child) = _MULTIPLEXERS.pop(node)
255 f7e6f3c8 Iustin Pop
    utils.KillProcess(child.pid, timeout=10, waitpid=True)
256 f7e6f3c8 Iustin Pop
    utils.RemoveFile(sname)
257 f7e6f3c8 Iustin Pop
258 f7e6f3c8 Iustin Pop
259 50265802 René Nussbaumer
def GetCommandOutput(node, cmd, tty=True):
260 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
261 4b62db14 Michael Hanselmann

262 4b62db14 Michael Hanselmann
  """
263 50265802 René Nussbaumer
  p = StartLocalCommand(GetSSHCommand(node, cmd, tty=tty),
264 50265802 René Nussbaumer
                        stdout=subprocess.PIPE)
265 4b62db14 Michael Hanselmann
  AssertEqual(p.wait(), 0)
266 4b62db14 Michael Hanselmann
  return p.stdout.read()
267 cec9845c Michael Hanselmann
268 cec9845c Michael Hanselmann
269 cec9845c Michael Hanselmann
def UploadFile(node, src):
270 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
271 cec9845c Michael Hanselmann

272 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
273 cec9845c Michael Hanselmann
  anymore.
274 49d50e52 Michael Hanselmann

275 cec9845c Michael Hanselmann
  """
276 cec9845c Michael Hanselmann
  # Make sure nobody else has access to it while preserving local permissions
277 cec9845c Michael Hanselmann
  mode = os.stat(src).st_mode & 0700
278 cec9845c Michael Hanselmann
279 cec9845c Michael Hanselmann
  cmd = ('tmp=$(tempfile --mode %o --prefix gnt) && '
280 cec9845c Michael Hanselmann
         '[[ -f "${tmp}" ]] && '
281 cec9845c Michael Hanselmann
         'cat > "${tmp}" && '
282 cec9845c Michael Hanselmann
         'echo "${tmp}"') % mode
283 cec9845c Michael Hanselmann
284 d0c8c01d Iustin Pop
  f = open(src, "r")
285 cec9845c Michael Hanselmann
  try:
286 cec9845c Michael Hanselmann
    p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
287 cec9845c Michael Hanselmann
                         stdout=subprocess.PIPE)
288 cec9845c Michael Hanselmann
    AssertEqual(p.wait(), 0)
289 cec9845c Michael Hanselmann
290 cec9845c Michael Hanselmann
    # Return temporary filename
291 cec9845c Michael Hanselmann
    return p.stdout.read().strip()
292 cec9845c Michael Hanselmann
  finally:
293 cec9845c Michael Hanselmann
    f.close()
294 5d640672 Michael Hanselmann
295 5d640672 Michael Hanselmann
296 b9955569 René Nussbaumer
def UploadData(node, data, mode=0600, filename=None):
297 b9955569 René Nussbaumer
  """Uploads data to a node and returns the filename.
298 b9955569 René Nussbaumer

299 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
300 b9955569 René Nussbaumer
  anymore.
301 b9955569 René Nussbaumer

302 b9955569 René Nussbaumer
  """
303 b9955569 René Nussbaumer
  if filename:
304 b9955569 René Nussbaumer
    tmp = "tmp=%s" % utils.ShellQuote(filename)
305 b9955569 René Nussbaumer
  else:
306 b9955569 René Nussbaumer
    tmp = "tmp=$(tempfile --mode %o --prefix gnt)" % mode
307 b9955569 René Nussbaumer
  cmd = ("%s && "
308 b9955569 René Nussbaumer
         "[[ -f \"${tmp}\" ]] && "
309 b9955569 René Nussbaumer
         "cat > \"${tmp}\" && "
310 b9955569 René Nussbaumer
         "echo \"${tmp}\"") % tmp
311 b9955569 René Nussbaumer
312 b9955569 René Nussbaumer
  p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False,
313 b9955569 René Nussbaumer
                       stdin=subprocess.PIPE, stdout=subprocess.PIPE)
314 b9955569 René Nussbaumer
  p.stdin.write(data)
315 b9955569 René Nussbaumer
  p.stdin.close()
316 b9955569 René Nussbaumer
  AssertEqual(p.wait(), 0)
317 b9955569 René Nussbaumer
318 b9955569 René Nussbaumer
  # Return temporary filename
319 b9955569 René Nussbaumer
  return p.stdout.read().strip()
320 b9955569 René Nussbaumer
321 b9955569 René Nussbaumer
322 49d50e52 Michael Hanselmann
def BackupFile(node, path):
323 49d50e52 Michael Hanselmann
  """Creates a backup of a file on the node and returns the filename.
324 49d50e52 Michael Hanselmann

325 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
326 49d50e52 Michael Hanselmann
  anymore.
327 49d50e52 Michael Hanselmann

328 49d50e52 Michael Hanselmann
  """
329 49d50e52 Michael Hanselmann
  cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
330 49d50e52 Michael Hanselmann
         "[[ -f \"$tmp\" ]] && "
331 49d50e52 Michael Hanselmann
         "cp %s $tmp && "
332 49d50e52 Michael Hanselmann
         "echo $tmp") % (utils.ShellQuote(path), utils.ShellQuote(path))
333 49d50e52 Michael Hanselmann
334 49d50e52 Michael Hanselmann
  # Return temporary filename
335 49d50e52 Michael Hanselmann
  return GetCommandOutput(node, cmd).strip()
336 49d50e52 Michael Hanselmann
337 49d50e52 Michael Hanselmann
338 4b62db14 Michael Hanselmann
def _ResolveName(cmd, key):
339 4b62db14 Michael Hanselmann
  """Helper function.
340 4b62db14 Michael Hanselmann

341 4b62db14 Michael Hanselmann
  """
342 4b62db14 Michael Hanselmann
  master = qa_config.GetMasterNode()
343 4b62db14 Michael Hanselmann
344 d0c8c01d Iustin Pop
  output = GetCommandOutput(master["primary"], utils.ShellQuoteArgs(cmd))
345 4b62db14 Michael Hanselmann
  for line in output.splitlines():
346 d0c8c01d Iustin Pop
    (lkey, lvalue) = line.split(":", 1)
347 4b62db14 Michael Hanselmann
    if lkey == key:
348 4b62db14 Michael Hanselmann
      return lvalue.lstrip()
349 4b62db14 Michael Hanselmann
  raise KeyError("Key not found")
350 4b62db14 Michael Hanselmann
351 4b62db14 Michael Hanselmann
352 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
353 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
354 5d640672 Michael Hanselmann

355 46f9a948 Michael Hanselmann
  @type instance: string
356 46f9a948 Michael Hanselmann
  @param instance: Instance name
357 46f9a948 Michael Hanselmann

358 5d640672 Michael Hanselmann
  """
359 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-instance", "info", instance],
360 d0c8c01d Iustin Pop
                      "Instance name")
361 4b62db14 Michael Hanselmann
362 4b62db14 Michael Hanselmann
363 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
364 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
365 4b62db14 Michael Hanselmann

366 4b62db14 Michael Hanselmann
  """
367 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-node", "info", node["primary"]],
368 d0c8c01d Iustin Pop
                      "Node name")
369 4b62db14 Michael Hanselmann
370 4b62db14 Michael Hanselmann
371 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
372 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
373 4b62db14 Michael Hanselmann

374 4b62db14 Michael Hanselmann
  """
375 5d640672 Michael Hanselmann
  master = qa_config.GetMasterNode()
376 4b62db14 Michael Hanselmann
  node_name = ResolveNodeName(node)
377 5d640672 Michael Hanselmann
378 4b62db14 Michael Hanselmann
  # Get list of all instances
379 d0c8c01d Iustin Pop
  cmd = ["gnt-instance", "list", "--separator=:", "--no-headers",
380 d0c8c01d Iustin Pop
         "--output=name,pnode,snodes"]
381 d0c8c01d Iustin Pop
  output = GetCommandOutput(master["primary"], utils.ShellQuoteArgs(cmd))
382 4b62db14 Michael Hanselmann
383 4b62db14 Michael Hanselmann
  instances = []
384 4b62db14 Michael Hanselmann
  for line in output.splitlines():
385 d0c8c01d Iustin Pop
    (name, pnode, snodes) = line.split(":", 2)
386 4b62db14 Michael Hanselmann
    if ((not secondaries and pnode == node_name) or
387 d0c8c01d Iustin Pop
        (secondaries and node_name in snodes.split(","))):
388 4b62db14 Michael Hanselmann
      instances.append(name)
389 5d640672 Michael Hanselmann
390 4b62db14 Michael Hanselmann
  return instances
391 23269c5b Michael Hanselmann
392 23269c5b Michael Hanselmann
393 288d6440 Michael Hanselmann
def _SelectQueryFields(rnd, fields):
394 288d6440 Michael Hanselmann
  """Generates a list of fields for query tests.
395 288d6440 Michael Hanselmann

396 288d6440 Michael Hanselmann
  """
397 288d6440 Michael Hanselmann
  # Create copy for shuffling
398 288d6440 Michael Hanselmann
  fields = list(fields)
399 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
400 288d6440 Michael Hanselmann
401 288d6440 Michael Hanselmann
  # Check all fields
402 288d6440 Michael Hanselmann
  yield fields
403 288d6440 Michael Hanselmann
  yield sorted(fields)
404 288d6440 Michael Hanselmann
405 288d6440 Michael Hanselmann
  # Duplicate fields
406 288d6440 Michael Hanselmann
  yield fields + fields
407 288d6440 Michael Hanselmann
408 288d6440 Michael Hanselmann
  # Check small groups of fields
409 288d6440 Michael Hanselmann
  while fields:
410 288d6440 Michael Hanselmann
    yield [fields.pop() for _ in range(rnd.randint(2, 10)) if fields]
411 288d6440 Michael Hanselmann
412 288d6440 Michael Hanselmann
413 288d6440 Michael Hanselmann
def _List(listcmd, fields, names):
414 288d6440 Michael Hanselmann
  """Runs a list command.
415 288d6440 Michael Hanselmann

416 288d6440 Michael Hanselmann
  """
417 288d6440 Michael Hanselmann
  master = qa_config.GetMasterNode()
418 288d6440 Michael Hanselmann
419 58ea8d17 Michael Hanselmann
  cmd = [listcmd, "list", "--separator=|", "--no-headers",
420 288d6440 Michael Hanselmann
         "--output", ",".join(fields)]
421 288d6440 Michael Hanselmann
422 288d6440 Michael Hanselmann
  if names:
423 288d6440 Michael Hanselmann
    cmd.extend(names)
424 288d6440 Michael Hanselmann
425 288d6440 Michael Hanselmann
  return GetCommandOutput(master["primary"],
426 288d6440 Michael Hanselmann
                          utils.ShellQuoteArgs(cmd)).splitlines()
427 288d6440 Michael Hanselmann
428 288d6440 Michael Hanselmann
429 6d1e4845 Michael Hanselmann
def GenericQueryTest(cmd, fields, namefield="name", test_unknown=True):
430 288d6440 Michael Hanselmann
  """Runs a number of tests on query commands.
431 288d6440 Michael Hanselmann

432 288d6440 Michael Hanselmann
  @param cmd: Command name
433 288d6440 Michael Hanselmann
  @param fields: List of field names
434 288d6440 Michael Hanselmann

435 288d6440 Michael Hanselmann
  """
436 288d6440 Michael Hanselmann
  rnd = random.Random(hash(cmd))
437 288d6440 Michael Hanselmann
438 3582eef6 Iustin Pop
  fields = list(fields)
439 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
440 288d6440 Michael Hanselmann
441 288d6440 Michael Hanselmann
  # Test a number of field combinations
442 288d6440 Michael Hanselmann
  for testfields in _SelectQueryFields(rnd, fields):
443 288d6440 Michael Hanselmann
    AssertCommand([cmd, "list", "--output", ",".join(testfields)])
444 288d6440 Michael Hanselmann
445 0fdf247d Michael Hanselmann
  if namefield is not None:
446 0fdf247d Michael Hanselmann
    namelist_fn = compat.partial(_List, cmd, [namefield])
447 288d6440 Michael Hanselmann
448 0fdf247d Michael Hanselmann
    # When no names were requested, the list must be sorted
449 0fdf247d Michael Hanselmann
    names = namelist_fn(None)
450 0fdf247d Michael Hanselmann
    AssertEqual(names, utils.NiceSort(names))
451 288d6440 Michael Hanselmann
452 0fdf247d Michael Hanselmann
    # When requesting specific names, the order must be kept
453 0fdf247d Michael Hanselmann
    revnames = list(reversed(names))
454 0fdf247d Michael Hanselmann
    AssertEqual(namelist_fn(revnames), revnames)
455 288d6440 Michael Hanselmann
456 0fdf247d Michael Hanselmann
    randnames = list(names)
457 0fdf247d Michael Hanselmann
    rnd.shuffle(randnames)
458 0fdf247d Michael Hanselmann
    AssertEqual(namelist_fn(randnames), randnames)
459 288d6440 Michael Hanselmann
460 6d1e4845 Michael Hanselmann
  if test_unknown:
461 6d1e4845 Michael Hanselmann
    # Listing unknown items must fail
462 6d1e4845 Michael Hanselmann
    AssertCommand([cmd, "list", "this.name.certainly.does.not.exist"],
463 6d1e4845 Michael Hanselmann
                  fail=True)
464 2214cf14 Michael Hanselmann
465 2214cf14 Michael Hanselmann
  # Check exit code for listing unknown field
466 2214cf14 Michael Hanselmann
  AssertEqual(AssertCommand([cmd, "list", "--output=field/does/not/exist"],
467 2214cf14 Michael Hanselmann
                            fail=True),
468 2214cf14 Michael Hanselmann
              constants.EXIT_UNKNOWN_FIELD)
469 2214cf14 Michael Hanselmann
470 2214cf14 Michael Hanselmann
471 2214cf14 Michael Hanselmann
def GenericQueryFieldsTest(cmd, fields):
472 2214cf14 Michael Hanselmann
  master = qa_config.GetMasterNode()
473 2214cf14 Michael Hanselmann
474 2214cf14 Michael Hanselmann
  # Listing fields
475 2214cf14 Michael Hanselmann
  AssertCommand([cmd, "list-fields"])
476 2214cf14 Michael Hanselmann
  AssertCommand([cmd, "list-fields"] + fields)
477 2214cf14 Michael Hanselmann
478 2214cf14 Michael Hanselmann
  # Check listed fields (all, must be sorted)
479 2214cf14 Michael Hanselmann
  realcmd = [cmd, "list-fields", "--separator=|", "--no-headers"]
480 2214cf14 Michael Hanselmann
  output = GetCommandOutput(master["primary"],
481 2214cf14 Michael Hanselmann
                            utils.ShellQuoteArgs(realcmd)).splitlines()
482 2214cf14 Michael Hanselmann
  AssertEqual([line.split("|", 1)[0] for line in output],
483 c694367b Michael Hanselmann
              utils.NiceSort(fields))
484 2214cf14 Michael Hanselmann
485 2214cf14 Michael Hanselmann
  # Check exit code for listing unknown field
486 2214cf14 Michael Hanselmann
  AssertEqual(AssertCommand([cmd, "list-fields", "field/does/not/exist"],
487 2214cf14 Michael Hanselmann
                            fail=True),
488 2214cf14 Michael Hanselmann
              constants.EXIT_UNKNOWN_FIELD)
489 2214cf14 Michael Hanselmann
490 288d6440 Michael Hanselmann
491 dfe11bad Michael Hanselmann
def _FormatWithColor(text, seq):
492 dfe11bad Michael Hanselmann
  if not seq:
493 dfe11bad Michael Hanselmann
    return text
494 dfe11bad Michael Hanselmann
  return "%s%s%s" % (seq, text, _RESET_SEQ)
495 23269c5b Michael Hanselmann
496 23269c5b Michael Hanselmann
497 dfe11bad Michael Hanselmann
FormatWarning = lambda text: _FormatWithColor(text, _WARNING_SEQ)
498 dfe11bad Michael Hanselmann
FormatError = lambda text: _FormatWithColor(text, _ERROR_SEQ)
499 dfe11bad Michael Hanselmann
FormatInfo = lambda text: _FormatWithColor(text, _INFO_SEQ)
500 31fe5102 René Nussbaumer
501 31fe5102 René Nussbaumer
502 31fe5102 René Nussbaumer
def AddToEtcHosts(hostnames):
503 31fe5102 René Nussbaumer
  """Adds hostnames to /etc/hosts.
504 31fe5102 René Nussbaumer

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

507 31fe5102 René Nussbaumer
  """
508 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
509 31fe5102 René Nussbaumer
  tmp_hosts = UploadData(master["primary"], "", mode=0644)
510 31fe5102 René Nussbaumer
511 31fe5102 René Nussbaumer
  quoted_tmp_hosts = utils.ShellQuote(tmp_hosts)
512 31fe5102 René Nussbaumer
  data = []
513 31fe5102 René Nussbaumer
  for localhost in ("::1", "127.0.0.1"):
514 31fe5102 René Nussbaumer
    data.append("%s %s" % (localhost, " ".join(hostnames)))
515 31fe5102 René Nussbaumer
516 31fe5102 René Nussbaumer
  try:
517 31fe5102 René Nussbaumer
    AssertCommand(("cat /etc/hosts > %s && echo -e '%s' >> %s && mv %s"
518 31fe5102 René Nussbaumer
                   " /etc/hosts") % (quoted_tmp_hosts, "\\n".join(data),
519 31fe5102 René Nussbaumer
                                     quoted_tmp_hosts, quoted_tmp_hosts))
520 31fe5102 René Nussbaumer
  except qa_error.Error:
521 31fe5102 René Nussbaumer
    AssertCommand(["rm", tmp_hosts])
522 31fe5102 René Nussbaumer
523 31fe5102 René Nussbaumer
524 31fe5102 René Nussbaumer
def RemoveFromEtcHosts(hostnames):
525 31fe5102 René Nussbaumer
  """Remove hostnames from /etc/hosts.
526 31fe5102 René Nussbaumer

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

529 31fe5102 René Nussbaumer
  """
530 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
531 31fe5102 René Nussbaumer
  tmp_hosts = UploadData(master["primary"], "", mode=0644)
532 31fe5102 René Nussbaumer
  quoted_tmp_hosts = utils.ShellQuote(tmp_hosts)
533 31fe5102 René Nussbaumer
534 31fe5102 René Nussbaumer
  sed_data = " ".join(hostnames)
535 31fe5102 René Nussbaumer
  try:
536 31fe5102 René Nussbaumer
    AssertCommand(("sed -e '/^\(::1\|127\.0\.0\.1\)\s\+%s/d' /etc/hosts > %s"
537 31fe5102 René Nussbaumer
                   " && mv %s /etc/hosts") % (sed_data, quoted_tmp_hosts,
538 31fe5102 René Nussbaumer
                                              quoted_tmp_hosts))
539 31fe5102 René Nussbaumer
  except qa_error.Error:
540 31fe5102 René Nussbaumer
    AssertCommand(["rm", tmp_hosts])
541 c9e05005 Michael Hanselmann
542 c9e05005 Michael Hanselmann
543 c9e05005 Michael Hanselmann
def RunInstanceCheck(instance, running):
544 c9e05005 Michael Hanselmann
  """Check if instance is running or not.
545 c9e05005 Michael Hanselmann

546 c9e05005 Michael Hanselmann
  """
547 2ac35588 Michael Hanselmann
  if isinstance(instance, basestring):
548 2ac35588 Michael Hanselmann
    instance_name = instance
549 2ac35588 Michael Hanselmann
  else:
550 2ac35588 Michael Hanselmann
    instance_name = instance["name"]
551 2ac35588 Michael Hanselmann
552 2ac35588 Michael Hanselmann
  if not ht.TNonEmptyString(instance_name):
553 2ac35588 Michael Hanselmann
    raise Exception("Invalid instance name '%s'" % instance_name)
554 2ac35588 Michael Hanselmann
555 c9e05005 Michael Hanselmann
  script = qa_config.GetInstanceCheckScript()
556 c9e05005 Michael Hanselmann
  if not script:
557 c9e05005 Michael Hanselmann
    return
558 c9e05005 Michael Hanselmann
559 c9e05005 Michael Hanselmann
  master_node = qa_config.GetMasterNode()
560 c9e05005 Michael Hanselmann
561 c9e05005 Michael Hanselmann
  # Build command to connect to master node
562 c9e05005 Michael Hanselmann
  master_ssh = GetSSHCommand(master_node["primary"], "--")
563 c9e05005 Michael Hanselmann
564 c9e05005 Michael Hanselmann
  if running:
565 c9e05005 Michael Hanselmann
    running_shellval = "1"
566 c9e05005 Michael Hanselmann
    running_text = ""
567 c9e05005 Michael Hanselmann
  else:
568 c9e05005 Michael Hanselmann
    running_shellval = ""
569 c9e05005 Michael Hanselmann
    running_text = "not "
570 c9e05005 Michael Hanselmann
571 c9e05005 Michael Hanselmann
  print FormatInfo("Checking if instance '%s' is %srunning" %
572 c9e05005 Michael Hanselmann
                   (instance_name, running_text))
573 c9e05005 Michael Hanselmann
574 c9e05005 Michael Hanselmann
  args = [script, instance_name]
575 c9e05005 Michael Hanselmann
  env = {
576 c9e05005 Michael Hanselmann
    "PATH": constants.HOOKS_PATH,
577 c9e05005 Michael Hanselmann
    "RUN_UUID": _RUN_UUID,
578 c9e05005 Michael Hanselmann
    "MASTER_SSH": utils.ShellQuoteArgs(master_ssh),
579 c9e05005 Michael Hanselmann
    "INSTANCE_NAME": instance_name,
580 c9e05005 Michael Hanselmann
    "INSTANCE_RUNNING": running_shellval,
581 c9e05005 Michael Hanselmann
    }
582 c9e05005 Michael Hanselmann
583 c9e05005 Michael Hanselmann
  result = os.spawnve(os.P_WAIT, script, args, env)
584 c9e05005 Michael Hanselmann
  if result != 0:
585 c9e05005 Michael Hanselmann
    raise qa_error.Error("Instance check failed with result %s" % result)
586 c9e05005 Michael Hanselmann
587 c9e05005 Michael Hanselmann
588 c9e05005 Michael Hanselmann
def _InstanceCheckInner(expected, instarg, args, result):
589 c9e05005 Michael Hanselmann
  """Helper function used by L{InstanceCheck}.
590 c9e05005 Michael Hanselmann

591 c9e05005 Michael Hanselmann
  """
592 c9e05005 Michael Hanselmann
  if instarg == FIRST_ARG:
593 c9e05005 Michael Hanselmann
    instance = args[0]
594 c9e05005 Michael Hanselmann
  elif instarg == RETURN_VALUE:
595 c9e05005 Michael Hanselmann
    instance = result
596 c9e05005 Michael Hanselmann
  else:
597 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s' for instance argument" % instarg)
598 c9e05005 Michael Hanselmann
599 c9e05005 Michael Hanselmann
  if expected in (INST_DOWN, INST_UP):
600 c9e05005 Michael Hanselmann
    RunInstanceCheck(instance, (expected == INST_UP))
601 c9e05005 Michael Hanselmann
  elif expected is not None:
602 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s'" % expected)
603 c9e05005 Michael Hanselmann
604 c9e05005 Michael Hanselmann
605 c9e05005 Michael Hanselmann
def InstanceCheck(before, after, instarg):
606 c9e05005 Michael Hanselmann
  """Decorator to check instance status before and after test.
607 c9e05005 Michael Hanselmann

608 c9e05005 Michael Hanselmann
  @param before: L{INST_DOWN} if instance must be stopped before test,
609 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running before test, L{None} to not check.
610 c9e05005 Michael Hanselmann
  @param after: L{INST_DOWN} if instance must be stopped after test,
611 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running after test, L{None} to not check.
612 c9e05005 Michael Hanselmann
  @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
613 c9e05005 Michael Hanselmann
    dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
614 c9e05005 Michael Hanselmann

615 c9e05005 Michael Hanselmann
  """
616 c9e05005 Michael Hanselmann
  def decorator(fn):
617 c9e05005 Michael Hanselmann
    @functools.wraps(fn)
618 c9e05005 Michael Hanselmann
    def wrapper(*args, **kwargs):
619 c9e05005 Michael Hanselmann
      _InstanceCheckInner(before, instarg, args, NotImplemented)
620 c9e05005 Michael Hanselmann
621 c9e05005 Michael Hanselmann
      result = fn(*args, **kwargs)
622 c9e05005 Michael Hanselmann
623 c9e05005 Michael Hanselmann
      _InstanceCheckInner(after, instarg, args, result)
624 c9e05005 Michael Hanselmann
625 c9e05005 Michael Hanselmann
      return result
626 c9e05005 Michael Hanselmann
    return wrapper
627 c9e05005 Michael Hanselmann
  return decorator