Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ 8a019a03

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 testHttpError(self):
155
    self.rapi.AddResponse(None, code=404)
156
    try:
157
      self.client.GetJobStatus(15140)
158
    except client.GanetiApiError, err:
159
      self.assertEqual(err.code, 404)
160
    else:
161
      self.fail("Didn't raise exception")
162

    
163
  def testGetVersion(self):
164
    self.client._version = None
165
    self.rapi.AddResponse("2")
166
    self.assertEqual(2, self.client.GetVersion())
167
    self.assertHandler(rlib2.R_version)
168

    
169
  def testGetOperatingSystems(self):
170
    self.rapi.AddResponse("[\"beos\"]")
171
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
172
    self.assertHandler(rlib2.R_2_os)
173

    
174
  def testGetClusterTags(self):
175
    self.rapi.AddResponse("[\"tag\"]")
176
    self.assertEqual(["tag"], self.client.GetClusterTags())
177
    self.assertHandler(rlib2.R_2_tags)
178

    
179
  def testAddClusterTags(self):
180
    self.rapi.AddResponse("1234")
181
    self.assertEqual(1234,
182
        self.client.AddClusterTags(["awesome"], dry_run=True))
183
    self.assertHandler(rlib2.R_2_tags)
184
    self.assertDryRun()
185
    self.assertQuery("tag", ["awesome"])
186

    
187
  def testDeleteClusterTags(self):
188
    self.rapi.AddResponse("5107")
189
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
190
                                                         dry_run=True))
191
    self.assertHandler(rlib2.R_2_tags)
192
    self.assertDryRun()
193
    self.assertQuery("tag", ["awesome"])
194

    
195
  def testGetInfo(self):
196
    self.rapi.AddResponse("{}")
197
    self.assertEqual({}, self.client.GetInfo())
198
    self.assertHandler(rlib2.R_2_info)
199

    
200
  def testGetInstances(self):
201
    self.rapi.AddResponse("[]")
202
    self.assertEqual([], self.client.GetInstances(bulk=True))
203
    self.assertHandler(rlib2.R_2_instances)
204
    self.assertBulk()
205

    
206
  def testGetInstanceInfo(self):
207
    self.rapi.AddResponse("[]")
208
    self.assertEqual([], self.client.GetInstanceInfo("instance"))
209
    self.assertHandler(rlib2.R_2_instances_name)
210
    self.assertItems(["instance"])
211

    
212
  def testCreateInstance(self):
213
    self.rapi.AddResponse("1234")
214
    self.assertEqual(1234, self.client.CreateInstance(dry_run=True))
215
    self.assertHandler(rlib2.R_2_instances)
216
    self.assertDryRun()
217

    
218
  def testDeleteInstance(self):
219
    self.rapi.AddResponse("1234")
220
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
221
    self.assertHandler(rlib2.R_2_instances_name)
222
    self.assertItems(["instance"])
223
    self.assertDryRun()
224

    
225
  def testGetInstanceTags(self):
226
    self.rapi.AddResponse("[]")
227
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
228
    self.assertHandler(rlib2.R_2_instances_name_tags)
229
    self.assertItems(["fooinstance"])
230

    
231
  def testAddInstanceTags(self):
232
    self.rapi.AddResponse("1234")
233
    self.assertEqual(1234,
234
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
235
    self.assertHandler(rlib2.R_2_instances_name_tags)
236
    self.assertItems(["fooinstance"])
237
    self.assertDryRun()
238
    self.assertQuery("tag", ["awesome"])
239

    
240
  def testDeleteInstanceTags(self):
241
    self.rapi.AddResponse("25826")
242
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
243
                                                           dry_run=True))
244
    self.assertHandler(rlib2.R_2_instances_name_tags)
245
    self.assertItems(["foo"])
246
    self.assertDryRun()
247
    self.assertQuery("tag", ["awesome"])
248

    
249
  def testRebootInstance(self):
250
    self.rapi.AddResponse("6146")
251
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
252
                                        ignore_secondaries=True, dry_run=True)
253
    self.assertEqual(6146, job_id)
254
    self.assertHandler(rlib2.R_2_instances_name_reboot)
255
    self.assertItems(["i-bar"])
256
    self.assertDryRun()
257
    self.assertQuery("type", ["hard"])
258
    self.assertQuery("ignore_secondaries", ["True"])
259

    
260
  def testShutdownInstance(self):
261
    self.rapi.AddResponse("1487")
262
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
263
                                                        dry_run=True))
264
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
265
    self.assertItems(["foo-instance"])
266
    self.assertDryRun()
267

    
268
  def testStartupInstance(self):
269
    self.rapi.AddResponse("27149")
270
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
271
                                                        dry_run=True))
272
    self.assertHandler(rlib2.R_2_instances_name_startup)
273
    self.assertItems(["bar-instance"])
274
    self.assertDryRun()
275

    
276
  def testReinstallInstance(self):
277
    self.rapi.AddResponse("19119")
278
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
279
                                                          no_startup=True))
280
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
281
    self.assertItems(["baz-instance"])
282
    self.assertQuery("os", ["DOS"])
283
    self.assertQuery("nostartup", ["1"])
284

    
285
  def testReplaceInstanceDisks(self):
286
    self.rapi.AddResponse("999")
287
    job_id = self.client.ReplaceInstanceDisks("instance-name",
288
        ["hda", "hdc"], dry_run=True, iallocator="hail")
289
    self.assertEqual(999, job_id)
290
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
291
    self.assertItems(["instance-name"])
292
    self.assertQuery("disks", ["hda,hdc"])
293
    self.assertQuery("mode", ["replace_auto"])
294
    self.assertQuery("iallocator", ["hail"])
295
    self.assertDryRun()
296

    
297
    self.assertRaises(client.InvalidReplacementMode,
298
                      self.client.ReplaceInstanceDisks,
299
                      "instance_a", ["hda"], mode="invalid_mode")
300
    self.assertRaises(client.GanetiApiError,
301
                      self.client.ReplaceInstanceDisks,
302
                      "instance-foo", ["hda"], mode="replace_on_secondary")
303

    
304
    self.rapi.AddResponse("1000")
305
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
306
        ["hda"], mode="replace_on_secondary", remote_node="foo-node",
307
        dry_run=True)
308
    self.assertEqual(1000, job_id)
309
    self.assertItems(["instance-bar"])
310
    self.assertQuery("disks", ["hda"])
311
    self.assertQuery("remote_node", ["foo-node"])
312
    self.assertDryRun()
313

    
314
  def testGetJobs(self):
315
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
316
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
317
    self.assertEqual([123, 124], self.client.GetJobs())
318
    self.assertHandler(rlib2.R_2_jobs)
319

    
320
  def testGetJobStatus(self):
321
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
322
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
323
    self.assertHandler(rlib2.R_2_jobs_id)
324
    self.assertItems(["1234"])
325

    
326
  def testWaitForJobChange(self):
327
    fields = ["id", "summary"]
328
    expected = {
329
      "job_info": [123, "something"],
330
      "log_entries": [],
331
      }
332

    
333
    self.rapi.AddResponse(serializer.DumpJson(expected))
334
    result = self.client.WaitForJobChange(123, fields, [], -1)
335
    self.assertEqualValues(expected, result)
336
    self.assertHandler(rlib2.R_2_jobs_id_wait)
337
    self.assertItems(["123"])
338

    
339
  def testCancelJob(self):
340
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
341
    self.assertEqual([True, "Job 123 will be canceled"],
342
                     self.client.CancelJob(999, dry_run=True))
343
    self.assertHandler(rlib2.R_2_jobs_id)
344
    self.assertItems(["999"])
345
    self.assertDryRun()
346

    
347
  def testGetNodes(self):
348
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
349
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
350
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
351
    self.assertHandler(rlib2.R_2_nodes)
352

    
353
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
354
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
355
    self.assertEqual([{"id": "node1", "uri": "uri1"},
356
                      {"id": "node2", "uri": "uri2"}],
357
                     self.client.GetNodes(bulk=True))
358
    self.assertHandler(rlib2.R_2_nodes)
359
    self.assertBulk()
360

    
361
  def testGetNodeInfo(self):
362
    self.rapi.AddResponse("{}")
363
    self.assertEqual({}, self.client.GetNodeInfo("node-foo"))
364
    self.assertHandler(rlib2.R_2_nodes_name)
365
    self.assertItems(["node-foo"])
366

    
367
  def testEvacuateNode(self):
368
    self.rapi.AddResponse("9876")
369
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
370
    self.assertEqual(9876, job_id)
371
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
372
    self.assertItems(["node-1"])
373
    self.assertQuery("remote_node", ["node-2"])
374

    
375
    self.rapi.AddResponse("8888")
376
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
377
    self.assertEqual(8888, job_id)
378
    self.assertItems(["node-3"])
379
    self.assertQuery("iallocator", ["hail"])
380
    self.assertDryRun()
381

    
382
    self.assertRaises(client.GanetiApiError,
383
                      self.client.EvacuateNode,
384
                      "node-4", iallocator="hail", remote_node="node-5")
385

    
386
  def testMigrateNode(self):
387
    self.rapi.AddResponse("1111")
388
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
389
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
390
    self.assertItems(["node-a"])
391
    self.assertQuery("live", ["1"])
392
    self.assertDryRun()
393

    
394
  def testGetNodeRole(self):
395
    self.rapi.AddResponse("\"master\"")
396
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
397
    self.assertHandler(rlib2.R_2_nodes_name_role)
398
    self.assertItems(["node-a"])
399

    
400
  def testSetNodeRole(self):
401
    self.rapi.AddResponse("789")
402
    self.assertEqual(789,
403
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
404
    self.assertHandler(rlib2.R_2_nodes_name_role)
405
    self.assertItems(["node-foo"])
406
    self.assertQuery("force", ["True"])
407
    self.assertEqual("\"master-candidate\"", self.http.last_request.data)
408

    
409
    self.assertRaises(client.InvalidNodeRole,
410
                      self.client.SetNodeRole, "node-bar", "fake-role")
411

    
412
  def testGetNodeStorageUnits(self):
413
    self.rapi.AddResponse("42")
414
    self.assertEqual(42,
415
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
416
    self.assertHandler(rlib2.R_2_nodes_name_storage)
417
    self.assertItems(["node-x"])
418
    self.assertQuery("storage_type", ["lvm-pv"])
419
    self.assertQuery("output_fields", ["fields"])
420

    
421
    self.assertRaises(client.InvalidStorageType,
422
                      self.client.GetNodeStorageUnits,
423
                      "node-y", "floppy-disk", "fields")
424

    
425
  def testModifyNodeStorageUnits(self):
426
    self.rapi.AddResponse("14")
427
    self.assertEqual(14,
428
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
429
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
430
    self.assertItems(["node-z"])
431
    self.assertQuery("storage_type", ["lvm-pv"])
432
    self.assertQuery("name", ["hda"])
433

    
434
    self.assertRaises(client.InvalidStorageType,
435
                      self.client.ModifyNodeStorageUnits,
436
                      "node-n", "floppy-disk", "hdc")
437

    
438
  def testRepairNodeStorageUnits(self):
439
    self.rapi.AddResponse("99")
440
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
441
                                                            "hda"))
442
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
443
    self.assertItems(["node-z"])
444
    self.assertQuery("storage_type", ["lvm-pv"])
445
    self.assertQuery("name", ["hda"])
446

    
447
    self.assertRaises(client.InvalidStorageType,
448
                      self.client.RepairNodeStorageUnits,
449
                      "node-n", "floppy-disk", "hdc")
450

    
451
  def testGetNodeTags(self):
452
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
453
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
454
    self.assertHandler(rlib2.R_2_nodes_name_tags)
455
    self.assertItems(["node-k"])
456

    
457
  def testAddNodeTags(self):
458
    self.rapi.AddResponse("1234")
459
    self.assertEqual(1234,
460
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
461
    self.assertHandler(rlib2.R_2_nodes_name_tags)
462
    self.assertItems(["node-v"])
463
    self.assertDryRun()
464
    self.assertQuery("tag", ["awesome"])
465

    
466
  def testDeleteNodeTags(self):
467
    self.rapi.AddResponse("16861")
468
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
469
                                                       dry_run=True))
470
    self.assertHandler(rlib2.R_2_nodes_name_tags)
471
    self.assertItems(["node-w"])
472
    self.assertDryRun()
473
    self.assertQuery("tag", ["awesome"])
474

    
475

    
476
if __name__ == '__main__':
477
  testutils.GanetiTestProgram()