Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ a02dbfca

History | View | Annotate | Download (25.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 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 23269c5b Michael Hanselmann
  curses.setupterm()
94 23269c5b Michael Hanselmann
95 23269c5b Michael Hanselmann
  _RESET_SEQ = curses.tigetstr("op")
96 23269c5b Michael Hanselmann
97 23269c5b Michael Hanselmann
  setaf = curses.tigetstr("setaf")
98 23269c5b Michael Hanselmann
  _INFO_SEQ = curses.tparm(setaf, curses.COLOR_GREEN)
99 23269c5b Michael Hanselmann
  _WARNING_SEQ = curses.tparm(setaf, curses.COLOR_YELLOW)
100 23269c5b Michael Hanselmann
  _ERROR_SEQ = curses.tparm(setaf, curses.COLOR_RED)
101 23269c5b Michael Hanselmann
102 23269c5b Michael Hanselmann
103 23269c5b Michael Hanselmann
_SetupColours()
104 23269c5b Michael Hanselmann
105 23269c5b Michael Hanselmann
106 eaef8a05 Michael Hanselmann
def AssertIn(item, sequence):
107 eaef8a05 Michael Hanselmann
  """Raises an error when item is not in sequence.
108 eaef8a05 Michael Hanselmann

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

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

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

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

141 889bed16 Michael Hanselmann
  @type entity: string or dict
142 6998aefe Michael Hanselmann
  @param fn: Function retrieving name from entity
143 889bed16 Michael Hanselmann

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

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

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

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

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

207 afd5ca04 Iustin Pop
  @param cmd: the command to be executed, as a list; a string is not
208 afd5ca04 Iustin Pop
      supported
209 afd5ca04 Iustin Pop

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

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

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

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

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

306 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
307 f7e6f3c8 Iustin Pop

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

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

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

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

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

369 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
370 cec9845c Michael Hanselmann
  anymore.
371 49d50e52 Michael Hanselmann

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

397 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
398 b9955569 René Nussbaumer
  anymore.
399 b9955569 René Nussbaumer

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

424 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
425 49d50e52 Michael Hanselmann
  anymore.
426 49d50e52 Michael Hanselmann

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

446 46f9a948 Michael Hanselmann
  @type instance: string
447 46f9a948 Michael Hanselmann
  @param instance: Instance name
448 46f9a948 Michael Hanselmann

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

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

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

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

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

523 288d6440 Michael Hanselmann
  @param cmd: Command name
524 288d6440 Michael Hanselmann
  @param fields: List of field names
525 288d6440 Michael Hanselmann

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

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

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

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

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

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

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

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

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

725 b4d2d2cb Michael Hanselmann
  @param count: Number of groups to get
726 ea7693c1 Helga Velroyen
  @rtype: integer
727 b4d2d2cb Michael Hanselmann

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

735 ea7693c1 Helga Velroyen
  The actualy names can refer to arbitrary entities (for example
736 ea7693c1 Helga Velroyen
  groups, networks).
737 ea7693c1 Helga Velroyen

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

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

768 7160f14a Michael Hanselmann
  @type node: string or L{qa_config._QaNode}
769 7160f14a Michael Hanselmann
  @param node: Node
770 7160f14a Michael Hanselmann
  @type path: string
771 7160f14a Michael Hanselmann
  @param path: Path without node-specific prefix
772 7160f14a Michael Hanselmann

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

799 ec996117 Bernardo Dal Seno
  At most one of new_specs or diff_specs can be specified.
800 ec996117 Bernardo Dal Seno

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

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

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

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

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