Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (15.4 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

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

    
35
import testutils
36

    
37

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

    
40

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

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

    
51

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

55
  """
56

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

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

    
64

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

68
  """
69

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

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

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

    
81

    
82
class RapiMock(object):
83

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

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

    
92
  def PopResponse(self):
93
    if len(self._responses) > 0:
94
      return self._responses.pop()
95
    else:
96
      return None
97

    
98
  def GetLastHandler(self):
99
    return self._last_handler
100

    
101
  def FetchResponse(self, path, method):
102
    code = 200
103
    response = None
104

    
105
    try:
106
      HandlerClass, items, args = self._mapper.getController(path)
107
      self._last_handler = HandlerClass(items, args, None)
108
      if not hasattr(self._last_handler, method.upper()):
109
        code = 400
110
        response = "Bad request"
111
    except http.HttpException, ex:
112
      code = ex.code
113
      response = ex.message
114

    
115
    if not response:
116
      response = self.PopResponse()
117

    
118
    return code, response
119

    
120

    
121
class RapiMockTest(unittest.TestCase):
122

    
123
  def test(self):
124
    rapi = RapiMock()
125
    path = "/version"
126
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET"))
127
    self.assertEqual((400, "Bad request"),
128
                     rapi.FetchResponse("/version", "POST"))
129
    rapi.AddResponse("2")
130
    code, response = rapi.FetchResponse("/version", "GET")
131
    self.assertEqual(200, code)
132
    self.assertEqual("2", response)
133
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
134

    
135

    
136
class GanetiRapiClientTests(unittest.TestCase):
137
  """Tests for remote API client.
138

139
  """
140

    
141
  def setUp(self):
142
    self.rapi = RapiMock()
143
    self.http = OpenerDirectorMock(self.rapi)
144
    self.client = client.GanetiRapiClient('master.foo.com')
145
    self.client._http = self.http
146
    # Hard-code the version for easier testing.
147
    self.client._version = 2
148

    
149
  def assertHandler(self, handler_cls):
150
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
151

    
152
  def assertQuery(self, key, value):
153
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
154

    
155
  def assertItems(self, items):
156
    self.assertEqual(items, self.rapi.GetLastHandler().items)
157

    
158
  def assertBulk(self):
159
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
160

    
161
  def assertDryRun(self):
162
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
163

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
370
    self.assertRaises(client.GanetiApiError,
371
                      self.client.EvacuateNode,
372
                      "node-4", iallocator="hail", remote_node="node-5")
373

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

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

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

    
397
    self.assertRaises(client.InvalidNodeRole,
398
                      self.client.SetNodeRole, "node-bar", "fake-role")
399

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

    
409
    self.assertRaises(client.InvalidStorageType,
410
                      self.client.GetNodeStorageUnits,
411
                      "node-y", "floppy-disk", "fields")
412

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

    
422
    self.assertRaises(client.InvalidStorageType,
423
                      self.client.ModifyNodeStorageUnits,
424
                      "node-n", "floppy-disk", "hdc")
425

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

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

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

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

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

    
463

    
464
if __name__ == '__main__':
465
  testutils.GanetiTestProgram()