Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ 0e79564a

History | View | Annotate | Download (21.3 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 0e79564a Bernardo Dal Seno
import operator
27 cec9845c Michael Hanselmann
import os
28 0e79564a Bernardo Dal Seno
import random
29 e6ce18ac René Nussbaumer
import re
30 cec9845c Michael Hanselmann
import subprocess
31 0e79564a Bernardo Dal Seno
import sys
32 f7e6f3c8 Iustin Pop
import tempfile
33 0e79564a Bernardo Dal Seno
import yaml
34 cec9845c Michael Hanselmann
35 c9e05005 Michael Hanselmann
try:
36 c9e05005 Michael Hanselmann
  import functools
37 c9e05005 Michael Hanselmann
except ImportError, err:
38 c9e05005 Michael Hanselmann
  raise ImportError("Python 2.5 or higher is required: %s" % err)
39 c9e05005 Michael Hanselmann
40 cec9845c Michael Hanselmann
from ganeti import utils
41 288d6440 Michael Hanselmann
from ganeti import compat
42 2214cf14 Michael Hanselmann
from ganeti import constants
43 c9e05005 Michael Hanselmann
from ganeti import ht
44 48967eb0 Michael Hanselmann
from ganeti import pathutils
45 50eaa5da Michael Hanselmann
from ganeti import vcluster
46 cec9845c Michael Hanselmann
47 cec9845c Michael Hanselmann
import qa_config
48 cec9845c Michael Hanselmann
import qa_error
49 cec9845c Michael Hanselmann
50 cec9845c Michael Hanselmann
51 23269c5b Michael Hanselmann
_INFO_SEQ = None
52 23269c5b Michael Hanselmann
_WARNING_SEQ = None
53 23269c5b Michael Hanselmann
_ERROR_SEQ = None
54 23269c5b Michael Hanselmann
_RESET_SEQ = None
55 23269c5b Michael Hanselmann
56 f7e6f3c8 Iustin Pop
_MULTIPLEXERS = {}
57 f7e6f3c8 Iustin Pop
58 c9e05005 Michael Hanselmann
#: Unique ID per QA run
59 c9e05005 Michael Hanselmann
_RUN_UUID = utils.NewUUID()
60 c9e05005 Michael Hanselmann
61 afd5ca04 Iustin Pop
#: Path to the QA query output log file
62 afd5ca04 Iustin Pop
_QA_OUTPUT = pathutils.GetLogFilename("qa-output")
63 afd5ca04 Iustin Pop
64 c9e05005 Michael Hanselmann
65 c9e05005 Michael Hanselmann
(INST_DOWN,
66 c9e05005 Michael Hanselmann
 INST_UP) = range(500, 502)
67 c9e05005 Michael Hanselmann
68 c9e05005 Michael Hanselmann
(FIRST_ARG,
69 c9e05005 Michael Hanselmann
 RETURN_VALUE) = range(1000, 1002)
70 c9e05005 Michael Hanselmann
71 23269c5b Michael Hanselmann
72 23269c5b Michael Hanselmann
def _SetupColours():
73 23269c5b Michael Hanselmann
  """Initializes the colour constants.
74 23269c5b Michael Hanselmann

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

106 eaef8a05 Michael Hanselmann
  """
107 eaef8a05 Michael Hanselmann
  if item not in sequence:
108 d0c8c01d Iustin Pop
    raise qa_error.Error("%r not in %r" % (item, sequence))
109 eaef8a05 Michael Hanselmann
110 eaef8a05 Michael Hanselmann
111 79eac09b Michael Hanselmann
def AssertNotIn(item, sequence):
112 79eac09b Michael Hanselmann
  """Raises an error when item is in sequence.
113 79eac09b Michael Hanselmann

114 79eac09b Michael Hanselmann
  """
115 79eac09b Michael Hanselmann
  if item in sequence:
116 d0c8c01d Iustin Pop
    raise qa_error.Error("%r in %r" % (item, sequence))
117 79eac09b Michael Hanselmann
118 79eac09b Michael Hanselmann
119 e8ae0c20 Michael Hanselmann
def AssertEqual(first, second):
120 cec9845c Michael Hanselmann
  """Raises an error when values aren't equal.
121 cec9845c Michael Hanselmann

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

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

138 889bed16 Michael Hanselmann
  @type entity: string or dict
139 6998aefe Michael Hanselmann
  @param fn: Function retrieving name from entity
140 889bed16 Michael Hanselmann

141 889bed16 Michael Hanselmann
  """
142 889bed16 Michael Hanselmann
  if isinstance(entity, basestring):
143 889bed16 Michael Hanselmann
    result = entity
144 889bed16 Michael Hanselmann
  else:
145 6998aefe Michael Hanselmann
    result = fn(entity)
146 889bed16 Michael Hanselmann
147 889bed16 Michael Hanselmann
  if not ht.TNonEmptyString(result):
148 889bed16 Michael Hanselmann
    raise Exception("Invalid name '%s'" % result)
149 889bed16 Michael Hanselmann
150 889bed16 Michael Hanselmann
  return result
151 889bed16 Michael Hanselmann
152 889bed16 Michael Hanselmann
153 587f8ff6 Bernardo Dal Seno
def _AssertRetCode(rcode, fail, cmdstr, nodename):
154 587f8ff6 Bernardo Dal Seno
  """Check the return value from a command and possibly raise an exception.
155 587f8ff6 Bernardo Dal Seno

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

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

180 2f4b4f78 Iustin Pop
  """
181 2f4b4f78 Iustin Pop
  if node is None:
182 2f4b4f78 Iustin Pop
    node = qa_config.GetMasterNode()
183 2f4b4f78 Iustin Pop
184 6998aefe Michael Hanselmann
  nodename = _GetName(node, operator.attrgetter("primary"))
185 2f4b4f78 Iustin Pop
186 2f4b4f78 Iustin Pop
  if isinstance(cmd, basestring):
187 2f4b4f78 Iustin Pop
    cmdstr = cmd
188 2f4b4f78 Iustin Pop
  else:
189 2f4b4f78 Iustin Pop
    cmdstr = utils.ShellQuoteArgs(cmd)
190 2f4b4f78 Iustin Pop
191 56b9f2db Iustin Pop
  rcode = StartSSH(nodename, cmdstr, log_cmd=log_cmd).wait()
192 587f8ff6 Bernardo Dal Seno
  _AssertRetCode(rcode, fail, cmdstr, nodename)
193 2f4b4f78 Iustin Pop
194 2214cf14 Michael Hanselmann
  return rcode
195 2214cf14 Michael Hanselmann
196 2f4b4f78 Iustin Pop
197 afd5ca04 Iustin Pop
def AssertRedirectedCommand(cmd, fail=False, node=None, log_cmd=True):
198 afd5ca04 Iustin Pop
  """Executes a command with redirected output.
199 afd5ca04 Iustin Pop

200 afd5ca04 Iustin Pop
  The log will go to the qa-output log file in the ganeti log
201 afd5ca04 Iustin Pop
  directory on the node where the command is executed. The fail and
202 afd5ca04 Iustin Pop
  node parameters are passed unchanged to AssertCommand.
203 afd5ca04 Iustin Pop

204 afd5ca04 Iustin Pop
  @param cmd: the command to be executed, as a list; a string is not
205 afd5ca04 Iustin Pop
      supported
206 afd5ca04 Iustin Pop

207 afd5ca04 Iustin Pop
  """
208 afd5ca04 Iustin Pop
  if not isinstance(cmd, list):
209 afd5ca04 Iustin Pop
    raise qa_error.Error("Non-list passed to AssertRedirectedCommand")
210 afd5ca04 Iustin Pop
  ofile = utils.ShellQuote(_QA_OUTPUT)
211 afd5ca04 Iustin Pop
  cmdstr = utils.ShellQuoteArgs(cmd)
212 afd5ca04 Iustin Pop
  AssertCommand("echo ---- $(date) %s ---- >> %s" % (cmdstr, ofile),
213 afd5ca04 Iustin Pop
                fail=False, node=node, log_cmd=False)
214 afd5ca04 Iustin Pop
  return AssertCommand(cmdstr + " >> %s" % ofile,
215 afd5ca04 Iustin Pop
                       fail=fail, node=node, log_cmd=log_cmd)
216 afd5ca04 Iustin Pop
217 afd5ca04 Iustin Pop
218 f14a8b15 Iustin Pop
def GetSSHCommand(node, cmd, strict=True, opts=None, tty=None):
219 cec9845c Michael Hanselmann
  """Builds SSH command to be executed.
220 cec9845c Michael Hanselmann

221 0a05f959 Adeodato Simo
  @type node: string
222 0a05f959 Adeodato Simo
  @param node: node the command should run on
223 0a05f959 Adeodato Simo
  @type cmd: string
224 f7e6f3c8 Iustin Pop
  @param cmd: command to be executed in the node; if None or empty
225 f7e6f3c8 Iustin Pop
      string, no command will be executed
226 0a05f959 Adeodato Simo
  @type strict: boolean
227 0a05f959 Adeodato Simo
  @param strict: whether to enable strict host key checking
228 f7e6f3c8 Iustin Pop
  @type opts: list
229 f7e6f3c8 Iustin Pop
  @param opts: list of additional options
230 f14a8b15 Iustin Pop
  @type tty: boolean or None
231 f14a8b15 Iustin Pop
  @param tty: if we should use tty; if None, will be auto-detected
232 c68d1f43 Michael Hanselmann

233 cec9845c Michael Hanselmann
  """
234 710bc88c Iustin Pop
  args = ["ssh", "-oEscapeChar=none", "-oBatchMode=yes", "-lroot"]
235 50265802 René Nussbaumer
236 f14a8b15 Iustin Pop
  if tty is None:
237 f14a8b15 Iustin Pop
    tty = sys.stdout.isatty()
238 f14a8b15 Iustin Pop
239 50265802 René Nussbaumer
  if tty:
240 50265802 René Nussbaumer
    args.append("-t")
241 cec9845c Michael Hanselmann
242 cec9845c Michael Hanselmann
  if strict:
243 d0c8c01d Iustin Pop
    tmp = "yes"
244 cec9845c Michael Hanselmann
  else:
245 d0c8c01d Iustin Pop
    tmp = "no"
246 d0c8c01d Iustin Pop
  args.append("-oStrictHostKeyChecking=%s" % tmp)
247 d0c8c01d Iustin Pop
  args.append("-oClearAllForwardings=yes")
248 d0c8c01d Iustin Pop
  args.append("-oForwardAgent=yes")
249 f7e6f3c8 Iustin Pop
  if opts:
250 f7e6f3c8 Iustin Pop
    args.extend(opts)
251 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
252 f7e6f3c8 Iustin Pop
    spath = _MULTIPLEXERS[node][0]
253 d0c8c01d Iustin Pop
    args.append("-oControlPath=%s" % spath)
254 d0c8c01d Iustin Pop
    args.append("-oControlMaster=no")
255 50eaa5da Michael Hanselmann
256 50eaa5da Michael Hanselmann
  (vcluster_master, vcluster_basedir) = \
257 50eaa5da Michael Hanselmann
    qa_config.GetVclusterSettings()
258 50eaa5da Michael Hanselmann
259 50eaa5da Michael Hanselmann
  if vcluster_master:
260 50eaa5da Michael Hanselmann
    args.append(vcluster_master)
261 50eaa5da Michael Hanselmann
    args.append("%s/%s/cmd" % (vcluster_basedir, node))
262 50eaa5da Michael Hanselmann
263 50eaa5da Michael Hanselmann
    if cmd:
264 50eaa5da Michael Hanselmann
      # For virtual clusters the whole command must be wrapped using the "cmd"
265 50eaa5da Michael Hanselmann
      # script, as that script sets a number of environment variables. If the
266 50eaa5da Michael Hanselmann
      # command contains shell meta characters the whole command needs to be
267 50eaa5da Michael Hanselmann
      # quoted.
268 50eaa5da Michael Hanselmann
      args.append(utils.ShellQuote(cmd))
269 50eaa5da Michael Hanselmann
  else:
270 50eaa5da Michael Hanselmann
    args.append(node)
271 50eaa5da Michael Hanselmann
272 50eaa5da Michael Hanselmann
    if cmd:
273 50eaa5da Michael Hanselmann
      args.append(cmd)
274 cec9845c Michael Hanselmann
275 cec9845c Michael Hanselmann
  return args
276 cec9845c Michael Hanselmann
277 cec9845c Michael Hanselmann
278 56b9f2db Iustin Pop
def StartLocalCommand(cmd, _nolog_opts=False, log_cmd=True, **kwargs):
279 5d831182 Michael Hanselmann
  """Starts a local command.
280 5d831182 Michael Hanselmann

281 5d831182 Michael Hanselmann
  """
282 56b9f2db Iustin Pop
  if log_cmd:
283 56b9f2db Iustin Pop
    if _nolog_opts:
284 56b9f2db Iustin Pop
      pcmd = [i for i in cmd if not i.startswith("-")]
285 56b9f2db Iustin Pop
    else:
286 56b9f2db Iustin Pop
      pcmd = cmd
287 56b9f2db Iustin Pop
    print "Command: %s" % utils.ShellQuoteArgs(pcmd)
288 5d831182 Michael Hanselmann
  return subprocess.Popen(cmd, shell=False, **kwargs)
289 5d831182 Michael Hanselmann
290 5d831182 Michael Hanselmann
291 56b9f2db Iustin Pop
def StartSSH(node, cmd, strict=True, log_cmd=True):
292 cec9845c Michael Hanselmann
  """Starts SSH.
293 cec9845c Michael Hanselmann

294 cec9845c Michael Hanselmann
  """
295 710bc88c Iustin Pop
  return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict),
296 56b9f2db Iustin Pop
                           _nolog_opts=True, log_cmd=log_cmd)
297 4b62db14 Michael Hanselmann
298 4b62db14 Michael Hanselmann
299 f7e6f3c8 Iustin Pop
def StartMultiplexer(node):
300 f7e6f3c8 Iustin Pop
  """Starts a multiplexer command.
301 f7e6f3c8 Iustin Pop

302 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
303 f7e6f3c8 Iustin Pop

304 f7e6f3c8 Iustin Pop
  """
305 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
306 f7e6f3c8 Iustin Pop
    return
307 f7e6f3c8 Iustin Pop
308 f7e6f3c8 Iustin Pop
  # Note: yes, we only need mktemp, since we'll remove the file anyway
309 f7e6f3c8 Iustin Pop
  sname = tempfile.mktemp(prefix="ganeti-qa-multiplexer.")
310 f7e6f3c8 Iustin Pop
  utils.RemoveFile(sname)
311 f7e6f3c8 Iustin Pop
  opts = ["-N", "-oControlPath=%s" % sname, "-oControlMaster=yes"]
312 f7e6f3c8 Iustin Pop
  print "Created socket at %s" % sname
313 f7e6f3c8 Iustin Pop
  child = StartLocalCommand(GetSSHCommand(node, None, opts=opts))
314 f7e6f3c8 Iustin Pop
  _MULTIPLEXERS[node] = (sname, child)
315 f7e6f3c8 Iustin Pop
316 f7e6f3c8 Iustin Pop
317 f7e6f3c8 Iustin Pop
def CloseMultiplexers():
318 f7e6f3c8 Iustin Pop
  """Closes all current multiplexers and cleans up.
319 f7e6f3c8 Iustin Pop

320 f7e6f3c8 Iustin Pop
  """
321 f7e6f3c8 Iustin Pop
  for node in _MULTIPLEXERS.keys():
322 f7e6f3c8 Iustin Pop
    (sname, child) = _MULTIPLEXERS.pop(node)
323 f7e6f3c8 Iustin Pop
    utils.KillProcess(child.pid, timeout=10, waitpid=True)
324 f7e6f3c8 Iustin Pop
    utils.RemoveFile(sname)
325 f7e6f3c8 Iustin Pop
326 f7e6f3c8 Iustin Pop
327 587f8ff6 Bernardo Dal Seno
def GetCommandOutput(node, cmd, tty=None, fail=False):
328 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
329 4b62db14 Michael Hanselmann

330 587f8ff6 Bernardo Dal Seno
  @type node: string
331 587f8ff6 Bernardo Dal Seno
  @param node: node the command should run on
332 587f8ff6 Bernardo Dal Seno
  @type cmd: string
333 587f8ff6 Bernardo Dal Seno
  @param cmd: command to be executed in the node (cannot be empty or None)
334 587f8ff6 Bernardo Dal Seno
  @type tty: bool or None
335 587f8ff6 Bernardo Dal Seno
  @param tty: if we should use tty; if None, it will be auto-detected
336 587f8ff6 Bernardo Dal Seno
  @type fail: bool
337 587f8ff6 Bernardo Dal Seno
  @param fail: whether the command is expected to fail
338 4b62db14 Michael Hanselmann
  """
339 587f8ff6 Bernardo Dal Seno
  assert cmd
340 50265802 René Nussbaumer
  p = StartLocalCommand(GetSSHCommand(node, cmd, tty=tty),
341 50265802 René Nussbaumer
                        stdout=subprocess.PIPE)
342 587f8ff6 Bernardo Dal Seno
  rcode = p.wait()
343 3b0db9e3 Michael Hanselmann
  _AssertRetCode(rcode, fail, cmd, node)
344 4b62db14 Michael Hanselmann
  return p.stdout.read()
345 cec9845c Michael Hanselmann
346 cec9845c Michael Hanselmann
347 0e79564a Bernardo Dal Seno
def GetObjectInfo(infocmd):
348 0e79564a Bernardo Dal Seno
  """Get and parse information about a Ganeti object.
349 0e79564a Bernardo Dal Seno

350 0e79564a Bernardo Dal Seno
  @type infocmd: list of strings
351 0e79564a Bernardo Dal Seno
  @param infocmd: command to be executed, e.g. ["gnt-cluster", "info"]
352 0e79564a Bernardo Dal Seno
  @return: the information parsed, appropriately stored in dictionaries,
353 0e79564a Bernardo Dal Seno
      lists...
354 0e79564a Bernardo Dal Seno

355 0e79564a Bernardo Dal Seno
  """
356 0e79564a Bernardo Dal Seno
  master = qa_config.GetMasterNode()
357 0e79564a Bernardo Dal Seno
  cmdline = utils.ShellQuoteArgs(infocmd)
358 0e79564a Bernardo Dal Seno
  info_out = GetCommandOutput(master.primary, cmdline)
359 0e79564a Bernardo Dal Seno
  return yaml.load(info_out)
360 0e79564a Bernardo Dal Seno
361 0e79564a Bernardo Dal Seno
362 cec9845c Michael Hanselmann
def UploadFile(node, src):
363 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
364 cec9845c Michael Hanselmann

365 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
366 cec9845c Michael Hanselmann
  anymore.
367 49d50e52 Michael Hanselmann

368 cec9845c Michael Hanselmann
  """
369 cec9845c Michael Hanselmann
  # Make sure nobody else has access to it while preserving local permissions
370 cec9845c Michael Hanselmann
  mode = os.stat(src).st_mode & 0700
371 cec9845c Michael Hanselmann
372 cec9845c Michael Hanselmann
  cmd = ('tmp=$(tempfile --mode %o --prefix gnt) && '
373 cec9845c Michael Hanselmann
         '[[ -f "${tmp}" ]] && '
374 cec9845c Michael Hanselmann
         'cat > "${tmp}" && '
375 cec9845c Michael Hanselmann
         'echo "${tmp}"') % mode
376 cec9845c Michael Hanselmann
377 d0c8c01d Iustin Pop
  f = open(src, "r")
378 cec9845c Michael Hanselmann
  try:
379 cec9845c Michael Hanselmann
    p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
380 cec9845c Michael Hanselmann
                         stdout=subprocess.PIPE)
381 cec9845c Michael Hanselmann
    AssertEqual(p.wait(), 0)
382 cec9845c Michael Hanselmann
383 cec9845c Michael Hanselmann
    # Return temporary filename
384 cec9845c Michael Hanselmann
    return p.stdout.read().strip()
385 cec9845c Michael Hanselmann
  finally:
386 cec9845c Michael Hanselmann
    f.close()
387 5d640672 Michael Hanselmann
388 5d640672 Michael Hanselmann
389 b9955569 René Nussbaumer
def UploadData(node, data, mode=0600, filename=None):
390 b9955569 René Nussbaumer
  """Uploads data to a node and returns the filename.
391 b9955569 René Nussbaumer

392 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
393 b9955569 René Nussbaumer
  anymore.
394 b9955569 René Nussbaumer

395 b9955569 René Nussbaumer
  """
396 b9955569 René Nussbaumer
  if filename:
397 b9955569 René Nussbaumer
    tmp = "tmp=%s" % utils.ShellQuote(filename)
398 b9955569 René Nussbaumer
  else:
399 b9955569 René Nussbaumer
    tmp = "tmp=$(tempfile --mode %o --prefix gnt)" % mode
400 b9955569 René Nussbaumer
  cmd = ("%s && "
401 b9955569 René Nussbaumer
         "[[ -f \"${tmp}\" ]] && "
402 b9955569 René Nussbaumer
         "cat > \"${tmp}\" && "
403 b9955569 René Nussbaumer
         "echo \"${tmp}\"") % tmp
404 b9955569 René Nussbaumer
405 b9955569 René Nussbaumer
  p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False,
406 b9955569 René Nussbaumer
                       stdin=subprocess.PIPE, stdout=subprocess.PIPE)
407 b9955569 René Nussbaumer
  p.stdin.write(data)
408 b9955569 René Nussbaumer
  p.stdin.close()
409 b9955569 René Nussbaumer
  AssertEqual(p.wait(), 0)
410 b9955569 René Nussbaumer
411 b9955569 René Nussbaumer
  # Return temporary filename
412 b9955569 René Nussbaumer
  return p.stdout.read().strip()
413 b9955569 René Nussbaumer
414 b9955569 René Nussbaumer
415 49d50e52 Michael Hanselmann
def BackupFile(node, path):
416 49d50e52 Michael Hanselmann
  """Creates a backup of a file on the node and returns the filename.
417 49d50e52 Michael Hanselmann

418 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
419 49d50e52 Michael Hanselmann
  anymore.
420 49d50e52 Michael Hanselmann

421 49d50e52 Michael Hanselmann
  """
422 7160f14a Michael Hanselmann
  vpath = MakeNodePath(node, path)
423 7160f14a Michael Hanselmann
424 49d50e52 Michael Hanselmann
  cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
425 49d50e52 Michael Hanselmann
         "[[ -f \"$tmp\" ]] && "
426 49d50e52 Michael Hanselmann
         "cp %s $tmp && "
427 7160f14a Michael Hanselmann
         "echo $tmp") % (utils.ShellQuote(vpath), utils.ShellQuote(vpath))
428 49d50e52 Michael Hanselmann
429 49d50e52 Michael Hanselmann
  # Return temporary filename
430 7160f14a Michael Hanselmann
  result = GetCommandOutput(node, cmd).strip()
431 7160f14a Michael Hanselmann
432 7160f14a Michael Hanselmann
  print "Backup filename: %s" % result
433 7160f14a Michael Hanselmann
434 7160f14a Michael Hanselmann
  return result
435 49d50e52 Michael Hanselmann
436 49d50e52 Michael Hanselmann
437 4b62db14 Michael Hanselmann
def _ResolveName(cmd, key):
438 4b62db14 Michael Hanselmann
  """Helper function.
439 4b62db14 Michael Hanselmann

440 4b62db14 Michael Hanselmann
  """
441 4b62db14 Michael Hanselmann
  master = qa_config.GetMasterNode()
442 4b62db14 Michael Hanselmann
443 aecba21e Michael Hanselmann
  output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
444 4b62db14 Michael Hanselmann
  for line in output.splitlines():
445 d0c8c01d Iustin Pop
    (lkey, lvalue) = line.split(":", 1)
446 4b62db14 Michael Hanselmann
    if lkey == key:
447 4b62db14 Michael Hanselmann
      return lvalue.lstrip()
448 4b62db14 Michael Hanselmann
  raise KeyError("Key not found")
449 4b62db14 Michael Hanselmann
450 4b62db14 Michael Hanselmann
451 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
452 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
453 5d640672 Michael Hanselmann

454 46f9a948 Michael Hanselmann
  @type instance: string
455 46f9a948 Michael Hanselmann
  @param instance: Instance name
456 46f9a948 Michael Hanselmann

457 5d640672 Michael Hanselmann
  """
458 d0c8c01d Iustin Pop
  return _ResolveName(["gnt-instance", "info", instance],
459 d0c8c01d Iustin Pop
                      "Instance name")
460 4b62db14 Michael Hanselmann
461 4b62db14 Michael Hanselmann
462 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
463 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
464 4b62db14 Michael Hanselmann

465 4b62db14 Michael Hanselmann
  """
466 aecba21e Michael Hanselmann
  return _ResolveName(["gnt-node", "info", node.primary],
467 d0c8c01d Iustin Pop
                      "Node name")
468 4b62db14 Michael Hanselmann
469 4b62db14 Michael Hanselmann
470 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
471 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
472 4b62db14 Michael Hanselmann

473 4b62db14 Michael Hanselmann
  """
474 5d640672 Michael Hanselmann
  master = qa_config.GetMasterNode()
475 4b62db14 Michael Hanselmann
  node_name = ResolveNodeName(node)
476 5d640672 Michael Hanselmann
477 4b62db14 Michael Hanselmann
  # Get list of all instances
478 d0c8c01d Iustin Pop
  cmd = ["gnt-instance", "list", "--separator=:", "--no-headers",
479 d0c8c01d Iustin Pop
         "--output=name,pnode,snodes"]
480 aecba21e Michael Hanselmann
  output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
481 4b62db14 Michael Hanselmann
482 4b62db14 Michael Hanselmann
  instances = []
483 4b62db14 Michael Hanselmann
  for line in output.splitlines():
484 d0c8c01d Iustin Pop
    (name, pnode, snodes) = line.split(":", 2)
485 4b62db14 Michael Hanselmann
    if ((not secondaries and pnode == node_name) or
486 d0c8c01d Iustin Pop
        (secondaries and node_name in snodes.split(","))):
487 4b62db14 Michael Hanselmann
      instances.append(name)
488 5d640672 Michael Hanselmann
489 4b62db14 Michael Hanselmann
  return instances
490 23269c5b Michael Hanselmann
491 23269c5b Michael Hanselmann
492 288d6440 Michael Hanselmann
def _SelectQueryFields(rnd, fields):
493 288d6440 Michael Hanselmann
  """Generates a list of fields for query tests.
494 288d6440 Michael Hanselmann

495 288d6440 Michael Hanselmann
  """
496 288d6440 Michael Hanselmann
  # Create copy for shuffling
497 288d6440 Michael Hanselmann
  fields = list(fields)
498 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
499 288d6440 Michael Hanselmann
500 288d6440 Michael Hanselmann
  # Check all fields
501 288d6440 Michael Hanselmann
  yield fields
502 288d6440 Michael Hanselmann
  yield sorted(fields)
503 288d6440 Michael Hanselmann
504 288d6440 Michael Hanselmann
  # Duplicate fields
505 288d6440 Michael Hanselmann
  yield fields + fields
506 288d6440 Michael Hanselmann
507 288d6440 Michael Hanselmann
  # Check small groups of fields
508 288d6440 Michael Hanselmann
  while fields:
509 288d6440 Michael Hanselmann
    yield [fields.pop() for _ in range(rnd.randint(2, 10)) if fields]
510 288d6440 Michael Hanselmann
511 288d6440 Michael Hanselmann
512 288d6440 Michael Hanselmann
def _List(listcmd, fields, names):
513 288d6440 Michael Hanselmann
  """Runs a list command.
514 288d6440 Michael Hanselmann

515 288d6440 Michael Hanselmann
  """
516 288d6440 Michael Hanselmann
  master = qa_config.GetMasterNode()
517 288d6440 Michael Hanselmann
518 58ea8d17 Michael Hanselmann
  cmd = [listcmd, "list", "--separator=|", "--no-headers",
519 288d6440 Michael Hanselmann
         "--output", ",".join(fields)]
520 288d6440 Michael Hanselmann
521 288d6440 Michael Hanselmann
  if names:
522 288d6440 Michael Hanselmann
    cmd.extend(names)
523 288d6440 Michael Hanselmann
524 aecba21e Michael Hanselmann
  return GetCommandOutput(master.primary,
525 288d6440 Michael Hanselmann
                          utils.ShellQuoteArgs(cmd)).splitlines()
526 288d6440 Michael Hanselmann
527 288d6440 Michael Hanselmann
528 6d1e4845 Michael Hanselmann
def GenericQueryTest(cmd, fields, namefield="name", test_unknown=True):
529 288d6440 Michael Hanselmann
  """Runs a number of tests on query commands.
530 288d6440 Michael Hanselmann

531 288d6440 Michael Hanselmann
  @param cmd: Command name
532 288d6440 Michael Hanselmann
  @param fields: List of field names
533 288d6440 Michael Hanselmann

534 288d6440 Michael Hanselmann
  """
535 288d6440 Michael Hanselmann
  rnd = random.Random(hash(cmd))
536 288d6440 Michael Hanselmann
537 3582eef6 Iustin Pop
  fields = list(fields)
538 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
539 288d6440 Michael Hanselmann
540 288d6440 Michael Hanselmann
  # Test a number of field combinations
541 288d6440 Michael Hanselmann
  for testfields in _SelectQueryFields(rnd, fields):
542 93146c8c Iustin Pop
    AssertRedirectedCommand([cmd, "list", "--output", ",".join(testfields)])
543 288d6440 Michael Hanselmann
544 0fdf247d Michael Hanselmann
  if namefield is not None:
545 0fdf247d Michael Hanselmann
    namelist_fn = compat.partial(_List, cmd, [namefield])
546 288d6440 Michael Hanselmann
547 0fdf247d Michael Hanselmann
    # When no names were requested, the list must be sorted
548 0fdf247d Michael Hanselmann
    names = namelist_fn(None)
549 0fdf247d Michael Hanselmann
    AssertEqual(names, utils.NiceSort(names))
550 288d6440 Michael Hanselmann
551 0fdf247d Michael Hanselmann
    # When requesting specific names, the order must be kept
552 0fdf247d Michael Hanselmann
    revnames = list(reversed(names))
553 0fdf247d Michael Hanselmann
    AssertEqual(namelist_fn(revnames), revnames)
554 288d6440 Michael Hanselmann
555 0fdf247d Michael Hanselmann
    randnames = list(names)
556 0fdf247d Michael Hanselmann
    rnd.shuffle(randnames)
557 0fdf247d Michael Hanselmann
    AssertEqual(namelist_fn(randnames), randnames)
558 288d6440 Michael Hanselmann
559 6d1e4845 Michael Hanselmann
  if test_unknown:
560 6d1e4845 Michael Hanselmann
    # Listing unknown items must fail
561 6d1e4845 Michael Hanselmann
    AssertCommand([cmd, "list", "this.name.certainly.does.not.exist"],
562 6d1e4845 Michael Hanselmann
                  fail=True)
563 2214cf14 Michael Hanselmann
564 2214cf14 Michael Hanselmann
  # Check exit code for listing unknown field
565 93146c8c Iustin Pop
  AssertEqual(AssertRedirectedCommand([cmd, "list",
566 93146c8c Iustin Pop
                                       "--output=field/does/not/exist"],
567 93146c8c Iustin Pop
                                      fail=True),
568 2214cf14 Michael Hanselmann
              constants.EXIT_UNKNOWN_FIELD)
569 2214cf14 Michael Hanselmann
570 2214cf14 Michael Hanselmann
571 2214cf14 Michael Hanselmann
def GenericQueryFieldsTest(cmd, fields):
572 2214cf14 Michael Hanselmann
  master = qa_config.GetMasterNode()
573 2214cf14 Michael Hanselmann
574 2214cf14 Michael Hanselmann
  # Listing fields
575 93146c8c Iustin Pop
  AssertRedirectedCommand([cmd, "list-fields"])
576 93146c8c Iustin Pop
  AssertRedirectedCommand([cmd, "list-fields"] + fields)
577 2214cf14 Michael Hanselmann
578 2214cf14 Michael Hanselmann
  # Check listed fields (all, must be sorted)
579 2214cf14 Michael Hanselmann
  realcmd = [cmd, "list-fields", "--separator=|", "--no-headers"]
580 aecba21e Michael Hanselmann
  output = GetCommandOutput(master.primary,
581 2214cf14 Michael Hanselmann
                            utils.ShellQuoteArgs(realcmd)).splitlines()
582 2214cf14 Michael Hanselmann
  AssertEqual([line.split("|", 1)[0] for line in output],
583 c694367b Michael Hanselmann
              utils.NiceSort(fields))
584 2214cf14 Michael Hanselmann
585 2214cf14 Michael Hanselmann
  # Check exit code for listing unknown field
586 2214cf14 Michael Hanselmann
  AssertEqual(AssertCommand([cmd, "list-fields", "field/does/not/exist"],
587 2214cf14 Michael Hanselmann
                            fail=True),
588 2214cf14 Michael Hanselmann
              constants.EXIT_UNKNOWN_FIELD)
589 2214cf14 Michael Hanselmann
590 288d6440 Michael Hanselmann
591 dfe11bad Michael Hanselmann
def _FormatWithColor(text, seq):
592 dfe11bad Michael Hanselmann
  if not seq:
593 dfe11bad Michael Hanselmann
    return text
594 dfe11bad Michael Hanselmann
  return "%s%s%s" % (seq, text, _RESET_SEQ)
595 23269c5b Michael Hanselmann
596 23269c5b Michael Hanselmann
597 dfe11bad Michael Hanselmann
FormatWarning = lambda text: _FormatWithColor(text, _WARNING_SEQ)
598 dfe11bad Michael Hanselmann
FormatError = lambda text: _FormatWithColor(text, _ERROR_SEQ)
599 dfe11bad Michael Hanselmann
FormatInfo = lambda text: _FormatWithColor(text, _INFO_SEQ)
600 31fe5102 René Nussbaumer
601 31fe5102 René Nussbaumer
602 31fe5102 René Nussbaumer
def AddToEtcHosts(hostnames):
603 31fe5102 René Nussbaumer
  """Adds hostnames to /etc/hosts.
604 31fe5102 René Nussbaumer

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

607 31fe5102 René Nussbaumer
  """
608 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
609 aecba21e Michael Hanselmann
  tmp_hosts = UploadData(master.primary, "", mode=0644)
610 31fe5102 René Nussbaumer
611 31fe5102 René Nussbaumer
  data = []
612 31fe5102 René Nussbaumer
  for localhost in ("::1", "127.0.0.1"):
613 31fe5102 René Nussbaumer
    data.append("%s %s" % (localhost, " ".join(hostnames)))
614 31fe5102 René Nussbaumer
615 31fe5102 René Nussbaumer
  try:
616 48967eb0 Michael Hanselmann
    AssertCommand("{ cat %s && echo -e '%s'; } > %s && mv %s %s" %
617 48967eb0 Michael Hanselmann
                  (utils.ShellQuote(pathutils.ETC_HOSTS),
618 48967eb0 Michael Hanselmann
                   "\\n".join(data),
619 48967eb0 Michael Hanselmann
                   utils.ShellQuote(tmp_hosts),
620 48967eb0 Michael Hanselmann
                   utils.ShellQuote(tmp_hosts),
621 48967eb0 Michael Hanselmann
                   utils.ShellQuote(pathutils.ETC_HOSTS)))
622 48967eb0 Michael Hanselmann
  except Exception:
623 48967eb0 Michael Hanselmann
    AssertCommand(["rm", "-f", tmp_hosts])
624 48967eb0 Michael Hanselmann
    raise
625 31fe5102 René Nussbaumer
626 31fe5102 René Nussbaumer
627 31fe5102 René Nussbaumer
def RemoveFromEtcHosts(hostnames):
628 31fe5102 René Nussbaumer
  """Remove hostnames from /etc/hosts.
629 31fe5102 René Nussbaumer

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

632 31fe5102 René Nussbaumer
  """
633 31fe5102 René Nussbaumer
  master = qa_config.GetMasterNode()
634 aecba21e Michael Hanselmann
  tmp_hosts = UploadData(master.primary, "", mode=0644)
635 31fe5102 René Nussbaumer
  quoted_tmp_hosts = utils.ShellQuote(tmp_hosts)
636 31fe5102 René Nussbaumer
637 31fe5102 René Nussbaumer
  sed_data = " ".join(hostnames)
638 31fe5102 René Nussbaumer
  try:
639 48967eb0 Michael Hanselmann
    AssertCommand(("sed -e '/^\(::1\|127\.0\.0\.1\)\s\+%s/d' %s > %s"
640 48967eb0 Michael Hanselmann
                   " && mv %s %s") %
641 48967eb0 Michael Hanselmann
                   (sed_data, utils.ShellQuote(pathutils.ETC_HOSTS),
642 48967eb0 Michael Hanselmann
                    quoted_tmp_hosts, quoted_tmp_hosts,
643 48967eb0 Michael Hanselmann
                    utils.ShellQuote(pathutils.ETC_HOSTS)))
644 48967eb0 Michael Hanselmann
  except Exception:
645 48967eb0 Michael Hanselmann
    AssertCommand(["rm", "-f", tmp_hosts])
646 48967eb0 Michael Hanselmann
    raise
647 c9e05005 Michael Hanselmann
648 c9e05005 Michael Hanselmann
649 c9e05005 Michael Hanselmann
def RunInstanceCheck(instance, running):
650 c9e05005 Michael Hanselmann
  """Check if instance is running or not.
651 c9e05005 Michael Hanselmann

652 c9e05005 Michael Hanselmann
  """
653 6998aefe Michael Hanselmann
  instance_name = _GetName(instance, operator.attrgetter("name"))
654 2ac35588 Michael Hanselmann
655 c9e05005 Michael Hanselmann
  script = qa_config.GetInstanceCheckScript()
656 c9e05005 Michael Hanselmann
  if not script:
657 c9e05005 Michael Hanselmann
    return
658 c9e05005 Michael Hanselmann
659 c9e05005 Michael Hanselmann
  master_node = qa_config.GetMasterNode()
660 c9e05005 Michael Hanselmann
661 c9e05005 Michael Hanselmann
  # Build command to connect to master node
662 aecba21e Michael Hanselmann
  master_ssh = GetSSHCommand(master_node.primary, "--")
663 c9e05005 Michael Hanselmann
664 c9e05005 Michael Hanselmann
  if running:
665 c9e05005 Michael Hanselmann
    running_shellval = "1"
666 c9e05005 Michael Hanselmann
    running_text = ""
667 c9e05005 Michael Hanselmann
  else:
668 c9e05005 Michael Hanselmann
    running_shellval = ""
669 c9e05005 Michael Hanselmann
    running_text = "not "
670 c9e05005 Michael Hanselmann
671 c9e05005 Michael Hanselmann
  print FormatInfo("Checking if instance '%s' is %srunning" %
672 c9e05005 Michael Hanselmann
                   (instance_name, running_text))
673 c9e05005 Michael Hanselmann
674 c9e05005 Michael Hanselmann
  args = [script, instance_name]
675 c9e05005 Michael Hanselmann
  env = {
676 c9e05005 Michael Hanselmann
    "PATH": constants.HOOKS_PATH,
677 c9e05005 Michael Hanselmann
    "RUN_UUID": _RUN_UUID,
678 c9e05005 Michael Hanselmann
    "MASTER_SSH": utils.ShellQuoteArgs(master_ssh),
679 c9e05005 Michael Hanselmann
    "INSTANCE_NAME": instance_name,
680 c9e05005 Michael Hanselmann
    "INSTANCE_RUNNING": running_shellval,
681 c9e05005 Michael Hanselmann
    }
682 c9e05005 Michael Hanselmann
683 c9e05005 Michael Hanselmann
  result = os.spawnve(os.P_WAIT, script, args, env)
684 c9e05005 Michael Hanselmann
  if result != 0:
685 c9e05005 Michael Hanselmann
    raise qa_error.Error("Instance check failed with result %s" % result)
686 c9e05005 Michael Hanselmann
687 c9e05005 Michael Hanselmann
688 c9e05005 Michael Hanselmann
def _InstanceCheckInner(expected, instarg, args, result):
689 c9e05005 Michael Hanselmann
  """Helper function used by L{InstanceCheck}.
690 c9e05005 Michael Hanselmann

691 c9e05005 Michael Hanselmann
  """
692 c9e05005 Michael Hanselmann
  if instarg == FIRST_ARG:
693 c9e05005 Michael Hanselmann
    instance = args[0]
694 c9e05005 Michael Hanselmann
  elif instarg == RETURN_VALUE:
695 c9e05005 Michael Hanselmann
    instance = result
696 c9e05005 Michael Hanselmann
  else:
697 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s' for instance argument" % instarg)
698 c9e05005 Michael Hanselmann
699 c9e05005 Michael Hanselmann
  if expected in (INST_DOWN, INST_UP):
700 c9e05005 Michael Hanselmann
    RunInstanceCheck(instance, (expected == INST_UP))
701 c9e05005 Michael Hanselmann
  elif expected is not None:
702 c9e05005 Michael Hanselmann
    raise Exception("Invalid value '%s'" % expected)
703 c9e05005 Michael Hanselmann
704 c9e05005 Michael Hanselmann
705 c9e05005 Michael Hanselmann
def InstanceCheck(before, after, instarg):
706 c9e05005 Michael Hanselmann
  """Decorator to check instance status before and after test.
707 c9e05005 Michael Hanselmann

708 c9e05005 Michael Hanselmann
  @param before: L{INST_DOWN} if instance must be stopped before test,
709 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running before test, L{None} to not check.
710 c9e05005 Michael Hanselmann
  @param after: L{INST_DOWN} if instance must be stopped after test,
711 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running after test, L{None} to not check.
712 c9e05005 Michael Hanselmann
  @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
713 c9e05005 Michael Hanselmann
    dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
714 c9e05005 Michael Hanselmann

715 c9e05005 Michael Hanselmann
  """
716 c9e05005 Michael Hanselmann
  def decorator(fn):
717 c9e05005 Michael Hanselmann
    @functools.wraps(fn)
718 c9e05005 Michael Hanselmann
    def wrapper(*args, **kwargs):
719 c9e05005 Michael Hanselmann
      _InstanceCheckInner(before, instarg, args, NotImplemented)
720 c9e05005 Michael Hanselmann
721 c9e05005 Michael Hanselmann
      result = fn(*args, **kwargs)
722 c9e05005 Michael Hanselmann
723 c9e05005 Michael Hanselmann
      _InstanceCheckInner(after, instarg, args, result)
724 c9e05005 Michael Hanselmann
725 c9e05005 Michael Hanselmann
      return result
726 c9e05005 Michael Hanselmann
    return wrapper
727 c9e05005 Michael Hanselmann
  return decorator
728 b4d2d2cb Michael Hanselmann
729 b4d2d2cb Michael Hanselmann
730 b4d2d2cb Michael Hanselmann
def GetNonexistentGroups(count):
731 b4d2d2cb Michael Hanselmann
  """Gets group names which shouldn't exist on the cluster.
732 b4d2d2cb Michael Hanselmann

733 b4d2d2cb Michael Hanselmann
  @param count: Number of groups to get
734 ea7693c1 Helga Velroyen
  @rtype: integer
735 b4d2d2cb Michael Hanselmann

736 b4d2d2cb Michael Hanselmann
  """
737 ea7693c1 Helga Velroyen
  return GetNonexistentEntityNames(count, "groups", "group")
738 b4d2d2cb Michael Hanselmann
739 ea7693c1 Helga Velroyen
740 ea7693c1 Helga Velroyen
def GetNonexistentEntityNames(count, name_config, name_prefix):
741 ea7693c1 Helga Velroyen
  """Gets entity names which shouldn't exist on the cluster.
742 ea7693c1 Helga Velroyen

743 ea7693c1 Helga Velroyen
  The actualy names can refer to arbitrary entities (for example
744 ea7693c1 Helga Velroyen
  groups, networks).
745 ea7693c1 Helga Velroyen

746 ea7693c1 Helga Velroyen
  @param count: Number of names to get
747 ea7693c1 Helga Velroyen
  @rtype: integer
748 ea7693c1 Helga Velroyen
  @param name_config: name of the leaf in the config containing
749 ea7693c1 Helga Velroyen
    this entity's configuration, including a 'inexistent-'
750 ea7693c1 Helga Velroyen
    element
751 ea7693c1 Helga Velroyen
  @rtype: string
752 ea7693c1 Helga Velroyen
  @param name_prefix: prefix of the entity's names, used to compose
753 ea7693c1 Helga Velroyen
    the default values; for example for groups, the prefix is
754 ea7693c1 Helga Velroyen
    'group' and the generated names are then group1, group2, ...
755 ea7693c1 Helga Velroyen
  @rtype: string
756 ea7693c1 Helga Velroyen

757 ea7693c1 Helga Velroyen
  """
758 ea7693c1 Helga Velroyen
  entities = qa_config.get(name_config, {})
759 ea7693c1 Helga Velroyen
760 ea7693c1 Helga Velroyen
  default = [name_prefix + str(i) for i in range(count)]
761 b4d2d2cb Michael Hanselmann
  assert count <= len(default)
762 b4d2d2cb Michael Hanselmann
763 ea7693c1 Helga Velroyen
  name_config_inexistent = "inexistent-" + name_config
764 ea7693c1 Helga Velroyen
  candidates = entities.get(name_config_inexistent, default)[:count]
765 b4d2d2cb Michael Hanselmann
766 b4d2d2cb Michael Hanselmann
  if len(candidates) < count:
767 ea7693c1 Helga Velroyen
    raise Exception("At least %s non-existent %s are needed" %
768 ea7693c1 Helga Velroyen
                    (count, name_config))
769 b4d2d2cb Michael Hanselmann
770 b4d2d2cb Michael Hanselmann
  return candidates
771 7160f14a Michael Hanselmann
772 7160f14a Michael Hanselmann
773 7160f14a Michael Hanselmann
def MakeNodePath(node, path):
774 7160f14a Michael Hanselmann
  """Builds an absolute path for a virtual node.
775 7160f14a Michael Hanselmann

776 7160f14a Michael Hanselmann
  @type node: string or L{qa_config._QaNode}
777 7160f14a Michael Hanselmann
  @param node: Node
778 7160f14a Michael Hanselmann
  @type path: string
779 7160f14a Michael Hanselmann
  @param path: Path without node-specific prefix
780 7160f14a Michael Hanselmann

781 7160f14a Michael Hanselmann
  """
782 7160f14a Michael Hanselmann
  (_, basedir) = qa_config.GetVclusterSettings()
783 7160f14a Michael Hanselmann
784 7160f14a Michael Hanselmann
  if isinstance(node, basestring):
785 7160f14a Michael Hanselmann
    name = node
786 7160f14a Michael Hanselmann
  else:
787 7160f14a Michael Hanselmann
    name = node.primary
788 7160f14a Michael Hanselmann
789 7160f14a Michael Hanselmann
  if basedir:
790 7160f14a Michael Hanselmann
    assert path.startswith("/")
791 7160f14a Michael Hanselmann
    return "%s%s" % (vcluster.MakeNodeRoot(basedir, name), path)
792 7160f14a Michael Hanselmann
  else:
793 7160f14a Michael Hanselmann
    return path