Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (14.6 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
try:
26
  import httplib2
27
  BaseHttp = httplib2.Http
28
  from ganeti.rapi import client
29
except ImportError:
30
  httplib2 = None
31
  BaseHttp = object
32

    
33
import re
34
import unittest
35
import warnings
36

    
37
from ganeti import http
38

    
39
from ganeti.rapi import connector
40
from ganeti.rapi import rlib2
41

    
42
import testutils
43

    
44

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

    
47

    
48
def _GetPathFromUri(uri):
49
  """Gets the path and query from a URI.
50

51
  """
52
  match = _URI_RE.match(uri)
53
  if match:
54
    return match.groupdict()["path"]
55
  else:
56
    return None
57

    
58

    
59
class HttpResponseMock(dict):
60
  """Dumb mock of httplib2.Response.
61

62
  """
63

    
64
  def __init__(self, status):
65
    self.status = status
66
    self['status'] = status
67

    
68

    
69
class HttpMock(BaseHttp):
70
  """Mock for httplib.Http.
71

72
  """
73

    
74
  def __init__(self, rapi):
75
    self._rapi = rapi
76
    self._last_request = None
77

    
78
  last_request_url = property(lambda self: self._last_request[0])
79
  last_request_method = property(lambda self: self._last_request[1])
80
  last_request_body = property(lambda self: self._last_request[2])
81

    
82
  def request(self, url, method, body, headers):
83
    self._last_request = (url, method, body)
84
    code, resp_body = self._rapi.FetchResponse(_GetPathFromUri(url), method)
85
    return HttpResponseMock(code), resp_body
86

    
87

    
88
class RapiMock(object):
89

    
90
  def __init__(self):
91
    self._mapper = connector.Mapper()
92
    self._responses = []
93
    self._last_handler = None
94

    
95
  def AddResponse(self, response):
96
    self._responses.insert(0, response)
97

    
98
  def PopResponse(self):
99
    if len(self._responses) > 0:
100
      return self._responses.pop()
101
    else:
102
      return None
103

    
104
  def GetLastHandler(self):
105
    return self._last_handler
106

    
107
  def FetchResponse(self, path, method):
108
    code = 200
109
    response = None
110

    
111
    try:
112
      HandlerClass, items, args = self._mapper.getController(path)
113
      self._last_handler = HandlerClass(items, args, None)
114
      if not hasattr(self._last_handler, method.upper()):
115
        code = 400
116
        response = "Bad request"
117
    except http.HttpException, ex:
118
      code = ex.code
119
      response = ex.message
120

    
121
    if not response:
122
      response = self.PopResponse()
123

    
124
    return code, response
125

    
126

    
127
class RapiMockTest(unittest.TestCase):
128

    
129
  def test(self):
130
    rapi = RapiMock()
131
    path = "/version"
132
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET"))
133
    self.assertEqual((400, "Bad request"),
134
                     rapi.FetchResponse("/version", "POST"))
135
    rapi.AddResponse("2")
136
    code, response = rapi.FetchResponse("/version", "GET")
137
    self.assertEqual(200, code)
138
    self.assertEqual("2", response)
139
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
140

    
141

    
142
class GanetiRapiClientTests(unittest.TestCase):
143
  """Tests for remote API client.
144

145
  """
146

    
147
  def setUp(self):
148
    # Monkey-patch a fake VerifyCertificate function
149
    self._verify_certificate = client._VerifyCertificate
150
    client._VerifyCertificate = lambda x, y, z: True
151

    
152
    self.rapi = RapiMock()
153
    self.http = HttpMock(self.rapi)
154
    self.client = client.GanetiRapiClient('master.foo.com')
155
    self.client._http = self.http
156
    # Hard-code the version for easier testing.
157
    self.client._version = 2
158

    
159
  def tearDown(self):
160
    # Un-do the monkey-patch
161
    client._VerifyCertificate = self._verify_certificate
162

    
163
  def assertHandler(self, handler_cls):
164
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
165

    
166
  def assertQuery(self, key, value):
167
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
168

    
169
  def assertItems(self, items):
170
    self.assertEqual(items, self.rapi.GetLastHandler().items)
171

    
172
  def assertBulk(self):
173
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
174

    
175
  def assertDryRun(self):
176
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
177

    
178
  def testGetVersion(self):
179
    self.client._version = None
180
    self.rapi.AddResponse("2")
181
    self.assertEqual(2, self.client.GetVersion())
182
    self.assertHandler(rlib2.R_version)
183

    
184
  def testGetOperatingSystems(self):
185
    self.rapi.AddResponse("[\"beos\"]")
186
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
187
    self.assertHandler(rlib2.R_2_os)
188

    
189
  def testGetClusterTags(self):
190
    self.rapi.AddResponse("[\"tag\"]")
191
    self.assertEqual(["tag"], self.client.GetClusterTags())
192
    self.assertHandler(rlib2.R_2_tags)
193

    
194
  def testAddClusterTags(self):
195
    self.rapi.AddResponse("1234")
196
    self.assertEqual(1234,
197
        self.client.AddClusterTags(["awesome"], dry_run=True))
198
    self.assertHandler(rlib2.R_2_tags)
199
    self.assertDryRun()
200
    self.assertQuery("tag", ["awesome"])
201

    
202
  def testDeleteClusterTags(self):
203
    self.client.DeleteClusterTags(["awesome"], dry_run=True)
204
    self.assertHandler(rlib2.R_2_tags)
205
    self.assertDryRun()
206
    self.assertQuery("tag", ["awesome"])
207

    
208
  def testGetInfo(self):
209
    self.rapi.AddResponse("{}")
210
    self.assertEqual({}, self.client.GetInfo())
211
    self.assertHandler(rlib2.R_2_info)
212

    
213
  def testGetInstances(self):
214
    self.rapi.AddResponse("[]")
215
    self.assertEqual([], self.client.GetInstances(bulk=True))
216
    self.assertHandler(rlib2.R_2_instances)
217
    self.assertBulk()
218

    
219
  def testGetInstanceInfo(self):
220
    self.rapi.AddResponse("[]")
221
    self.assertEqual([], self.client.GetInstanceInfo("instance"))
222
    self.assertHandler(rlib2.R_2_instances_name)
223
    self.assertItems(["instance"])
224

    
225
  def testCreateInstance(self):
226
    self.rapi.AddResponse("1234")
227
    self.assertEqual(1234, self.client.CreateInstance(dry_run=True))
228
    self.assertHandler(rlib2.R_2_instances)
229
    self.assertDryRun()
230

    
231
  def testDeleteInstance(self):
232
    self.rapi.AddResponse("1234")
233
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
234
    self.assertHandler(rlib2.R_2_instances_name)
235
    self.assertItems(["instance"])
236
    self.assertDryRun()
237

    
238
  def testGetInstanceTags(self):
239
    self.rapi.AddResponse("[]")
240
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
241
    self.assertHandler(rlib2.R_2_instances_name_tags)
242
    self.assertItems(["fooinstance"])
243

    
244
  def testAddInstanceTags(self):
245
    self.rapi.AddResponse("1234")
246
    self.assertEqual(1234,
247
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
248
    self.assertHandler(rlib2.R_2_instances_name_tags)
249
    self.assertItems(["fooinstance"])
250
    self.assertDryRun()
251
    self.assertQuery("tag", ["awesome"])
252

    
253
  def testDeleteInstanceTags(self):
254
    self.client.DeleteInstanceTags("foo", ["awesome"], dry_run=True)
255
    self.assertHandler(rlib2.R_2_instances_name_tags)
256
    self.assertItems(["foo"])
257
    self.assertDryRun()
258
    self.assertQuery("tag", ["awesome"])
259

    
260
  def testRebootInstance(self):
261
    self.client.RebootInstance("i-bar", reboot_type="hard",
262
                               ignore_secondaries=True, dry_run=True)
263
    self.assertHandler(rlib2.R_2_instances_name_reboot)
264
    self.assertItems(["i-bar"])
265
    self.assertDryRun()
266
    self.assertQuery("type", ["hard"])
267
    self.assertQuery("ignore_secondaries", ["True"])
268

    
269
  def testShutdownInstance(self):
270
    self.client.ShutdownInstance("foo-instance", dry_run=True)
271
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
272
    self.assertItems(["foo-instance"])
273
    self.assertDryRun()
274

    
275
  def testStartupInstance(self):
276
    self.client.StartupInstance("bar-instance", dry_run=True)
277
    self.assertHandler(rlib2.R_2_instances_name_startup)
278
    self.assertItems(["bar-instance"])
279
    self.assertDryRun()
280

    
281
  def testReinstallInstance(self):
282
    self.client.ReinstallInstance("baz-instance", "DOS", no_startup=True)
283
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
284
    self.assertItems(["baz-instance"])
285
    self.assertQuery("os", ["DOS"])
286
    self.assertQuery("nostartup", ["1"])
287

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

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

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

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

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

    
329
  def testDeleteJob(self):
330
    self.client.DeleteJob(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_body)
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 testGetNodeTags(self):
427
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
428
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
429
    self.assertHandler(rlib2.R_2_nodes_name_tags)
430
    self.assertItems(["node-k"])
431

    
432
  def testAddNodeTags(self):
433
    self.rapi.AddResponse("1234")
434
    self.assertEqual(1234,
435
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
436
    self.assertHandler(rlib2.R_2_nodes_name_tags)
437
    self.assertItems(["node-v"])
438
    self.assertDryRun()
439
    self.assertQuery("tag", ["awesome"])
440

    
441
  def testDeleteNodeTags(self):
442
    self.client.DeleteNodeTags("node-w", ["awesome"], dry_run=True)
443
    self.assertHandler(rlib2.R_2_nodes_name_tags)
444
    self.assertItems(["node-w"])
445
    self.assertDryRun()
446
    self.assertQuery("tag", ["awesome"])
447

    
448

    
449
if __name__ == '__main__':
450
  if httplib2 is None:
451
    warnings.warn("These tests require the httplib2 library")
452
  else:
453
    testutils.GanetiTestProgram()