Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ bfca72bc

History | View | Annotate | Download (24 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 cec9845c Michael Hanselmann
import subprocess
32 0e79564a Bernardo Dal Seno
import sys
33 f7e6f3c8 Iustin Pop
import tempfile
34 0e79564a Bernardo Dal Seno
import yaml
35 cec9845c Michael Hanselmann
36 c9e05005 Michael Hanselmann
try:
37 c9e05005 Michael Hanselmann
  import functools
38 c9e05005 Michael Hanselmann
except ImportError, err:
39 c9e05005 Michael Hanselmann
  raise ImportError("Python 2.5 or higher is required: %s" % err)
40 c9e05005 Michael Hanselmann
41 cec9845c Michael Hanselmann
from ganeti import utils
42 288d6440 Michael Hanselmann
from ganeti import compat
43 2214cf14 Michael Hanselmann
from ganeti import constants
44 c9e05005 Michael Hanselmann
from ganeti import ht
45 48967eb0 Michael Hanselmann
from ganeti import pathutils
46 50eaa5da Michael Hanselmann
from ganeti import vcluster
47 cec9845c Michael Hanselmann
48 cec9845c Michael Hanselmann
import qa_config
49 cec9845c Michael Hanselmann
import qa_error
50 cec9845c Michael Hanselmann
51 6d96ede4 Hrvoje Ribicic
from qa_logging import FormatInfo
52 cec9845c Michael Hanselmann
53 23269c5b Michael Hanselmann
54 f7e6f3c8 Iustin Pop
_MULTIPLEXERS = {}
55 f7e6f3c8 Iustin Pop
56 c9e05005 Michael Hanselmann
#: Unique ID per QA run
57 c9e05005 Michael Hanselmann
_RUN_UUID = utils.NewUUID()
58 c9e05005 Michael Hanselmann
59 afd5ca04 Iustin Pop
#: Path to the QA query output log file
60 afd5ca04 Iustin Pop
_QA_OUTPUT = pathutils.GetLogFilename("qa-output")
61 afd5ca04 Iustin Pop
62 c9e05005 Michael Hanselmann
63 c9e05005 Michael Hanselmann
(INST_DOWN,
64 c9e05005 Michael Hanselmann
 INST_UP) = range(500, 502)
65 c9e05005 Michael Hanselmann
66 c9e05005 Michael Hanselmann
(FIRST_ARG,
67 c9e05005 Michael Hanselmann
 RETURN_VALUE) = range(1000, 1002)
68 c9e05005 Michael Hanselmann
69 23269c5b Michael Hanselmann
70 eaef8a05 Michael Hanselmann
def AssertIn(item, sequence):
71 eaef8a05 Michael Hanselmann
  """Raises an error when item is not in sequence.
72 eaef8a05 Michael Hanselmann

73 eaef8a05 Michael Hanselmann
  """
74 eaef8a05 Michael Hanselmann
  if item not in sequence:
75 d0c8c01d Iustin Pop
    raise qa_error.Error("%r not in %r" % (item, sequence))
76 eaef8a05 Michael Hanselmann
77 eaef8a05 Michael Hanselmann
78 79eac09b Michael Hanselmann
def AssertNotIn(item, sequence):
79 79eac09b Michael Hanselmann
  """Raises an error when item is in sequence.
80 79eac09b Michael Hanselmann

81 79eac09b Michael Hanselmann
  """
82 79eac09b Michael Hanselmann
  if item in sequence:
83 d0c8c01d Iustin Pop
    raise qa_error.Error("%r in %r" % (item, sequence))
84 79eac09b Michael Hanselmann
85 79eac09b Michael Hanselmann
86 e8ae0c20 Michael Hanselmann
def AssertEqual(first, second):
87 cec9845c Michael Hanselmann
  """Raises an error when values aren't equal.
88 cec9845c Michael Hanselmann

89 cec9845c Michael Hanselmann
  """
90 cec9845c Michael Hanselmann
  if not first == second:
91 d0c8c01d Iustin Pop
    raise qa_error.Error("%r == %r" % (first, second))
92 e8ae0c20 Michael Hanselmann
93 e8ae0c20 Michael Hanselmann
94 e6ce18ac René Nussbaumer
def AssertMatch(string, pattern):
95 e6ce18ac René Nussbaumer
  """Raises an error when string doesn't match regexp pattern.
96 e6ce18ac René Nussbaumer

97 e6ce18ac René Nussbaumer
  """
98 e6ce18ac René Nussbaumer
  if not re.match(pattern, string):
99 e6ce18ac René Nussbaumer
    raise qa_error.Error("%r doesn't match /%r/" % (string, pattern))
100 e6ce18ac René Nussbaumer
101 e6ce18ac René Nussbaumer
102 6998aefe Michael Hanselmann
def _GetName(entity, fn):
103 889bed16 Michael Hanselmann
  """Tries to get name of an entity.
104 889bed16 Michael Hanselmann

105 889bed16 Michael Hanselmann
  @type entity: string or dict
106 6998aefe Michael Hanselmann
  @param fn: Function retrieving name from entity
107 889bed16 Michael Hanselmann

108 889bed16 Michael Hanselmann
  """
109 889bed16 Michael Hanselmann
  if isinstance(entity, basestring):
110 889bed16 Michael Hanselmann
    result = entity
111 889bed16 Michael Hanselmann
  else:
112 6998aefe Michael Hanselmann
    result = fn(entity)
113 889bed16 Michael Hanselmann
114 889bed16 Michael Hanselmann
  if not ht.TNonEmptyString(result):
115 889bed16 Michael Hanselmann
    raise Exception("Invalid name '%s'" % result)
116 889bed16 Michael Hanselmann
117 889bed16 Michael Hanselmann
  return result
118 889bed16 Michael Hanselmann
119 889bed16 Michael Hanselmann
120 587f8ff6 Bernardo Dal Seno
def _AssertRetCode(rcode, fail, cmdstr, nodename):
121 587f8ff6 Bernardo Dal Seno
  """Check the return value from a command and possibly raise an exception.
122 587f8ff6 Bernardo Dal Seno

123 587f8ff6 Bernardo Dal Seno
  """
124 587f8ff6 Bernardo Dal Seno
  if fail and rcode == 0:
125 587f8ff6 Bernardo Dal Seno
    raise qa_error.Error("Command '%s' on node %s was expected to fail but"
126 587f8ff6 Bernardo Dal Seno
                         " didn't" % (cmdstr, nodename))
127 587f8ff6 Bernardo Dal Seno
  elif not fail and rcode != 0:
128 587f8ff6 Bernardo Dal Seno
    raise qa_error.Error("Command '%s' on node %s failed, exit code %s" %
129 587f8ff6 Bernardo Dal Seno
                         (cmdstr, nodename, rcode))
130 587f8ff6 Bernardo Dal Seno
131 587f8ff6 Bernardo Dal Seno
132 56b9f2db Iustin Pop
def AssertCommand(cmd, fail=False, node=None, log_cmd=True):
133 2f4b4f78 Iustin Pop
  """Checks that a remote command succeeds.
134 2f4b4f78 Iustin Pop

135 2f4b4f78 Iustin Pop
  @param cmd: either a string (the command to execute) or a list (to
136 2f4b4f78 Iustin Pop
      be converted using L{utils.ShellQuoteArgs} into a string)
137 2f4b4f78 Iustin Pop
  @type fail: boolean
138 2f4b4f78 Iustin Pop
  @param fail: if the command is expected to fail instead of succeeding
139 2f4b4f78 Iustin Pop
  @param node: if passed, it should be the node on which the command
140 2f4b4f78 Iustin Pop
      should be executed, instead of the master node (can be either a
141 2f4b4f78 Iustin Pop
      dict or a string)
142 56b9f2db Iustin Pop
  @param log_cmd: if False, the command won't be logged (simply passed to
143 56b9f2db Iustin Pop
      StartSSH)
144 05325a35 Bernardo Dal Seno
  @return: the return code of the command
145 05325a35 Bernardo Dal Seno
  @raise qa_error.Error: if the command fails when it shouldn't or vice versa
146 2f4b4f78 Iustin Pop

147 2f4b4f78 Iustin Pop
  """
148 2f4b4f78 Iustin Pop
  if node is None:
149 2f4b4f78 Iustin Pop
    node = qa_config.GetMasterNode()
150 2f4b4f78 Iustin Pop
151 6998aefe Michael Hanselmann
  nodename = _GetName(node, operator.attrgetter("primary"))
152 2f4b4f78 Iustin Pop
153 2f4b4f78 Iustin Pop
  if isinstance(cmd, basestring):
154 2f4b4f78 Iustin Pop
    cmdstr = cmd
155 2f4b4f78 Iustin Pop
  else:
156 2f4b4f78 Iustin Pop
    cmdstr = utils.ShellQuoteArgs(cmd)
157 2f4b4f78 Iustin Pop
158 56b9f2db Iustin Pop
  rcode = StartSSH(nodename, cmdstr, log_cmd=log_cmd).wait()
159 587f8ff6 Bernardo Dal Seno
  _AssertRetCode(rcode, fail, cmdstr, nodename)
160 2f4b4f78 Iustin Pop
161 2214cf14 Michael Hanselmann
  return rcode
162 2214cf14 Michael Hanselmann
163 2f4b4f78 Iustin Pop
164 afd5ca04 Iustin Pop
def AssertRedirectedCommand(cmd, fail=False, node=None, log_cmd=True):
165 afd5ca04 Iustin Pop
  """Executes a command with redirected output.
166 afd5ca04 Iustin Pop

167 afd5ca04 Iustin Pop
  The log will go to the qa-output log file in the ganeti log
168 afd5ca04 Iustin Pop
  directory on the node where the command is executed. The fail and
169 afd5ca04 Iustin Pop
  node parameters are passed unchanged to AssertCommand.
170 afd5ca04 Iustin Pop

171 afd5ca04 Iustin Pop
  @param cmd: the command to be executed, as a list; a string is not
172 afd5ca04 Iustin Pop
      supported
173 afd5ca04 Iustin Pop

174 afd5ca04 Iustin Pop
  """
175 afd5ca04 Iustin Pop
  if not isinstance(cmd, list):
176 afd5ca04 Iustin Pop
    raise qa_error.Error("Non-list passed to AssertRedirectedCommand")
177 afd5ca04 Iustin Pop
  ofile = utils.ShellQuote(_QA_OUTPUT)
178 afd5ca04 Iustin Pop
  cmdstr = utils.ShellQuoteArgs(cmd)
179 afd5ca04 Iustin Pop
  AssertCommand("echo ---- $(date) %s ---- >> %s" % (cmdstr, ofile),
180 afd5ca04 Iustin Pop
                fail=False, node=node, log_cmd=False)
181 afd5ca04 Iustin Pop
  return AssertCommand(cmdstr + " >> %s" % ofile,
182 afd5ca04 Iustin Pop
                       fail=fail, node=node, log_cmd=log_cmd)
183 afd5ca04 Iustin Pop
184 afd5ca04 Iustin Pop
185 f14a8b15 Iustin Pop
def GetSSHCommand(node, cmd, strict=True, opts=None, tty=None):
186 cec9845c Michael Hanselmann
  """Builds SSH command to be executed.
187 cec9845c Michael Hanselmann

188 0a05f959 Adeodato Simo
  @type node: string
189 0a05f959 Adeodato Simo
  @param node: node the command should run on
190 0a05f959 Adeodato Simo
  @type cmd: string
191 f7e6f3c8 Iustin Pop
  @param cmd: command to be executed in the node; if None or empty
192 f7e6f3c8 Iustin Pop
      string, no command will be executed
193 0a05f959 Adeodato Simo
  @type strict: boolean
194 0a05f959 Adeodato Simo
  @param strict: whether to enable strict host key checking
195 f7e6f3c8 Iustin Pop
  @type opts: list
196 f7e6f3c8 Iustin Pop
  @param opts: list of additional options
197 f14a8b15 Iustin Pop
  @type tty: boolean or None
198 f14a8b15 Iustin Pop
  @param tty: if we should use tty; if None, will be auto-detected
199 c68d1f43 Michael Hanselmann

200 cec9845c Michael Hanselmann
  """
201 710bc88c Iustin Pop
  args = ["ssh", "-oEscapeChar=none", "-oBatchMode=yes", "-lroot"]
202 50265802 René Nussbaumer
203 f14a8b15 Iustin Pop
  if tty is None:
204 f14a8b15 Iustin Pop
    tty = sys.stdout.isatty()
205 f14a8b15 Iustin Pop
206 50265802 René Nussbaumer
  if tty:
207 50265802 René Nussbaumer
    args.append("-t")
208 cec9845c Michael Hanselmann
209 cec9845c Michael Hanselmann
  if strict:
210 d0c8c01d Iustin Pop
    tmp = "yes"
211 cec9845c Michael Hanselmann
  else:
212 d0c8c01d Iustin Pop
    tmp = "no"
213 d0c8c01d Iustin Pop
  args.append("-oStrictHostKeyChecking=%s" % tmp)
214 d0c8c01d Iustin Pop
  args.append("-oClearAllForwardings=yes")
215 d0c8c01d Iustin Pop
  args.append("-oForwardAgent=yes")
216 f7e6f3c8 Iustin Pop
  if opts:
217 f7e6f3c8 Iustin Pop
    args.extend(opts)
218 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
219 f7e6f3c8 Iustin Pop
    spath = _MULTIPLEXERS[node][0]
220 d0c8c01d Iustin Pop
    args.append("-oControlPath=%s" % spath)
221 d0c8c01d Iustin Pop
    args.append("-oControlMaster=no")
222 50eaa5da Michael Hanselmann
223 50eaa5da Michael Hanselmann
  (vcluster_master, vcluster_basedir) = \
224 50eaa5da Michael Hanselmann
    qa_config.GetVclusterSettings()
225 50eaa5da Michael Hanselmann
226 50eaa5da Michael Hanselmann
  if vcluster_master:
227 50eaa5da Michael Hanselmann
    args.append(vcluster_master)
228 50eaa5da Michael Hanselmann
    args.append("%s/%s/cmd" % (vcluster_basedir, node))
229 50eaa5da Michael Hanselmann
230 50eaa5da Michael Hanselmann
    if cmd:
231 50eaa5da Michael Hanselmann
      # For virtual clusters the whole command must be wrapped using the "cmd"
232 50eaa5da Michael Hanselmann
      # script, as that script sets a number of environment variables. If the
233 50eaa5da Michael Hanselmann
      # command contains shell meta characters the whole command needs to be
234 50eaa5da Michael Hanselmann
      # quoted.
235 50eaa5da Michael Hanselmann
      args.append(utils.ShellQuote(cmd))
236 50eaa5da Michael Hanselmann
  else:
237 50eaa5da Michael Hanselmann
    args.append(node)
238 50eaa5da Michael Hanselmann
239 50eaa5da Michael Hanselmann
    if cmd:
240 50eaa5da Michael Hanselmann
      args.append(cmd)
241 cec9845c Michael Hanselmann
242 cec9845c Michael Hanselmann
  return args
243 cec9845c Michael Hanselmann
244 cec9845c Michael Hanselmann
245 56b9f2db Iustin Pop
def StartLocalCommand(cmd, _nolog_opts=False, log_cmd=True, **kwargs):
246 5d831182 Michael Hanselmann
  """Starts a local command.
247 5d831182 Michael Hanselmann

248 5d831182 Michael Hanselmann
  """
249 56b9f2db Iustin Pop
  if log_cmd:
250 56b9f2db Iustin Pop
    if _nolog_opts:
251 56b9f2db Iustin Pop
      pcmd = [i for i in cmd if not i.startswith("-")]
252 56b9f2db Iustin Pop
    else:
253 56b9f2db Iustin Pop
      pcmd = cmd
254 56b9f2db Iustin Pop
    print "Command: %s" % utils.ShellQuoteArgs(pcmd)
255 5d831182 Michael Hanselmann
  return subprocess.Popen(cmd, shell=False, **kwargs)
256 5d831182 Michael Hanselmann
257 5d831182 Michael Hanselmann
258 56b9f2db Iustin Pop
def StartSSH(node, cmd, strict=True, log_cmd=True):
259 cec9845c Michael Hanselmann
  """Starts SSH.
260 cec9845c Michael Hanselmann

261 cec9845c Michael Hanselmann
  """
262 710bc88c Iustin Pop
  return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict),
263 56b9f2db Iustin Pop
                           _nolog_opts=True, log_cmd=log_cmd)
264 4b62db14 Michael Hanselmann
265 4b62db14 Michael Hanselmann
266 f7e6f3c8 Iustin Pop
def StartMultiplexer(node):
267 f7e6f3c8 Iustin Pop
  """Starts a multiplexer command.
268 f7e6f3c8 Iustin Pop

269 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
270 f7e6f3c8 Iustin Pop

271 f7e6f3c8 Iustin Pop
  """
272 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
273 f7e6f3c8 Iustin Pop
    return
274 f7e6f3c8 Iustin Pop
275 f7e6f3c8 Iustin Pop
  # Note: yes, we only need mktemp, since we'll remove the file anyway
276 f7e6f3c8 Iustin Pop
  sname = tempfile.mktemp(prefix="ganeti-qa-multiplexer.")
277 f7e6f3c8 Iustin Pop
  utils.RemoveFile(sname)
278 f7e6f3c8 Iustin Pop
  opts = ["-N", "-oControlPath=%s" % sname, "-oControlMaster=yes"]
279 f7e6f3c8 Iustin Pop
  print "Created socket at %s" % sname
280 f7e6f3c8 Iustin Pop
  child = StartLocalCommand(GetSSHCommand(node, None, opts=opts))
281 f7e6f3c8 Iustin Pop
  _MULTIPLEXERS[node] = (sname, child)
282 f7e6f3c8 Iustin Pop
283 f7e6f3c8 Iustin Pop
284 f7e6f3c8 Iustin Pop
def CloseMultiplexers():
285 f7e6f3c8 Iustin Pop
  """Closes all current multiplexers and cleans up.
286 f7e6f3c8 Iustin Pop

287 f7e6f3c8 Iustin Pop
  """
288 f7e6f3c8 Iustin Pop
  for node in _MULTIPLEXERS.keys():
289 f7e6f3c8 Iustin Pop
    (sname, child) = _MULTIPLEXERS.pop(node)
290 f7e6f3c8 Iustin Pop
    utils.KillProcess(child.pid, timeout=10, waitpid=True)
291 f7e6f3c8 Iustin Pop
    utils.RemoveFile(sname)
292 f7e6f3c8 Iustin Pop
293 f7e6f3c8 Iustin Pop
294 587f8ff6 Bernardo Dal Seno
def GetCommandOutput(node, cmd, tty=None, fail=False):
295 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
296 4b62db14 Michael Hanselmann

297 587f8ff6 Bernardo Dal Seno
  @type node: string
298 587f8ff6 Bernardo Dal Seno
  @param node: node the command should run on
299 587f8ff6 Bernardo Dal Seno
  @type cmd: string
300 587f8ff6 Bernardo Dal Seno
  @param cmd: command to be executed in the node (cannot be empty or None)
301 587f8ff6 Bernardo Dal Seno
  @type tty: bool or None
302 587f8ff6 Bernardo Dal Seno
  @param tty: if we should use tty; if None, it will be auto-detected
303 587f8ff6 Bernardo Dal Seno
  @type fail: bool
304 587f8ff6 Bernardo Dal Seno
  @param fail: whether the command is expected to fail
305 4b62db14 Michael Hanselmann
  """
306 587f8ff6 Bernardo Dal Seno
  assert cmd
307 50265802 René Nussbaumer
  p = StartLocalCommand(GetSSHCommand(node, cmd, tty=tty),
308 50265802 René Nussbaumer
                        stdout=subprocess.PIPE)
309 587f8ff6 Bernardo Dal Seno
  rcode = p.wait()
310 3b0db9e3 Michael Hanselmann
  _AssertRetCode(rcode, fail, cmd, node)
311 4b62db14 Michael Hanselmann
  return p.stdout.read()
312 cec9845c Michael Hanselmann
313 cec9845c Michael Hanselmann
314 0e79564a Bernardo Dal Seno
def GetObjectInfo(infocmd):
315 0e79564a Bernardo Dal Seno
  """Get and parse information about a Ganeti object.
316 0e79564a Bernardo Dal Seno

317 0e79564a Bernardo Dal Seno
  @type infocmd: list of strings
318 0e79564a Bernardo Dal Seno
  @param infocmd: command to be executed, e.g. ["gnt-cluster", "info"]
319 0e79564a Bernardo Dal Seno
  @return: the information parsed, appropriately stored in dictionaries,
320 0e79564a Bernardo Dal Seno
      lists...
321 0e79564a Bernardo Dal Seno

322 0e79564a Bernardo Dal Seno
  """
323 0e79564a Bernardo Dal Seno
  master = qa_config.GetMasterNode()
324 0e79564a Bernardo Dal Seno
  cmdline = utils.ShellQuoteArgs(infocmd)
325 0e79564a Bernardo Dal Seno
  info_out = GetCommandOutput(master.primary, cmdline)
326 0e79564a Bernardo Dal Seno
  return yaml.load(info_out)
327 0e79564a Bernardo Dal Seno
328 0e79564a Bernardo Dal Seno
329 cec9845c Michael Hanselmann
def UploadFile(node, src):
330 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
331 cec9845c Michael Hanselmann

332 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
333 cec9845c Michael Hanselmann
  anymore.
334 49d50e52 Michael Hanselmann

335 cec9845c Michael Hanselmann
  """
336 cec9845c Michael Hanselmann
  # Make sure nobody else has access to it while preserving local permissions
337 cec9845c Michael Hanselmann
  mode = os.stat(src).st_mode & 0700
338 cec9845c Michael Hanselmann
339 cec9845c Michael Hanselmann
  cmd = ('tmp=$(tempfile --mode %o --prefix gnt) && '
340 cec9845c Michael Hanselmann
         '[[ -f "${tmp}" ]] && '
341 cec9845c Michael Hanselmann
         'cat > "${tmp}" && '
342 cec9845c Michael Hanselmann
         'echo "${tmp}"') % mode
343 cec9845c Michael Hanselmann
344 d0c8c01d Iustin Pop
  f = open(src, "r")
345 cec9845c Michael Hanselmann
  try:
346 cec9845c Michael Hanselmann
    p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
347 cec9845c Michael Hanselmann
                         stdout=subprocess.PIPE)
348 cec9845c Michael Hanselmann
    AssertEqual(p.wait(), 0)
349 cec9845c Michael Hanselmann
350 cec9845c Michael Hanselmann
    # Return temporary filename
351 cec9845c Michael Hanselmann
    return p.stdout.read().strip()
352 cec9845c Michael Hanselmann
  finally:
353 cec9845c Michael Hanselmann
    f.close()
354 5d640672 Michael Hanselmann
355 5d640672 Michael Hanselmann
356 b9955569 René Nussbaumer
def UploadData(node, data, mode=0600, filename=None):
357 b9955569 René Nussbaumer
  """Uploads data to a node and returns the filename.
358 b9955569 René Nussbaumer

359 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
360 b9955569 René Nussbaumer
  anymore.
361 b9955569 René Nussbaumer

362 b9955569 René Nussbaumer
  """
363 b9955569 René Nussbaumer
  if filename:
364 b9955569 René Nussbaumer
    tmp = "tmp=%s" % utils.ShellQuote(filename)
365 b9955569 René Nussbaumer
  else:
366 b9955569 René Nussbaumer
    tmp = "tmp=$(tempfile --mode %o --prefix gnt)" % mode
367 b9955569 René Nussbaumer
  cmd = ("%s && "
368 b9955569 René Nussbaumer
         "[[ -f \"${tmp}\" ]] && "
369 b9955569 René Nussbaumer
         "cat > \"${tmp}\" && "
370 b9955569 René Nussbaumer
         "echo \"${tmp}\"") % tmp
371 b9955569 René Nussbaumer
372 b9955569 René Nussbaumer
  p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False,
373 b9955569 René Nussbaumer
                       stdin=subprocess.PIPE, stdout=subprocess.PIPE)
374 b9955569 René Nussbaumer
  p.stdin.write(data)
375 b9955569 René Nussbaumer
  p.stdin.close()
376 b9955569 René Nussbaumer
  AssertEqual(p.wait(), 0)
377 b9955569 René Nussbaumer
378 b9955569 René Nussbaumer
  # Return temporary filename
379 b9955569 René Nussbaumer
  return p.stdout.read().strip()
380 b9955569 René Nussbaumer
381 b9955569 René Nussbaumer
382 49d50e52 Michael Hanselmann
def BackupFile(node, path):
383 49d50e52 Michael Hanselmann
  """Creates a backup of a file on the node and returns the filename.
384 49d50e52 Michael Hanselmann

385 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
386 49d50e52 Michael Hanselmann
  anymore.
387 49d50e52 Michael Hanselmann

388 49d50e52 Michael Hanselmann
  """
389 7160f14a Michael Hanselmann
  vpath = MakeNodePath(node, path)
390 7160f14a Michael Hanselmann
391 49d50e52 Michael Hanselmann
  cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
392 49d50e52 Michael Hanselmann
         "[[ -f \"$tmp\" ]] && "
393 49d50e52 Michael Hanselmann
         "cp %s $tmp && "
394 7160f14a Michael Hanselmann
         "echo $tmp") % (utils.ShellQuote(vpath), utils.ShellQuote(vpath))
395 49d50e52 Michael Hanselmann
396 49d50e52 Michael Hanselmann
  # Return temporary filename
397 7160f14a Michael Hanselmann
  result = GetCommandOutput(node, cmd).strip()
398 7160f14a Michael Hanselmann
399 7160f14a Michael Hanselmann
  print "Backup filename: %s" % result
400 7160f14a Michael Hanselmann
401 7160f14a Michael Hanselmann
  return result
402 49d50e52 Michael Hanselmann
403 49d50e52 Michael Hanselmann
404 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
405 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
406 5d640672 Michael Hanselmann

407 46f9a948 Michael Hanselmann
  @type instance: string
408 46f9a948 Michael Hanselmann
  @param instance: Instance name
409 46f9a948 Michael Hanselmann

410 5d640672 Michael Hanselmann
  """
411 2cbcf95d Bernardo Dal Seno
  info = GetObjectInfo(["gnt-instance", "info", instance])
412 2cbcf95d Bernardo Dal Seno
  return info[0]["Instance name"]
413 4b62db14 Michael Hanselmann
414 4b62db14 Michael Hanselmann
415 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
416 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
417 4b62db14 Michael Hanselmann

418 4b62db14 Michael Hanselmann
  """
419 5f6d1b42 Bernardo Dal Seno
  info = GetObjectInfo(["gnt-node", "info", node.primary])
420 5f6d1b42 Bernardo Dal Seno
  return info[0]["Node name"]
421 4b62db14 Michael Hanselmann
422 4b62db14 Michael Hanselmann
423 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
424 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
425 4b62db14 Michael Hanselmann

426 4b62db14 Michael Hanselmann
  """
427 5d640672 Michael Hanselmann
  master = qa_config.GetMasterNode()
428 4b62db14 Michael Hanselmann
  node_name = ResolveNodeName(node)
429 5d640672 Michael Hanselmann
430 4b62db14 Michael Hanselmann
  # Get list of all instances
431 d0c8c01d Iustin Pop
  cmd = ["gnt-instance", "list", "--separator=:", "--no-headers",
432 d0c8c01d Iustin Pop
         "--output=name,pnode,snodes"]
433 aecba21e Michael Hanselmann
  output = GetCommandOutput(master.primary, utils.ShellQuoteArgs(cmd))
434 4b62db14 Michael Hanselmann
435 4b62db14 Michael Hanselmann
  instances = []
436 4b62db14 Michael Hanselmann
  for line in output.splitlines():
437 d0c8c01d Iustin Pop
    (name, pnode, snodes) = line.split(":", 2)
438 4b62db14 Michael Hanselmann
    if ((not secondaries and pnode == node_name) or
439 d0c8c01d Iustin Pop
        (secondaries and node_name in snodes.split(","))):
440 4b62db14 Michael Hanselmann
      instances.append(name)
441 5d640672 Michael Hanselmann
442 4b62db14 Michael Hanselmann
  return instances
443 23269c5b Michael Hanselmann
444 23269c5b Michael Hanselmann
445 288d6440 Michael Hanselmann
def _SelectQueryFields(rnd, fields):
446 288d6440 Michael Hanselmann
  """Generates a list of fields for query tests.
447 288d6440 Michael Hanselmann

448 288d6440 Michael Hanselmann
  """
449 288d6440 Michael Hanselmann
  # Create copy for shuffling
450 288d6440 Michael Hanselmann
  fields = list(fields)
451 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
452 288d6440 Michael Hanselmann
453 288d6440 Michael Hanselmann
  # Check all fields
454 288d6440 Michael Hanselmann
  yield fields
455 288d6440 Michael Hanselmann
  yield sorted(fields)
456 288d6440 Michael Hanselmann
457 288d6440 Michael Hanselmann
  # Duplicate fields
458 288d6440 Michael Hanselmann
  yield fields + fields
459 288d6440 Michael Hanselmann
460 288d6440 Michael Hanselmann
  # Check small groups of fields
461 288d6440 Michael Hanselmann
  while fields:
462 288d6440 Michael Hanselmann
    yield [fields.pop() for _ in range(rnd.randint(2, 10)) if fields]
463 288d6440 Michael Hanselmann
464 288d6440 Michael Hanselmann
465 288d6440 Michael Hanselmann
def _List(listcmd, fields, names):
466 288d6440 Michael Hanselmann
  """Runs a list command.
467 288d6440 Michael Hanselmann

468 288d6440 Michael Hanselmann
  """
469 288d6440 Michael Hanselmann
  master = qa_config.GetMasterNode()
470 288d6440 Michael Hanselmann
471 58ea8d17 Michael Hanselmann
  cmd = [listcmd, "list", "--separator=|", "--no-headers",
472 288d6440 Michael Hanselmann
         "--output", ",".join(fields)]
473 288d6440 Michael Hanselmann
474 288d6440 Michael Hanselmann
  if names:
475 288d6440 Michael Hanselmann
    cmd.extend(names)
476 288d6440 Michael Hanselmann
477 aecba21e Michael Hanselmann
  return GetCommandOutput(master.primary,
478 288d6440 Michael Hanselmann
                          utils.ShellQuoteArgs(cmd)).splitlines()
479 288d6440 Michael Hanselmann
480 288d6440 Michael Hanselmann
481 6d1e4845 Michael Hanselmann
def GenericQueryTest(cmd, fields, namefield="name", test_unknown=True):
482 288d6440 Michael Hanselmann
  """Runs a number of tests on query commands.
483 288d6440 Michael Hanselmann

484 288d6440 Michael Hanselmann
  @param cmd: Command name
485 288d6440 Michael Hanselmann
  @param fields: List of field names
486 288d6440 Michael Hanselmann

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

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

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

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

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

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

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

650 c9e05005 Michael Hanselmann
  @param before: L{INST_DOWN} if instance must be stopped before test,
651 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running before test, L{None} to not check.
652 c9e05005 Michael Hanselmann
  @param after: L{INST_DOWN} if instance must be stopped after test,
653 c9e05005 Michael Hanselmann
    L{INST_UP} if instance must be running after test, L{None} to not check.
654 c9e05005 Michael Hanselmann
  @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
655 c9e05005 Michael Hanselmann
    dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
656 c9e05005 Michael Hanselmann

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

675 b4d2d2cb Michael Hanselmann
  @param count: Number of groups to get
676 ea7693c1 Helga Velroyen
  @rtype: integer
677 b4d2d2cb Michael Hanselmann

678 b4d2d2cb Michael Hanselmann
  """
679 ea7693c1 Helga Velroyen
  return GetNonexistentEntityNames(count, "groups", "group")
680 b4d2d2cb Michael Hanselmann
681 ea7693c1 Helga Velroyen
682 ea7693c1 Helga Velroyen
def GetNonexistentEntityNames(count, name_config, name_prefix):
683 ea7693c1 Helga Velroyen
  """Gets entity names which shouldn't exist on the cluster.
684 ea7693c1 Helga Velroyen

685 ea7693c1 Helga Velroyen
  The actualy names can refer to arbitrary entities (for example
686 ea7693c1 Helga Velroyen
  groups, networks).
687 ea7693c1 Helga Velroyen

688 ea7693c1 Helga Velroyen
  @param count: Number of names to get
689 ea7693c1 Helga Velroyen
  @rtype: integer
690 ea7693c1 Helga Velroyen
  @param name_config: name of the leaf in the config containing
691 ea7693c1 Helga Velroyen
    this entity's configuration, including a 'inexistent-'
692 ea7693c1 Helga Velroyen
    element
693 ea7693c1 Helga Velroyen
  @rtype: string
694 ea7693c1 Helga Velroyen
  @param name_prefix: prefix of the entity's names, used to compose
695 ea7693c1 Helga Velroyen
    the default values; for example for groups, the prefix is
696 ea7693c1 Helga Velroyen
    'group' and the generated names are then group1, group2, ...
697 ea7693c1 Helga Velroyen
  @rtype: string
698 ea7693c1 Helga Velroyen

699 ea7693c1 Helga Velroyen
  """
700 ea7693c1 Helga Velroyen
  entities = qa_config.get(name_config, {})
701 ea7693c1 Helga Velroyen
702 ea7693c1 Helga Velroyen
  default = [name_prefix + str(i) for i in range(count)]
703 b4d2d2cb Michael Hanselmann
  assert count <= len(default)
704 b4d2d2cb Michael Hanselmann
705 ea7693c1 Helga Velroyen
  name_config_inexistent = "inexistent-" + name_config
706 ea7693c1 Helga Velroyen
  candidates = entities.get(name_config_inexistent, default)[:count]
707 b4d2d2cb Michael Hanselmann
708 b4d2d2cb Michael Hanselmann
  if len(candidates) < count:
709 ea7693c1 Helga Velroyen
    raise Exception("At least %s non-existent %s are needed" %
710 ea7693c1 Helga Velroyen
                    (count, name_config))
711 b4d2d2cb Michael Hanselmann
712 b4d2d2cb Michael Hanselmann
  return candidates
713 7160f14a Michael Hanselmann
714 7160f14a Michael Hanselmann
715 7160f14a Michael Hanselmann
def MakeNodePath(node, path):
716 7160f14a Michael Hanselmann
  """Builds an absolute path for a virtual node.
717 7160f14a Michael Hanselmann

718 7160f14a Michael Hanselmann
  @type node: string or L{qa_config._QaNode}
719 7160f14a Michael Hanselmann
  @param node: Node
720 7160f14a Michael Hanselmann
  @type path: string
721 7160f14a Michael Hanselmann
  @param path: Path without node-specific prefix
722 7160f14a Michael Hanselmann

723 7160f14a Michael Hanselmann
  """
724 7160f14a Michael Hanselmann
  (_, basedir) = qa_config.GetVclusterSettings()
725 7160f14a Michael Hanselmann
726 7160f14a Michael Hanselmann
  if isinstance(node, basestring):
727 7160f14a Michael Hanselmann
    name = node
728 7160f14a Michael Hanselmann
  else:
729 7160f14a Michael Hanselmann
    name = node.primary
730 7160f14a Michael Hanselmann
731 7160f14a Michael Hanselmann
  if basedir:
732 7160f14a Michael Hanselmann
    assert path.startswith("/")
733 7160f14a Michael Hanselmann
    return "%s%s" % (vcluster.MakeNodeRoot(basedir, name), path)
734 7160f14a Michael Hanselmann
  else:
735 7160f14a Michael Hanselmann
    return path
736 63e08b25 Bernardo Dal Seno
737 63e08b25 Bernardo Dal Seno
738 ec996117 Bernardo Dal Seno
def _GetParameterOptions(specs):
739 63e08b25 Bernardo Dal Seno
  """Helper to build policy options."""
740 ec996117 Bernardo Dal Seno
  values = ["%s=%s" % (par, val)
741 ec996117 Bernardo Dal Seno
            for (par, val) in specs.items()]
742 63e08b25 Bernardo Dal Seno
  return ",".join(values)
743 63e08b25 Bernardo Dal Seno
744 63e08b25 Bernardo Dal Seno
745 ec996117 Bernardo Dal Seno
def TestSetISpecs(new_specs=None, diff_specs=None, get_policy_fn=None,
746 ec996117 Bernardo Dal Seno
                  build_cmd_fn=None, fail=False, old_values=None):
747 63e08b25 Bernardo Dal Seno
  """Change instance specs for an object.
748 63e08b25 Bernardo Dal Seno

749 ec996117 Bernardo Dal Seno
  At most one of new_specs or diff_specs can be specified.
750 ec996117 Bernardo Dal Seno

751 ec996117 Bernardo Dal Seno
  @type new_specs: dict
752 ec996117 Bernardo Dal Seno
  @param new_specs: new complete specs, in the same format returned by
753 ec996117 Bernardo Dal Seno
      L{ParseIPolicy}.
754 ec996117 Bernardo Dal Seno
  @type diff_specs: dict
755 7c8ae421 Bernardo Dal Seno
  @param diff_specs: partial specs, it can be an incomplete specifications, but
756 7c8ae421 Bernardo Dal Seno
      if min/max specs are specified, their number must match the number of the
757 7c8ae421 Bernardo Dal Seno
      existing specs
758 63e08b25 Bernardo Dal Seno
  @type get_policy_fn: function
759 63e08b25 Bernardo Dal Seno
  @param get_policy_fn: function that returns the current policy as in
760 ec996117 Bernardo Dal Seno
      L{ParseIPolicy}
761 63e08b25 Bernardo Dal Seno
  @type build_cmd_fn: function
762 63e08b25 Bernardo Dal Seno
  @param build_cmd_fn: function that return the full command line from the
763 63e08b25 Bernardo Dal Seno
      options alone
764 63e08b25 Bernardo Dal Seno
  @type fail: bool
765 63e08b25 Bernardo Dal Seno
  @param fail: if the change is expected to fail
766 63e08b25 Bernardo Dal Seno
  @type old_values: tuple
767 63e08b25 Bernardo Dal Seno
  @param old_values: (old_policy, old_specs), as returned by
768 ec996117 Bernardo Dal Seno
     L{ParseIPolicy}
769 ec996117 Bernardo Dal Seno
  @return: same as L{ParseIPolicy}
770 63e08b25 Bernardo Dal Seno

771 63e08b25 Bernardo Dal Seno
  """
772 63e08b25 Bernardo Dal Seno
  assert get_policy_fn is not None
773 63e08b25 Bernardo Dal Seno
  assert build_cmd_fn is not None
774 ec996117 Bernardo Dal Seno
  assert new_specs is None or diff_specs is None
775 63e08b25 Bernardo Dal Seno
776 63e08b25 Bernardo Dal Seno
  if old_values:
777 63e08b25 Bernardo Dal Seno
    (old_policy, old_specs) = old_values
778 63e08b25 Bernardo Dal Seno
  else:
779 63e08b25 Bernardo Dal Seno
    (old_policy, old_specs) = get_policy_fn()
780 ec996117 Bernardo Dal Seno
781 ec996117 Bernardo Dal Seno
  if diff_specs:
782 ec996117 Bernardo Dal Seno
    new_specs = copy.deepcopy(old_specs)
783 7c8ae421 Bernardo Dal Seno
    if constants.ISPECS_MINMAX in diff_specs:
784 7c8ae421 Bernardo Dal Seno
      AssertEqual(len(new_specs[constants.ISPECS_MINMAX]),
785 7c8ae421 Bernardo Dal Seno
                  len(diff_specs[constants.ISPECS_MINMAX]))
786 7c8ae421 Bernardo Dal Seno
      for (new_minmax, diff_minmax) in zip(new_specs[constants.ISPECS_MINMAX],
787 7c8ae421 Bernardo Dal Seno
                                           diff_specs[constants.ISPECS_MINMAX]):
788 7c8ae421 Bernardo Dal Seno
        for (key, parvals) in diff_minmax.items():
789 7c8ae421 Bernardo Dal Seno
          for (par, val) in parvals.items():
790 7c8ae421 Bernardo Dal Seno
            new_minmax[key][par] = val
791 7c8ae421 Bernardo Dal Seno
    for (par, val) in diff_specs.get(constants.ISPECS_STD, {}).items():
792 7c8ae421 Bernardo Dal Seno
      new_specs[constants.ISPECS_STD][par] = val
793 ec996117 Bernardo Dal Seno
794 63e08b25 Bernardo Dal Seno
  if new_specs:
795 63e08b25 Bernardo Dal Seno
    cmd = []
796 7c8ae421 Bernardo Dal Seno
    if (diff_specs is None or constants.ISPECS_MINMAX in diff_specs):
797 63e08b25 Bernardo Dal Seno
      minmax_opt_items = []
798 7c8ae421 Bernardo Dal Seno
      for minmax in new_specs[constants.ISPECS_MINMAX]:
799 7c8ae421 Bernardo Dal Seno
        minmax_opts = []
800 7c8ae421 Bernardo Dal Seno
        for key in ["min", "max"]:
801 7c8ae421 Bernardo Dal Seno
          keyopt = _GetParameterOptions(minmax[key])
802 7c8ae421 Bernardo Dal Seno
          minmax_opts.append("%s:%s" % (key, keyopt))
803 7c8ae421 Bernardo Dal Seno
        minmax_opt_items.append("/".join(minmax_opts))
804 63e08b25 Bernardo Dal Seno
      cmd.extend([
805 63e08b25 Bernardo Dal Seno
        "--ipolicy-bounds-specs",
806 7c8ae421 Bernardo Dal Seno
        "//".join(minmax_opt_items)
807 63e08b25 Bernardo Dal Seno
        ])
808 7c8ae421 Bernardo Dal Seno
    if diff_specs is None:
809 ec996117 Bernardo Dal Seno
      std_source = new_specs
810 7c8ae421 Bernardo Dal Seno
    else:
811 7c8ae421 Bernardo Dal Seno
      std_source = diff_specs
812 ec996117 Bernardo Dal Seno
    std_opt = _GetParameterOptions(std_source.get("std", {}))
813 63e08b25 Bernardo Dal Seno
    if std_opt:
814 63e08b25 Bernardo Dal Seno
      cmd.extend(["--ipolicy-std-specs", std_opt])
815 63e08b25 Bernardo Dal Seno
    AssertCommand(build_cmd_fn(cmd), fail=fail)
816 63e08b25 Bernardo Dal Seno
817 ec996117 Bernardo Dal Seno
    # Check the new state
818 ec996117 Bernardo Dal Seno
    (eff_policy, eff_specs) = get_policy_fn()
819 ec996117 Bernardo Dal Seno
    AssertEqual(eff_policy, old_policy)
820 ec996117 Bernardo Dal Seno
    if fail:
821 ec996117 Bernardo Dal Seno
      AssertEqual(eff_specs, old_specs)
822 ec996117 Bernardo Dal Seno
    else:
823 ec996117 Bernardo Dal Seno
      AssertEqual(eff_specs, new_specs)
824 ec996117 Bernardo Dal Seno
825 63e08b25 Bernardo Dal Seno
  else:
826 ec996117 Bernardo Dal Seno
    (eff_policy, eff_specs) = (old_policy, old_specs)
827 ec996117 Bernardo Dal Seno
828 63e08b25 Bernardo Dal Seno
  return (eff_policy, eff_specs)
829 63e08b25 Bernardo Dal Seno
830 63e08b25 Bernardo Dal Seno
831 63e08b25 Bernardo Dal Seno
def ParseIPolicy(policy):
832 63e08b25 Bernardo Dal Seno
  """Parse and split instance an instance policy.
833 63e08b25 Bernardo Dal Seno

834 63e08b25 Bernardo Dal Seno
  @type policy: dict
835 63e08b25 Bernardo Dal Seno
  @param policy: policy, as returned by L{GetObjectInfo}
836 63e08b25 Bernardo Dal Seno
  @rtype: tuple
837 63e08b25 Bernardo Dal Seno
  @return: (policy, specs), where:
838 63e08b25 Bernardo Dal Seno
      - policy is a dictionary of the policy values, instance specs excluded
839 7c8ae421 Bernardo Dal Seno
      - specs is a dictionary containing only the specs, using the internal
840 7c8ae421 Bernardo Dal Seno
        format (see L{constants.IPOLICY_DEFAULTS} for an example)
841 63e08b25 Bernardo Dal Seno

842 63e08b25 Bernardo Dal Seno
  """
843 63e08b25 Bernardo Dal Seno
  ret_specs = {}
844 63e08b25 Bernardo Dal Seno
  ret_policy = {}
845 63e08b25 Bernardo Dal Seno
  for (key, val) in policy.items():
846 7c8ae421 Bernardo Dal Seno
    if key == "bounds specs":
847 7c8ae421 Bernardo Dal Seno
      ret_specs[constants.ISPECS_MINMAX] = []
848 7c8ae421 Bernardo Dal Seno
      for minmax in val:
849 7c8ae421 Bernardo Dal Seno
        ret_minmax = {}
850 7c8ae421 Bernardo Dal Seno
        for key in minmax:
851 7c8ae421 Bernardo Dal Seno
          keyparts = key.split("/", 1)
852 7c8ae421 Bernardo Dal Seno
          assert len(keyparts) > 1
853 7c8ae421 Bernardo Dal Seno
          ret_minmax[keyparts[0]] = minmax[key]
854 7c8ae421 Bernardo Dal Seno
        ret_specs[constants.ISPECS_MINMAX].append(ret_minmax)
855 7c8ae421 Bernardo Dal Seno
    elif key == constants.ISPECS_STD:
856 ec996117 Bernardo Dal Seno
      ret_specs[key] = val
857 63e08b25 Bernardo Dal Seno
    else:
858 63e08b25 Bernardo Dal Seno
      ret_policy[key] = val
859 63e08b25 Bernardo Dal Seno
  return (ret_policy, ret_specs)