Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ de4f53e6

History | View | Annotate | Download (15.8 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 testGetVersion(self):
155
    self.client._version = None
156
    self.rapi.AddResponse("2")
157
    self.assertEqual(2, self.client.GetVersion())
158
    self.assertHandler(rlib2.R_version)
159

    
160
  def testGetOperatingSystems(self):
161
    self.rapi.AddResponse("[\"beos\"]")
162
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
163
    self.assertHandler(rlib2.R_2_os)
164

    
165
  def testGetClusterTags(self):
166
    self.rapi.AddResponse("[\"tag\"]")
167
    self.assertEqual(["tag"], self.client.GetClusterTags())
168
    self.assertHandler(rlib2.R_2_tags)
169

    
170
  def testAddClusterTags(self):
171
    self.rapi.AddResponse("1234")
172
    self.assertEqual(1234,
173
        self.client.AddClusterTags(["awesome"], dry_run=True))
174
    self.assertHandler(rlib2.R_2_tags)
175
    self.assertDryRun()
176
    self.assertQuery("tag", ["awesome"])
177

    
178
  def testDeleteClusterTags(self):
179
    self.rapi.AddResponse("5107")
180
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
181
                                                         dry_run=True))
182
    self.assertHandler(rlib2.R_2_tags)
183
    self.assertDryRun()
184
    self.assertQuery("tag", ["awesome"])
185

    
186
  def testGetInfo(self):
187
    self.rapi.AddResponse("{}")
188
    self.assertEqual({}, self.client.GetInfo())
189
    self.assertHandler(rlib2.R_2_info)
190

    
191
  def testGetInstances(self):
192
    self.rapi.AddResponse("[]")
193
    self.assertEqual([], self.client.GetInstances(bulk=True))
194
    self.assertHandler(rlib2.R_2_instances)
195
    self.assertBulk()
196

    
197
  def testGetInstanceInfo(self):
198
    self.rapi.AddResponse("[]")
199
    self.assertEqual([], self.client.GetInstanceInfo("instance"))
200
    self.assertHandler(rlib2.R_2_instances_name)
201
    self.assertItems(["instance"])
202

    
203
  def testCreateInstance(self):
204
    self.rapi.AddResponse("1234")
205
    self.assertEqual(1234, self.client.CreateInstance(dry_run=True))
206
    self.assertHandler(rlib2.R_2_instances)
207
    self.assertDryRun()
208

    
209
  def testDeleteInstance(self):
210
    self.rapi.AddResponse("1234")
211
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
212
    self.assertHandler(rlib2.R_2_instances_name)
213
    self.assertItems(["instance"])
214
    self.assertDryRun()
215

    
216
  def testGetInstanceTags(self):
217
    self.rapi.AddResponse("[]")
218
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
219
    self.assertHandler(rlib2.R_2_instances_name_tags)
220
    self.assertItems(["fooinstance"])
221

    
222
  def testAddInstanceTags(self):
223
    self.rapi.AddResponse("1234")
224
    self.assertEqual(1234,
225
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
226
    self.assertHandler(rlib2.R_2_instances_name_tags)
227
    self.assertItems(["fooinstance"])
228
    self.assertDryRun()
229
    self.assertQuery("tag", ["awesome"])
230

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

    
240
  def testRebootInstance(self):
241
    self.rapi.AddResponse("6146")
242
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
243
                                        ignore_secondaries=True, dry_run=True)
244
    self.assertEqual(6146, job_id)
245
    self.assertHandler(rlib2.R_2_instances_name_reboot)
246
    self.assertItems(["i-bar"])
247
    self.assertDryRun()
248
    self.assertQuery("type", ["hard"])
249
    self.assertQuery("ignore_secondaries", ["True"])
250

    
251
  def testShutdownInstance(self):
252
    self.rapi.AddResponse("1487")
253
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
254
                                                        dry_run=True))
255
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
256
    self.assertItems(["foo-instance"])
257
    self.assertDryRun()
258

    
259
  def testStartupInstance(self):
260
    self.rapi.AddResponse("27149")
261
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
262
                                                        dry_run=True))
263
    self.assertHandler(rlib2.R_2_instances_name_startup)
264
    self.assertItems(["bar-instance"])
265
    self.assertDryRun()
266

    
267
  def testReinstallInstance(self):
268
    self.rapi.AddResponse("19119")
269
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
270
                                                          no_startup=True))
271
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
272
    self.assertItems(["baz-instance"])
273
    self.assertQuery("os", ["DOS"])
274
    self.assertQuery("nostartup", ["1"])
275

    
276
  def testReplaceInstanceDisks(self):
277
    self.rapi.AddResponse("999")
278
    job_id = self.client.ReplaceInstanceDisks("instance-name",
279
        ["hda", "hdc"], dry_run=True, iallocator="hail")
280
    self.assertEqual(999, job_id)
281
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
282
    self.assertItems(["instance-name"])
283
    self.assertQuery("disks", ["hda,hdc"])
284
    self.assertQuery("mode", ["replace_auto"])
285
    self.assertQuery("iallocator", ["hail"])
286
    self.assertDryRun()
287

    
288
    self.assertRaises(client.InvalidReplacementMode,
289
                      self.client.ReplaceInstanceDisks,
290
                      "instance_a", ["hda"], mode="invalid_mode")
291
    self.assertRaises(client.GanetiApiError,
292
                      self.client.ReplaceInstanceDisks,
293
                      "instance-foo", ["hda"], mode="replace_on_secondary")
294

    
295
    self.rapi.AddResponse("1000")
296
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
297
        ["hda"], mode="replace_on_secondary", remote_node="foo-node",
298
        dry_run=True)
299
    self.assertEqual(1000, job_id)
300
    self.assertItems(["instance-bar"])
301
    self.assertQuery("disks", ["hda"])
302
    self.assertQuery("remote_node", ["foo-node"])
303
    self.assertDryRun()
304

    
305
  def testGetJobs(self):
306
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
307
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
308
    self.assertEqual([123, 124], self.client.GetJobs())
309
    self.assertHandler(rlib2.R_2_jobs)
310

    
311
  def testGetJobStatus(self):
312
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
313
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
314
    self.assertHandler(rlib2.R_2_jobs_id)
315
    self.assertItems(["1234"])
316

    
317
  def testWaitForJobChange(self):
318
    fields = ["id", "summary"]
319
    expected = {
320
      "job_info": [123, "something"],
321
      "log_entries": [],
322
      }
323

    
324
    self.rapi.AddResponse(serializer.DumpJson(expected))
325
    result = self.client.WaitForJobChange(123, fields, [], -1)
326
    self.assertEqualValues(expected, result)
327
    self.assertHandler(rlib2.R_2_jobs_id_wait)
328
    self.assertItems(["123"])
329

    
330
  def testCancelJob(self):
331
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
332
    self.assertEqual([True, "Job 123 will be canceled"],
333
                     self.client.CancelJob(999, dry_run=True))
334
    self.assertHandler(rlib2.R_2_jobs_id)
335
    self.assertItems(["999"])
336
    self.assertDryRun()
337

    
338
  def testGetNodes(self):
339
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
340
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
341
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
342
    self.assertHandler(rlib2.R_2_nodes)
343

    
344
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
345
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
346
    self.assertEqual([{"id": "node1", "uri": "uri1"},
347
                      {"id": "node2", "uri": "uri2"}],
348
                     self.client.GetNodes(bulk=True))
349
    self.assertHandler(rlib2.R_2_nodes)
350
    self.assertBulk()
351

    
352
  def testGetNodeInfo(self):
353
    self.rapi.AddResponse("{}")
354
    self.assertEqual({}, self.client.GetNodeInfo("node-foo"))
355
    self.assertHandler(rlib2.R_2_nodes_name)
356
    self.assertItems(["node-foo"])
357

    
358
  def testEvacuateNode(self):
359
    self.rapi.AddResponse("9876")
360
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
361
    self.assertEqual(9876, job_id)
362
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
363
    self.assertItems(["node-1"])
364
    self.assertQuery("remote_node", ["node-2"])
365

    
366
    self.rapi.AddResponse("8888")
367
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
368
    self.assertEqual(8888, job_id)
369
    self.assertItems(["node-3"])
370
    self.assertQuery("iallocator", ["hail"])
371
    self.assertDryRun()
372

    
373
    self.assertRaises(client.GanetiApiError,
374
                      self.client.EvacuateNode,
375
                      "node-4", iallocator="hail", remote_node="node-5")
376

    
377
  def testMigrateNode(self):
378
    self.rapi.AddResponse("1111")
379
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
380
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
381
    self.assertItems(["node-a"])
382
    self.assertQuery("live", ["1"])
383
    self.assertDryRun()
384

    
385
  def testGetNodeRole(self):
386
    self.rapi.AddResponse("\"master\"")
387
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
388
    self.assertHandler(rlib2.R_2_nodes_name_role)
389
    self.assertItems(["node-a"])
390

    
391
  def testSetNodeRole(self):
392
    self.rapi.AddResponse("789")
393
    self.assertEqual(789,
394
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
395
    self.assertHandler(rlib2.R_2_nodes_name_role)
396
    self.assertItems(["node-foo"])
397
    self.assertQuery("force", ["True"])
398
    self.assertEqual("\"master-candidate\"", self.http.last_request.data)
399

    
400
    self.assertRaises(client.InvalidNodeRole,
401
                      self.client.SetNodeRole, "node-bar", "fake-role")
402

    
403
  def testGetNodeStorageUnits(self):
404
    self.rapi.AddResponse("42")
405
    self.assertEqual(42,
406
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
407
    self.assertHandler(rlib2.R_2_nodes_name_storage)
408
    self.assertItems(["node-x"])
409
    self.assertQuery("storage_type", ["lvm-pv"])
410
    self.assertQuery("output_fields", ["fields"])
411

    
412
    self.assertRaises(client.InvalidStorageType,
413
                      self.client.GetNodeStorageUnits,
414
                      "node-y", "floppy-disk", "fields")
415

    
416
  def testModifyNodeStorageUnits(self):
417
    self.rapi.AddResponse("14")
418
    self.assertEqual(14,
419
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
420
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
421
    self.assertItems(["node-z"])
422
    self.assertQuery("storage_type", ["lvm-pv"])
423
    self.assertQuery("name", ["hda"])
424

    
425
    self.assertRaises(client.InvalidStorageType,
426
                      self.client.ModifyNodeStorageUnits,
427
                      "node-n", "floppy-disk", "hdc")
428

    
429
  def testRepairNodeStorageUnits(self):
430
    self.rapi.AddResponse("99")
431
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
432
                                                            "hda"))
433
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
434
    self.assertItems(["node-z"])
435
    self.assertQuery("storage_type", ["lvm-pv"])
436
    self.assertQuery("name", ["hda"])
437

    
438
    self.assertRaises(client.InvalidStorageType,
439
                      self.client.RepairNodeStorageUnits,
440
                      "node-n", "floppy-disk", "hdc")
441

    
442
  def testGetNodeTags(self):
443
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
444
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
445
    self.assertHandler(rlib2.R_2_nodes_name_tags)
446
    self.assertItems(["node-k"])
447

    
448
  def testAddNodeTags(self):
449
    self.rapi.AddResponse("1234")
450
    self.assertEqual(1234,
451
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
452
    self.assertHandler(rlib2.R_2_nodes_name_tags)
453
    self.assertItems(["node-v"])
454
    self.assertDryRun()
455
    self.assertQuery("tag", ["awesome"])
456

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

    
466

    
467
if __name__ == '__main__':
468
  testutils.GanetiTestProgram()