ganeti-cleaner: Remove expired X509 certs
[ganeti-local] / test / ganeti.rapi.client_unittest.py
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 testGetFeatures(self):
194     for features in [[], ["foo", "bar", "baz"]]:
195       self.rapi.AddResponse(serializer.DumpJson(features))
196       self.assertEqual(features, self.client.GetFeatures())
197       self.assertHandler(rlib2.R_2_features)
198
199   def testGetOperatingSystems(self):
200     self.rapi.AddResponse("[\"beos\"]")
201     self.assertEqual(["beos"], self.client.GetOperatingSystems())
202     self.assertHandler(rlib2.R_2_os)
203
204   def testGetClusterTags(self):
205     self.rapi.AddResponse("[\"tag\"]")
206     self.assertEqual(["tag"], self.client.GetClusterTags())
207     self.assertHandler(rlib2.R_2_tags)
208
209   def testAddClusterTags(self):
210     self.rapi.AddResponse("1234")
211     self.assertEqual(1234,
212         self.client.AddClusterTags(["awesome"], dry_run=True))
213     self.assertHandler(rlib2.R_2_tags)
214     self.assertDryRun()
215     self.assertQuery("tag", ["awesome"])
216
217   def testDeleteClusterTags(self):
218     self.rapi.AddResponse("5107")
219     self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
220                                                          dry_run=True))
221     self.assertHandler(rlib2.R_2_tags)
222     self.assertDryRun()
223     self.assertQuery("tag", ["awesome"])
224
225   def testGetInfo(self):
226     self.rapi.AddResponse("{}")
227     self.assertEqual({}, self.client.GetInfo())
228     self.assertHandler(rlib2.R_2_info)
229
230   def testGetInstances(self):
231     self.rapi.AddResponse("[]")
232     self.assertEqual([], self.client.GetInstances(bulk=True))
233     self.assertHandler(rlib2.R_2_instances)
234     self.assertBulk()
235
236   def testGetInstance(self):
237     self.rapi.AddResponse("[]")
238     self.assertEqual([], self.client.GetInstance("instance"))
239     self.assertHandler(rlib2.R_2_instances_name)
240     self.assertItems(["instance"])
241
242   def testGetInstanceInfo(self):
243     self.rapi.AddResponse("21291")
244     self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
245     self.assertHandler(rlib2.R_2_instances_name_info)
246     self.assertItems(["inst3"])
247     self.assertQuery("static", None)
248
249     self.rapi.AddResponse("3428")
250     self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
251     self.assertHandler(rlib2.R_2_instances_name_info)
252     self.assertItems(["inst31"])
253     self.assertQuery("static", ["0"])
254
255     self.rapi.AddResponse("15665")
256     self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
257     self.assertHandler(rlib2.R_2_instances_name_info)
258     self.assertItems(["inst32"])
259     self.assertQuery("static", ["1"])
260
261   def testCreateInstanceOldVersion(self):
262     self.rapi.AddResponse(serializer.DumpJson([]))
263     self.assertRaises(NotImplementedError, self.client.CreateInstance,
264                       "create", "inst1.example.com", "plain", [], [],
265                       dry_run=True)
266
267   def testCreateInstance(self):
268     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
269     self.rapi.AddResponse("23030")
270     job_id = self.client.CreateInstance("create", "inst1.example.com",
271                                         "plain", [], [], dry_run=True)
272     self.assertEqual(job_id, 23030)
273     self.assertHandler(rlib2.R_2_instances)
274     self.assertDryRun()
275
276     data = serializer.LoadJson(self.http.last_request.data)
277
278     for field in ["dry_run", "beparams", "hvparams", "start"]:
279       self.assertFalse(field in data)
280
281     self.assertEqual(data["name"], "inst1.example.com")
282     self.assertEqual(data["disk_template"], "plain")
283
284   def testCreateInstance2(self):
285     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
286     self.rapi.AddResponse("24740")
287     job_id = self.client.CreateInstance("import", "inst2.example.com",
288                                         "drbd8", [{"size": 100,}],
289                                         [{}, {"bridge": "br1", }],
290                                         dry_run=False, start=True,
291                                         pnode="node1", snode="node9",
292                                         ip_check=False)
293     self.assertEqual(job_id, 24740)
294     self.assertHandler(rlib2.R_2_instances)
295
296     data = serializer.LoadJson(self.http.last_request.data)
297     self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
298     self.assertEqual(data["name"], "inst2.example.com")
299     self.assertEqual(data["disk_template"], "drbd8")
300     self.assertEqual(data["start"], True)
301     self.assertEqual(data["ip_check"], False)
302     self.assertEqualValues(data["disks"], [{"size": 100,}])
303     self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
304
305   def testDeleteInstance(self):
306     self.rapi.AddResponse("1234")
307     self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
308     self.assertHandler(rlib2.R_2_instances_name)
309     self.assertItems(["instance"])
310     self.assertDryRun()
311
312   def testGetInstanceTags(self):
313     self.rapi.AddResponse("[]")
314     self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
315     self.assertHandler(rlib2.R_2_instances_name_tags)
316     self.assertItems(["fooinstance"])
317
318   def testAddInstanceTags(self):
319     self.rapi.AddResponse("1234")
320     self.assertEqual(1234,
321         self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
322     self.assertHandler(rlib2.R_2_instances_name_tags)
323     self.assertItems(["fooinstance"])
324     self.assertDryRun()
325     self.assertQuery("tag", ["awesome"])
326
327   def testDeleteInstanceTags(self):
328     self.rapi.AddResponse("25826")
329     self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
330                                                            dry_run=True))
331     self.assertHandler(rlib2.R_2_instances_name_tags)
332     self.assertItems(["foo"])
333     self.assertDryRun()
334     self.assertQuery("tag", ["awesome"])
335
336   def testRebootInstance(self):
337     self.rapi.AddResponse("6146")
338     job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
339                                         ignore_secondaries=True, dry_run=True)
340     self.assertEqual(6146, job_id)
341     self.assertHandler(rlib2.R_2_instances_name_reboot)
342     self.assertItems(["i-bar"])
343     self.assertDryRun()
344     self.assertQuery("type", ["hard"])
345     self.assertQuery("ignore_secondaries", ["1"])
346
347   def testShutdownInstance(self):
348     self.rapi.AddResponse("1487")
349     self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
350                                                         dry_run=True))
351     self.assertHandler(rlib2.R_2_instances_name_shutdown)
352     self.assertItems(["foo-instance"])
353     self.assertDryRun()
354
355   def testStartupInstance(self):
356     self.rapi.AddResponse("27149")
357     self.assertEqual(27149, self.client.StartupInstance("bar-instance",
358                                                         dry_run=True))
359     self.assertHandler(rlib2.R_2_instances_name_startup)
360     self.assertItems(["bar-instance"])
361     self.assertDryRun()
362
363   def testReinstallInstance(self):
364     self.rapi.AddResponse("19119")
365     self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
366                                                           no_startup=True))
367     self.assertHandler(rlib2.R_2_instances_name_reinstall)
368     self.assertItems(["baz-instance"])
369     self.assertQuery("os", ["DOS"])
370     self.assertQuery("nostartup", ["1"])
371
372   def testReplaceInstanceDisks(self):
373     self.rapi.AddResponse("999")
374     job_id = self.client.ReplaceInstanceDisks("instance-name",
375         disks=[0, 1], dry_run=True, iallocator="hail")
376     self.assertEqual(999, job_id)
377     self.assertHandler(rlib2.R_2_instances_name_replace_disks)
378     self.assertItems(["instance-name"])
379     self.assertQuery("disks", ["0,1"])
380     self.assertQuery("mode", ["replace_auto"])
381     self.assertQuery("iallocator", ["hail"])
382     self.assertDryRun()
383
384     self.rapi.AddResponse("1000")
385     job_id = self.client.ReplaceInstanceDisks("instance-bar",
386         disks=[1], mode="replace_on_secondary", remote_node="foo-node",
387         dry_run=True)
388     self.assertEqual(1000, job_id)
389     self.assertItems(["instance-bar"])
390     self.assertQuery("disks", ["1"])
391     self.assertQuery("remote_node", ["foo-node"])
392     self.assertDryRun()
393
394     self.rapi.AddResponse("5175")
395     self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
396     self.assertItems(["instance-moo"])
397     self.assertQuery("disks", None)
398
399   def testGetJobs(self):
400     self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
401                           '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
402     self.assertEqual([123, 124], self.client.GetJobs())
403     self.assertHandler(rlib2.R_2_jobs)
404
405   def testGetJobStatus(self):
406     self.rapi.AddResponse("{\"foo\": \"bar\"}")
407     self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
408     self.assertHandler(rlib2.R_2_jobs_id)
409     self.assertItems(["1234"])
410
411   def testWaitForJobChange(self):
412     fields = ["id", "summary"]
413     expected = {
414       "job_info": [123, "something"],
415       "log_entries": [],
416       }
417
418     self.rapi.AddResponse(serializer.DumpJson(expected))
419     result = self.client.WaitForJobChange(123, fields, [], -1)
420     self.assertEqualValues(expected, result)
421     self.assertHandler(rlib2.R_2_jobs_id_wait)
422     self.assertItems(["123"])
423
424   def testCancelJob(self):
425     self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
426     self.assertEqual([True, "Job 123 will be canceled"],
427                      self.client.CancelJob(999, dry_run=True))
428     self.assertHandler(rlib2.R_2_jobs_id)
429     self.assertItems(["999"])
430     self.assertDryRun()
431
432   def testGetNodes(self):
433     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
434                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
435     self.assertEqual(["node1", "node2"], self.client.GetNodes())
436     self.assertHandler(rlib2.R_2_nodes)
437
438     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
439                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
440     self.assertEqual([{"id": "node1", "uri": "uri1"},
441                       {"id": "node2", "uri": "uri2"}],
442                      self.client.GetNodes(bulk=True))
443     self.assertHandler(rlib2.R_2_nodes)
444     self.assertBulk()
445
446   def testGetNode(self):
447     self.rapi.AddResponse("{}")
448     self.assertEqual({}, self.client.GetNode("node-foo"))
449     self.assertHandler(rlib2.R_2_nodes_name)
450     self.assertItems(["node-foo"])
451
452   def testEvacuateNode(self):
453     self.rapi.AddResponse("9876")
454     job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
455     self.assertEqual(9876, job_id)
456     self.assertHandler(rlib2.R_2_nodes_name_evacuate)
457     self.assertItems(["node-1"])
458     self.assertQuery("remote_node", ["node-2"])
459
460     self.rapi.AddResponse("8888")
461     job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
462     self.assertEqual(8888, job_id)
463     self.assertItems(["node-3"])
464     self.assertQuery("iallocator", ["hail"])
465     self.assertDryRun()
466
467     self.assertRaises(client.GanetiApiError,
468                       self.client.EvacuateNode,
469                       "node-4", iallocator="hail", remote_node="node-5")
470
471   def testMigrateNode(self):
472     self.rapi.AddResponse("1111")
473     self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
474     self.assertHandler(rlib2.R_2_nodes_name_migrate)
475     self.assertItems(["node-a"])
476     self.assertQuery("live", ["1"])
477     self.assertDryRun()
478
479   def testGetNodeRole(self):
480     self.rapi.AddResponse("\"master\"")
481     self.assertEqual("master", self.client.GetNodeRole("node-a"))
482     self.assertHandler(rlib2.R_2_nodes_name_role)
483     self.assertItems(["node-a"])
484
485   def testSetNodeRole(self):
486     self.rapi.AddResponse("789")
487     self.assertEqual(789,
488         self.client.SetNodeRole("node-foo", "master-candidate", force=True))
489     self.assertHandler(rlib2.R_2_nodes_name_role)
490     self.assertItems(["node-foo"])
491     self.assertQuery("force", ["1"])
492     self.assertEqual("\"master-candidate\"", self.http.last_request.data)
493
494   def testGetNodeStorageUnits(self):
495     self.rapi.AddResponse("42")
496     self.assertEqual(42,
497         self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
498     self.assertHandler(rlib2.R_2_nodes_name_storage)
499     self.assertItems(["node-x"])
500     self.assertQuery("storage_type", ["lvm-pv"])
501     self.assertQuery("output_fields", ["fields"])
502
503   def testModifyNodeStorageUnits(self):
504     self.rapi.AddResponse("14")
505     self.assertEqual(14,
506         self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
507     self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
508     self.assertItems(["node-z"])
509     self.assertQuery("storage_type", ["lvm-pv"])
510     self.assertQuery("name", ["hda"])
511     self.assertQuery("allocatable", None)
512
513     for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
514       self.rapi.AddResponse("7205")
515       job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
516                                                   allocatable=allocatable)
517       self.assertEqual(7205, job_id)
518       self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
519       self.assertItems(["node-z"])
520       self.assertQuery("storage_type", ["lvm-pv"])
521       self.assertQuery("name", ["hda"])
522       self.assertQuery("allocatable", [query_allocatable])
523
524   def testRepairNodeStorageUnits(self):
525     self.rapi.AddResponse("99")
526     self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
527                                                             "hda"))
528     self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
529     self.assertItems(["node-z"])
530     self.assertQuery("storage_type", ["lvm-pv"])
531     self.assertQuery("name", ["hda"])
532
533   def testGetNodeTags(self):
534     self.rapi.AddResponse("[\"fry\", \"bender\"]")
535     self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
536     self.assertHandler(rlib2.R_2_nodes_name_tags)
537     self.assertItems(["node-k"])
538
539   def testAddNodeTags(self):
540     self.rapi.AddResponse("1234")
541     self.assertEqual(1234,
542         self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
543     self.assertHandler(rlib2.R_2_nodes_name_tags)
544     self.assertItems(["node-v"])
545     self.assertDryRun()
546     self.assertQuery("tag", ["awesome"])
547
548   def testDeleteNodeTags(self):
549     self.rapi.AddResponse("16861")
550     self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
551                                                        dry_run=True))
552     self.assertHandler(rlib2.R_2_nodes_name_tags)
553     self.assertItems(["node-w"])
554     self.assertDryRun()
555     self.assertQuery("tag", ["awesome"])
556
557
558 if __name__ == '__main__':
559   testutils.GanetiTestProgram()