Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ f985ecbd

History | View | Annotate | Download (25.5 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 f985ecbd Jose A. Lopes
def AssertEqual(first, second, msg=""):
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 f985ecbd Jose A. Lopes
    if msg:
132 f985ecbd Jose A. Lopes
      raise qa_error.Error("%s: %r == %r" % (msg, first, second))
133 f985ecbd Jose A. Lopes
    else:
134 f985ecbd Jose A. Lopes
      raise qa_error.Error("%r == %r" % (first, second))
135 e8ae0c20 Michael Hanselmann
136 e8ae0c20 Michael Hanselmann
137 e6ce18ac René Nussbaumer
def AssertMatch(string, pattern):
138 e6ce18ac René Nussbaumer
  """Raises an error when string doesn't match regexp pattern.
139 e6ce18ac René Nussbaumer

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

148 889bed16 Michael Hanselmann
  @type entity: string or dict
149 6998aefe Michael Hanselmann
  @param fn: Function retrieving name from entity
150 889bed16 Michael Hanselmann

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

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

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

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

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

214 afd5ca04 Iustin Pop
  @param cmd: the command to be executed, as a list; a string is not
215 afd5ca04 Iustin Pop
      supported
216 afd5ca04 Iustin Pop

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

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

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

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

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

313 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
314 f7e6f3c8 Iustin Pop

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

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

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

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

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

376 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
377 cec9845c Michael Hanselmann
  anymore.
378 49d50e52 Michael Hanselmann

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

404 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
405 b9955569 René Nussbaumer
  anymore.
406 b9955569 René Nussbaumer

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

431 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
432 49d50e52 Michael Hanselmann
  anymore.
433 49d50e52 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

806 ec996117 Bernardo Dal Seno
  At most one of new_specs or diff_specs can be specified.
807 ec996117 Bernardo Dal Seno

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

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

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

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

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