Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ 889bed16

History | View | Annotate | Download (16.5 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 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 50265802 René Nussbaumer
def GetSSHCommand(node, cmd, strict=True, opts=None, tty=True):
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 50265802 René Nussbaumer
  @type tty: Bool
207 50265802 René Nussbaumer
  @param tty: If we should use tty
208 c68d1f43 Michael Hanselmann

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

238 5d831182 Michael Hanselmann
  """
239 5d831182 Michael Hanselmann
  print "Command: %s" % utils.ShellQuoteArgs(cmd)
240 5d831182 Michael Hanselmann
  return subprocess.Popen(cmd, shell=False, **kwargs)
241 5d831182 Michael Hanselmann
242 5d831182 Michael Hanselmann
243 cec9845c Michael Hanselmann
def StartSSH(node, cmd, strict=True):
244 cec9845c Michael Hanselmann
  """Starts SSH.
245 cec9845c Michael Hanselmann

246 cec9845c Michael Hanselmann
  """
247 5d831182 Michael Hanselmann
  return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict))
248 4b62db14 Michael Hanselmann
249 4b62db14 Michael Hanselmann
250 f7e6f3c8 Iustin Pop
def StartMultiplexer(node):
251 f7e6f3c8 Iustin Pop
  """Starts a multiplexer command.
252 f7e6f3c8 Iustin Pop

253 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
254 f7e6f3c8 Iustin Pop

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

271 f7e6f3c8 Iustin Pop
  """
272 f7e6f3c8 Iustin Pop
  for node in _MULTIPLEXERS.keys():
273 f7e6f3c8 Iustin Pop
    (sname, child) = _MULTIPLEXERS.pop(node)
274 f7e6f3c8 Iustin Pop
    utils.KillProcess(child.pid, timeout=10, waitpid=True)
275 f7e6f3c8 Iustin Pop
    utils.RemoveFile(sname)
276 f7e6f3c8 Iustin Pop
277 f7e6f3c8 Iustin Pop
278 50265802 René Nussbaumer
def GetCommandOutput(node, cmd, tty=True):
279 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
280 4b62db14 Michael Hanselmann

281 4b62db14 Michael Hanselmann
  """
282 50265802 René Nussbaumer
  p = StartLocalCommand(GetSSHCommand(node, cmd, tty=tty),
283 50265802 René Nussbaumer
                        stdout=subprocess.PIPE)
284 4b62db14 Michael Hanselmann
  AssertEqual(p.wait(), 0)
285 4b62db14 Michael Hanselmann
  return p.stdout.read()
286 cec9845c Michael Hanselmann
287 cec9845c Michael Hanselmann
288 cec9845c Michael Hanselmann
def UploadFile(node, src):
289 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
290 cec9845c Michael Hanselmann

291 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
292 cec9845c Michael Hanselmann
  anymore.
293 49d50e52 Michael Hanselmann

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

318 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
319 b9955569 René Nussbaumer
  anymore.
320 b9955569 René Nussbaumer

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

344 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
345 49d50e52 Michael Hanselmann
  anymore.
346 49d50e52 Michael Hanselmann

347 49d50e52 Michael Hanselmann
  """
348 49d50e52 Michael Hanselmann
  cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
349 49d50e52 Michael Hanselmann
         "[[ -f \"$tmp\" ]] && "
350 49d50e52 Michael Hanselmann
         "cp %s $tmp && "
351 49d50e52 Michael Hanselmann
         "echo $tmp") % (utils.ShellQuote(path), utils.ShellQuote(path))
352 49d50e52 Michael Hanselmann
353 49d50e52 Michael Hanselmann
  # Return temporary filename
354 49d50e52 Michael Hanselmann
  return GetCommandOutput(node, cmd).strip()
355 49d50e52 Michael Hanselmann
356 49d50e52 Michael Hanselmann
357 4b62db14 Michael Hanselmann
def _ResolveName(cmd, key):
358 4b62db14 Michael Hanselmann
  """Helper function.
359 4b62db14 Michael Hanselmann

360 4b62db14 Michael Hanselmann
  """
361 4b62db14 Michael Hanselmann
  master = qa_config.GetMasterNode()
362 4b62db14 Michael Hanselmann
363 d0c8c01d Iustin Pop
  output = GetCommandOutput(master["primary"], utils.ShellQuoteArgs(cmd))
364 4b62db14 Michael Hanselmann
  for line in output.splitlines():
365 d0c8c01d Iustin Pop
    (lkey, lvalue) = line.split(":", 1)
366 4b62db14 Michael Hanselmann
    if lkey == key:
367 4b62db14 Michael Hanselmann
      return lvalue.lstrip()
368 4b62db14 Michael Hanselmann
  raise KeyError("Key not found")
369 4b62db14 Michael Hanselmann
370 4b62db14 Michael Hanselmann
371 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
372 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
373 5d640672 Michael Hanselmann

374 46f9a948 Michael Hanselmann
  @type instance: string
375 46f9a948 Michael Hanselmann
  @param instance: Instance name
376 46f9a948 Michael Hanselmann

377 5d640672 Michael Hanselmann
  """
378 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-instance", "info", instance],
379 d0c8c01d Iustin Pop
                      "Instance name")
380 4b62db14 Michael Hanselmann
381 4b62db14 Michael Hanselmann
382 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
383 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
384 4b62db14 Michael Hanselmann

385 4b62db14 Michael Hanselmann
  """
386 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-node", "info", node["primary"]],
387 d0c8c01d Iustin Pop
                      "Node name")
388 4b62db14 Michael Hanselmann
389 4b62db14 Michael Hanselmann
390 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
391 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
392 4b62db14 Michael Hanselmann

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

415 288d6440 Michael Hanselmann
  """
416 288d6440 Michael Hanselmann
  # Create copy for shuffling
417 288d6440 Michael Hanselmann
  fields = list(fields)
418 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
419 288d6440 Michael Hanselmann
420 288d6440 Michael Hanselmann
  # Check all fields
421 288d6440 Michael Hanselmann
  yield fields
422 288d6440 Michael Hanselmann
  yield sorted(fields)
423 288d6440 Michael Hanselmann
424 288d6440 Michael Hanselmann
  # Duplicate fields
425 288d6440 Michael Hanselmann
  yield fields + fields
426 288d6440 Michael Hanselmann
427 288d6440 Michael Hanselmann
  # Check small groups of fields
428 288d6440 Michael Hanselmann
  while fields:
429 288d6440 Michael Hanselmann
    yield [fields.pop() for _ in range(rnd.randint(2, 10)) if fields]
430 288d6440 Michael Hanselmann
431 288d6440 Michael Hanselmann
432 288d6440 Michael Hanselmann
def _List(listcmd, fields, names):
433 288d6440 Michael Hanselmann
  """Runs a list command.
434 288d6440 Michael Hanselmann

435 288d6440 Michael Hanselmann
  """
436 288d6440 Michael Hanselmann
  master = qa_config.GetMasterNode()
437 288d6440 Michael Hanselmann
438 58ea8d17 Michael Hanselmann
  cmd = [listcmd, "list", "--separator=|", "--no-headers",
439 288d6440 Michael Hanselmann
         "--output", ",".join(fields)]
440 288d6440 Michael Hanselmann
441 288d6440 Michael Hanselmann
  if names:
442 288d6440 Michael Hanselmann
    cmd.extend(names)
443 288d6440 Michael Hanselmann
444 288d6440 Michael Hanselmann
  return GetCommandOutput(master["primary"],
445 288d6440 Michael Hanselmann
                          utils.ShellQuoteArgs(cmd)).splitlines()
446 288d6440 Michael Hanselmann
447 288d6440 Michael Hanselmann
448 6d1e4845 Michael Hanselmann
def GenericQueryTest(cmd, fields, namefield="name", test_unknown=True):
449 288d6440 Michael Hanselmann
  """Runs a number of tests on query commands.
450 288d6440 Michael Hanselmann

451 288d6440 Michael Hanselmann
  @param cmd: Command name
452 288d6440 Michael Hanselmann
  @param fields: List of field names
453 288d6440 Michael Hanselmann

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

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

526 31fe5102 René Nussbaumer
  """
527 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
528 31fe5102 René Nussbaumer
  tmp_hosts = UploadData(master["primary"], "", mode=0644)
529 31fe5102 René Nussbaumer
530 31fe5102 René Nussbaumer
  quoted_tmp_hosts = utils.ShellQuote(tmp_hosts)
531 31fe5102 René Nussbaumer
  data = []
532 31fe5102 René Nussbaumer
  for localhost in ("::1", "127.0.0.1"):
533 31fe5102 René Nussbaumer
    data.append("%s %s" % (localhost, " ".join(hostnames)))
534 31fe5102 René Nussbaumer
535 31fe5102 René Nussbaumer
  try:
536 31fe5102 René Nussbaumer
    AssertCommand(("cat /etc/hosts > %s && echo -e '%s' >> %s && mv %s"
537 31fe5102 René Nussbaumer
                   " /etc/hosts") % (quoted_tmp_hosts, "\\n".join(data),
538 31fe5102 René Nussbaumer
                                     quoted_tmp_hosts, quoted_tmp_hosts))
539 31fe5102 René Nussbaumer
  except qa_error.Error:
540 31fe5102 René Nussbaumer
    AssertCommand(["rm", tmp_hosts])
541 31fe5102 René Nussbaumer
542 31fe5102 René Nussbaumer
543 31fe5102 René Nussbaumer
def RemoveFromEtcHosts(hostnames):
544 31fe5102 René Nussbaumer
  """Remove hostnames from /etc/hosts.
545 31fe5102 René Nussbaumer

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

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

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

604 c9e05005 Michael Hanselmann
  """
605 c9e05005 Michael Hanselmann
  if instarg == FIRST_ARG:
606 c9e05005 Michael Hanselmann
    instance = args[0]
607 c9e05005 Michael Hanselmann
  elif instarg == RETURN_VALUE:
608 c9e05005 Michael Hanselmann
    instance = result
609 c9e05005 Michael Hanselmann
  else:
610 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s' for instance argument" % instarg)
611 c9e05005 Michael Hanselmann
612 c9e05005 Michael Hanselmann
  if expected in (INST_DOWN, INST_UP):
613 c9e05005 Michael Hanselmann
    RunInstanceCheck(instance, (expected == INST_UP))
614 c9e05005 Michael Hanselmann
  elif expected is not None:
615 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s'" % expected)
616 c9e05005 Michael Hanselmann
617 c9e05005 Michael Hanselmann
618 c9e05005 Michael Hanselmann
def InstanceCheck(before, after, instarg):
619 c9e05005 Michael Hanselmann
  """Decorator to check instance status before and after test.
620 c9e05005 Michael Hanselmann

621 c9e05005 Michael Hanselmann
  @param before: L{INST_DOWN} if instance must be stopped before test,
622 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running before test, L{None} to not check.
623 c9e05005 Michael Hanselmann
  @param after: L{INST_DOWN} if instance must be stopped after test,
624 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running after test, L{None} to not check.
625 c9e05005 Michael Hanselmann
  @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
626 c9e05005 Michael Hanselmann
    dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
627 c9e05005 Michael Hanselmann

628 c9e05005 Michael Hanselmann
  """
629 c9e05005 Michael Hanselmann
  def decorator(fn):
630 c9e05005 Michael Hanselmann
    @functools.wraps(fn)
631 c9e05005 Michael Hanselmann
    def wrapper(*args, **kwargs):
632 c9e05005 Michael Hanselmann
      _InstanceCheckInner(before, instarg, args, NotImplemented)
633 c9e05005 Michael Hanselmann
634 c9e05005 Michael Hanselmann
      result = fn(*args, **kwargs)
635 c9e05005 Michael Hanselmann
636 c9e05005 Michael Hanselmann
      _InstanceCheckInner(after, instarg, args, result)
637 c9e05005 Michael Hanselmann
638 c9e05005 Michael Hanselmann
      return result
639 c9e05005 Michael Hanselmann
    return wrapper
640 c9e05005 Michael Hanselmann
  return decorator