Statistics
| Branch: | Tag: | Revision:

root / qa / qa_utils.py @ 46f9a948

History | View | Annotate | Download (6.1 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Utilities for QA tests.
23

24
"""
25

    
26
import os
27
import re
28
import sys
29
import subprocess
30

    
31
from ganeti import utils
32

    
33
import qa_config
34
import qa_error
35

    
36

    
37
_INFO_SEQ = None
38
_WARNING_SEQ = None
39
_ERROR_SEQ = None
40
_RESET_SEQ = None
41

    
42

    
43
def _SetupColours():
44
  """Initializes the colour constants.
45

46
  """
47
  global _INFO_SEQ, _WARNING_SEQ, _ERROR_SEQ, _RESET_SEQ
48

    
49
  # Don't use colours if stdout isn't a terminal
50
  if not sys.stdout.isatty():
51
    return
52

    
53
  try:
54
    import curses
55
  except ImportError:
56
    # Don't use colours if curses module can't be imported
57
    return
58

    
59
  curses.setupterm()
60

    
61
  _RESET_SEQ = curses.tigetstr("op")
62

    
63
  setaf = curses.tigetstr("setaf")
64
  _INFO_SEQ = curses.tparm(setaf, curses.COLOR_GREEN)
65
  _WARNING_SEQ = curses.tparm(setaf, curses.COLOR_YELLOW)
66
  _ERROR_SEQ = curses.tparm(setaf, curses.COLOR_RED)
67

    
68

    
69
_SetupColours()
70

    
71

    
72
def AssertIn(item, sequence):
73
  """Raises an error when item is not in sequence.
74

75
  """
76
  if item not in sequence:
77
    raise qa_error.Error('%r not in %r' % (item, sequence))
78

    
79

    
80
def AssertEqual(first, second):
81
  """Raises an error when values aren't equal.
82

83
  """
84
  if not first == second:
85
    raise qa_error.Error('%r == %r' % (first, second))
86

    
87

    
88
def AssertNotEqual(first, second):
89
  """Raises an error when values are equal.
90

91
  """
92
  if not first != second:
93
    raise qa_error.Error('%r != %r' % (first, second))
94

    
95

    
96
def AssertMatch(string, pattern):
97
  """Raises an error when string doesn't match regexp pattern.
98

99
  """
100
  if not re.match(pattern, string):
101
    raise qa_error.Error("%r doesn't match /%r/" % (string, pattern))
102

    
103

    
104
def GetSSHCommand(node, cmd, strict=True):
105
  """Builds SSH command to be executed.
106

107
  Args:
108
  - node: Node the command should run on
109
  - cmd: Command to be executed as a list with all parameters
110
  - strict: Whether to enable strict host key checking
111

112
  """
113
  args = [ 'ssh', '-oEscapeChar=none', '-oBatchMode=yes', '-l', 'root', '-t' ]
114

    
115
  if strict:
116
    tmp = 'yes'
117
  else:
118
    tmp = 'no'
119
  args.append('-oStrictHostKeyChecking=%s' % tmp)
120
  args.append('-oClearAllForwardings=yes')
121
  args.append('-oForwardAgent=yes')
122
  args.append(node)
123
  args.append(cmd)
124

    
125
  return args
126

    
127

    
128
def StartLocalCommand(cmd, **kwargs):
129
  """Starts a local command.
130

131
  """
132
  print "Command: %s" % utils.ShellQuoteArgs(cmd)
133
  return subprocess.Popen(cmd, shell=False, **kwargs)
134

    
135

    
136
def StartSSH(node, cmd, strict=True):
137
  """Starts SSH.
138

139
  """
140
  return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict))
141

    
142

    
143
def GetCommandOutput(node, cmd):
144
  """Returns the output of a command executed on the given node.
145

146
  """
147
  p = StartLocalCommand(GetSSHCommand(node, cmd), stdout=subprocess.PIPE)
148
  AssertEqual(p.wait(), 0)
149
  return p.stdout.read()
150

    
151

    
152
def UploadFile(node, src):
153
  """Uploads a file to a node and returns the filename.
154

155
  Caller needs to remove the returned file on the node when it's not needed
156
  anymore.
157

158
  """
159
  # Make sure nobody else has access to it while preserving local permissions
160
  mode = os.stat(src).st_mode & 0700
161

    
162
  cmd = ('tmp=$(tempfile --mode %o --prefix gnt) && '
163
         '[[ -f "${tmp}" ]] && '
164
         'cat > "${tmp}" && '
165
         'echo "${tmp}"') % mode
166

    
167
  f = open(src, 'r')
168
  try:
169
    p = subprocess.Popen(GetSSHCommand(node, cmd), shell=False, stdin=f,
170
                         stdout=subprocess.PIPE)
171
    AssertEqual(p.wait(), 0)
172

    
173
    # Return temporary filename
174
    return p.stdout.read().strip()
175
  finally:
176
    f.close()
177

    
178

    
179
def BackupFile(node, path):
180
  """Creates a backup of a file on the node and returns the filename.
181

182
  Caller needs to remove the returned file on the node when it's not needed
183
  anymore.
184

185
  """
186
  cmd = ("tmp=$(tempfile --prefix .gnt --directory=$(dirname %s)) && "
187
         "[[ -f \"$tmp\" ]] && "
188
         "cp %s $tmp && "
189
         "echo $tmp") % (utils.ShellQuote(path), utils.ShellQuote(path))
190

    
191
  # Return temporary filename
192
  return GetCommandOutput(node, cmd).strip()
193

    
194

    
195
def _ResolveName(cmd, key):
196
  """Helper function.
197

198
  """
199
  master = qa_config.GetMasterNode()
200

    
201
  output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
202
  for line in output.splitlines():
203
    (lkey, lvalue) = line.split(':', 1)
204
    if lkey == key:
205
      return lvalue.lstrip()
206
  raise KeyError("Key not found")
207

    
208

    
209
def ResolveInstanceName(instance):
210
  """Gets the full name of an instance.
211

212
  @type instance: string
213
  @param instance: Instance name
214

215
  """
216
  return _ResolveName(['gnt-instance', 'info', instance],
217
                      'Instance name')
218

    
219

    
220
def ResolveNodeName(node):
221
  """Gets the full name of a node.
222

223
  """
224
  return _ResolveName(['gnt-node', 'info', node['primary']],
225
                      'Node name')
226

    
227

    
228
def GetNodeInstances(node, secondaries=False):
229
  """Gets a list of instances on a node.
230

231
  """
232
  master = qa_config.GetMasterNode()
233
  node_name = ResolveNodeName(node)
234

    
235
  # Get list of all instances
236
  cmd = ['gnt-instance', 'list', '--separator=:', '--no-headers',
237
         '--output=name,pnode,snodes']
238
  output = GetCommandOutput(master['primary'], utils.ShellQuoteArgs(cmd))
239

    
240
  instances = []
241
  for line in output.splitlines():
242
    (name, pnode, snodes) = line.split(':', 2)
243
    if ((not secondaries and pnode == node_name) or
244
        (secondaries and node_name in snodes.split(','))):
245
      instances.append(name)
246

    
247
  return instances
248

    
249

    
250
def _FormatWithColor(text, seq):
251
  if not seq:
252
    return text
253
  return "%s%s%s" % (seq, text, _RESET_SEQ)
254

    
255

    
256
FormatWarning = lambda text: _FormatWithColor(text, _WARNING_SEQ)
257
FormatError = lambda text: _FormatWithColor(text, _ERROR_SEQ)
258
FormatInfo = lambda text: _FormatWithColor(text, _INFO_SEQ)