Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ d3737aca

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

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

113 eaef8a05 Michael Hanselmann
  """
114 eaef8a05 Michael Hanselmann
  if item not in sequence:
115 d0c8c01d Iustin Pop
    raise qa_error.Error("%r not in %r" % (item, sequence))
116 eaef8a05 Michael Hanselmann
117 eaef8a05 Michael Hanselmann
118 79eac09b Michael Hanselmann
def AssertNotIn(item, sequence):
119 79eac09b Michael Hanselmann
  """Raises an error when item is in sequence.
120 79eac09b Michael Hanselmann

121 79eac09b Michael Hanselmann
  """
122 79eac09b Michael Hanselmann
  if item in sequence:
123 d0c8c01d Iustin Pop
    raise qa_error.Error("%r in %r" % (item, sequence))
124 79eac09b Michael Hanselmann
125 79eac09b Michael Hanselmann
126 e8ae0c20 Michael Hanselmann
def AssertEqual(first, second):
127 cec9845c Michael Hanselmann
  """Raises an error when values aren't equal.
128 cec9845c Michael Hanselmann

129 cec9845c Michael Hanselmann
  """
130 cec9845c Michael Hanselmann
  if not first == second:
131 d0c8c01d Iustin Pop
    raise qa_error.Error("%r == %r" % (first, second))
132 e8ae0c20 Michael Hanselmann
133 e8ae0c20 Michael Hanselmann
134 e6ce18ac René Nussbaumer
def AssertMatch(string, pattern):
135 e6ce18ac René Nussbaumer
  """Raises an error when string doesn't match regexp pattern.
136 e6ce18ac René Nussbaumer

137 e6ce18ac René Nussbaumer
  """
138 e6ce18ac René Nussbaumer
  if not re.match(pattern, string):
139 e6ce18ac René Nussbaumer
    raise qa_error.Error("%r doesn't match /%r/" % (string, pattern))
140 e6ce18ac René Nussbaumer
141 e6ce18ac René Nussbaumer
142 6998aefe Michael Hanselmann
def _GetName(entity, fn):
143 889bed16 Michael Hanselmann
  """Tries to get name of an entity.
144 889bed16 Michael Hanselmann

145 889bed16 Michael Hanselmann
  @type entity: string or dict
146 6998aefe Michael Hanselmann
  @param fn: Function retrieving name from entity
147 889bed16 Michael Hanselmann

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

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

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

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

207 afd5ca04 Iustin Pop
  The log will go to the qa-output log file in the ganeti log
208 afd5ca04 Iustin Pop
  directory on the node where the command is executed. The fail and
209 afd5ca04 Iustin Pop
  node parameters are passed unchanged to AssertCommand.
210 afd5ca04 Iustin Pop

211 afd5ca04 Iustin Pop
  @param cmd: the command to be executed, as a list; a string is not
212 afd5ca04 Iustin Pop
      supported
213 afd5ca04 Iustin Pop

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

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

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

288 5d831182 Michael Hanselmann
  """
289 56b9f2db Iustin Pop
  if log_cmd:
290 56b9f2db Iustin Pop
    if _nolog_opts:
291 56b9f2db Iustin Pop
      pcmd = [i for i in cmd if not i.startswith("-")]
292 56b9f2db Iustin Pop
    else:
293 56b9f2db Iustin Pop
      pcmd = cmd
294 d5a9b556 Petr Pudlak
    print "%s %s" % (colors.colorize("Command:", colors.CYAN),
295 d5a9b556 Petr Pudlak
                     utils.ShellQuoteArgs(pcmd))
296 5d831182 Michael Hanselmann
  return subprocess.Popen(cmd, shell=False, **kwargs)
297 5d831182 Michael Hanselmann
298 5d831182 Michael Hanselmann
299 56b9f2db Iustin Pop
def StartSSH(node, cmd, strict=True, log_cmd=True):
300 cec9845c Michael Hanselmann
  """Starts SSH.
301 cec9845c Michael Hanselmann

302 cec9845c Michael Hanselmann
  """
303 710bc88c Iustin Pop
  return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict),
304 56b9f2db Iustin Pop
                           _nolog_opts=True, log_cmd=log_cmd)
305 4b62db14 Michael Hanselmann
306 4b62db14 Michael Hanselmann
307 f7e6f3c8 Iustin Pop
def StartMultiplexer(node):
308 f7e6f3c8 Iustin Pop
  """Starts a multiplexer command.
309 f7e6f3c8 Iustin Pop

310 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
311 f7e6f3c8 Iustin Pop

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

328 f7e6f3c8 Iustin Pop
  """
329 f7e6f3c8 Iustin Pop
  for node in _MULTIPLEXERS.keys():
330 f7e6f3c8 Iustin Pop
    (sname, child) = _MULTIPLEXERS.pop(node)
331 f7e6f3c8 Iustin Pop
    utils.KillProcess(child.pid, timeout=10, waitpid=True)
332 f7e6f3c8 Iustin Pop
    utils.RemoveFile(sname)
333 f7e6f3c8 Iustin Pop
334 f7e6f3c8 Iustin Pop
335 587f8ff6 Bernardo Dal Seno
def GetCommandOutput(node, cmd, tty=None, fail=False):
336 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
337 4b62db14 Michael Hanselmann

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

358 0e79564a Bernardo Dal Seno
  @type infocmd: list of strings
359 0e79564a Bernardo Dal Seno
  @param infocmd: command to be executed, e.g. ["gnt-cluster", "info"]
360 0e79564a Bernardo Dal Seno
  @return: the information parsed, appropriately stored in dictionaries,
361 0e79564a Bernardo Dal Seno
      lists...
362 0e79564a Bernardo Dal Seno

363 0e79564a Bernardo Dal Seno
  """
364 0e79564a Bernardo Dal Seno
  master = qa_config.GetMasterNode()
365 0e79564a Bernardo Dal Seno
  cmdline = utils.ShellQuoteArgs(infocmd)
366 0e79564a Bernardo Dal Seno
  info_out = GetCommandOutput(master.primary, cmdline)
367 0e79564a Bernardo Dal Seno
  return yaml.load(info_out)
368 0e79564a Bernardo Dal Seno
369 0e79564a Bernardo Dal Seno
370 cec9845c Michael Hanselmann
def UploadFile(node, src):
371 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
372 cec9845c Michael Hanselmann

373 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
374 cec9845c Michael Hanselmann
  anymore.
375 49d50e52 Michael Hanselmann

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

401 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
402 b9955569 René Nussbaumer
  anymore.
403 b9955569 René Nussbaumer

404 b9955569 René Nussbaumer
  """
405 b9955569 René Nussbaumer
  if filename:
406 b9955569 René Nussbaumer
    tmp = "tmp=%s" % utils.ShellQuote(filename)
407 b9955569 René Nussbaumer
  else:
408 f90a2d0c Thomas Thrainer
    tmp = ('tmp=$(mktemp --tmpdir gnt.XXXXXX) && '
409 f90a2d0c Thomas Thrainer
           'chmod %o "${tmp}"') % mode
410 b9955569 René Nussbaumer
  cmd = ("%s && "
411 b9955569 René Nussbaumer
         "[[ -f \"${tmp}\" ]] && "
412 b9955569 René Nussbaumer
         "cat > \"${tmp}\" && "
413 b9955569 René Nussbaumer
         "echo \"${tmp}\"") % tmp
414 b9955569 René Nussbaumer
415 b9955569 René Nussbaumer
  p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False,
416 b9955569 René Nussbaumer
                       stdin=subprocess.PIPE, stdout=subprocess.PIPE)
417 b9955569 René Nussbaumer
  p.stdin.write(data)
418 b9955569 René Nussbaumer
  p.stdin.close()
419 b9955569 René Nussbaumer
  AssertEqual(p.wait(), 0)
420 b9955569 René Nussbaumer
421 b9955569 René Nussbaumer
  # Return temporary filename
422 b9955569 René Nussbaumer
  return p.stdout.read().strip()
423 b9955569 René Nussbaumer
424 b9955569 René Nussbaumer
425 49d50e52 Michael Hanselmann
def BackupFile(node, path):
426 49d50e52 Michael Hanselmann
  """Creates a backup of a file on the node and returns the filename.
427 49d50e52 Michael Hanselmann

428 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
429 49d50e52 Michael Hanselmann
  anymore.
430 49d50e52 Michael Hanselmann

431 49d50e52 Michael Hanselmann
  """
432 7160f14a Michael Hanselmann
  vpath = MakeNodePath(node, path)
433 7160f14a Michael Hanselmann
434 f90a2d0c Thomas Thrainer
  cmd = ("tmp=$(mktemp .gnt.XXXXXX --tmpdir=$(dirname %s)) && "
435 49d50e52 Michael Hanselmann
         "[[ -f \"$tmp\" ]] && "
436 49d50e52 Michael Hanselmann
         "cp %s $tmp && "
437 7160f14a Michael Hanselmann
         "echo $tmp") % (utils.ShellQuote(vpath), utils.ShellQuote(vpath))
438 49d50e52 Michael Hanselmann
439 49d50e52 Michael Hanselmann
  # Return temporary filename
440 7160f14a Michael Hanselmann
  result = GetCommandOutput(node, cmd).strip()
441 7160f14a Michael Hanselmann
442 7160f14a Michael Hanselmann
  print "Backup filename: %s" % result
443 7160f14a Michael Hanselmann
444 7160f14a Michael Hanselmann
  return result
445 49d50e52 Michael Hanselmann
446 49d50e52 Michael Hanselmann
447 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
448 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
449 5d640672 Michael Hanselmann

450 46f9a948 Michael Hanselmann
  @type instance: string
451 46f9a948 Michael Hanselmann
  @param instance: Instance name
452 46f9a948 Michael Hanselmann

453 5d640672 Michael Hanselmann
  """
454 2cbcf95d Bernardo Dal Seno
  info = GetObjectInfo(["gnt-instance", "info", instance])
455 2cbcf95d Bernardo Dal Seno
  return info[0]["Instance name"]
456 4b62db14 Michael Hanselmann
457 4b62db14 Michael Hanselmann
458 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
459 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
460 4b62db14 Michael Hanselmann

461 4b62db14 Michael Hanselmann
  """
462 5f6d1b42 Bernardo Dal Seno
  info = GetObjectInfo(["gnt-node", "info", node.primary])
463 5f6d1b42 Bernardo Dal Seno
  return info[0]["Node name"]
464 4b62db14 Michael Hanselmann
465 4b62db14 Michael Hanselmann
466 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
467 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
468 4b62db14 Michael Hanselmann

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

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

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

527 288d6440 Michael Hanselmann
  @param cmd: Command name
528 288d6440 Michael Hanselmann
  @param fields: List of field names
529 288d6440 Michael Hanselmann

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

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

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

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

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

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

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

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

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

729 b4d2d2cb Michael Hanselmann
  @param count: Number of groups to get
730 ea7693c1 Helga Velroyen
  @rtype: integer
731 b4d2d2cb Michael Hanselmann

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

739 ea7693c1 Helga Velroyen
  The actualy names can refer to arbitrary entities (for example
740 ea7693c1 Helga Velroyen
  groups, networks).
741 ea7693c1 Helga Velroyen

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

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

772 7160f14a Michael Hanselmann
  @type node: string or L{qa_config._QaNode}
773 7160f14a Michael Hanselmann
  @param node: Node
774 7160f14a Michael Hanselmann
  @type path: string
775 7160f14a Michael Hanselmann
  @param path: Path without node-specific prefix
776 7160f14a Michael Hanselmann

777 7160f14a Michael Hanselmann
  """
778 7160f14a Michael Hanselmann
  (_, basedir) = qa_config.GetVclusterSettings()
779 7160f14a Michael Hanselmann
780 7160f14a Michael Hanselmann
  if isinstance(node, basestring):
781 7160f14a Michael Hanselmann
    name = node
782 7160f14a Michael Hanselmann
  else:
783 7160f14a Michael Hanselmann
    name = node.primary
784 7160f14a Michael Hanselmann
785 7160f14a Michael Hanselmann
  if basedir:
786 7160f14a Michael Hanselmann
    assert path.startswith("/")
787 7160f14a Michael Hanselmann
    return "%s%s" % (vcluster.MakeNodeRoot(basedir, name), path)
788 7160f14a Michael Hanselmann
  else:
789 7160f14a Michael Hanselmann
    return path
790 63e08b25 Bernardo Dal Seno
791 63e08b25 Bernardo Dal Seno
792 ec996117 Bernardo Dal Seno
def _GetParameterOptions(specs):
793 63e08b25 Bernardo Dal Seno
  """Helper to build policy options."""
794 ec996117 Bernardo Dal Seno
  values = ["%s=%s" % (par, val)
795 ec996117 Bernardo Dal Seno
            for (par, val) in specs.items()]
796 63e08b25 Bernardo Dal Seno
  return ",".join(values)
797 63e08b25 Bernardo Dal Seno
798 63e08b25 Bernardo Dal Seno
799 ec996117 Bernardo Dal Seno
def TestSetISpecs(new_specs=None, diff_specs=None, get_policy_fn=None,
800 ec996117 Bernardo Dal Seno
                  build_cmd_fn=None, fail=False, old_values=None):
801 63e08b25 Bernardo Dal Seno
  """Change instance specs for an object.
802 63e08b25 Bernardo Dal Seno

803 ec996117 Bernardo Dal Seno
  At most one of new_specs or diff_specs can be specified.
804 ec996117 Bernardo Dal Seno

805 ec996117 Bernardo Dal Seno
  @type new_specs: dict
806 ec996117 Bernardo Dal Seno
  @param new_specs: new complete specs, in the same format returned by
807 ec996117 Bernardo Dal Seno
      L{ParseIPolicy}.
808 ec996117 Bernardo Dal Seno
  @type diff_specs: dict
809 7c8ae421 Bernardo Dal Seno
  @param diff_specs: partial specs, it can be an incomplete specifications, but
810 7c8ae421 Bernardo Dal Seno
      if min/max specs are specified, their number must match the number of the
811 7c8ae421 Bernardo Dal Seno
      existing specs
812 63e08b25 Bernardo Dal Seno
  @type get_policy_fn: function
813 63e08b25 Bernardo Dal Seno
  @param get_policy_fn: function that returns the current policy as in
814 ec996117 Bernardo Dal Seno
      L{ParseIPolicy}
815 63e08b25 Bernardo Dal Seno
  @type build_cmd_fn: function
816 63e08b25 Bernardo Dal Seno
  @param build_cmd_fn: function that return the full command line from the
817 63e08b25 Bernardo Dal Seno
      options alone
818 63e08b25 Bernardo Dal Seno
  @type fail: bool
819 63e08b25 Bernardo Dal Seno
  @param fail: if the change is expected to fail
820 63e08b25 Bernardo Dal Seno
  @type old_values: tuple
821 63e08b25 Bernardo Dal Seno
  @param old_values: (old_policy, old_specs), as returned by
822 ec996117 Bernardo Dal Seno
     L{ParseIPolicy}
823 ec996117 Bernardo Dal Seno
  @return: same as L{ParseIPolicy}
824 63e08b25 Bernardo Dal Seno

825 63e08b25 Bernardo Dal Seno
  """
826 63e08b25 Bernardo Dal Seno
  assert get_policy_fn is not None
827 63e08b25 Bernardo Dal Seno
  assert build_cmd_fn is not None
828 ec996117 Bernardo Dal Seno
  assert new_specs is None or diff_specs is None
829 63e08b25 Bernardo Dal Seno
830 63e08b25 Bernardo Dal Seno
  if old_values:
831 63e08b25 Bernardo Dal Seno
    (old_policy, old_specs) = old_values
832 63e08b25 Bernardo Dal Seno
  else:
833 63e08b25 Bernardo Dal Seno
    (old_policy, old_specs) = get_policy_fn()
834 ec996117 Bernardo Dal Seno
835 ec996117 Bernardo Dal Seno
  if diff_specs:
836 ec996117 Bernardo Dal Seno
    new_specs = copy.deepcopy(old_specs)
837 7c8ae421 Bernardo Dal Seno
    if constants.ISPECS_MINMAX in diff_specs:
838 7c8ae421 Bernardo Dal Seno
      AssertEqual(len(new_specs[constants.ISPECS_MINMAX]),
839 7c8ae421 Bernardo Dal Seno
                  len(diff_specs[constants.ISPECS_MINMAX]))
840 7c8ae421 Bernardo Dal Seno
      for (new_minmax, diff_minmax) in zip(new_specs[constants.ISPECS_MINMAX],
841 7c8ae421 Bernardo Dal Seno
                                           diff_specs[constants.ISPECS_MINMAX]):
842 7c8ae421 Bernardo Dal Seno
        for (key, parvals) in diff_minmax.items():
843 7c8ae421 Bernardo Dal Seno
          for (par, val) in parvals.items():
844 7c8ae421 Bernardo Dal Seno
            new_minmax[key][par] = val
845 7c8ae421 Bernardo Dal Seno
    for (par, val) in diff_specs.get(constants.ISPECS_STD, {}).items():
846 7c8ae421 Bernardo Dal Seno
      new_specs[constants.ISPECS_STD][par] = val
847 ec996117 Bernardo Dal Seno
848 63e08b25 Bernardo Dal Seno
  if new_specs:
849 63e08b25 Bernardo Dal Seno
    cmd = []
850 7c8ae421 Bernardo Dal Seno
    if (diff_specs is None or constants.ISPECS_MINMAX in diff_specs):
851 63e08b25 Bernardo Dal Seno
      minmax_opt_items = []
852 7c8ae421 Bernardo Dal Seno
      for minmax in new_specs[constants.ISPECS_MINMAX]:
853 7c8ae421 Bernardo Dal Seno
        minmax_opts = []
854 7c8ae421 Bernardo Dal Seno
        for key in ["min", "max"]:
855 7c8ae421 Bernardo Dal Seno
          keyopt = _GetParameterOptions(minmax[key])
856 7c8ae421 Bernardo Dal Seno
          minmax_opts.append("%s:%s" % (key, keyopt))
857 7c8ae421 Bernardo Dal Seno
        minmax_opt_items.append("/".join(minmax_opts))
858 63e08b25 Bernardo Dal Seno
      cmd.extend([
859 63e08b25 Bernardo Dal Seno
        "--ipolicy-bounds-specs",
860 7c8ae421 Bernardo Dal Seno
        "//".join(minmax_opt_items)
861 63e08b25 Bernardo Dal Seno
        ])
862 7c8ae421 Bernardo Dal Seno
    if diff_specs is None:
863 ec996117 Bernardo Dal Seno
      std_source = new_specs
864 7c8ae421 Bernardo Dal Seno
    else:
865 7c8ae421 Bernardo Dal Seno
      std_source = diff_specs
866 ec996117 Bernardo Dal Seno
    std_opt = _GetParameterOptions(std_source.get("std", {}))
867 63e08b25 Bernardo Dal Seno
    if std_opt:
868 63e08b25 Bernardo Dal Seno
      cmd.extend(["--ipolicy-std-specs", std_opt])
869 63e08b25 Bernardo Dal Seno
    AssertCommand(build_cmd_fn(cmd), fail=fail)
870 63e08b25 Bernardo Dal Seno
871 ec996117 Bernardo Dal Seno
    # Check the new state
872 ec996117 Bernardo Dal Seno
    (eff_policy, eff_specs) = get_policy_fn()
873 ec996117 Bernardo Dal Seno
    AssertEqual(eff_policy, old_policy)
874 ec996117 Bernardo Dal Seno
    if fail:
875 ec996117 Bernardo Dal Seno
      AssertEqual(eff_specs, old_specs)
876 ec996117 Bernardo Dal Seno
    else:
877 ec996117 Bernardo Dal Seno
      AssertEqual(eff_specs, new_specs)
878 ec996117 Bernardo Dal Seno
879 63e08b25 Bernardo Dal Seno
  else:
880 ec996117 Bernardo Dal Seno
    (eff_policy, eff_specs) = (old_policy, old_specs)
881 ec996117 Bernardo Dal Seno
882 63e08b25 Bernardo Dal Seno
  return (eff_policy, eff_specs)
883 63e08b25 Bernardo Dal Seno
884 63e08b25 Bernardo Dal Seno
885 63e08b25 Bernardo Dal Seno
def ParseIPolicy(policy):
886 63e08b25 Bernardo Dal Seno
  """Parse and split instance an instance policy.
887 63e08b25 Bernardo Dal Seno

888 63e08b25 Bernardo Dal Seno
  @type policy: dict
889 63e08b25 Bernardo Dal Seno
  @param policy: policy, as returned by L{GetObjectInfo}
890 63e08b25 Bernardo Dal Seno
  @rtype: tuple
891 63e08b25 Bernardo Dal Seno
  @return: (policy, specs), where:
892 63e08b25 Bernardo Dal Seno
      - policy is a dictionary of the policy values, instance specs excluded
893 7c8ae421 Bernardo Dal Seno
      - specs is a dictionary containing only the specs, using the internal
894 7c8ae421 Bernardo Dal Seno
        format (see L{constants.IPOLICY_DEFAULTS} for an example)
895 63e08b25 Bernardo Dal Seno

896 63e08b25 Bernardo Dal Seno
  """
897 63e08b25 Bernardo Dal Seno
  ret_specs = {}
898 63e08b25 Bernardo Dal Seno
  ret_policy = {}
899 63e08b25 Bernardo Dal Seno
  for (key, val) in policy.items():
900 7c8ae421 Bernardo Dal Seno
    if key == "bounds specs":
901 7c8ae421 Bernardo Dal Seno
      ret_specs[constants.ISPECS_MINMAX] = []
902 7c8ae421 Bernardo Dal Seno
      for minmax in val:
903 7c8ae421 Bernardo Dal Seno
        ret_minmax = {}
904 7c8ae421 Bernardo Dal Seno
        for key in minmax:
905 7c8ae421 Bernardo Dal Seno
          keyparts = key.split("/", 1)
906 7c8ae421 Bernardo Dal Seno
          assert len(keyparts) > 1
907 7c8ae421 Bernardo Dal Seno
          ret_minmax[keyparts[0]] = minmax[key]
908 7c8ae421 Bernardo Dal Seno
        ret_specs[constants.ISPECS_MINMAX].append(ret_minmax)
909 7c8ae421 Bernardo Dal Seno
    elif key == constants.ISPECS_STD:
910 ec996117 Bernardo Dal Seno
      ret_specs[key] = val
911 63e08b25 Bernardo Dal Seno
    else:
912 63e08b25 Bernardo Dal Seno
      ret_policy[key] = val
913 63e08b25 Bernardo Dal Seno
  return (ret_policy, ret_specs)
914 a02dbfca Petr Pudlak
915 a02dbfca Petr Pudlak
916 a02dbfca Petr Pudlak
def UsesIPv6Connection(host, port):
917 a02dbfca Petr Pudlak
  """Returns True if the connection to a given host/port could go through IPv6.
918 a02dbfca Petr Pudlak

919 a02dbfca Petr Pudlak
  """
920 a02dbfca Petr Pudlak
  return any(t[0] == socket.AF_INET6 for t in socket.getaddrinfo(host, port))