Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ 587f8ff6

History | View | Annotate | Download (17.9 kB)

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

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

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

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

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

116 cec9845c Michael Hanselmann
  """
117 cec9845c Michael Hanselmann
  if not first == second:
118 d0c8c01d Iustin Pop
    raise qa_error.Error("%r == %r" % (first, second))
119 e8ae0c20 Michael Hanselmann
120 e8ae0c20 Michael Hanselmann
121 e6ce18ac René Nussbaumer
def AssertMatch(string, pattern):
122 e6ce18ac René Nussbaumer
  """Raises an error when string doesn't match regexp pattern.
123 e6ce18ac René Nussbaumer

124 e6ce18ac René Nussbaumer
  """
125 e6ce18ac René Nussbaumer
  if not re.match(pattern, string):
126 e6ce18ac René Nussbaumer
    raise qa_error.Error("%r doesn't match /%r/" % (string, pattern))
127 e6ce18ac René Nussbaumer
128 e6ce18ac René Nussbaumer
129 889bed16 Michael Hanselmann
def _GetName(entity, key):
130 889bed16 Michael Hanselmann
  """Tries to get name of an entity.
131 889bed16 Michael Hanselmann

132 889bed16 Michael Hanselmann
  @type entity: string or dict
133 889bed16 Michael Hanselmann
  @type key: string
134 889bed16 Michael Hanselmann
  @param key: Dictionary key containing name
135 889bed16 Michael Hanselmann

136 889bed16 Michael Hanselmann
  """
137 889bed16 Michael Hanselmann
  if isinstance(entity, basestring):
138 889bed16 Michael Hanselmann
    result = entity
139 889bed16 Michael Hanselmann
  elif isinstance(entity, dict):
140 889bed16 Michael Hanselmann
    result = entity[key]
141 889bed16 Michael Hanselmann
  else:
142 889bed16 Michael Hanselmann
    raise qa_error.Error("Expected string or dictionary, got %s: %s" %
143 889bed16 Michael Hanselmann
                         (type(entity), entity))
144 889bed16 Michael Hanselmann
145 889bed16 Michael Hanselmann
  if not ht.TNonEmptyString(result):
146 889bed16 Michael Hanselmann
    raise Exception("Invalid name '%s'" % result)
147 889bed16 Michael Hanselmann
148 889bed16 Michael Hanselmann
  return result
149 889bed16 Michael Hanselmann
150 889bed16 Michael Hanselmann
151 587f8ff6 Bernardo Dal Seno
def _AssertRetCode(rcode, fail, cmdstr, nodename):
152 587f8ff6 Bernardo Dal Seno
  """Check the return value from a command and possibly raise an exception.
153 587f8ff6 Bernardo Dal Seno

154 587f8ff6 Bernardo Dal Seno
  """
155 587f8ff6 Bernardo Dal Seno
  if fail and rcode == 0:
156 587f8ff6 Bernardo Dal Seno
    raise qa_error.Error("Command '%s' on node %s was expected to fail but"
157 587f8ff6 Bernardo Dal Seno
                         " didn't" % (cmdstr, nodename))
158 587f8ff6 Bernardo Dal Seno
  elif not fail and rcode != 0:
159 587f8ff6 Bernardo Dal Seno
    raise qa_error.Error("Command '%s' on node %s failed, exit code %s" %
160 587f8ff6 Bernardo Dal Seno
                         (cmdstr, nodename, rcode))
161 587f8ff6 Bernardo Dal Seno
162 587f8ff6 Bernardo Dal Seno
163 2f4b4f78 Iustin Pop
def AssertCommand(cmd, fail=False, node=None):
164 2f4b4f78 Iustin Pop
  """Checks that a remote command succeeds.
165 2f4b4f78 Iustin Pop

166 2f4b4f78 Iustin Pop
  @param cmd: either a string (the command to execute) or a list (to
167 2f4b4f78 Iustin Pop
      be converted using L{utils.ShellQuoteArgs} into a string)
168 2f4b4f78 Iustin Pop
  @type fail: boolean
169 2f4b4f78 Iustin Pop
  @param fail: if the command is expected to fail instead of succeeding
170 2f4b4f78 Iustin Pop
  @param node: if passed, it should be the node on which the command
171 2f4b4f78 Iustin Pop
      should be executed, instead of the master node (can be either a
172 2f4b4f78 Iustin Pop
      dict or a string)
173 05325a35 Bernardo Dal Seno
  @return: the return code of the command
174 05325a35 Bernardo Dal Seno
  @raise qa_error.Error: if the command fails when it shouldn't or vice versa
175 2f4b4f78 Iustin Pop

176 2f4b4f78 Iustin Pop
  """
177 2f4b4f78 Iustin Pop
  if node is None:
178 2f4b4f78 Iustin Pop
    node = qa_config.GetMasterNode()
179 2f4b4f78 Iustin Pop
180 889bed16 Michael Hanselmann
  nodename = _GetName(node, "primary")
181 2f4b4f78 Iustin Pop
182 2f4b4f78 Iustin Pop
  if isinstance(cmd, basestring):
183 2f4b4f78 Iustin Pop
    cmdstr = cmd
184 2f4b4f78 Iustin Pop
  else:
185 2f4b4f78 Iustin Pop
    cmdstr = utils.ShellQuoteArgs(cmd)
186 2f4b4f78 Iustin Pop
187 2f4b4f78 Iustin Pop
  rcode = StartSSH(nodename, cmdstr).wait()
188 587f8ff6 Bernardo Dal Seno
  _AssertRetCode(rcode, fail, cmdstr, nodename)
189 2f4b4f78 Iustin Pop
190 2214cf14 Michael Hanselmann
  return rcode
191 2214cf14 Michael Hanselmann
192 2f4b4f78 Iustin Pop
193 f14a8b15 Iustin Pop
def GetSSHCommand(node, cmd, strict=True, opts=None, tty=None):
194 cec9845c Michael Hanselmann
  """Builds SSH command to be executed.
195 cec9845c Michael Hanselmann

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

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

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

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

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

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

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

288 587f8ff6 Bernardo Dal Seno
  @type node: string
289 587f8ff6 Bernardo Dal Seno
  @param node: node the command should run on
290 587f8ff6 Bernardo Dal Seno
  @type cmd: string
291 587f8ff6 Bernardo Dal Seno
  @param cmd: command to be executed in the node (cannot be empty or None)
292 587f8ff6 Bernardo Dal Seno
  @type tty: bool or None
293 587f8ff6 Bernardo Dal Seno
  @param tty: if we should use tty; if None, it will be auto-detected
294 587f8ff6 Bernardo Dal Seno
  @type fail: bool
295 587f8ff6 Bernardo Dal Seno
  @param fail: whether the command is expected to fail
296 4b62db14 Michael Hanselmann
  """
297 587f8ff6 Bernardo Dal Seno
  assert cmd
298 50265802 René Nussbaumer
  p = StartLocalCommand(GetSSHCommand(node, cmd, tty=tty),
299 50265802 René Nussbaumer
                        stdout=subprocess.PIPE)
300 587f8ff6 Bernardo Dal Seno
  rcode = p.wait()
301 587f8ff6 Bernardo Dal Seno
  _AssertRetCode(rcode, fail, node, cmd)
302 4b62db14 Michael Hanselmann
  return p.stdout.read()
303 cec9845c Michael Hanselmann
304 cec9845c Michael Hanselmann
305 cec9845c Michael Hanselmann
def UploadFile(node, src):
306 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
307 cec9845c Michael Hanselmann

308 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
309 cec9845c Michael Hanselmann
  anymore.
310 49d50e52 Michael Hanselmann

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

335 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
336 b9955569 René Nussbaumer
  anymore.
337 b9955569 René Nussbaumer

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

361 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
362 49d50e52 Michael Hanselmann
  anymore.
363 49d50e52 Michael Hanselmann

364 49d50e52 Michael Hanselmann
  """
365 49d50e52 Michael Hanselmann
  cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
366 49d50e52 Michael Hanselmann
         "[[ -f \"$tmp\" ]] && "
367 49d50e52 Michael Hanselmann
         "cp %s $tmp && "
368 49d50e52 Michael Hanselmann
         "echo $tmp") % (utils.ShellQuote(path), utils.ShellQuote(path))
369 49d50e52 Michael Hanselmann
370 49d50e52 Michael Hanselmann
  # Return temporary filename
371 49d50e52 Michael Hanselmann
  return GetCommandOutput(node, cmd).strip()
372 49d50e52 Michael Hanselmann
373 49d50e52 Michael Hanselmann
374 4b62db14 Michael Hanselmann
def _ResolveName(cmd, key):
375 4b62db14 Michael Hanselmann
  """Helper function.
376 4b62db14 Michael Hanselmann

377 4b62db14 Michael Hanselmann
  """
378 4b62db14 Michael Hanselmann
  master = qa_config.GetMasterNode()
379 4b62db14 Michael Hanselmann
380 d0c8c01d Iustin Pop
  output = GetCommandOutput(master["primary"], utils.ShellQuoteArgs(cmd))
381 4b62db14 Michael Hanselmann
  for line in output.splitlines():
382 d0c8c01d Iustin Pop
    (lkey, lvalue) = line.split(":", 1)
383 4b62db14 Michael Hanselmann
    if lkey == key:
384 4b62db14 Michael Hanselmann
      return lvalue.lstrip()
385 4b62db14 Michael Hanselmann
  raise KeyError("Key not found")
386 4b62db14 Michael Hanselmann
387 4b62db14 Michael Hanselmann
388 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
389 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
390 5d640672 Michael Hanselmann

391 46f9a948 Michael Hanselmann
  @type instance: string
392 46f9a948 Michael Hanselmann
  @param instance: Instance name
393 46f9a948 Michael Hanselmann

394 5d640672 Michael Hanselmann
  """
395 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-instance", "info", instance],
396 d0c8c01d Iustin Pop
                      "Instance name")
397 4b62db14 Michael Hanselmann
398 4b62db14 Michael Hanselmann
399 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
400 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
401 4b62db14 Michael Hanselmann

402 4b62db14 Michael Hanselmann
  """
403 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-node", "info", node["primary"]],
404 d0c8c01d Iustin Pop
                      "Node name")
405 4b62db14 Michael Hanselmann
406 4b62db14 Michael Hanselmann
407 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
408 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
409 4b62db14 Michael Hanselmann

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

432 288d6440 Michael Hanselmann
  """
433 288d6440 Michael Hanselmann
  # Create copy for shuffling
434 288d6440 Michael Hanselmann
  fields = list(fields)
435 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
436 288d6440 Michael Hanselmann
437 288d6440 Michael Hanselmann
  # Check all fields
438 288d6440 Michael Hanselmann
  yield fields
439 288d6440 Michael Hanselmann
  yield sorted(fields)
440 288d6440 Michael Hanselmann
441 288d6440 Michael Hanselmann
  # Duplicate fields
442 288d6440 Michael Hanselmann
  yield fields + fields
443 288d6440 Michael Hanselmann
444 288d6440 Michael Hanselmann
  # Check small groups of fields
445 288d6440 Michael Hanselmann
  while fields:
446 288d6440 Michael Hanselmann
    yield [fields.pop() for _ in range(rnd.randint(2, 10)) if fields]
447 288d6440 Michael Hanselmann
448 288d6440 Michael Hanselmann
449 288d6440 Michael Hanselmann
def _List(listcmd, fields, names):
450 288d6440 Michael Hanselmann
  """Runs a list command.
451 288d6440 Michael Hanselmann

452 288d6440 Michael Hanselmann
  """
453 288d6440 Michael Hanselmann
  master = qa_config.GetMasterNode()
454 288d6440 Michael Hanselmann
455 58ea8d17 Michael Hanselmann
  cmd = [listcmd, "list", "--separator=|", "--no-headers",
456 288d6440 Michael Hanselmann
         "--output", ",".join(fields)]
457 288d6440 Michael Hanselmann
458 288d6440 Michael Hanselmann
  if names:
459 288d6440 Michael Hanselmann
    cmd.extend(names)
460 288d6440 Michael Hanselmann
461 288d6440 Michael Hanselmann
  return GetCommandOutput(master["primary"],
462 288d6440 Michael Hanselmann
                          utils.ShellQuoteArgs(cmd)).splitlines()
463 288d6440 Michael Hanselmann
464 288d6440 Michael Hanselmann
465 6d1e4845 Michael Hanselmann
def GenericQueryTest(cmd, fields, namefield="name", test_unknown=True):
466 288d6440 Michael Hanselmann
  """Runs a number of tests on query commands.
467 288d6440 Michael Hanselmann

468 288d6440 Michael Hanselmann
  @param cmd: Command name
469 288d6440 Michael Hanselmann
  @param fields: List of field names
470 288d6440 Michael Hanselmann

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

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

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

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

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

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

627 c9e05005 Michael Hanselmann
  """
628 c9e05005 Michael Hanselmann
  if instarg == FIRST_ARG:
629 c9e05005 Michael Hanselmann
    instance = args[0]
630 c9e05005 Michael Hanselmann
  elif instarg == RETURN_VALUE:
631 c9e05005 Michael Hanselmann
    instance = result
632 c9e05005 Michael Hanselmann
  else:
633 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s' for instance argument" % instarg)
634 c9e05005 Michael Hanselmann
635 c9e05005 Michael Hanselmann
  if expected in (INST_DOWN, INST_UP):
636 c9e05005 Michael Hanselmann
    RunInstanceCheck(instance, (expected == INST_UP))
637 c9e05005 Michael Hanselmann
  elif expected is not None:
638 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s'" % expected)
639 c9e05005 Michael Hanselmann
640 c9e05005 Michael Hanselmann
641 c9e05005 Michael Hanselmann
def InstanceCheck(before, after, instarg):
642 c9e05005 Michael Hanselmann
  """Decorator to check instance status before and after test.
643 c9e05005 Michael Hanselmann

644 c9e05005 Michael Hanselmann
  @param before: L{INST_DOWN} if instance must be stopped before test,
645 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running before test, L{None} to not check.
646 c9e05005 Michael Hanselmann
  @param after: L{INST_DOWN} if instance must be stopped after test,
647 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running after test, L{None} to not check.
648 c9e05005 Michael Hanselmann
  @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
649 c9e05005 Michael Hanselmann
    dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
650 c9e05005 Michael Hanselmann

651 c9e05005 Michael Hanselmann
  """
652 c9e05005 Michael Hanselmann
  def decorator(fn):
653 c9e05005 Michael Hanselmann
    @functools.wraps(fn)
654 c9e05005 Michael Hanselmann
    def wrapper(*args, **kwargs):
655 c9e05005 Michael Hanselmann
      _InstanceCheckInner(before, instarg, args, NotImplemented)
656 c9e05005 Michael Hanselmann
657 c9e05005 Michael Hanselmann
      result = fn(*args, **kwargs)
658 c9e05005 Michael Hanselmann
659 c9e05005 Michael Hanselmann
      _InstanceCheckInner(after, instarg, args, result)
660 c9e05005 Michael Hanselmann
661 c9e05005 Michael Hanselmann
      return result
662 c9e05005 Michael Hanselmann
    return wrapper
663 c9e05005 Michael Hanselmann
  return decorator
664 b4d2d2cb Michael Hanselmann
665 b4d2d2cb Michael Hanselmann
666 b4d2d2cb Michael Hanselmann
def GetNonexistentGroups(count):
667 b4d2d2cb Michael Hanselmann
  """Gets group names which shouldn't exist on the cluster.
668 b4d2d2cb Michael Hanselmann

669 b4d2d2cb Michael Hanselmann
  @param count: Number of groups to get
670 b4d2d2cb Michael Hanselmann
  @rtype: list
671 b4d2d2cb Michael Hanselmann

672 b4d2d2cb Michael Hanselmann
  """
673 b4d2d2cb Michael Hanselmann
  groups = qa_config.get("groups", {})
674 b4d2d2cb Michael Hanselmann
675 b4d2d2cb Michael Hanselmann
  default = ["group1", "group2", "group3"]
676 b4d2d2cb Michael Hanselmann
  assert count <= len(default)
677 b4d2d2cb Michael Hanselmann
678 b4d2d2cb Michael Hanselmann
  candidates = groups.get("inexistent-groups", default)[:count]
679 b4d2d2cb Michael Hanselmann
680 b4d2d2cb Michael Hanselmann
  if len(candidates) < count:
681 b4d2d2cb Michael Hanselmann
    raise Exception("At least %s non-existent groups are needed" % count)
682 b4d2d2cb Michael Hanselmann
683 b4d2d2cb Michael Hanselmann
  return candidates