Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / testutils.py @ 912b2278

History | View | Annotate | Download (9.9 kB)

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

24 a85f23fa Michael Hanselmann
"""
25 a85f23fa Michael Hanselmann
26 a85f23fa Michael Hanselmann
import logging
27 f90a1ab5 René Nussbaumer
import re
28 d9492490 Michael Hanselmann
import base64
29 f90a1ab5 René Nussbaumer
import pycurl
30 d9492490 Michael Hanselmann
from cStringIO import StringIO
31 a85f23fa Michael Hanselmann
32 a85f23fa Michael Hanselmann
from ganeti import errors
33 a85f23fa Michael Hanselmann
from ganeti import opcodes
34 d9492490 Michael Hanselmann
from ganeti import http
35 1afa108c Michael Hanselmann
from ganeti import server
36 1afa108c Michael Hanselmann
from ganeti import utils
37 1afa108c Michael Hanselmann
from ganeti import compat
38 1afa108c Michael Hanselmann
from ganeti import luxi
39 912b2278 Petr Pudlak
import ganeti.rpc.client as rpccl
40 1afa108c Michael Hanselmann
from ganeti import rapi
41 1afa108c Michael Hanselmann
42 1afa108c Michael Hanselmann
import ganeti.http.server # pylint: disable=W0611
43 1afa108c Michael Hanselmann
import ganeti.server.rapi
44 1afa108c Michael Hanselmann
import ganeti.rapi.client
45 a85f23fa Michael Hanselmann
46 a85f23fa Michael Hanselmann
47 f90a1ab5 René Nussbaumer
_URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
48 f90a1ab5 René Nussbaumer
49 f90a1ab5 René Nussbaumer
50 a85f23fa Michael Hanselmann
class VerificationError(Exception):
51 a85f23fa Michael Hanselmann
  """Dedicated error class for test utilities.
52 a85f23fa Michael Hanselmann

53 a85f23fa Michael Hanselmann
  This class is used to hide all of Ganeti's internal exception, so that
54 a85f23fa Michael Hanselmann
  external users of these utilities don't have to integrate Ganeti's exception
55 a85f23fa Michael Hanselmann
  hierarchy.
56 a85f23fa Michael Hanselmann

57 a85f23fa Michael Hanselmann
  """
58 a85f23fa Michael Hanselmann
59 a85f23fa Michael Hanselmann
60 a85f23fa Michael Hanselmann
def _GetOpById(op_id):
61 a85f23fa Michael Hanselmann
  """Tries to get an opcode class based on its C{OP_ID}.
62 a85f23fa Michael Hanselmann

63 a85f23fa Michael Hanselmann
  """
64 a85f23fa Michael Hanselmann
  try:
65 a85f23fa Michael Hanselmann
    return opcodes.OP_MAPPING[op_id]
66 a85f23fa Michael Hanselmann
  except KeyError:
67 a85f23fa Michael Hanselmann
    raise VerificationError("Unknown opcode ID '%s'" % op_id)
68 a85f23fa Michael Hanselmann
69 a85f23fa Michael Hanselmann
70 a85f23fa Michael Hanselmann
def _HideInternalErrors(fn):
71 a85f23fa Michael Hanselmann
  """Hides Ganeti-internal exceptions, see L{VerificationError}.
72 a85f23fa Michael Hanselmann

73 a85f23fa Michael Hanselmann
  """
74 a85f23fa Michael Hanselmann
  def wrapper(*args, **kwargs):
75 a85f23fa Michael Hanselmann
    try:
76 a85f23fa Michael Hanselmann
      return fn(*args, **kwargs)
77 1afa108c Michael Hanselmann
    except (errors.GenericError, rapi.client.GanetiApiError), err:
78 a85f23fa Michael Hanselmann
      raise VerificationError("Unhandled Ganeti error: %s" % err)
79 a85f23fa Michael Hanselmann
80 a85f23fa Michael Hanselmann
  return wrapper
81 a85f23fa Michael Hanselmann
82 a85f23fa Michael Hanselmann
83 a85f23fa Michael Hanselmann
@_HideInternalErrors
84 a85f23fa Michael Hanselmann
def VerifyOpInput(op_id, data):
85 a85f23fa Michael Hanselmann
  """Verifies opcode parameters according to their definition.
86 a85f23fa Michael Hanselmann

87 a85f23fa Michael Hanselmann
  @type op_id: string
88 a85f23fa Michael Hanselmann
  @param op_id: Opcode ID (C{OP_ID} attribute), e.g. C{OP_CLUSTER_VERIFY}
89 a85f23fa Michael Hanselmann
  @type data: dict
90 a85f23fa Michael Hanselmann
  @param data: Opcode parameter values
91 a85f23fa Michael Hanselmann
  @raise VerificationError: Parameter verification failed
92 a85f23fa Michael Hanselmann

93 a85f23fa Michael Hanselmann
  """
94 a85f23fa Michael Hanselmann
  op_cls = _GetOpById(op_id)
95 a85f23fa Michael Hanselmann
96 a85f23fa Michael Hanselmann
  try:
97 a85f23fa Michael Hanselmann
    op = op_cls(**data) # pylint: disable=W0142
98 a85f23fa Michael Hanselmann
  except TypeError, err:
99 a85f23fa Michael Hanselmann
    raise VerificationError("Unable to create opcode instance: %s" % err)
100 a85f23fa Michael Hanselmann
101 a85f23fa Michael Hanselmann
  try:
102 a85f23fa Michael Hanselmann
    op.Validate(False)
103 a85f23fa Michael Hanselmann
  except errors.OpPrereqError, err:
104 a85f23fa Michael Hanselmann
    raise VerificationError("Parameter validation for opcode '%s' failed: %s" %
105 a85f23fa Michael Hanselmann
                            (op_id, err))
106 a85f23fa Michael Hanselmann
107 a85f23fa Michael Hanselmann
108 a85f23fa Michael Hanselmann
@_HideInternalErrors
109 a85f23fa Michael Hanselmann
def VerifyOpResult(op_id, result):
110 a85f23fa Michael Hanselmann
  """Verifies opcode results used in tests (e.g. in a mock).
111 a85f23fa Michael Hanselmann

112 a85f23fa Michael Hanselmann
  @type op_id: string
113 a85f23fa Michael Hanselmann
  @param op_id: Opcode ID (C{OP_ID} attribute), e.g. C{OP_CLUSTER_VERIFY}
114 a85f23fa Michael Hanselmann
  @param result: Mocked opcode result
115 a85f23fa Michael Hanselmann
  @raise VerificationError: Return value verification failed
116 a85f23fa Michael Hanselmann

117 a85f23fa Michael Hanselmann
  """
118 a85f23fa Michael Hanselmann
  resultcheck_fn = _GetOpById(op_id).OP_RESULT
119 a85f23fa Michael Hanselmann
120 a85f23fa Michael Hanselmann
  if not resultcheck_fn:
121 a85f23fa Michael Hanselmann
    logging.warning("Opcode '%s' has no result type definition", op_id)
122 a85f23fa Michael Hanselmann
  elif not resultcheck_fn(result):
123 a85f23fa Michael Hanselmann
    raise VerificationError("Given result does not match result description"
124 a85f23fa Michael Hanselmann
                            " for opcode '%s': %s" % (op_id, resultcheck_fn))
125 f90a1ab5 René Nussbaumer
126 f90a1ab5 René Nussbaumer
127 f90a1ab5 René Nussbaumer
def _GetPathFromUri(uri):
128 f90a1ab5 René Nussbaumer
  """Gets the path and query from a URI.
129 f90a1ab5 René Nussbaumer

130 f90a1ab5 René Nussbaumer
  """
131 f90a1ab5 René Nussbaumer
  match = _URI_RE.match(uri)
132 f90a1ab5 René Nussbaumer
  if match:
133 f90a1ab5 René Nussbaumer
    return match.groupdict()["path"]
134 f90a1ab5 René Nussbaumer
  else:
135 f90a1ab5 René Nussbaumer
    return None
136 f90a1ab5 René Nussbaumer
137 f90a1ab5 René Nussbaumer
138 1b8e72f3 Michael Hanselmann
def _FormatHeaders(headers):
139 1b8e72f3 Michael Hanselmann
  """Formats HTTP headers.
140 1b8e72f3 Michael Hanselmann

141 1b8e72f3 Michael Hanselmann
  @type headers: sequence of strings
142 1b8e72f3 Michael Hanselmann
  @rtype: string
143 1b8e72f3 Michael Hanselmann

144 1b8e72f3 Michael Hanselmann
  """
145 1b8e72f3 Michael Hanselmann
  assert compat.all(": " in header for header in headers)
146 1b8e72f3 Michael Hanselmann
  return "\n".join(headers)
147 1b8e72f3 Michael Hanselmann
148 1b8e72f3 Michael Hanselmann
149 f90a1ab5 René Nussbaumer
class FakeCurl:
150 f90a1ab5 René Nussbaumer
  """Fake cURL object.
151 f90a1ab5 René Nussbaumer

152 f90a1ab5 René Nussbaumer
  """
153 f90a1ab5 René Nussbaumer
  def __init__(self, handler):
154 f90a1ab5 René Nussbaumer
    """Initialize this class
155 f90a1ab5 René Nussbaumer

156 f90a1ab5 René Nussbaumer
    @param handler: Request handler instance
157 f90a1ab5 René Nussbaumer

158 f90a1ab5 René Nussbaumer
    """
159 f90a1ab5 René Nussbaumer
    self._handler = handler
160 f90a1ab5 René Nussbaumer
    self._opts = {}
161 f90a1ab5 René Nussbaumer
    self._info = {}
162 f90a1ab5 René Nussbaumer
163 f90a1ab5 René Nussbaumer
  def setopt(self, opt, value):
164 f90a1ab5 René Nussbaumer
    self._opts[opt] = value
165 f90a1ab5 René Nussbaumer
166 f90a1ab5 René Nussbaumer
  def getopt(self, opt):
167 f90a1ab5 René Nussbaumer
    return self._opts.get(opt)
168 f90a1ab5 René Nussbaumer
169 f90a1ab5 René Nussbaumer
  def unsetopt(self, opt):
170 f90a1ab5 René Nussbaumer
    self._opts.pop(opt, None)
171 f90a1ab5 René Nussbaumer
172 f90a1ab5 René Nussbaumer
  def getinfo(self, info):
173 f90a1ab5 René Nussbaumer
    return self._info[info]
174 f90a1ab5 René Nussbaumer
175 f90a1ab5 René Nussbaumer
  def perform(self):
176 f90a1ab5 René Nussbaumer
    method = self._opts[pycurl.CUSTOMREQUEST]
177 f90a1ab5 René Nussbaumer
    url = self._opts[pycurl.URL]
178 f90a1ab5 René Nussbaumer
    request_body = self._opts[pycurl.POSTFIELDS]
179 f90a1ab5 René Nussbaumer
    writefn = self._opts[pycurl.WRITEFUNCTION]
180 f90a1ab5 René Nussbaumer
181 d9492490 Michael Hanselmann
    if pycurl.HTTPHEADER in self._opts:
182 1b8e72f3 Michael Hanselmann
      baseheaders = _FormatHeaders(self._opts[pycurl.HTTPHEADER])
183 d9492490 Michael Hanselmann
    else:
184 d9492490 Michael Hanselmann
      baseheaders = ""
185 d9492490 Michael Hanselmann
186 0e632cbd Michael Hanselmann
    headers = http.ParseHeaders(StringIO(baseheaders))
187 d9492490 Michael Hanselmann
188 d9492490 Michael Hanselmann
    if request_body:
189 d9492490 Michael Hanselmann
      headers[http.HTTP_CONTENT_LENGTH] = str(len(request_body))
190 d9492490 Michael Hanselmann
191 d9492490 Michael Hanselmann
    if self._opts.get(pycurl.HTTPAUTH, 0) & pycurl.HTTPAUTH_BASIC:
192 d9492490 Michael Hanselmann
      try:
193 d9492490 Michael Hanselmann
        userpwd = self._opts[pycurl.USERPWD]
194 d9492490 Michael Hanselmann
      except KeyError:
195 d9492490 Michael Hanselmann
        raise errors.ProgrammerError("Basic authentication requires username"
196 d9492490 Michael Hanselmann
                                     " and password")
197 d9492490 Michael Hanselmann
198 d9492490 Michael Hanselmann
      headers[http.HTTP_AUTHORIZATION] = \
199 d9492490 Michael Hanselmann
        "%s %s" % (http.auth.HTTP_BASIC_AUTH, base64.b64encode(userpwd))
200 d9492490 Michael Hanselmann
201 f90a1ab5 René Nussbaumer
    path = _GetPathFromUri(url)
202 0351944b Michael Hanselmann
    (code, _, resp_body) = \
203 d9492490 Michael Hanselmann
      self._handler.FetchResponse(path, method, headers, request_body)
204 f90a1ab5 René Nussbaumer
205 f90a1ab5 René Nussbaumer
    self._info[pycurl.RESPONSE_CODE] = code
206 f90a1ab5 René Nussbaumer
    if resp_body is not None:
207 f90a1ab5 René Nussbaumer
      writefn(resp_body)
208 1afa108c Michael Hanselmann
209 1afa108c Michael Hanselmann
210 1afa108c Michael Hanselmann
class _RapiMock:
211 1afa108c Michael Hanselmann
  """Mocking out the RAPI server parts.
212 1afa108c Michael Hanselmann

213 1afa108c Michael Hanselmann
  """
214 27a8a190 Michael Hanselmann
  def __init__(self, user_fn, luxi_client, reqauth=False):
215 1afa108c Michael Hanselmann
    """Initialize this class.
216 1afa108c Michael Hanselmann

217 1afa108c Michael Hanselmann
    @type user_fn: callable
218 1afa108c Michael Hanselmann
    @param user_fn: Function to authentication username
219 1afa108c Michael Hanselmann
    @param luxi_client: A LUXI client implementation
220 1afa108c Michael Hanselmann

221 1afa108c Michael Hanselmann
    """
222 1afa108c Michael Hanselmann
    self.handler = \
223 27a8a190 Michael Hanselmann
      server.rapi.RemoteApiHandler(user_fn, reqauth, _client_cls=luxi_client)
224 1afa108c Michael Hanselmann
225 1afa108c Michael Hanselmann
  def FetchResponse(self, path, method, headers, request_body):
226 1afa108c Michael Hanselmann
    """This is a callback method used to fetch a response.
227 1afa108c Michael Hanselmann

228 1afa108c Michael Hanselmann
    This method is called by the FakeCurl.perform method
229 1afa108c Michael Hanselmann

230 1afa108c Michael Hanselmann
    @type path: string
231 1afa108c Michael Hanselmann
    @param path: Requested path
232 1afa108c Michael Hanselmann
    @type method: string
233 1afa108c Michael Hanselmann
    @param method: HTTP method
234 1afa108c Michael Hanselmann
    @type request_body: string
235 1afa108c Michael Hanselmann
    @param request_body: Request body
236 1afa108c Michael Hanselmann
    @type headers: mimetools.Message
237 1afa108c Michael Hanselmann
    @param headers: Request headers
238 0351944b Michael Hanselmann
    @return: Tuple containing status code, response headers and response body
239 1afa108c Michael Hanselmann

240 1afa108c Michael Hanselmann
    """
241 1afa108c Michael Hanselmann
    req_msg = http.HttpMessage()
242 1afa108c Michael Hanselmann
    req_msg.start_line = \
243 1afa108c Michael Hanselmann
      http.HttpClientToServerStartLine(method, path, http.HTTP_1_0)
244 1afa108c Michael Hanselmann
    req_msg.headers = headers
245 1afa108c Michael Hanselmann
    req_msg.body = request_body
246 1afa108c Michael Hanselmann
247 1afa108c Michael Hanselmann
    (_, _, _, resp_msg) = \
248 1afa108c Michael Hanselmann
      http.server.HttpResponder(self.handler)(lambda: (req_msg, None))
249 1afa108c Michael Hanselmann
250 0351944b Michael Hanselmann
    return (resp_msg.start_line.code, resp_msg.headers, resp_msg.body)
251 1afa108c Michael Hanselmann
252 1afa108c Michael Hanselmann
253 1afa108c Michael Hanselmann
class _TestLuxiTransport:
254 1afa108c Michael Hanselmann
  """Mocked LUXI transport.
255 1afa108c Michael Hanselmann

256 1afa108c Michael Hanselmann
  Raises L{errors.RapiTestResult} for all method calls, no matter the
257 1afa108c Michael Hanselmann
  arguments.
258 1afa108c Michael Hanselmann

259 1afa108c Michael Hanselmann
  """
260 1afa108c Michael Hanselmann
  def __init__(self, record_fn, address, timeouts=None): # pylint: disable=W0613
261 1afa108c Michael Hanselmann
    """Initializes this class.
262 1afa108c Michael Hanselmann

263 1afa108c Michael Hanselmann
    """
264 1afa108c Michael Hanselmann
    self._record_fn = record_fn
265 1afa108c Michael Hanselmann
266 1afa108c Michael Hanselmann
  def Close(self):
267 1afa108c Michael Hanselmann
    pass
268 1afa108c Michael Hanselmann
269 1afa108c Michael Hanselmann
  def Call(self, data):
270 1afa108c Michael Hanselmann
    """Calls LUXI method.
271 1afa108c Michael Hanselmann

272 1afa108c Michael Hanselmann
    In this test class the method is not actually called, but added to a list
273 1afa108c Michael Hanselmann
    of called methods and then an exception (L{errors.RapiTestResult}) is
274 1afa108c Michael Hanselmann
    raised. There is no return value.
275 1afa108c Michael Hanselmann

276 1afa108c Michael Hanselmann
    """
277 912b2278 Petr Pudlak
    (method, _, _) = rpccl.ParseRequest(data)
278 1afa108c Michael Hanselmann
279 1afa108c Michael Hanselmann
    # Take a note of called method
280 1afa108c Michael Hanselmann
    self._record_fn(method)
281 1afa108c Michael Hanselmann
282 1afa108c Michael Hanselmann
    # Everything went fine until here, so let's abort the test
283 1afa108c Michael Hanselmann
    raise errors.RapiTestResult
284 1afa108c Michael Hanselmann
285 1afa108c Michael Hanselmann
286 1afa108c Michael Hanselmann
class _LuxiCallRecorder:
287 1afa108c Michael Hanselmann
  """Records all called LUXI client methods.
288 1afa108c Michael Hanselmann

289 1afa108c Michael Hanselmann
  """
290 1afa108c Michael Hanselmann
  def __init__(self):
291 1afa108c Michael Hanselmann
    """Initializes this class.
292 1afa108c Michael Hanselmann

293 1afa108c Michael Hanselmann
    """
294 1afa108c Michael Hanselmann
    self._called = set()
295 1afa108c Michael Hanselmann
296 1afa108c Michael Hanselmann
  def Record(self, name):
297 1afa108c Michael Hanselmann
    """Records a called function name.
298 1afa108c Michael Hanselmann

299 1afa108c Michael Hanselmann
    """
300 1afa108c Michael Hanselmann
    self._called.add(name)
301 1afa108c Michael Hanselmann
302 1afa108c Michael Hanselmann
  def CalledNames(self):
303 1afa108c Michael Hanselmann
    """Returns a list of called LUXI methods.
304 1afa108c Michael Hanselmann

305 1afa108c Michael Hanselmann
    """
306 1afa108c Michael Hanselmann
    return self._called
307 1afa108c Michael Hanselmann
308 303bc802 Iustin Pop
  def __call__(self, address=None):
309 1afa108c Michael Hanselmann
    """Creates an instrumented LUXI client.
310 1afa108c Michael Hanselmann

311 1afa108c Michael Hanselmann
    The LUXI client will record all method calls (use L{CalledNames} to
312 1afa108c Michael Hanselmann
    retrieve them).
313 1afa108c Michael Hanselmann

314 1afa108c Michael Hanselmann
    """
315 1afa108c Michael Hanselmann
    return luxi.Client(transport=compat.partial(_TestLuxiTransport,
316 303bc802 Iustin Pop
                                                self.Record),
317 303bc802 Iustin Pop
                       address=address)
318 1afa108c Michael Hanselmann
319 1afa108c Michael Hanselmann
320 1afa108c Michael Hanselmann
def _TestWrapper(fn, *args, **kwargs):
321 1afa108c Michael Hanselmann
  """Wrapper for ignoring L{errors.RapiTestResult}.
322 1afa108c Michael Hanselmann

323 1afa108c Michael Hanselmann
  """
324 1afa108c Michael Hanselmann
  try:
325 1afa108c Michael Hanselmann
    return fn(*args, **kwargs)
326 1afa108c Michael Hanselmann
  except errors.RapiTestResult:
327 1afa108c Michael Hanselmann
    # Everything was fine up to the point of sending a LUXI request
328 1afa108c Michael Hanselmann
    return NotImplemented
329 1afa108c Michael Hanselmann
330 1afa108c Michael Hanselmann
331 1afa108c Michael Hanselmann
class InputTestClient:
332 1afa108c Michael Hanselmann
  """Test version of RAPI client.
333 1afa108c Michael Hanselmann

334 1afa108c Michael Hanselmann
  Instances of this class can be used to test input arguments for RAPI client
335 1afa108c Michael Hanselmann
  calls. See L{rapi.client.GanetiRapiClient} for available methods and their
336 1afa108c Michael Hanselmann
  arguments. Functions can return C{NotImplemented} if all arguments are
337 1afa108c Michael Hanselmann
  acceptable, but a LUXI request would be necessary to provide an actual return
338 1afa108c Michael Hanselmann
  value. In case of an error, L{VerificationError} is raised.
339 1afa108c Michael Hanselmann

340 1afa108c Michael Hanselmann
  @see: An example on how to use this class can be found in
341 1afa108c Michael Hanselmann
    C{doc/examples/rapi_testutils.py}
342 1afa108c Michael Hanselmann

343 1afa108c Michael Hanselmann
  """
344 1afa108c Michael Hanselmann
  def __init__(self):
345 1afa108c Michael Hanselmann
    """Initializes this class.
346 1afa108c Michael Hanselmann

347 1afa108c Michael Hanselmann
    """
348 1afa108c Michael Hanselmann
    username = utils.GenerateSecret()
349 1afa108c Michael Hanselmann
    password = utils.GenerateSecret()
350 1afa108c Michael Hanselmann
351 1afa108c Michael Hanselmann
    def user_fn(wanted):
352 1afa108c Michael Hanselmann
      """Called to verify user credentials given in HTTP request.
353 1afa108c Michael Hanselmann

354 1afa108c Michael Hanselmann
      """
355 1afa108c Michael Hanselmann
      assert username == wanted
356 1afa108c Michael Hanselmann
      return http.auth.PasswordFileUser(username, password,
357 1afa108c Michael Hanselmann
                                        [rapi.RAPI_ACCESS_WRITE])
358 1afa108c Michael Hanselmann
359 1afa108c Michael Hanselmann
    self._lcr = _LuxiCallRecorder()
360 1afa108c Michael Hanselmann
361 1afa108c Michael Hanselmann
    # Create a mock RAPI server
362 1afa108c Michael Hanselmann
    handler = _RapiMock(user_fn, self._lcr)
363 1afa108c Michael Hanselmann
364 1afa108c Michael Hanselmann
    self._client = \
365 1afa108c Michael Hanselmann
      rapi.client.GanetiRapiClient("master.example.com",
366 1afa108c Michael Hanselmann
                                   username=username, password=password,
367 1afa108c Michael Hanselmann
                                   curl_factory=lambda: FakeCurl(handler))
368 1afa108c Michael Hanselmann
369 1afa108c Michael Hanselmann
  def _GetLuxiCalls(self):
370 1afa108c Michael Hanselmann
    """Returns the names of all called LUXI client functions.
371 1afa108c Michael Hanselmann

372 1afa108c Michael Hanselmann
    """
373 1afa108c Michael Hanselmann
    return self._lcr.CalledNames()
374 1afa108c Michael Hanselmann
375 1afa108c Michael Hanselmann
  def __getattr__(self, name):
376 1afa108c Michael Hanselmann
    """Finds method by name.
377 1afa108c Michael Hanselmann

378 1afa108c Michael Hanselmann
    The method is wrapped using L{_TestWrapper} to produce the actual test
379 1afa108c Michael Hanselmann
    result.
380 1afa108c Michael Hanselmann

381 1afa108c Michael Hanselmann
    """
382 1afa108c Michael Hanselmann
    return _HideInternalErrors(compat.partial(_TestWrapper,
383 1afa108c Michael Hanselmann
                                              getattr(self._client, name)))