Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 10f5ab6c

History | View | Annotate | Download (16 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2010 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
"""Script for unittesting the RAPI client module"""
23

    
24

    
25
import re
26
import unittest
27
import warnings
28

    
29
from ganeti import http
30
from ganeti import serializer
31

    
32
from ganeti.rapi import connector
33
from ganeti.rapi import rlib2
34
from ganeti.rapi import client
35

    
36
import testutils
37

    
38

    
39
_URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
40

    
41

    
42
def _GetPathFromUri(uri):
43
  """Gets the path and query from a URI.
44

45
  """
46
  match = _URI_RE.match(uri)
47
  if match:
48
    return match.groupdict()["path"]
49
  else:
50
    return None
51

    
52

    
53
class HttpResponseMock:
54
  """Dumb mock of httplib.HTTPResponse.
55

56
  """
57

    
58
  def __init__(self, code, data):
59
    self.code = code
60
    self._data = data
61

    
62
  def read(self):
63
    return self._data
64

    
65

    
66
class OpenerDirectorMock:
67
  """Mock for urllib.OpenerDirector.
68

69
  """
70

    
71
  def __init__(self, rapi):
72
    self._rapi = rapi
73
    self.last_request = None
74

    
75
  def open(self, req):
76
    self.last_request = req
77

    
78
    path = _GetPathFromUri(req.get_full_url())
79
    code, resp_body = self._rapi.FetchResponse(path, req.get_method())
80
    return HttpResponseMock(code, resp_body)
81

    
82

    
83
class RapiMock(object):
84
  def __init__(self):
85
    self._mapper = connector.Mapper()
86
    self._responses = []
87
    self._last_handler = None
88

    
89
  def AddResponse(self, response, code=200):
90
    self._responses.insert(0, (code, response))
91

    
92
  def GetLastHandler(self):
93
    return self._last_handler
94

    
95
  def FetchResponse(self, path, method):
96
    try:
97
      HandlerClass, items, args = self._mapper.getController(path)
98
      self._last_handler = HandlerClass(items, args, None)
99
      if not hasattr(self._last_handler, method.upper()):
100
        raise http.HttpNotImplemented(message="Method not implemented")
101

    
102
    except http.HttpException, ex:
103
      code = ex.code
104
      response = ex.message
105
    else:
106
      if not self._responses:
107
        raise Exception("No responses")
108

    
109
      (code, response) = self._responses.pop()
110

    
111
    return code, response
112

    
113

    
114
class RapiMockTest(unittest.TestCase):
115
  def test(self):
116
    rapi = RapiMock()
117
    path = "/version"
118
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET"))
119
    self.assertEqual((501, "Method not implemented"),
120
                     rapi.FetchResponse("/version", "POST"))
121
    rapi.AddResponse("2")
122
    code, response = rapi.FetchResponse("/version", "GET")
123
    self.assertEqual(200, code)
124
    self.assertEqual("2", response)
125
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
126

    
127

    
128
class GanetiRapiClientTests(testutils.GanetiTestCase):
129
  def setUp(self):
130
    testutils.GanetiTestCase.setUp(self)
131

    
132
    self.rapi = RapiMock()
133
    self.http = OpenerDirectorMock(self.rapi)
134
    self.client = client.GanetiRapiClient('master.foo.com')
135
    self.client._http = self.http
136
    # Hard-code the version for easier testing.
137
    self.client._version = 2
138

    
139
  def assertHandler(self, handler_cls):
140
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
141

    
142
  def assertQuery(self, key, value):
143
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
144

    
145
  def assertItems(self, items):
146
    self.assertEqual(items, self.rapi.GetLastHandler().items)
147

    
148
  def assertBulk(self):
149
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
150

    
151
  def assertDryRun(self):
152
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
153

    
154
  def testEncodeQuery(self):
155
    query = [
156
      ("a", None),
157
      ("b", 1),
158
      ("c", 2),
159
      ("d", "Foo"),
160
      ("e", True),
161
      ]
162

    
163
    expected = [
164
      ("a", ""),
165
      ("b", 1),
166
      ("c", 2),
167
      ("d", "Foo"),
168
      ("e", 1),
169
      ]
170

    
171
    self.assertEqualValues(self.client._EncodeQuery(query),
172
                           expected)
173

    
174
    # invalid types
175
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
176
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
177

    
178
  def testHttpError(self):
179
    self.rapi.AddResponse(None, code=404)
180
    try:
181
      self.client.GetJobStatus(15140)
182
    except client.GanetiApiError, err:
183
      self.assertEqual(err.code, 404)
184
    else:
185
      self.fail("Didn't raise exception")
186

    
187
  def testGetVersion(self):
188
    self.client._version = None
189
    self.rapi.AddResponse("2")
190
    self.assertEqual(2, self.client.GetVersion())
191
    self.assertHandler(rlib2.R_version)
192

    
193
  def testGetOperatingSystems(self):
194
    self.rapi.AddResponse("[\"beos\"]")
195
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
196
    self.assertHandler(rlib2.R_2_os)
197

    
198
  def testGetClusterTags(self):
199
    self.rapi.AddResponse("[\"tag\"]")
200
    self.assertEqual(["tag"], self.client.GetClusterTags())
201
    self.assertHandler(rlib2.R_2_tags)
202

    
203
  def testAddClusterTags(self):
204
    self.rapi.AddResponse("1234")
205
    self.assertEqual(1234,
206
        self.client.AddClusterTags(["awesome"], dry_run=True))
207
    self.assertHandler(rlib2.R_2_tags)
208
    self.assertDryRun()
209
    self.assertQuery("tag", ["awesome"])
210

    
211
  def testDeleteClusterTags(self):
212
    self.rapi.AddResponse("5107")
213
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
214
                                                         dry_run=True))
215
    self.assertHandler(rlib2.R_2_tags)
216
    self.assertDryRun()
217
    self.assertQuery("tag", ["awesome"])
218

    
219
  def testGetInfo(self):
220
    self.rapi.AddResponse("{}")
221
    self.assertEqual({}, self.client.GetInfo())
222
    self.assertHandler(rlib2.R_2_info)
223

    
224
  def testGetInstances(self):
225
    self.rapi.AddResponse("[]")
226
    self.assertEqual([], self.client.GetInstances(bulk=True))
227
    self.assertHandler(rlib2.R_2_instances)
228
    self.assertBulk()
229

    
230
  def testGetInstanceInfo(self):
231
    self.rapi.AddResponse("[]")
232
    self.assertEqual([], self.client.GetInstanceInfo("instance"))
233
    self.assertHandler(rlib2.R_2_instances_name)
234
    self.assertItems(["instance"])
235

    
236
  def testCreateInstance(self):
237
    self.rapi.AddResponse("1234")
238
    self.assertEqual(1234, self.client.CreateInstance(dry_run=True))
239
    self.assertHandler(rlib2.R_2_instances)
240
    self.assertDryRun()
241

    
242
  def testDeleteInstance(self):
243
    self.rapi.AddResponse("1234")
244
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
245
    self.assertHandler(rlib2.R_2_instances_name)
246
    self.assertItems(["instance"])
247
    self.assertDryRun()
248

    
249
  def testGetInstanceTags(self):
250
    self.rapi.AddResponse("[]")
251
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
252
    self.assertHandler(rlib2.R_2_instances_name_tags)
253
    self.assertItems(["fooinstance"])
254

    
255
  def testAddInstanceTags(self):
256
    self.rapi.AddResponse("1234")
257
    self.assertEqual(1234,
258
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
259
    self.assertHandler(rlib2.R_2_instances_name_tags)
260
    self.assertItems(["fooinstance"])
261
    self.assertDryRun()
262
    self.assertQuery("tag", ["awesome"])
263

    
264
  def testDeleteInstanceTags(self):
265
    self.rapi.AddResponse("25826")
266
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
267
                                                           dry_run=True))
268
    self.assertHandler(rlib2.R_2_instances_name_tags)
269
    self.assertItems(["foo"])
270
    self.assertDryRun()
271
    self.assertQuery("tag", ["awesome"])
272

    
273
  def testRebootInstance(self):
274
    self.rapi.AddResponse("6146")
275
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
276
                                        ignore_secondaries=True, dry_run=True)
277
    self.assertEqual(6146, job_id)
278
    self.assertHandler(rlib2.R_2_instances_name_reboot)
279
    self.assertItems(["i-bar"])
280
    self.assertDryRun()
281
    self.assertQuery("type", ["hard"])
282
    self.assertQuery("ignore_secondaries", ["1"])
283

    
284
  def testShutdownInstance(self):
285
    self.rapi.AddResponse("1487")
286
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
287
                                                        dry_run=True))
288
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
289
    self.assertItems(["foo-instance"])
290
    self.assertDryRun()
291

    
292
  def testStartupInstance(self):
293
    self.rapi.AddResponse("27149")
294
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
295
                                                        dry_run=True))
296
    self.assertHandler(rlib2.R_2_instances_name_startup)
297
    self.assertItems(["bar-instance"])
298
    self.assertDryRun()
299

    
300
  def testReinstallInstance(self):
301
    self.rapi.AddResponse("19119")
302
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
303
                                                          no_startup=True))
304
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
305
    self.assertItems(["baz-instance"])
306
    self.assertQuery("os", ["DOS"])
307
    self.assertQuery("nostartup", ["1"])
308

    
309
  def testReplaceInstanceDisks(self):
310
    self.rapi.AddResponse("999")
311
    job_id = self.client.ReplaceInstanceDisks("instance-name",
312
        ["hda", "hdc"], dry_run=True, iallocator="hail")
313
    self.assertEqual(999, job_id)
314
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
315
    self.assertItems(["instance-name"])
316
    self.assertQuery("disks", ["hda,hdc"])
317
    self.assertQuery("mode", ["replace_auto"])
318
    self.assertQuery("iallocator", ["hail"])
319
    self.assertDryRun()
320

    
321
    self.assertRaises(client.InvalidReplacementMode,
322
                      self.client.ReplaceInstanceDisks,
323
                      "instance_a", ["hda"], mode="invalid_mode")
324
    self.assertRaises(client.GanetiApiError,
325
                      self.client.ReplaceInstanceDisks,
326
                      "instance-foo", ["hda"], mode="replace_on_secondary")
327

    
328
    self.rapi.AddResponse("1000")
329
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
330
        ["hda"], mode="replace_on_secondary", remote_node="foo-node",
331
        dry_run=True)
332
    self.assertEqual(1000, job_id)
333
    self.assertItems(["instance-bar"])
334
    self.assertQuery("disks", ["hda"])
335
    self.assertQuery("remote_node", ["foo-node"])
336
    self.assertDryRun()
337

    
338
  def testGetJobs(self):
339
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
340
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
341
    self.assertEqual([123, 124], self.client.GetJobs())
342
    self.assertHandler(rlib2.R_2_jobs)
343

    
344
  def testGetJobStatus(self):
345
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
346
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
347
    self.assertHandler(rlib2.R_2_jobs_id)
348
    self.assertItems(["1234"])
349

    
350
  def testWaitForJobChange(self):
351
    fields = ["id", "summary"]
352
    expected = {
353
      "job_info": [123, "something"],
354
      "log_entries": [],
355
      }
356

    
357
    self.rapi.AddResponse(serializer.DumpJson(expected))
358
    result = self.client.WaitForJobChange(123, fields, [], -1)
359
    self.assertEqualValues(expected, result)
360
    self.assertHandler(rlib2.R_2_jobs_id_wait)
361
    self.assertItems(["123"])
362

    
363
  def testCancelJob(self):
364
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
365
    self.assertEqual([True, "Job 123 will be canceled"],
366
                     self.client.CancelJob(999, dry_run=True))
367
    self.assertHandler(rlib2.R_2_jobs_id)
368
    self.assertItems(["999"])
369
    self.assertDryRun()
370

    
371
  def testGetNodes(self):
372
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
373
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
374
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
375
    self.assertHandler(rlib2.R_2_nodes)
376

    
377
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
378
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
379
    self.assertEqual([{"id": "node1", "uri": "uri1"},
380
                      {"id": "node2", "uri": "uri2"}],
381
                     self.client.GetNodes(bulk=True))
382
    self.assertHandler(rlib2.R_2_nodes)
383
    self.assertBulk()
384

    
385
  def testGetNodeInfo(self):
386
    self.rapi.AddResponse("{}")
387
    self.assertEqual({}, self.client.GetNodeInfo("node-foo"))
388
    self.assertHandler(rlib2.R_2_nodes_name)
389
    self.assertItems(["node-foo"])
390

    
391
  def testEvacuateNode(self):
392
    self.rapi.AddResponse("9876")
393
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
394
    self.assertEqual(9876, job_id)
395
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
396
    self.assertItems(["node-1"])
397
    self.assertQuery("remote_node", ["node-2"])
398

    
399
    self.rapi.AddResponse("8888")
400
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
401
    self.assertEqual(8888, job_id)
402
    self.assertItems(["node-3"])
403
    self.assertQuery("iallocator", ["hail"])
404
    self.assertDryRun()
405

    
406
    self.assertRaises(client.GanetiApiError,
407
                      self.client.EvacuateNode,
408
                      "node-4", iallocator="hail", remote_node="node-5")
409

    
410
  def testMigrateNode(self):
411
    self.rapi.AddResponse("1111")
412
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
413
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
414
    self.assertItems(["node-a"])
415
    self.assertQuery("live", ["1"])
416
    self.assertDryRun()
417

    
418
  def testGetNodeRole(self):
419
    self.rapi.AddResponse("\"master\"")
420
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
421
    self.assertHandler(rlib2.R_2_nodes_name_role)
422
    self.assertItems(["node-a"])
423

    
424
  def testSetNodeRole(self):
425
    self.rapi.AddResponse("789")
426
    self.assertEqual(789,
427
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
428
    self.assertHandler(rlib2.R_2_nodes_name_role)
429
    self.assertItems(["node-foo"])
430
    self.assertQuery("force", ["1"])
431
    self.assertEqual("\"master-candidate\"", self.http.last_request.data)
432

    
433
    self.assertRaises(client.InvalidNodeRole,
434
                      self.client.SetNodeRole, "node-bar", "fake-role")
435

    
436
  def testGetNodeStorageUnits(self):
437
    self.rapi.AddResponse("42")
438
    self.assertEqual(42,
439
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
440
    self.assertHandler(rlib2.R_2_nodes_name_storage)
441
    self.assertItems(["node-x"])
442
    self.assertQuery("storage_type", ["lvm-pv"])
443
    self.assertQuery("output_fields", ["fields"])
444

    
445
  def testModifyNodeStorageUnits(self):
446
    self.rapi.AddResponse("14")
447
    self.assertEqual(14,
448
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
449
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
450
    self.assertItems(["node-z"])
451
    self.assertQuery("storage_type", ["lvm-pv"])
452
    self.assertQuery("name", ["hda"])
453

    
454
  def testRepairNodeStorageUnits(self):
455
    self.rapi.AddResponse("99")
456
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
457
                                                            "hda"))
458
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
459
    self.assertItems(["node-z"])
460
    self.assertQuery("storage_type", ["lvm-pv"])
461
    self.assertQuery("name", ["hda"])
462

    
463
  def testGetNodeTags(self):
464
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
465
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
466
    self.assertHandler(rlib2.R_2_nodes_name_tags)
467
    self.assertItems(["node-k"])
468

    
469
  def testAddNodeTags(self):
470
    self.rapi.AddResponse("1234")
471
    self.assertEqual(1234,
472
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
473
    self.assertHandler(rlib2.R_2_nodes_name_tags)
474
    self.assertItems(["node-v"])
475
    self.assertDryRun()
476
    self.assertQuery("tag", ["awesome"])
477

    
478
  def testDeleteNodeTags(self):
479
    self.rapi.AddResponse("16861")
480
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
481
                                                       dry_run=True))
482
    self.assertHandler(rlib2.R_2_nodes_name_tags)
483
    self.assertItems(["node-w"])
484
    self.assertDryRun()
485
    self.assertQuery("tag", ["awesome"])
486

    
487

    
488
if __name__ == '__main__':
489
  testutils.GanetiTestProgram()