Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ cce6f357

History | View | Annotate | Download (11.8 kB)

1 c68d1f43 Michael Hanselmann
#
2 c68d1f43 Michael Hanselmann
#
3 c68d1f43 Michael Hanselmann
4 f7e6f3c8 Iustin Pop
# Copyright (C) 2007, 2011 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 cec9845c Michael Hanselmann
import os
27 e6ce18ac René Nussbaumer
import re
28 23269c5b Michael Hanselmann
import sys
29 cec9845c Michael Hanselmann
import subprocess
30 288d6440 Michael Hanselmann
import random
31 f7e6f3c8 Iustin Pop
import tempfile
32 cec9845c Michael Hanselmann
33 cec9845c Michael Hanselmann
from ganeti import utils
34 288d6440 Michael Hanselmann
from ganeti import compat
35 2214cf14 Michael Hanselmann
from ganeti import constants
36 cec9845c Michael Hanselmann
37 cec9845c Michael Hanselmann
import qa_config
38 cec9845c Michael Hanselmann
import qa_error
39 cec9845c Michael Hanselmann
40 cec9845c Michael Hanselmann
41 23269c5b Michael Hanselmann
_INFO_SEQ = None
42 23269c5b Michael Hanselmann
_WARNING_SEQ = None
43 23269c5b Michael Hanselmann
_ERROR_SEQ = None
44 23269c5b Michael Hanselmann
_RESET_SEQ = None
45 23269c5b Michael Hanselmann
46 f7e6f3c8 Iustin Pop
_MULTIPLEXERS = {}
47 f7e6f3c8 Iustin Pop
48 23269c5b Michael Hanselmann
49 23269c5b Michael Hanselmann
def _SetupColours():
50 23269c5b Michael Hanselmann
  """Initializes the colour constants.
51 23269c5b Michael Hanselmann

52 23269c5b Michael Hanselmann
  """
53 3582eef6 Iustin Pop
  # pylint: disable-msg=W0603
54 3582eef6 Iustin Pop
  # due to global usage
55 23269c5b Michael Hanselmann
  global _INFO_SEQ, _WARNING_SEQ, _ERROR_SEQ, _RESET_SEQ
56 23269c5b Michael Hanselmann
57 dfe11bad Michael Hanselmann
  # Don't use colours if stdout isn't a terminal
58 dfe11bad Michael Hanselmann
  if not sys.stdout.isatty():
59 dfe11bad Michael Hanselmann
    return
60 dfe11bad Michael Hanselmann
61 23269c5b Michael Hanselmann
  try:
62 23269c5b Michael Hanselmann
    import curses
63 23269c5b Michael Hanselmann
  except ImportError:
64 23269c5b Michael Hanselmann
    # Don't use colours if curses module can't be imported
65 23269c5b Michael Hanselmann
    return
66 23269c5b Michael Hanselmann
67 23269c5b Michael Hanselmann
  curses.setupterm()
68 23269c5b Michael Hanselmann
69 23269c5b Michael Hanselmann
  _RESET_SEQ = curses.tigetstr("op")
70 23269c5b Michael Hanselmann
71 23269c5b Michael Hanselmann
  setaf = curses.tigetstr("setaf")
72 23269c5b Michael Hanselmann
  _INFO_SEQ = curses.tparm(setaf, curses.COLOR_GREEN)
73 23269c5b Michael Hanselmann
  _WARNING_SEQ = curses.tparm(setaf, curses.COLOR_YELLOW)
74 23269c5b Michael Hanselmann
  _ERROR_SEQ = curses.tparm(setaf, curses.COLOR_RED)
75 23269c5b Michael Hanselmann
76 23269c5b Michael Hanselmann
77 23269c5b Michael Hanselmann
_SetupColours()
78 23269c5b Michael Hanselmann
79 23269c5b Michael Hanselmann
80 eaef8a05 Michael Hanselmann
def AssertIn(item, sequence):
81 eaef8a05 Michael Hanselmann
  """Raises an error when item is not in sequence.
82 eaef8a05 Michael Hanselmann

83 eaef8a05 Michael Hanselmann
  """
84 eaef8a05 Michael Hanselmann
  if item not in sequence:
85 eaef8a05 Michael Hanselmann
    raise qa_error.Error('%r not in %r' % (item, sequence))
86 eaef8a05 Michael Hanselmann
87 eaef8a05 Michael Hanselmann
88 e8ae0c20 Michael Hanselmann
def AssertEqual(first, second):
89 cec9845c Michael Hanselmann
  """Raises an error when values aren't equal.
90 cec9845c Michael Hanselmann

91 cec9845c Michael Hanselmann
  """
92 cec9845c Michael Hanselmann
  if not first == second:
93 e8ae0c20 Michael Hanselmann
    raise qa_error.Error('%r == %r' % (first, second))
94 e8ae0c20 Michael Hanselmann
95 e8ae0c20 Michael Hanselmann
96 e8ae0c20 Michael Hanselmann
def AssertNotEqual(first, second):
97 e8ae0c20 Michael Hanselmann
  """Raises an error when values are equal.
98 e8ae0c20 Michael Hanselmann

99 e8ae0c20 Michael Hanselmann
  """
100 e8ae0c20 Michael Hanselmann
  if not first != second:
101 e8ae0c20 Michael Hanselmann
    raise qa_error.Error('%r != %r' % (first, second))
102 cec9845c Michael Hanselmann
103 cec9845c Michael Hanselmann
104 e6ce18ac René Nussbaumer
def AssertMatch(string, pattern):
105 e6ce18ac René Nussbaumer
  """Raises an error when string doesn't match regexp pattern.
106 e6ce18ac René Nussbaumer

107 e6ce18ac René Nussbaumer
  """
108 e6ce18ac René Nussbaumer
  if not re.match(pattern, string):
109 e6ce18ac René Nussbaumer
    raise qa_error.Error("%r doesn't match /%r/" % (string, pattern))
110 e6ce18ac René Nussbaumer
111 e6ce18ac René Nussbaumer
112 2f4b4f78 Iustin Pop
def AssertCommand(cmd, fail=False, node=None):
113 2f4b4f78 Iustin Pop
  """Checks that a remote command succeeds.
114 2f4b4f78 Iustin Pop

115 2f4b4f78 Iustin Pop
  @param cmd: either a string (the command to execute) or a list (to
116 2f4b4f78 Iustin Pop
      be converted using L{utils.ShellQuoteArgs} into a string)
117 2f4b4f78 Iustin Pop
  @type fail: boolean
118 2f4b4f78 Iustin Pop
  @param fail: if the command is expected to fail instead of succeeding
119 2f4b4f78 Iustin Pop
  @param node: if passed, it should be the node on which the command
120 2f4b4f78 Iustin Pop
      should be executed, instead of the master node (can be either a
121 2f4b4f78 Iustin Pop
      dict or a string)
122 2f4b4f78 Iustin Pop

123 2f4b4f78 Iustin Pop
  """
124 2f4b4f78 Iustin Pop
  if node is None:
125 2f4b4f78 Iustin Pop
    node = qa_config.GetMasterNode()
126 2f4b4f78 Iustin Pop
127 2f4b4f78 Iustin Pop
  if isinstance(node, basestring):
128 2f4b4f78 Iustin Pop
    nodename = node
129 2f4b4f78 Iustin Pop
  else:
130 2f4b4f78 Iustin Pop
    nodename = node["primary"]
131 2f4b4f78 Iustin Pop
132 2f4b4f78 Iustin Pop
  if isinstance(cmd, basestring):
133 2f4b4f78 Iustin Pop
    cmdstr = cmd
134 2f4b4f78 Iustin Pop
  else:
135 2f4b4f78 Iustin Pop
    cmdstr = utils.ShellQuoteArgs(cmd)
136 2f4b4f78 Iustin Pop
137 2f4b4f78 Iustin Pop
  rcode = StartSSH(nodename, cmdstr).wait()
138 2f4b4f78 Iustin Pop
139 2f4b4f78 Iustin Pop
  if fail:
140 2f4b4f78 Iustin Pop
    if rcode == 0:
141 2f4b4f78 Iustin Pop
      raise qa_error.Error("Command '%s' on node %s was expected to fail but"
142 2f4b4f78 Iustin Pop
                           " didn't" % (cmdstr, nodename))
143 2f4b4f78 Iustin Pop
  else:
144 2f4b4f78 Iustin Pop
    if rcode != 0:
145 2f4b4f78 Iustin Pop
      raise qa_error.Error("Command '%s' on node %s failed, exit code %s" %
146 2f4b4f78 Iustin Pop
                           (cmdstr, nodename, rcode))
147 2f4b4f78 Iustin Pop
148 2214cf14 Michael Hanselmann
  return rcode
149 2214cf14 Michael Hanselmann
150 2f4b4f78 Iustin Pop
151 f7e6f3c8 Iustin Pop
def GetSSHCommand(node, cmd, strict=True, opts=None):
152 cec9845c Michael Hanselmann
  """Builds SSH command to be executed.
153 cec9845c Michael Hanselmann

154 0a05f959 Adeodato Simo
  @type node: string
155 0a05f959 Adeodato Simo
  @param node: node the command should run on
156 0a05f959 Adeodato Simo
  @type cmd: string
157 f7e6f3c8 Iustin Pop
  @param cmd: command to be executed in the node; if None or empty
158 f7e6f3c8 Iustin Pop
      string, no command will be executed
159 0a05f959 Adeodato Simo
  @type strict: boolean
160 0a05f959 Adeodato Simo
  @param strict: whether to enable strict host key checking
161 f7e6f3c8 Iustin Pop
  @type opts: list
162 f7e6f3c8 Iustin Pop
  @param opts: list of additional options
163 c68d1f43 Michael Hanselmann

164 cec9845c Michael Hanselmann
  """
165 305cb9bb Michael Hanselmann
  args = [ 'ssh', '-oEscapeChar=none', '-oBatchMode=yes', '-l', 'root', '-t' ]
166 cec9845c Michael Hanselmann
167 cec9845c Michael Hanselmann
  if strict:
168 cec9845c Michael Hanselmann
    tmp = 'yes'
169 cec9845c Michael Hanselmann
  else:
170 cec9845c Michael Hanselmann
    tmp = 'no'
171 cec9845c Michael Hanselmann
  args.append('-oStrictHostKeyChecking=%s' % tmp)
172 cec9845c Michael Hanselmann
  args.append('-oClearAllForwardings=yes')
173 cec9845c Michael Hanselmann
  args.append('-oForwardAgent=yes')
174 f7e6f3c8 Iustin Pop
  if opts:
175 f7e6f3c8 Iustin Pop
    args.extend(opts)
176 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
177 f7e6f3c8 Iustin Pop
    spath = _MULTIPLEXERS[node][0]
178 f7e6f3c8 Iustin Pop
    args.append('-oControlPath=%s' % spath)
179 f7e6f3c8 Iustin Pop
    args.append('-oControlMaster=no')
180 cec9845c Michael Hanselmann
  args.append(node)
181 f7e6f3c8 Iustin Pop
  if cmd:
182 f7e6f3c8 Iustin Pop
    args.append(cmd)
183 cec9845c Michael Hanselmann
184 cec9845c Michael Hanselmann
  return args
185 cec9845c Michael Hanselmann
186 cec9845c Michael Hanselmann
187 5d831182 Michael Hanselmann
def StartLocalCommand(cmd, **kwargs):
188 5d831182 Michael Hanselmann
  """Starts a local command.
189 5d831182 Michael Hanselmann

190 5d831182 Michael Hanselmann
  """
191 5d831182 Michael Hanselmann
  print "Command: %s" % utils.ShellQuoteArgs(cmd)
192 5d831182 Michael Hanselmann
  return subprocess.Popen(cmd, shell=False, **kwargs)
193 5d831182 Michael Hanselmann
194 5d831182 Michael Hanselmann
195 cec9845c Michael Hanselmann
def StartSSH(node, cmd, strict=True):
196 cec9845c Michael Hanselmann
  """Starts SSH.
197 cec9845c Michael Hanselmann

198 cec9845c Michael Hanselmann
  """
199 5d831182 Michael Hanselmann
  return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict))
200 4b62db14 Michael Hanselmann
201 4b62db14 Michael Hanselmann
202 f7e6f3c8 Iustin Pop
def StartMultiplexer(node):
203 f7e6f3c8 Iustin Pop
  """Starts a multiplexer command.
204 f7e6f3c8 Iustin Pop

205 f7e6f3c8 Iustin Pop
  @param node: the node for which to open the multiplexer
206 f7e6f3c8 Iustin Pop

207 f7e6f3c8 Iustin Pop
  """
208 f7e6f3c8 Iustin Pop
  if node in _MULTIPLEXERS:
209 f7e6f3c8 Iustin Pop
    return
210 f7e6f3c8 Iustin Pop
211 f7e6f3c8 Iustin Pop
  # Note: yes, we only need mktemp, since we'll remove the file anyway
212 f7e6f3c8 Iustin Pop
  sname = tempfile.mktemp(prefix="ganeti-qa-multiplexer.")
213 f7e6f3c8 Iustin Pop
  utils.RemoveFile(sname)
214 f7e6f3c8 Iustin Pop
  opts = ["-N", "-oControlPath=%s" % sname, "-oControlMaster=yes"]
215 f7e6f3c8 Iustin Pop
  print "Created socket at %s" % sname
216 f7e6f3c8 Iustin Pop
  child = StartLocalCommand(GetSSHCommand(node, None, opts=opts))
217 f7e6f3c8 Iustin Pop
  _MULTIPLEXERS[node] = (sname, child)
218 f7e6f3c8 Iustin Pop
219 f7e6f3c8 Iustin Pop
220 f7e6f3c8 Iustin Pop
def CloseMultiplexers():
221 f7e6f3c8 Iustin Pop
  """Closes all current multiplexers and cleans up.
222 f7e6f3c8 Iustin Pop

223 f7e6f3c8 Iustin Pop
  """
224 f7e6f3c8 Iustin Pop
  for node in _MULTIPLEXERS.keys():
225 f7e6f3c8 Iustin Pop
    (sname, child) = _MULTIPLEXERS.pop(node)
226 f7e6f3c8 Iustin Pop
    utils.KillProcess(child.pid, timeout=10, waitpid=True)
227 f7e6f3c8 Iustin Pop
    utils.RemoveFile(sname)
228 f7e6f3c8 Iustin Pop
229 f7e6f3c8 Iustin Pop
230 4b62db14 Michael Hanselmann
def GetCommandOutput(node, cmd):
231 4b62db14 Michael Hanselmann
  """Returns the output of a command executed on the given node.
232 4b62db14 Michael Hanselmann

233 4b62db14 Michael Hanselmann
  """
234 5d831182 Michael Hanselmann
  p = StartLocalCommand(GetSSHCommand(node, cmd), stdout=subprocess.PIPE)
235 4b62db14 Michael Hanselmann
  AssertEqual(p.wait(), 0)
236 4b62db14 Michael Hanselmann
  return p.stdout.read()
237 cec9845c Michael Hanselmann
238 cec9845c Michael Hanselmann
239 cec9845c Michael Hanselmann
def UploadFile(node, src):
240 cec9845c Michael Hanselmann
  """Uploads a file to a node and returns the filename.
241 cec9845c Michael Hanselmann

242 cec9845c Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
243 cec9845c Michael Hanselmann
  anymore.
244 49d50e52 Michael Hanselmann

245 cec9845c Michael Hanselmann
  """
246 cec9845c Michael Hanselmann
  # Make sure nobody else has access to it while preserving local permissions
247 cec9845c Michael Hanselmann
  mode = os.stat(src).st_mode & 0700
248 cec9845c Michael Hanselmann
249 cec9845c Michael Hanselmann
  cmd = ('tmp=$(tempfile --mode %o --prefix gnt) && '
250 cec9845c Michael Hanselmann
         '[[ -f "${tmp}" ]] && '
251 cec9845c Michael Hanselmann
         'cat > "${tmp}" && '
252 cec9845c Michael Hanselmann
         'echo "${tmp}"') % mode
253 cec9845c Michael Hanselmann
254 cec9845c Michael Hanselmann
  f = open(src, 'r')
255 cec9845c Michael Hanselmann
  try:
256 cec9845c Michael Hanselmann
    p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
257 cec9845c Michael Hanselmann
                         stdout=subprocess.PIPE)
258 cec9845c Michael Hanselmann
    AssertEqual(p.wait(), 0)
259 cec9845c Michael Hanselmann
260 cec9845c Michael Hanselmann
    # Return temporary filename
261 cec9845c Michael Hanselmann
    return p.stdout.read().strip()
262 cec9845c Michael Hanselmann
  finally:
263 cec9845c Michael Hanselmann
    f.close()
264 5d640672 Michael Hanselmann
265 5d640672 Michael Hanselmann
266 b9955569 René Nussbaumer
def UploadData(node, data, mode=0600, filename=None):
267 b9955569 René Nussbaumer
  """Uploads data to a node and returns the filename.
268 b9955569 René Nussbaumer

269 b9955569 René Nussbaumer
  Caller needs to remove the returned file on the node when it's not needed
270 b9955569 René Nussbaumer
  anymore.
271 b9955569 René Nussbaumer

272 b9955569 René Nussbaumer
  """
273 b9955569 René Nussbaumer
  if filename:
274 b9955569 René Nussbaumer
    tmp = "tmp=%s" % utils.ShellQuote(filename)
275 b9955569 René Nussbaumer
  else:
276 b9955569 René Nussbaumer
    tmp = "tmp=$(tempfile --mode %o --prefix gnt)" % mode
277 b9955569 René Nussbaumer
  cmd = ("%s && "
278 b9955569 René Nussbaumer
         "[[ -f \"${tmp}\" ]] && "
279 b9955569 René Nussbaumer
         "cat > \"${tmp}\" && "
280 b9955569 René Nussbaumer
         "echo \"${tmp}\"") % tmp
281 b9955569 René Nussbaumer
282 b9955569 René Nussbaumer
  p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False,
283 b9955569 René Nussbaumer
                       stdin=subprocess.PIPE, stdout=subprocess.PIPE)
284 b9955569 René Nussbaumer
  p.stdin.write(data)
285 b9955569 René Nussbaumer
  p.stdin.close()
286 b9955569 René Nussbaumer
  AssertEqual(p.wait(), 0)
287 b9955569 René Nussbaumer
288 b9955569 René Nussbaumer
  # Return temporary filename
289 b9955569 René Nussbaumer
  return p.stdout.read().strip()
290 b9955569 René Nussbaumer
291 b9955569 René Nussbaumer
292 49d50e52 Michael Hanselmann
def BackupFile(node, path):
293 49d50e52 Michael Hanselmann
  """Creates a backup of a file on the node and returns the filename.
294 49d50e52 Michael Hanselmann

295 49d50e52 Michael Hanselmann
  Caller needs to remove the returned file on the node when it's not needed
296 49d50e52 Michael Hanselmann
  anymore.
297 49d50e52 Michael Hanselmann

298 49d50e52 Michael Hanselmann
  """
299 49d50e52 Michael Hanselmann
  cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
300 49d50e52 Michael Hanselmann
         "[[ -f \"$tmp\" ]] && "
301 49d50e52 Michael Hanselmann
         "cp %s $tmp && "
302 49d50e52 Michael Hanselmann
         "echo $tmp") % (utils.ShellQuote(path), utils.ShellQuote(path))
303 49d50e52 Michael Hanselmann
304 49d50e52 Michael Hanselmann
  # Return temporary filename
305 49d50e52 Michael Hanselmann
  return GetCommandOutput(node, cmd).strip()
306 49d50e52 Michael Hanselmann
307 49d50e52 Michael Hanselmann
308 4b62db14 Michael Hanselmann
def _ResolveName(cmd, key):
309 4b62db14 Michael Hanselmann
  """Helper function.
310 4b62db14 Michael Hanselmann

311 4b62db14 Michael Hanselmann
  """
312 4b62db14 Michael Hanselmann
  master = qa_config.GetMasterNode()
313 4b62db14 Michael Hanselmann
314 4b62db14 Michael Hanselmann
  output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
315 4b62db14 Michael Hanselmann
  for line in output.splitlines():
316 4b62db14 Michael Hanselmann
    (lkey, lvalue) = line.split(':', 1)
317 4b62db14 Michael Hanselmann
    if lkey == key:
318 4b62db14 Michael Hanselmann
      return lvalue.lstrip()
319 4b62db14 Michael Hanselmann
  raise KeyError("Key not found")
320 4b62db14 Michael Hanselmann
321 4b62db14 Michael Hanselmann
322 5d640672 Michael Hanselmann
def ResolveInstanceName(instance):
323 5d640672 Michael Hanselmann
  """Gets the full name of an instance.
324 5d640672 Michael Hanselmann

325 46f9a948 Michael Hanselmann
  @type instance: string
326 46f9a948 Michael Hanselmann
  @param instance: Instance name
327 46f9a948 Michael Hanselmann

328 5d640672 Michael Hanselmann
  """
329 46f9a948 Michael Hanselmann
  return _ResolveName(['gnt-instance', 'info', instance],
330 4b62db14 Michael Hanselmann
                      'Instance name')
331 4b62db14 Michael Hanselmann
332 4b62db14 Michael Hanselmann
333 4b62db14 Michael Hanselmann
def ResolveNodeName(node):
334 4b62db14 Michael Hanselmann
  """Gets the full name of a node.
335 4b62db14 Michael Hanselmann

336 4b62db14 Michael Hanselmann
  """
337 4b62db14 Michael Hanselmann
  return _ResolveName(['gnt-node', 'info', node['primary']],
338 4b62db14 Michael Hanselmann
                      'Node name')
339 4b62db14 Michael Hanselmann
340 4b62db14 Michael Hanselmann
341 4b62db14 Michael Hanselmann
def GetNodeInstances(node, secondaries=False):
342 4b62db14 Michael Hanselmann
  """Gets a list of instances on a node.
343 4b62db14 Michael Hanselmann

344 4b62db14 Michael Hanselmann
  """
345 5d640672 Michael Hanselmann
  master = qa_config.GetMasterNode()
346 4b62db14 Michael Hanselmann
  node_name = ResolveNodeName(node)
347 5d640672 Michael Hanselmann
348 4b62db14 Michael Hanselmann
  # Get list of all instances
349 4b62db14 Michael Hanselmann
  cmd = ['gnt-instance', 'list', '--separator=:', '--no-headers',
350 4b62db14 Michael Hanselmann
         '--output=name,pnode,snodes']
351 4b62db14 Michael Hanselmann
  output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
352 4b62db14 Michael Hanselmann
353 4b62db14 Michael Hanselmann
  instances = []
354 4b62db14 Michael Hanselmann
  for line in output.splitlines():
355 4b62db14 Michael Hanselmann
    (name, pnode, snodes) = line.split(':', 2)
356 4b62db14 Michael Hanselmann
    if ((not secondaries and pnode == node_name) or
357 4b62db14 Michael Hanselmann
        (secondaries and node_name in snodes.split(','))):
358 4b62db14 Michael Hanselmann
      instances.append(name)
359 5d640672 Michael Hanselmann
360 4b62db14 Michael Hanselmann
  return instances
361 23269c5b Michael Hanselmann
362 23269c5b Michael Hanselmann
363 288d6440 Michael Hanselmann
def _SelectQueryFields(rnd, fields):
364 288d6440 Michael Hanselmann
  """Generates a list of fields for query tests.
365 288d6440 Michael Hanselmann

366 288d6440 Michael Hanselmann
  """
367 288d6440 Michael Hanselmann
  # Create copy for shuffling
368 288d6440 Michael Hanselmann
  fields = list(fields)
369 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
370 288d6440 Michael Hanselmann
371 288d6440 Michael Hanselmann
  # Check all fields
372 288d6440 Michael Hanselmann
  yield fields
373 288d6440 Michael Hanselmann
  yield sorted(fields)
374 288d6440 Michael Hanselmann
375 288d6440 Michael Hanselmann
  # Duplicate fields
376 288d6440 Michael Hanselmann
  yield fields + fields
377 288d6440 Michael Hanselmann
378 288d6440 Michael Hanselmann
  # Check small groups of fields
379 288d6440 Michael Hanselmann
  while fields:
380 288d6440 Michael Hanselmann
    yield [fields.pop() for _ in range(rnd.randint(2, 10)) if fields]
381 288d6440 Michael Hanselmann
382 288d6440 Michael Hanselmann
383 288d6440 Michael Hanselmann
def _List(listcmd, fields, names):
384 288d6440 Michael Hanselmann
  """Runs a list command.
385 288d6440 Michael Hanselmann

386 288d6440 Michael Hanselmann
  """
387 288d6440 Michael Hanselmann
  master = qa_config.GetMasterNode()
388 288d6440 Michael Hanselmann
389 288d6440 Michael Hanselmann
  cmd = [listcmd, "list", "--separator=|", "--no-header",
390 288d6440 Michael Hanselmann
         "--output", ",".join(fields)]
391 288d6440 Michael Hanselmann
392 288d6440 Michael Hanselmann
  if names:
393 288d6440 Michael Hanselmann
    cmd.extend(names)
394 288d6440 Michael Hanselmann
395 288d6440 Michael Hanselmann
  return GetCommandOutput(master["primary"],
396 288d6440 Michael Hanselmann
                          utils.ShellQuoteArgs(cmd)).splitlines()
397 288d6440 Michael Hanselmann
398 288d6440 Michael Hanselmann
399 288d6440 Michael Hanselmann
def GenericQueryTest(cmd, fields):
400 288d6440 Michael Hanselmann
  """Runs a number of tests on query commands.
401 288d6440 Michael Hanselmann

402 288d6440 Michael Hanselmann
  @param cmd: Command name
403 288d6440 Michael Hanselmann
  @param fields: List of field names
404 288d6440 Michael Hanselmann

405 288d6440 Michael Hanselmann
  """
406 288d6440 Michael Hanselmann
  rnd = random.Random(hash(cmd))
407 288d6440 Michael Hanselmann
408 3582eef6 Iustin Pop
  fields = list(fields)
409 288d6440 Michael Hanselmann
  rnd.shuffle(fields)
410 288d6440 Michael Hanselmann
411 288d6440 Michael Hanselmann
  # Test a number of field combinations
412 288d6440 Michael Hanselmann
  for testfields in _SelectQueryFields(rnd, fields):
413 288d6440 Michael Hanselmann
    AssertCommand([cmd, "list", "--output", ",".join(testfields)])
414 288d6440 Michael Hanselmann
415 288d6440 Michael Hanselmann
  namelist_fn = compat.partial(_List, cmd, ["name"])
416 288d6440 Michael Hanselmann
417 288d6440 Michael Hanselmann
  # When no names were requested, the list must be sorted
418 288d6440 Michael Hanselmann
  names = namelist_fn(None)
419 288d6440 Michael Hanselmann
  AssertEqual(names, utils.NiceSort(names))
420 288d6440 Michael Hanselmann
421 288d6440 Michael Hanselmann
  # When requesting specific names, the order must be kept
422 288d6440 Michael Hanselmann
  revnames = list(reversed(names))
423 288d6440 Michael Hanselmann
  AssertEqual(namelist_fn(revnames), revnames)
424 288d6440 Michael Hanselmann
425 288d6440 Michael Hanselmann
  randnames = list(names)
426 288d6440 Michael Hanselmann
  rnd.shuffle(randnames)
427 288d6440 Michael Hanselmann
  AssertEqual(namelist_fn(randnames), randnames)
428 288d6440 Michael Hanselmann
429 2214cf14 Michael Hanselmann
  # Listing unknown items must fail
430 2214cf14 Michael Hanselmann
  AssertCommand([cmd, "list", "this.name.certainly.does.not.exist"], fail=True)
431 2214cf14 Michael Hanselmann
432 2214cf14 Michael Hanselmann
  # Check exit code for listing unknown field
433 2214cf14 Michael Hanselmann
  AssertEqual(AssertCommand([cmd, "list", "--output=field/does/not/exist"],
434 2214cf14 Michael Hanselmann
                            fail=True),
435 2214cf14 Michael Hanselmann
              constants.EXIT_UNKNOWN_FIELD)
436 2214cf14 Michael Hanselmann
437 2214cf14 Michael Hanselmann
438 2214cf14 Michael Hanselmann
def GenericQueryFieldsTest(cmd, fields):
439 2214cf14 Michael Hanselmann
  master = qa_config.GetMasterNode()
440 2214cf14 Michael Hanselmann
441 2214cf14 Michael Hanselmann
  # Listing fields
442 2214cf14 Michael Hanselmann
  AssertCommand([cmd, "list-fields"])
443 2214cf14 Michael Hanselmann
  AssertCommand([cmd, "list-fields"] + fields)
444 2214cf14 Michael Hanselmann
445 2214cf14 Michael Hanselmann
  # Check listed fields (all, must be sorted)
446 2214cf14 Michael Hanselmann
  realcmd = [cmd, "list-fields", "--separator=|", "--no-headers"]
447 2214cf14 Michael Hanselmann
  output = GetCommandOutput(master["primary"],
448 2214cf14 Michael Hanselmann
                            utils.ShellQuoteArgs(realcmd)).splitlines()
449 2214cf14 Michael Hanselmann
  AssertEqual([line.split("|", 1)[0] for line in output],
450 c694367b Michael Hanselmann
              utils.NiceSort(fields))
451 2214cf14 Michael Hanselmann
452 2214cf14 Michael Hanselmann
  # Check exit code for listing unknown field
453 2214cf14 Michael Hanselmann
  AssertEqual(AssertCommand([cmd, "list-fields", "field/does/not/exist"],
454 2214cf14 Michael Hanselmann
                            fail=True),
455 2214cf14 Michael Hanselmann
              constants.EXIT_UNKNOWN_FIELD)
456 2214cf14 Michael Hanselmann
457 288d6440 Michael Hanselmann
458 dfe11bad Michael Hanselmann
def _FormatWithColor(text, seq):
459 dfe11bad Michael Hanselmann
  if not seq:
460 dfe11bad Michael Hanselmann
    return text
461 dfe11bad Michael Hanselmann
  return "%s%s%s" % (seq, text, _RESET_SEQ)
462 23269c5b Michael Hanselmann
463 23269c5b Michael Hanselmann
464 dfe11bad Michael Hanselmann
FormatWarning = lambda text: _FormatWithColor(text, _WARNING_SEQ)
465 dfe11bad Michael Hanselmann
FormatError = lambda text: _FormatWithColor(text, _ERROR_SEQ)
466 dfe11bad Michael Hanselmann
FormatInfo = lambda text: _FormatWithColor(text, _INFO_SEQ)