Update the RAPI client for the migration mode
[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 import pycurl
29
30 from ganeti import constants
31 from ganeti import http
32 from ganeti import serializer
33
34 from ganeti.rapi import connector
35 from ganeti.rapi import rlib2
36 from ganeti.rapi import client
37
38 import testutils
39
40
41 _URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
42
43
44 def _GetPathFromUri(uri):
45   """Gets the path and query from a URI.
46
47   """
48   match = _URI_RE.match(uri)
49   if match:
50     return match.groupdict()["path"]
51   else:
52     return None
53
54
55 class FakeCurl:
56   def __init__(self, rapi):
57     self._rapi = rapi
58     self._opts = {}
59     self._info = {}
60
61   def setopt(self, opt, value):
62     self._opts[opt] = value
63
64   def getopt(self, opt):
65     return self._opts.get(opt)
66
67   def unsetopt(self, opt):
68     self._opts.pop(opt, None)
69
70   def getinfo(self, info):
71     return self._info[info]
72
73   def perform(self):
74     method = self._opts[pycurl.CUSTOMREQUEST]
75     url = self._opts[pycurl.URL]
76     request_body = self._opts[pycurl.POSTFIELDS]
77     writefn = self._opts[pycurl.WRITEFUNCTION]
78
79     path = _GetPathFromUri(url)
80     (code, resp_body) = self._rapi.FetchResponse(path, method, request_body)
81
82     self._info[pycurl.RESPONSE_CODE] = code
83     if resp_body is not None:
84       writefn(resp_body)
85
86
87 class RapiMock(object):
88   def __init__(self):
89     self._mapper = connector.Mapper()
90     self._responses = []
91     self._last_handler = None
92     self._last_req_data = None
93
94   def AddResponse(self, response, code=200):
95     self._responses.insert(0, (code, response))
96
97   def CountPending(self):
98     return len(self._responses)
99
100   def GetLastHandler(self):
101     return self._last_handler
102
103   def GetLastRequestData(self):
104     return self._last_req_data
105
106   def FetchResponse(self, path, method, request_body):
107     self._last_req_data = request_body
108
109     try:
110       HandlerClass, items, args = self._mapper.getController(path)
111       self._last_handler = HandlerClass(items, args, None)
112       if not hasattr(self._last_handler, method.upper()):
113         raise http.HttpNotImplemented(message="Method not implemented")
114
115     except http.HttpException, ex:
116       code = ex.code
117       response = ex.message
118     else:
119       if not self._responses:
120         raise Exception("No responses")
121
122       (code, response) = self._responses.pop()
123
124     return code, response
125
126
127 class TestConstants(unittest.TestCase):
128   def test(self):
129     self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
130     self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
131     self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
132     self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
133     self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
134     self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
135
136
137 class RapiMockTest(unittest.TestCase):
138   def test(self):
139     rapi = RapiMock()
140     path = "/version"
141     self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
142     self.assertEqual((501, "Method not implemented"),
143                      rapi.FetchResponse("/version", "POST", None))
144     rapi.AddResponse("2")
145     code, response = rapi.FetchResponse("/version", "GET", None)
146     self.assertEqual(200, code)
147     self.assertEqual("2", response)
148     self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
149
150
151 def _FakeNoSslPycurlVersion():
152   # Note: incomplete version tuple
153   return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
154
155
156 def _FakeFancySslPycurlVersion():
157   # Note: incomplete version tuple
158   return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
159
160
161 def _FakeOpenSslPycurlVersion():
162   # Note: incomplete version tuple
163   return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
164
165
166 def _FakeGnuTlsPycurlVersion():
167   # Note: incomplete version tuple
168   return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
169
170
171 class TestExtendedConfig(unittest.TestCase):
172   def testAuth(self):
173     curl = FakeCurl(RapiMock())
174     cl = client.GanetiRapiClient("master.example.com",
175                                  username="user", password="pw",
176                                  curl=curl)
177
178     self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
179     self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
180
181   def testInvalidAuth(self):
182     # No username
183     self.assertRaises(client.Error, client.GanetiRapiClient,
184                       "master-a.example.com", password="pw")
185     # No password
186     self.assertRaises(client.Error, client.GanetiRapiClient,
187                       "master-b.example.com", username="user")
188
189   def testCertVerifyInvalidCombinations(self):
190     self.assertRaises(client.Error, client.GenericCurlConfig,
191                       use_curl_cabundle=True, cafile="cert1.pem")
192     self.assertRaises(client.Error, client.GenericCurlConfig,
193                       use_curl_cabundle=True, capath="certs/")
194     self.assertRaises(client.Error, client.GenericCurlConfig,
195                       use_curl_cabundle=True,
196                       cafile="cert1.pem", capath="certs/")
197
198   def testProxySignalVerifyHostname(self):
199     for use_gnutls in [False, True]:
200       if use_gnutls:
201         pcverfn = _FakeGnuTlsPycurlVersion
202       else:
203         pcverfn = _FakeOpenSslPycurlVersion
204
205       for proxy in ["", "http://127.0.0.1:1234"]:
206         for use_signal in [False, True]:
207           for verify_hostname in [False, True]:
208             cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
209                                              verify_hostname=verify_hostname,
210                                              _pycurl_version_fn=pcverfn)
211
212             curl = FakeCurl(RapiMock())
213             cl = client.GanetiRapiClient("master.example.com",
214                                          curl_config_fn=cfgfn, curl=curl)
215
216             self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
217             self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
218
219             if verify_hostname:
220               self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
221             else:
222               self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
223
224   def testNoCertVerify(self):
225     cfgfn = client.GenericCurlConfig()
226
227     curl = FakeCurl(RapiMock())
228     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
229                                  curl=curl)
230
231     self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
232     self.assertFalse(curl.getopt(pycurl.CAINFO))
233     self.assertFalse(curl.getopt(pycurl.CAPATH))
234
235   def testCertVerifyCurlBundle(self):
236     cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
237
238     curl = FakeCurl(RapiMock())
239     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
240                                  curl=curl)
241
242     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
243     self.assertFalse(curl.getopt(pycurl.CAINFO))
244     self.assertFalse(curl.getopt(pycurl.CAPATH))
245
246   def testCertVerifyCafile(self):
247     mycert = "/tmp/some/UNUSED/cert/file.pem"
248     cfgfn = client.GenericCurlConfig(cafile=mycert)
249
250     curl = FakeCurl(RapiMock())
251     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
252                                  curl=curl)
253
254     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
255     self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
256     self.assertFalse(curl.getopt(pycurl.CAPATH))
257
258   def testCertVerifyCapath(self):
259     certdir = "/tmp/some/UNUSED/cert/directory"
260     pcverfn = _FakeOpenSslPycurlVersion
261     cfgfn = client.GenericCurlConfig(capath=certdir,
262                                      _pycurl_version_fn=pcverfn)
263
264     curl = FakeCurl(RapiMock())
265     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
266                                  curl=curl)
267
268     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
269     self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
270     self.assertFalse(curl.getopt(pycurl.CAINFO))
271
272   def testCertVerifyCapathGnuTls(self):
273     certdir = "/tmp/some/UNUSED/cert/directory"
274     pcverfn = _FakeGnuTlsPycurlVersion
275     cfgfn = client.GenericCurlConfig(capath=certdir,
276                                      _pycurl_version_fn=pcverfn)
277
278     curl = FakeCurl(RapiMock())
279     self.assertRaises(client.Error, client.GanetiRapiClient,
280                       "master.example.com", curl_config_fn=cfgfn, curl=curl)
281
282   def testCertVerifyNoSsl(self):
283     certdir = "/tmp/some/UNUSED/cert/directory"
284     pcverfn = _FakeNoSslPycurlVersion
285     cfgfn = client.GenericCurlConfig(capath=certdir,
286                                      _pycurl_version_fn=pcverfn)
287
288     curl = FakeCurl(RapiMock())
289     self.assertRaises(client.Error, client.GanetiRapiClient,
290                       "master.example.com", curl_config_fn=cfgfn, curl=curl)
291
292   def testCertVerifyFancySsl(self):
293     certdir = "/tmp/some/UNUSED/cert/directory"
294     pcverfn = _FakeFancySslPycurlVersion
295     cfgfn = client.GenericCurlConfig(capath=certdir,
296                                      _pycurl_version_fn=pcverfn)
297
298     curl = FakeCurl(RapiMock())
299     self.assertRaises(NotImplementedError, client.GanetiRapiClient,
300                       "master.example.com", curl_config_fn=cfgfn, curl=curl)
301
302   def testCertVerifyCapath(self):
303     for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
304       for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
305         cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
306                                          timeout=timeout)
307
308         curl = FakeCurl(RapiMock())
309         cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
310                                      curl=curl)
311
312         self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
313         self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
314
315
316 class GanetiRapiClientTests(testutils.GanetiTestCase):
317   def setUp(self):
318     testutils.GanetiTestCase.setUp(self)
319
320     self.rapi = RapiMock()
321     self.curl = FakeCurl(self.rapi)
322     self.client = client.GanetiRapiClient("master.example.com",
323                                           curl=self.curl)
324
325     # Signals should be disabled by default
326     self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
327
328     # No auth and no proxy
329     self.assertFalse(self.curl.getopt(pycurl.USERPWD))
330     self.assert_(self.curl.getopt(pycurl.PROXY) is None)
331
332     # Content-type is required for requests
333     headers = self.curl.getopt(pycurl.HTTPHEADER)
334     self.assert_("Content-type: application/json" in headers)
335
336   def assertHandler(self, handler_cls):
337     self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
338
339   def assertQuery(self, key, value):
340     self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
341
342   def assertItems(self, items):
343     self.assertEqual(items, self.rapi.GetLastHandler().items)
344
345   def assertBulk(self):
346     self.assertTrue(self.rapi.GetLastHandler().useBulk())
347
348   def assertDryRun(self):
349     self.assertTrue(self.rapi.GetLastHandler().dryRun())
350
351   def testEncodeQuery(self):
352     query = [
353       ("a", None),
354       ("b", 1),
355       ("c", 2),
356       ("d", "Foo"),
357       ("e", True),
358       ]
359
360     expected = [
361       ("a", ""),
362       ("b", 1),
363       ("c", 2),
364       ("d", "Foo"),
365       ("e", 1),
366       ]
367
368     self.assertEqualValues(self.client._EncodeQuery(query),
369                            expected)
370
371     # invalid types
372     for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
373       self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
374
375   def testHttpError(self):
376     self.rapi.AddResponse(None, code=404)
377     try:
378       self.client.GetJobStatus(15140)
379     except client.GanetiApiError, err:
380       self.assertEqual(err.code, 404)
381     else:
382       self.fail("Didn't raise exception")
383
384   def testGetVersion(self):
385     self.client._version = None
386     self.rapi.AddResponse("2")
387     self.assertEqual(2, self.client.GetVersion())
388     self.assertHandler(rlib2.R_version)
389
390   def testGetFeatures(self):
391     for features in [[], ["foo", "bar", "baz"]]:
392       self.rapi.AddResponse(serializer.DumpJson(features))
393       self.assertEqual(features, self.client.GetFeatures())
394       self.assertHandler(rlib2.R_2_features)
395
396   def testGetFeaturesNotFound(self):
397     self.rapi.AddResponse(None, code=404)
398     self.assertEqual([], self.client.GetFeatures())
399
400   def testGetOperatingSystems(self):
401     self.rapi.AddResponse("[\"beos\"]")
402     self.assertEqual(["beos"], self.client.GetOperatingSystems())
403     self.assertHandler(rlib2.R_2_os)
404
405   def testGetClusterTags(self):
406     self.rapi.AddResponse("[\"tag\"]")
407     self.assertEqual(["tag"], self.client.GetClusterTags())
408     self.assertHandler(rlib2.R_2_tags)
409
410   def testAddClusterTags(self):
411     self.rapi.AddResponse("1234")
412     self.assertEqual(1234,
413         self.client.AddClusterTags(["awesome"], dry_run=True))
414     self.assertHandler(rlib2.R_2_tags)
415     self.assertDryRun()
416     self.assertQuery("tag", ["awesome"])
417
418   def testDeleteClusterTags(self):
419     self.rapi.AddResponse("5107")
420     self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
421                                                          dry_run=True))
422     self.assertHandler(rlib2.R_2_tags)
423     self.assertDryRun()
424     self.assertQuery("tag", ["awesome"])
425
426   def testGetInfo(self):
427     self.rapi.AddResponse("{}")
428     self.assertEqual({}, self.client.GetInfo())
429     self.assertHandler(rlib2.R_2_info)
430
431   def testGetInstances(self):
432     self.rapi.AddResponse("[]")
433     self.assertEqual([], self.client.GetInstances(bulk=True))
434     self.assertHandler(rlib2.R_2_instances)
435     self.assertBulk()
436
437   def testGetInstance(self):
438     self.rapi.AddResponse("[]")
439     self.assertEqual([], self.client.GetInstance("instance"))
440     self.assertHandler(rlib2.R_2_instances_name)
441     self.assertItems(["instance"])
442
443   def testGetInstanceInfo(self):
444     self.rapi.AddResponse("21291")
445     self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
446     self.assertHandler(rlib2.R_2_instances_name_info)
447     self.assertItems(["inst3"])
448     self.assertQuery("static", None)
449
450     self.rapi.AddResponse("3428")
451     self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
452     self.assertHandler(rlib2.R_2_instances_name_info)
453     self.assertItems(["inst31"])
454     self.assertQuery("static", ["0"])
455
456     self.rapi.AddResponse("15665")
457     self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
458     self.assertHandler(rlib2.R_2_instances_name_info)
459     self.assertItems(["inst32"])
460     self.assertQuery("static", ["1"])
461
462   def testCreateInstanceOldVersion(self):
463     # No NICs
464     self.rapi.AddResponse(None, code=404)
465     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
466                       "create", "inst1.example.com", "plain", [], [])
467     self.assertEqual(self.rapi.CountPending(), 0)
468
469     # More than one NIC
470     self.rapi.AddResponse(None, code=404)
471     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
472                       "create", "inst1.example.com", "plain", [],
473                       [{}, {}, {}])
474     self.assertEqual(self.rapi.CountPending(), 0)
475
476     # Unsupported NIC fields
477     self.rapi.AddResponse(None, code=404)
478     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
479                       "create", "inst1.example.com", "plain", [],
480                       [{"x": True, "y": False}])
481     self.assertEqual(self.rapi.CountPending(), 0)
482
483     # Unsupported disk fields
484     self.rapi.AddResponse(None, code=404)
485     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
486                       "create", "inst1.example.com", "plain",
487                       [{}, {"moo": "foo",}], [{}])
488     self.assertEqual(self.rapi.CountPending(), 0)
489
490     # Unsupported fields
491     self.rapi.AddResponse(None, code=404)
492     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
493                       "create", "inst1.example.com", "plain", [], [{}],
494                       hello_world=123)
495     self.assertEqual(self.rapi.CountPending(), 0)
496
497     self.rapi.AddResponse(None, code=404)
498     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
499                       "create", "inst1.example.com", "plain", [], [{}],
500                       memory=128)
501     self.assertEqual(self.rapi.CountPending(), 0)
502
503     # Normal creation
504     testnics = [
505       [{}],
506       [{ "mac": constants.VALUE_AUTO, }],
507       [{ "ip": "192.0.2.99", "mode": constants.NIC_MODE_ROUTED, }],
508       ]
509
510     testdisks = [
511       [],
512       [{ "size": 128, }],
513       [{ "size": 321, }, { "size": 4096, }],
514       ]
515
516     for idx, nics in enumerate(testnics):
517       for disks in testdisks:
518         beparams = {
519           constants.BE_MEMORY: 512,
520           constants.BE_AUTO_BALANCE: False,
521           }
522         hvparams = {
523           constants.HV_MIGRATION_PORT: 9876,
524           constants.HV_VNC_TLS: True,
525           }
526
527         self.rapi.AddResponse(None, code=404)
528         self.rapi.AddResponse(serializer.DumpJson(3122617 + idx))
529         job_id = self.client.CreateInstance("create", "inst1.example.com",
530                                             "plain", disks, nics,
531                                             pnode="node99", dry_run=True,
532                                             hvparams=hvparams,
533                                             beparams=beparams)
534         self.assertEqual(job_id, 3122617 + idx)
535         self.assertHandler(rlib2.R_2_instances)
536         self.assertDryRun()
537         self.assertEqual(self.rapi.CountPending(), 0)
538
539         data = serializer.LoadJson(self.rapi.GetLastRequestData())
540         self.assertEqual(data["name"], "inst1.example.com")
541         self.assertEqual(data["disk_template"], "plain")
542         self.assertEqual(data["pnode"], "node99")
543         self.assertEqual(data[constants.BE_MEMORY], 512)
544         self.assertEqual(data[constants.BE_AUTO_BALANCE], False)
545         self.assertEqual(data[constants.HV_MIGRATION_PORT], 9876)
546         self.assertEqual(data[constants.HV_VNC_TLS], True)
547         self.assertEqual(data["disks"], [disk["size"] for disk in disks])
548
549   def testCreateInstance(self):
550     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
551     self.rapi.AddResponse("23030")
552     job_id = self.client.CreateInstance("create", "inst1.example.com",
553                                         "plain", [], [], dry_run=True)
554     self.assertEqual(job_id, 23030)
555     self.assertHandler(rlib2.R_2_instances)
556     self.assertDryRun()
557
558     data = serializer.LoadJson(self.rapi.GetLastRequestData())
559
560     for field in ["dry_run", "beparams", "hvparams", "start"]:
561       self.assertFalse(field in data)
562
563     self.assertEqual(data["name"], "inst1.example.com")
564     self.assertEqual(data["disk_template"], "plain")
565
566   def testCreateInstance2(self):
567     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
568     self.rapi.AddResponse("24740")
569     job_id = self.client.CreateInstance("import", "inst2.example.com",
570                                         "drbd8", [{"size": 100,}],
571                                         [{}, {"bridge": "br1", }],
572                                         dry_run=False, start=True,
573                                         pnode="node1", snode="node9",
574                                         ip_check=False)
575     self.assertEqual(job_id, 24740)
576     self.assertHandler(rlib2.R_2_instances)
577
578     data = serializer.LoadJson(self.rapi.GetLastRequestData())
579     self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
580     self.assertEqual(data["name"], "inst2.example.com")
581     self.assertEqual(data["disk_template"], "drbd8")
582     self.assertEqual(data["start"], True)
583     self.assertEqual(data["ip_check"], False)
584     self.assertEqualValues(data["disks"], [{"size": 100,}])
585     self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
586
587   def testDeleteInstance(self):
588     self.rapi.AddResponse("1234")
589     self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
590     self.assertHandler(rlib2.R_2_instances_name)
591     self.assertItems(["instance"])
592     self.assertDryRun()
593
594   def testGetInstanceTags(self):
595     self.rapi.AddResponse("[]")
596     self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
597     self.assertHandler(rlib2.R_2_instances_name_tags)
598     self.assertItems(["fooinstance"])
599
600   def testAddInstanceTags(self):
601     self.rapi.AddResponse("1234")
602     self.assertEqual(1234,
603         self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
604     self.assertHandler(rlib2.R_2_instances_name_tags)
605     self.assertItems(["fooinstance"])
606     self.assertDryRun()
607     self.assertQuery("tag", ["awesome"])
608
609   def testDeleteInstanceTags(self):
610     self.rapi.AddResponse("25826")
611     self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
612                                                            dry_run=True))
613     self.assertHandler(rlib2.R_2_instances_name_tags)
614     self.assertItems(["foo"])
615     self.assertDryRun()
616     self.assertQuery("tag", ["awesome"])
617
618   def testRebootInstance(self):
619     self.rapi.AddResponse("6146")
620     job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
621                                         ignore_secondaries=True, dry_run=True)
622     self.assertEqual(6146, job_id)
623     self.assertHandler(rlib2.R_2_instances_name_reboot)
624     self.assertItems(["i-bar"])
625     self.assertDryRun()
626     self.assertQuery("type", ["hard"])
627     self.assertQuery("ignore_secondaries", ["1"])
628
629   def testShutdownInstance(self):
630     self.rapi.AddResponse("1487")
631     self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
632                                                         dry_run=True))
633     self.assertHandler(rlib2.R_2_instances_name_shutdown)
634     self.assertItems(["foo-instance"])
635     self.assertDryRun()
636
637   def testStartupInstance(self):
638     self.rapi.AddResponse("27149")
639     self.assertEqual(27149, self.client.StartupInstance("bar-instance",
640                                                         dry_run=True))
641     self.assertHandler(rlib2.R_2_instances_name_startup)
642     self.assertItems(["bar-instance"])
643     self.assertDryRun()
644
645   def testReinstallInstance(self):
646     self.rapi.AddResponse("19119")
647     self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
648                                                           no_startup=True))
649     self.assertHandler(rlib2.R_2_instances_name_reinstall)
650     self.assertItems(["baz-instance"])
651     self.assertQuery("os", ["DOS"])
652     self.assertQuery("nostartup", ["1"])
653
654   def testReplaceInstanceDisks(self):
655     self.rapi.AddResponse("999")
656     job_id = self.client.ReplaceInstanceDisks("instance-name",
657         disks=[0, 1], dry_run=True, iallocator="hail")
658     self.assertEqual(999, job_id)
659     self.assertHandler(rlib2.R_2_instances_name_replace_disks)
660     self.assertItems(["instance-name"])
661     self.assertQuery("disks", ["0,1"])
662     self.assertQuery("mode", ["replace_auto"])
663     self.assertQuery("iallocator", ["hail"])
664     self.assertDryRun()
665
666     self.rapi.AddResponse("1000")
667     job_id = self.client.ReplaceInstanceDisks("instance-bar",
668         disks=[1], mode="replace_on_secondary", remote_node="foo-node",
669         dry_run=True)
670     self.assertEqual(1000, job_id)
671     self.assertItems(["instance-bar"])
672     self.assertQuery("disks", ["1"])
673     self.assertQuery("remote_node", ["foo-node"])
674     self.assertDryRun()
675
676     self.rapi.AddResponse("5175")
677     self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
678     self.assertItems(["instance-moo"])
679     self.assertQuery("disks", None)
680
681   def testPrepareExport(self):
682     self.rapi.AddResponse("8326")
683     self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
684     self.assertHandler(rlib2.R_2_instances_name_prepare_export)
685     self.assertItems(["inst1"])
686     self.assertQuery("mode", ["local"])
687
688   def testExportInstance(self):
689     self.rapi.AddResponse("19695")
690     job_id = self.client.ExportInstance("inst2", "local", "nodeX",
691                                         shutdown=True)
692     self.assertEqual(job_id, 19695)
693     self.assertHandler(rlib2.R_2_instances_name_export)
694     self.assertItems(["inst2"])
695
696     data = serializer.LoadJson(self.rapi.GetLastRequestData())
697     self.assertEqual(data["mode"], "local")
698     self.assertEqual(data["destination"], "nodeX")
699     self.assertEqual(data["shutdown"], True)
700
701   def testGetJobs(self):
702     self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
703                           '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
704     self.assertEqual([123, 124], self.client.GetJobs())
705     self.assertHandler(rlib2.R_2_jobs)
706
707   def testGetJobStatus(self):
708     self.rapi.AddResponse("{\"foo\": \"bar\"}")
709     self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
710     self.assertHandler(rlib2.R_2_jobs_id)
711     self.assertItems(["1234"])
712
713   def testWaitForJobChange(self):
714     fields = ["id", "summary"]
715     expected = {
716       "job_info": [123, "something"],
717       "log_entries": [],
718       }
719
720     self.rapi.AddResponse(serializer.DumpJson(expected))
721     result = self.client.WaitForJobChange(123, fields, [], -1)
722     self.assertEqualValues(expected, result)
723     self.assertHandler(rlib2.R_2_jobs_id_wait)
724     self.assertItems(["123"])
725
726   def testCancelJob(self):
727     self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
728     self.assertEqual([True, "Job 123 will be canceled"],
729                      self.client.CancelJob(999, dry_run=True))
730     self.assertHandler(rlib2.R_2_jobs_id)
731     self.assertItems(["999"])
732     self.assertDryRun()
733
734   def testGetNodes(self):
735     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
736                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
737     self.assertEqual(["node1", "node2"], self.client.GetNodes())
738     self.assertHandler(rlib2.R_2_nodes)
739
740     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
741                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
742     self.assertEqual([{"id": "node1", "uri": "uri1"},
743                       {"id": "node2", "uri": "uri2"}],
744                      self.client.GetNodes(bulk=True))
745     self.assertHandler(rlib2.R_2_nodes)
746     self.assertBulk()
747
748   def testGetNode(self):
749     self.rapi.AddResponse("{}")
750     self.assertEqual({}, self.client.GetNode("node-foo"))
751     self.assertHandler(rlib2.R_2_nodes_name)
752     self.assertItems(["node-foo"])
753
754   def testEvacuateNode(self):
755     self.rapi.AddResponse("9876")
756     job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
757     self.assertEqual(9876, job_id)
758     self.assertHandler(rlib2.R_2_nodes_name_evacuate)
759     self.assertItems(["node-1"])
760     self.assertQuery("remote_node", ["node-2"])
761
762     self.rapi.AddResponse("8888")
763     job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
764     self.assertEqual(8888, job_id)
765     self.assertItems(["node-3"])
766     self.assertQuery("iallocator", ["hail"])
767     self.assertDryRun()
768
769     self.assertRaises(client.GanetiApiError,
770                       self.client.EvacuateNode,
771                       "node-4", iallocator="hail", remote_node="node-5")
772
773   def testMigrateNode(self):
774     self.rapi.AddResponse("1111")
775     self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
776     self.assertHandler(rlib2.R_2_nodes_name_migrate)
777     self.assertItems(["node-a"])
778     self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
779     self.assertDryRun()
780
781     self.rapi.AddResponse("1112")
782     self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
783                                                    mode="live"))
784     self.assertHandler(rlib2.R_2_nodes_name_migrate)
785     self.assertItems(["node-a"])
786     self.assertQuery("mode", ["live"])
787     self.assertDryRun()
788
789   def testGetNodeRole(self):
790     self.rapi.AddResponse("\"master\"")
791     self.assertEqual("master", self.client.GetNodeRole("node-a"))
792     self.assertHandler(rlib2.R_2_nodes_name_role)
793     self.assertItems(["node-a"])
794
795   def testSetNodeRole(self):
796     self.rapi.AddResponse("789")
797     self.assertEqual(789,
798         self.client.SetNodeRole("node-foo", "master-candidate", force=True))
799     self.assertHandler(rlib2.R_2_nodes_name_role)
800     self.assertItems(["node-foo"])
801     self.assertQuery("force", ["1"])
802     self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
803
804   def testGetNodeStorageUnits(self):
805     self.rapi.AddResponse("42")
806     self.assertEqual(42,
807         self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
808     self.assertHandler(rlib2.R_2_nodes_name_storage)
809     self.assertItems(["node-x"])
810     self.assertQuery("storage_type", ["lvm-pv"])
811     self.assertQuery("output_fields", ["fields"])
812
813   def testModifyNodeStorageUnits(self):
814     self.rapi.AddResponse("14")
815     self.assertEqual(14,
816         self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
817     self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
818     self.assertItems(["node-z"])
819     self.assertQuery("storage_type", ["lvm-pv"])
820     self.assertQuery("name", ["hda"])
821     self.assertQuery("allocatable", None)
822
823     for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
824       self.rapi.AddResponse("7205")
825       job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
826                                                   allocatable=allocatable)
827       self.assertEqual(7205, job_id)
828       self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
829       self.assertItems(["node-z"])
830       self.assertQuery("storage_type", ["lvm-pv"])
831       self.assertQuery("name", ["hda"])
832       self.assertQuery("allocatable", [query_allocatable])
833
834   def testRepairNodeStorageUnits(self):
835     self.rapi.AddResponse("99")
836     self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
837                                                             "hda"))
838     self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
839     self.assertItems(["node-z"])
840     self.assertQuery("storage_type", ["lvm-pv"])
841     self.assertQuery("name", ["hda"])
842
843   def testGetNodeTags(self):
844     self.rapi.AddResponse("[\"fry\", \"bender\"]")
845     self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
846     self.assertHandler(rlib2.R_2_nodes_name_tags)
847     self.assertItems(["node-k"])
848
849   def testAddNodeTags(self):
850     self.rapi.AddResponse("1234")
851     self.assertEqual(1234,
852         self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
853     self.assertHandler(rlib2.R_2_nodes_name_tags)
854     self.assertItems(["node-v"])
855     self.assertDryRun()
856     self.assertQuery("tag", ["awesome"])
857
858   def testDeleteNodeTags(self):
859     self.rapi.AddResponse("16861")
860     self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
861                                                        dry_run=True))
862     self.assertHandler(rlib2.R_2_nodes_name_tags)
863     self.assertItems(["node-w"])
864     self.assertDryRun()
865     self.assertQuery("tag", ["awesome"])
866
867
868 if __name__ == '__main__':
869   client.UsesRapiClient(testutils.GanetiTestProgram)()