gnt-node migrate: Use LU-generated jobs
[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 from ganeti import utils
34 from ganeti import query
35 from ganeti import objects
36
37 from ganeti.rapi import connector
38 from ganeti.rapi import rlib2
39 from ganeti.rapi import client
40
41 import testutils
42
43
44 _URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
45
46 # List of resource handlers which aren't used by the RAPI client
47 _KNOWN_UNUSED = set([
48   connector.R_root,
49   connector.R_2,
50   ])
51
52 # Global variable for collecting used handlers
53 _used_handlers = None
54
55
56 def _GetPathFromUri(uri):
57   """Gets the path and query from a URI.
58
59   """
60   match = _URI_RE.match(uri)
61   if match:
62     return match.groupdict()["path"]
63   else:
64     return None
65
66
67 class FakeCurl:
68   def __init__(self, rapi):
69     self._rapi = rapi
70     self._opts = {}
71     self._info = {}
72
73   def setopt(self, opt, value):
74     self._opts[opt] = value
75
76   def getopt(self, opt):
77     return self._opts.get(opt)
78
79   def unsetopt(self, opt):
80     self._opts.pop(opt, None)
81
82   def getinfo(self, info):
83     return self._info[info]
84
85   def perform(self):
86     method = self._opts[pycurl.CUSTOMREQUEST]
87     url = self._opts[pycurl.URL]
88     request_body = self._opts[pycurl.POSTFIELDS]
89     writefn = self._opts[pycurl.WRITEFUNCTION]
90
91     path = _GetPathFromUri(url)
92     (code, resp_body) = self._rapi.FetchResponse(path, method, request_body)
93
94     self._info[pycurl.RESPONSE_CODE] = code
95     if resp_body is not None:
96       writefn(resp_body)
97
98
99 class RapiMock(object):
100   def __init__(self):
101     self._mapper = connector.Mapper()
102     self._responses = []
103     self._last_handler = None
104     self._last_req_data = None
105
106   def ResetResponses(self):
107     del self._responses[:]
108
109   def AddResponse(self, response, code=200):
110     self._responses.insert(0, (code, response))
111
112   def CountPending(self):
113     return len(self._responses)
114
115   def GetLastHandler(self):
116     return self._last_handler
117
118   def GetLastRequestData(self):
119     return self._last_req_data
120
121   def FetchResponse(self, path, method, request_body):
122     self._last_req_data = request_body
123
124     try:
125       (handler_cls, items, args) = self._mapper.getController(path)
126
127       # Record handler as used
128       _used_handlers.add(handler_cls)
129
130       self._last_handler = handler_cls(items, args, None)
131       if not hasattr(self._last_handler, method.upper()):
132         raise http.HttpNotImplemented(message="Method not implemented")
133
134     except http.HttpException, ex:
135       code = ex.code
136       response = ex.message
137     else:
138       if not self._responses:
139         raise Exception("No responses")
140
141       (code, response) = self._responses.pop()
142
143     return code, response
144
145
146 class TestConstants(unittest.TestCase):
147   def test(self):
148     self.assertEqual(client.GANETI_RAPI_PORT, constants.DEFAULT_RAPI_PORT)
149     self.assertEqual(client.GANETI_RAPI_VERSION, constants.RAPI_VERSION)
150     self.assertEqual(client.HTTP_APP_JSON, http.HTTP_APP_JSON)
151     self.assertEqual(client._REQ_DATA_VERSION_FIELD, rlib2._REQ_DATA_VERSION)
152     self.assertEqual(client._INST_CREATE_REQV1, rlib2._INST_CREATE_REQV1)
153     self.assertEqual(client._INST_REINSTALL_REQV1, rlib2._INST_REINSTALL_REQV1)
154     self.assertEqual(client._NODE_MIGRATE_REQV1, rlib2._NODE_MIGRATE_REQV1)
155     self.assertEqual(client._INST_NIC_PARAMS, constants.INIC_PARAMS)
156     self.assertEqual(client.JOB_STATUS_QUEUED, constants.JOB_STATUS_QUEUED)
157     self.assertEqual(client.JOB_STATUS_WAITLOCK, constants.JOB_STATUS_WAITLOCK)
158     self.assertEqual(client.JOB_STATUS_CANCELING,
159                      constants.JOB_STATUS_CANCELING)
160     self.assertEqual(client.JOB_STATUS_RUNNING, constants.JOB_STATUS_RUNNING)
161     self.assertEqual(client.JOB_STATUS_CANCELED, constants.JOB_STATUS_CANCELED)
162     self.assertEqual(client.JOB_STATUS_SUCCESS, constants.JOB_STATUS_SUCCESS)
163     self.assertEqual(client.JOB_STATUS_ERROR, constants.JOB_STATUS_ERROR)
164     self.assertEqual(client.JOB_STATUS_FINALIZED, constants.JOBS_FINALIZED)
165     self.assertEqual(client.JOB_STATUS_ALL, constants.JOB_STATUS_ALL)
166
167
168 class RapiMockTest(unittest.TestCase):
169   def test(self):
170     rapi = RapiMock()
171     path = "/version"
172     self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET", None))
173     self.assertEqual((501, "Method not implemented"),
174                      rapi.FetchResponse("/version", "POST", None))
175     rapi.AddResponse("2")
176     code, response = rapi.FetchResponse("/version", "GET", None)
177     self.assertEqual(200, code)
178     self.assertEqual("2", response)
179     self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
180
181
182 def _FakeNoSslPycurlVersion():
183   # Note: incomplete version tuple
184   return (3, "7.16.0", 462848, "mysystem", 1581, None, 0)
185
186
187 def _FakeFancySslPycurlVersion():
188   # Note: incomplete version tuple
189   return (3, "7.16.0", 462848, "mysystem", 1581, "FancySSL/1.2.3", 0)
190
191
192 def _FakeOpenSslPycurlVersion():
193   # Note: incomplete version tuple
194   return (2, "7.15.5", 462597, "othersystem", 668, "OpenSSL/0.9.8c", 0)
195
196
197 def _FakeGnuTlsPycurlVersion():
198   # Note: incomplete version tuple
199   return (3, "7.18.0", 463360, "somesystem", 1581, "GnuTLS/2.0.4", 0)
200
201
202 class TestExtendedConfig(unittest.TestCase):
203   def testAuth(self):
204     cl = client.GanetiRapiClient("master.example.com",
205                                  username="user", password="pw",
206                                  curl_factory=lambda: FakeCurl(RapiMock()))
207
208     curl = cl._CreateCurl()
209     self.assertEqual(curl.getopt(pycurl.HTTPAUTH), pycurl.HTTPAUTH_BASIC)
210     self.assertEqual(curl.getopt(pycurl.USERPWD), "user:pw")
211
212   def testInvalidAuth(self):
213     # No username
214     self.assertRaises(client.Error, client.GanetiRapiClient,
215                       "master-a.example.com", password="pw")
216     # No password
217     self.assertRaises(client.Error, client.GanetiRapiClient,
218                       "master-b.example.com", username="user")
219
220   def testCertVerifyInvalidCombinations(self):
221     self.assertRaises(client.Error, client.GenericCurlConfig,
222                       use_curl_cabundle=True, cafile="cert1.pem")
223     self.assertRaises(client.Error, client.GenericCurlConfig,
224                       use_curl_cabundle=True, capath="certs/")
225     self.assertRaises(client.Error, client.GenericCurlConfig,
226                       use_curl_cabundle=True,
227                       cafile="cert1.pem", capath="certs/")
228
229   def testProxySignalVerifyHostname(self):
230     for use_gnutls in [False, True]:
231       if use_gnutls:
232         pcverfn = _FakeGnuTlsPycurlVersion
233       else:
234         pcverfn = _FakeOpenSslPycurlVersion
235
236       for proxy in ["", "http://127.0.0.1:1234"]:
237         for use_signal in [False, True]:
238           for verify_hostname in [False, True]:
239             cfgfn = client.GenericCurlConfig(proxy=proxy, use_signal=use_signal,
240                                              verify_hostname=verify_hostname,
241                                              _pycurl_version_fn=pcverfn)
242
243             curl_factory = lambda: FakeCurl(RapiMock())
244             cl = client.GanetiRapiClient("master.example.com",
245                                          curl_config_fn=cfgfn,
246                                          curl_factory=curl_factory)
247
248             curl = cl._CreateCurl()
249             self.assertEqual(curl.getopt(pycurl.PROXY), proxy)
250             self.assertEqual(curl.getopt(pycurl.NOSIGNAL), not use_signal)
251
252             if verify_hostname:
253               self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 2)
254             else:
255               self.assertEqual(curl.getopt(pycurl.SSL_VERIFYHOST), 0)
256
257   def testNoCertVerify(self):
258     cfgfn = client.GenericCurlConfig()
259
260     curl_factory = lambda: FakeCurl(RapiMock())
261     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
262                                  curl_factory=curl_factory)
263
264     curl = cl._CreateCurl()
265     self.assertFalse(curl.getopt(pycurl.SSL_VERIFYPEER))
266     self.assertFalse(curl.getopt(pycurl.CAINFO))
267     self.assertFalse(curl.getopt(pycurl.CAPATH))
268
269   def testCertVerifyCurlBundle(self):
270     cfgfn = client.GenericCurlConfig(use_curl_cabundle=True)
271
272     curl_factory = lambda: FakeCurl(RapiMock())
273     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
274                                  curl_factory=curl_factory)
275
276     curl = cl._CreateCurl()
277     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
278     self.assertFalse(curl.getopt(pycurl.CAINFO))
279     self.assertFalse(curl.getopt(pycurl.CAPATH))
280
281   def testCertVerifyCafile(self):
282     mycert = "/tmp/some/UNUSED/cert/file.pem"
283     cfgfn = client.GenericCurlConfig(cafile=mycert)
284
285     curl_factory = lambda: FakeCurl(RapiMock())
286     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
287                                  curl_factory=curl_factory)
288
289     curl = cl._CreateCurl()
290     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
291     self.assertEqual(curl.getopt(pycurl.CAINFO), mycert)
292     self.assertFalse(curl.getopt(pycurl.CAPATH))
293
294   def testCertVerifyCapath(self):
295     certdir = "/tmp/some/UNUSED/cert/directory"
296     pcverfn = _FakeOpenSslPycurlVersion
297     cfgfn = client.GenericCurlConfig(capath=certdir,
298                                      _pycurl_version_fn=pcverfn)
299
300     curl_factory = lambda: FakeCurl(RapiMock())
301     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
302                                  curl_factory=curl_factory)
303
304     curl = cl._CreateCurl()
305     self.assert_(curl.getopt(pycurl.SSL_VERIFYPEER))
306     self.assertEqual(curl.getopt(pycurl.CAPATH), certdir)
307     self.assertFalse(curl.getopt(pycurl.CAINFO))
308
309   def testCertVerifyCapathGnuTls(self):
310     certdir = "/tmp/some/UNUSED/cert/directory"
311     pcverfn = _FakeGnuTlsPycurlVersion
312     cfgfn = client.GenericCurlConfig(capath=certdir,
313                                      _pycurl_version_fn=pcverfn)
314
315     curl_factory = lambda: FakeCurl(RapiMock())
316     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
317                                  curl_factory=curl_factory)
318
319     self.assertRaises(client.Error, cl._CreateCurl)
320
321   def testCertVerifyNoSsl(self):
322     certdir = "/tmp/some/UNUSED/cert/directory"
323     pcverfn = _FakeNoSslPycurlVersion
324     cfgfn = client.GenericCurlConfig(capath=certdir,
325                                      _pycurl_version_fn=pcverfn)
326
327     curl_factory = lambda: FakeCurl(RapiMock())
328     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
329                                  curl_factory=curl_factory)
330
331     self.assertRaises(client.Error, cl._CreateCurl)
332
333   def testCertVerifyFancySsl(self):
334     certdir = "/tmp/some/UNUSED/cert/directory"
335     pcverfn = _FakeFancySslPycurlVersion
336     cfgfn = client.GenericCurlConfig(capath=certdir,
337                                      _pycurl_version_fn=pcverfn)
338
339     curl_factory = lambda: FakeCurl(RapiMock())
340     cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
341                                  curl_factory=curl_factory)
342
343     self.assertRaises(NotImplementedError, cl._CreateCurl)
344
345   def testCertVerifyCapath(self):
346     for connect_timeout in [None, 1, 5, 10, 30, 60, 300]:
347       for timeout in [None, 1, 30, 60, 3600, 24 * 3600]:
348         cfgfn = client.GenericCurlConfig(connect_timeout=connect_timeout,
349                                          timeout=timeout)
350
351         curl_factory = lambda: FakeCurl(RapiMock())
352         cl = client.GanetiRapiClient("master.example.com", curl_config_fn=cfgfn,
353                                      curl_factory=curl_factory)
354
355         curl = cl._CreateCurl()
356         self.assertEqual(curl.getopt(pycurl.CONNECTTIMEOUT), connect_timeout)
357         self.assertEqual(curl.getopt(pycurl.TIMEOUT), timeout)
358
359
360 class GanetiRapiClientTests(testutils.GanetiTestCase):
361   def setUp(self):
362     testutils.GanetiTestCase.setUp(self)
363
364     self.rapi = RapiMock()
365     self.curl = FakeCurl(self.rapi)
366     self.client = client.GanetiRapiClient("master.example.com",
367                                           curl_factory=lambda: self.curl)
368
369   def assertHandler(self, handler_cls):
370     self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
371
372   def assertQuery(self, key, value):
373     self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
374
375   def assertItems(self, items):
376     self.assertEqual(items, self.rapi.GetLastHandler().items)
377
378   def assertBulk(self):
379     self.assertTrue(self.rapi.GetLastHandler().useBulk())
380
381   def assertDryRun(self):
382     self.assertTrue(self.rapi.GetLastHandler().dryRun())
383
384   def assertUseForce(self):
385     self.assertTrue(self.rapi.GetLastHandler().useForce())
386
387   def testEncodeQuery(self):
388     query = [
389       ("a", None),
390       ("b", 1),
391       ("c", 2),
392       ("d", "Foo"),
393       ("e", True),
394       ]
395
396     expected = [
397       ("a", ""),
398       ("b", 1),
399       ("c", 2),
400       ("d", "Foo"),
401       ("e", 1),
402       ]
403
404     self.assertEqualValues(self.client._EncodeQuery(query),
405                            expected)
406
407     # invalid types
408     for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
409       self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
410
411   def testCurlSettings(self):
412     self.rapi.AddResponse("2")
413     self.assertEqual(2, self.client.GetVersion())
414     self.assertHandler(rlib2.R_version)
415
416     # Signals should be disabled by default
417     self.assert_(self.curl.getopt(pycurl.NOSIGNAL))
418
419     # No auth and no proxy
420     self.assertFalse(self.curl.getopt(pycurl.USERPWD))
421     self.assert_(self.curl.getopt(pycurl.PROXY) is None)
422
423     # Content-type is required for requests
424     headers = self.curl.getopt(pycurl.HTTPHEADER)
425     self.assert_("Content-type: application/json" in headers)
426
427   def testHttpError(self):
428     self.rapi.AddResponse(None, code=404)
429     try:
430       self.client.GetJobStatus(15140)
431     except client.GanetiApiError, err:
432       self.assertEqual(err.code, 404)
433     else:
434       self.fail("Didn't raise exception")
435
436   def testGetVersion(self):
437     self.rapi.AddResponse("2")
438     self.assertEqual(2, self.client.GetVersion())
439     self.assertHandler(rlib2.R_version)
440
441   def testGetFeatures(self):
442     for features in [[], ["foo", "bar", "baz"]]:
443       self.rapi.AddResponse(serializer.DumpJson(features))
444       self.assertEqual(features, self.client.GetFeatures())
445       self.assertHandler(rlib2.R_2_features)
446
447   def testGetFeaturesNotFound(self):
448     self.rapi.AddResponse(None, code=404)
449     self.assertEqual([], self.client.GetFeatures())
450
451   def testGetOperatingSystems(self):
452     self.rapi.AddResponse("[\"beos\"]")
453     self.assertEqual(["beos"], self.client.GetOperatingSystems())
454     self.assertHandler(rlib2.R_2_os)
455
456   def testGetClusterTags(self):
457     self.rapi.AddResponse("[\"tag\"]")
458     self.assertEqual(["tag"], self.client.GetClusterTags())
459     self.assertHandler(rlib2.R_2_tags)
460
461   def testAddClusterTags(self):
462     self.rapi.AddResponse("1234")
463     self.assertEqual(1234,
464         self.client.AddClusterTags(["awesome"], dry_run=True))
465     self.assertHandler(rlib2.R_2_tags)
466     self.assertDryRun()
467     self.assertQuery("tag", ["awesome"])
468
469   def testDeleteClusterTags(self):
470     self.rapi.AddResponse("5107")
471     self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
472                                                          dry_run=True))
473     self.assertHandler(rlib2.R_2_tags)
474     self.assertDryRun()
475     self.assertQuery("tag", ["awesome"])
476
477   def testGetInfo(self):
478     self.rapi.AddResponse("{}")
479     self.assertEqual({}, self.client.GetInfo())
480     self.assertHandler(rlib2.R_2_info)
481
482   def testGetInstances(self):
483     self.rapi.AddResponse("[]")
484     self.assertEqual([], self.client.GetInstances(bulk=True))
485     self.assertHandler(rlib2.R_2_instances)
486     self.assertBulk()
487
488   def testGetInstance(self):
489     self.rapi.AddResponse("[]")
490     self.assertEqual([], self.client.GetInstance("instance"))
491     self.assertHandler(rlib2.R_2_instances_name)
492     self.assertItems(["instance"])
493
494   def testGetInstanceInfo(self):
495     self.rapi.AddResponse("21291")
496     self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
497     self.assertHandler(rlib2.R_2_instances_name_info)
498     self.assertItems(["inst3"])
499     self.assertQuery("static", None)
500
501     self.rapi.AddResponse("3428")
502     self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
503     self.assertHandler(rlib2.R_2_instances_name_info)
504     self.assertItems(["inst31"])
505     self.assertQuery("static", ["0"])
506
507     self.rapi.AddResponse("15665")
508     self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
509     self.assertHandler(rlib2.R_2_instances_name_info)
510     self.assertItems(["inst32"])
511     self.assertQuery("static", ["1"])
512
513   def testCreateInstanceOldVersion(self):
514     # The old request format, version 0, is no longer supported
515     self.rapi.AddResponse(None, code=404)
516     self.assertRaises(client.GanetiApiError, self.client.CreateInstance,
517                       "create", "inst1.example.com", "plain", [], [])
518     self.assertEqual(self.rapi.CountPending(), 0)
519
520   def testCreateInstance(self):
521     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
522     self.rapi.AddResponse("23030")
523     job_id = self.client.CreateInstance("create", "inst1.example.com",
524                                         "plain", [], [], dry_run=True)
525     self.assertEqual(job_id, 23030)
526     self.assertHandler(rlib2.R_2_instances)
527     self.assertDryRun()
528
529     data = serializer.LoadJson(self.rapi.GetLastRequestData())
530
531     for field in ["dry_run", "beparams", "hvparams", "start"]:
532       self.assertFalse(field in data)
533
534     self.assertEqual(data["name"], "inst1.example.com")
535     self.assertEqual(data["disk_template"], "plain")
536
537   def testCreateInstance2(self):
538     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
539     self.rapi.AddResponse("24740")
540     job_id = self.client.CreateInstance("import", "inst2.example.com",
541                                         "drbd8", [{"size": 100,}],
542                                         [{}, {"bridge": "br1", }],
543                                         dry_run=False, start=True,
544                                         pnode="node1", snode="node9",
545                                         ip_check=False)
546     self.assertEqual(job_id, 24740)
547     self.assertHandler(rlib2.R_2_instances)
548
549     data = serializer.LoadJson(self.rapi.GetLastRequestData())
550     self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
551     self.assertEqual(data["name"], "inst2.example.com")
552     self.assertEqual(data["disk_template"], "drbd8")
553     self.assertEqual(data["start"], True)
554     self.assertEqual(data["ip_check"], False)
555     self.assertEqualValues(data["disks"], [{"size": 100,}])
556     self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
557
558   def testDeleteInstance(self):
559     self.rapi.AddResponse("1234")
560     self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
561     self.assertHandler(rlib2.R_2_instances_name)
562     self.assertItems(["instance"])
563     self.assertDryRun()
564
565   def testGetInstanceTags(self):
566     self.rapi.AddResponse("[]")
567     self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
568     self.assertHandler(rlib2.R_2_instances_name_tags)
569     self.assertItems(["fooinstance"])
570
571   def testAddInstanceTags(self):
572     self.rapi.AddResponse("1234")
573     self.assertEqual(1234,
574         self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
575     self.assertHandler(rlib2.R_2_instances_name_tags)
576     self.assertItems(["fooinstance"])
577     self.assertDryRun()
578     self.assertQuery("tag", ["awesome"])
579
580   def testDeleteInstanceTags(self):
581     self.rapi.AddResponse("25826")
582     self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
583                                                            dry_run=True))
584     self.assertHandler(rlib2.R_2_instances_name_tags)
585     self.assertItems(["foo"])
586     self.assertDryRun()
587     self.assertQuery("tag", ["awesome"])
588
589   def testRebootInstance(self):
590     self.rapi.AddResponse("6146")
591     job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
592                                         ignore_secondaries=True, dry_run=True)
593     self.assertEqual(6146, job_id)
594     self.assertHandler(rlib2.R_2_instances_name_reboot)
595     self.assertItems(["i-bar"])
596     self.assertDryRun()
597     self.assertQuery("type", ["hard"])
598     self.assertQuery("ignore_secondaries", ["1"])
599
600   def testShutdownInstance(self):
601     self.rapi.AddResponse("1487")
602     self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
603                                                         dry_run=True))
604     self.assertHandler(rlib2.R_2_instances_name_shutdown)
605     self.assertItems(["foo-instance"])
606     self.assertDryRun()
607
608   def testStartupInstance(self):
609     self.rapi.AddResponse("27149")
610     self.assertEqual(27149, self.client.StartupInstance("bar-instance",
611                                                         dry_run=True))
612     self.assertHandler(rlib2.R_2_instances_name_startup)
613     self.assertItems(["bar-instance"])
614     self.assertDryRun()
615
616   def testReinstallInstance(self):
617     self.rapi.AddResponse(serializer.DumpJson([]))
618     self.rapi.AddResponse("19119")
619     self.assertEqual(19119, self.client.ReinstallInstance("baz-instance",
620                                                           os="DOS",
621                                                           no_startup=True))
622     self.assertHandler(rlib2.R_2_instances_name_reinstall)
623     self.assertItems(["baz-instance"])
624     self.assertQuery("os", ["DOS"])
625     self.assertQuery("nostartup", ["1"])
626     self.assertEqual(self.rapi.CountPending(), 0)
627
628   def testReinstallInstanceNew(self):
629     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
630     self.rapi.AddResponse("25689")
631     self.assertEqual(25689, self.client.ReinstallInstance("moo-instance",
632                                                           os="Debian",
633                                                           no_startup=True))
634     self.assertHandler(rlib2.R_2_instances_name_reinstall)
635     self.assertItems(["moo-instance"])
636     data = serializer.LoadJson(self.rapi.GetLastRequestData())
637     self.assertEqual(len(data), 2)
638     self.assertEqual(data["os"], "Debian")
639     self.assertEqual(data["start"], False)
640     self.assertEqual(self.rapi.CountPending(), 0)
641
642   def testReinstallInstanceWithOsparams1(self):
643     self.rapi.AddResponse(serializer.DumpJson([]))
644     self.assertRaises(client.GanetiApiError, self.client.ReinstallInstance,
645                       "doo-instance", osparams={"x": "y"})
646     self.assertEqual(self.rapi.CountPending(), 0)
647
648   def testReinstallInstanceWithOsparams2(self):
649     osparams = {
650       "Hello": "World",
651       "foo": "bar",
652       }
653     self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_REINSTALL_REQV1]))
654     self.rapi.AddResponse("1717")
655     self.assertEqual(1717, self.client.ReinstallInstance("zoo-instance",
656                                                          osparams=osparams))
657     self.assertHandler(rlib2.R_2_instances_name_reinstall)
658     self.assertItems(["zoo-instance"])
659     data = serializer.LoadJson(self.rapi.GetLastRequestData())
660     self.assertEqual(len(data), 2)
661     self.assertEqual(data["osparams"], osparams)
662     self.assertEqual(data["start"], True)
663     self.assertEqual(self.rapi.CountPending(), 0)
664
665   def testReplaceInstanceDisks(self):
666     self.rapi.AddResponse("999")
667     job_id = self.client.ReplaceInstanceDisks("instance-name",
668         disks=[0, 1], dry_run=True, iallocator="hail")
669     self.assertEqual(999, job_id)
670     self.assertHandler(rlib2.R_2_instances_name_replace_disks)
671     self.assertItems(["instance-name"])
672     self.assertQuery("disks", ["0,1"])
673     self.assertQuery("mode", ["replace_auto"])
674     self.assertQuery("iallocator", ["hail"])
675     self.assertDryRun()
676
677     self.rapi.AddResponse("1000")
678     job_id = self.client.ReplaceInstanceDisks("instance-bar",
679         disks=[1], mode="replace_on_secondary", remote_node="foo-node",
680         dry_run=True)
681     self.assertEqual(1000, job_id)
682     self.assertItems(["instance-bar"])
683     self.assertQuery("disks", ["1"])
684     self.assertQuery("remote_node", ["foo-node"])
685     self.assertDryRun()
686
687     self.rapi.AddResponse("5175")
688     self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
689     self.assertItems(["instance-moo"])
690     self.assertQuery("disks", None)
691
692   def testPrepareExport(self):
693     self.rapi.AddResponse("8326")
694     self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
695     self.assertHandler(rlib2.R_2_instances_name_prepare_export)
696     self.assertItems(["inst1"])
697     self.assertQuery("mode", ["local"])
698
699   def testExportInstance(self):
700     self.rapi.AddResponse("19695")
701     job_id = self.client.ExportInstance("inst2", "local", "nodeX",
702                                         shutdown=True)
703     self.assertEqual(job_id, 19695)
704     self.assertHandler(rlib2.R_2_instances_name_export)
705     self.assertItems(["inst2"])
706
707     data = serializer.LoadJson(self.rapi.GetLastRequestData())
708     self.assertEqual(data["mode"], "local")
709     self.assertEqual(data["destination"], "nodeX")
710     self.assertEqual(data["shutdown"], True)
711
712   def testMigrateInstanceDefaults(self):
713     self.rapi.AddResponse("24873")
714     job_id = self.client.MigrateInstance("inst91")
715     self.assertEqual(job_id, 24873)
716     self.assertHandler(rlib2.R_2_instances_name_migrate)
717     self.assertItems(["inst91"])
718
719     data = serializer.LoadJson(self.rapi.GetLastRequestData())
720     self.assertFalse(data)
721
722   def testMigrateInstance(self):
723     for mode in constants.HT_MIGRATION_MODES:
724       for cleanup in [False, True]:
725         self.rapi.AddResponse("31910")
726         job_id = self.client.MigrateInstance("inst289", mode=mode,
727                                              cleanup=cleanup)
728         self.assertEqual(job_id, 31910)
729         self.assertHandler(rlib2.R_2_instances_name_migrate)
730         self.assertItems(["inst289"])
731
732         data = serializer.LoadJson(self.rapi.GetLastRequestData())
733         self.assertEqual(len(data), 2)
734         self.assertEqual(data["mode"], mode)
735         self.assertEqual(data["cleanup"], cleanup)
736
737   def testRenameInstanceDefaults(self):
738     new_name = "newnametha7euqu"
739     self.rapi.AddResponse("8791")
740     job_id = self.client.RenameInstance("inst18821", new_name)
741     self.assertEqual(job_id, 8791)
742     self.assertHandler(rlib2.R_2_instances_name_rename)
743     self.assertItems(["inst18821"])
744
745     data = serializer.LoadJson(self.rapi.GetLastRequestData())
746     self.assertEqualValues(data, {"new_name": new_name, })
747
748   def testRenameInstance(self):
749     new_name = "new-name-yiux1iin"
750     for ip_check in [False, True]:
751       for name_check in [False, True]:
752         self.rapi.AddResponse("24776")
753         job_id = self.client.RenameInstance("inst20967", new_name,
754                                              ip_check=ip_check,
755                                              name_check=name_check)
756         self.assertEqual(job_id, 24776)
757         self.assertHandler(rlib2.R_2_instances_name_rename)
758         self.assertItems(["inst20967"])
759
760         data = serializer.LoadJson(self.rapi.GetLastRequestData())
761         self.assertEqual(len(data), 3)
762         self.assertEqual(data["new_name"], new_name)
763         self.assertEqual(data["ip_check"], ip_check)
764         self.assertEqual(data["name_check"], name_check)
765
766   def testGetJobs(self):
767     self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
768                           '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
769     self.assertEqual([123, 124], self.client.GetJobs())
770     self.assertHandler(rlib2.R_2_jobs)
771
772   def testGetJobStatus(self):
773     self.rapi.AddResponse("{\"foo\": \"bar\"}")
774     self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
775     self.assertHandler(rlib2.R_2_jobs_id)
776     self.assertItems(["1234"])
777
778   def testWaitForJobChange(self):
779     fields = ["id", "summary"]
780     expected = {
781       "job_info": [123, "something"],
782       "log_entries": [],
783       }
784
785     self.rapi.AddResponse(serializer.DumpJson(expected))
786     result = self.client.WaitForJobChange(123, fields, [], -1)
787     self.assertEqualValues(expected, result)
788     self.assertHandler(rlib2.R_2_jobs_id_wait)
789     self.assertItems(["123"])
790
791   def testCancelJob(self):
792     self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
793     self.assertEqual([True, "Job 123 will be canceled"],
794                      self.client.CancelJob(999, dry_run=True))
795     self.assertHandler(rlib2.R_2_jobs_id)
796     self.assertItems(["999"])
797     self.assertDryRun()
798
799   def testGetNodes(self):
800     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
801                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
802     self.assertEqual(["node1", "node2"], self.client.GetNodes())
803     self.assertHandler(rlib2.R_2_nodes)
804
805     self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
806                           " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
807     self.assertEqual([{"id": "node1", "uri": "uri1"},
808                       {"id": "node2", "uri": "uri2"}],
809                      self.client.GetNodes(bulk=True))
810     self.assertHandler(rlib2.R_2_nodes)
811     self.assertBulk()
812
813   def testGetNode(self):
814     self.rapi.AddResponse("{}")
815     self.assertEqual({}, self.client.GetNode("node-foo"))
816     self.assertHandler(rlib2.R_2_nodes_name)
817     self.assertItems(["node-foo"])
818
819   def testEvacuateNode(self):
820     self.rapi.AddResponse("9876")
821     job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
822     self.assertEqual(9876, job_id)
823     self.assertHandler(rlib2.R_2_nodes_name_evacuate)
824     self.assertItems(["node-1"])
825     self.assertQuery("remote_node", ["node-2"])
826
827     self.rapi.AddResponse("8888")
828     job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
829     self.assertEqual(8888, job_id)
830     self.assertItems(["node-3"])
831     self.assertQuery("iallocator", ["hail"])
832     self.assertDryRun()
833
834     self.assertRaises(client.GanetiApiError,
835                       self.client.EvacuateNode,
836                       "node-4", iallocator="hail", remote_node="node-5")
837
838   def testMigrateNode(self):
839     self.rapi.AddResponse(serializer.DumpJson([]))
840     self.rapi.AddResponse("1111")
841     self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
842     self.assertHandler(rlib2.R_2_nodes_name_migrate)
843     self.assertItems(["node-a"])
844     self.assert_("mode" not in self.rapi.GetLastHandler().queryargs)
845     self.assertDryRun()
846     self.assertFalse(self.rapi.GetLastRequestData())
847
848     self.rapi.AddResponse(serializer.DumpJson([]))
849     self.rapi.AddResponse("1112")
850     self.assertEqual(1112, self.client.MigrateNode("node-a", dry_run=True,
851                                                    mode="live"))
852     self.assertHandler(rlib2.R_2_nodes_name_migrate)
853     self.assertItems(["node-a"])
854     self.assertQuery("mode", ["live"])
855     self.assertDryRun()
856     self.assertFalse(self.rapi.GetLastRequestData())
857
858     self.rapi.AddResponse(serializer.DumpJson([]))
859     self.assertRaises(client.GanetiApiError, self.client.MigrateNode,
860                       "node-c", target_node="foonode")
861     self.assertEqual(self.rapi.CountPending(), 0)
862
863   def testMigrateNodeBodyData(self):
864     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
865     self.rapi.AddResponse("27539")
866     self.assertEqual(27539, self.client.MigrateNode("node-a", dry_run=False,
867                                                     mode="live"))
868     self.assertHandler(rlib2.R_2_nodes_name_migrate)
869     self.assertItems(["node-a"])
870     self.assertFalse(self.rapi.GetLastHandler().queryargs)
871     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
872                      { "mode": "live", })
873
874     self.rapi.AddResponse(serializer.DumpJson([rlib2._NODE_MIGRATE_REQV1]))
875     self.rapi.AddResponse("14219")
876     self.assertEqual(14219, self.client.MigrateNode("node-x", dry_run=True,
877                                                     target_node="node9",
878                                                     iallocator="ial"))
879     self.assertHandler(rlib2.R_2_nodes_name_migrate)
880     self.assertItems(["node-x"])
881     self.assertDryRun()
882     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
883                      { "target_node": "node9", "iallocator": "ial", })
884
885     self.assertEqual(self.rapi.CountPending(), 0)
886
887   def testGetNodeRole(self):
888     self.rapi.AddResponse("\"master\"")
889     self.assertEqual("master", self.client.GetNodeRole("node-a"))
890     self.assertHandler(rlib2.R_2_nodes_name_role)
891     self.assertItems(["node-a"])
892
893   def testSetNodeRole(self):
894     self.rapi.AddResponse("789")
895     self.assertEqual(789,
896         self.client.SetNodeRole("node-foo", "master-candidate", force=True))
897     self.assertHandler(rlib2.R_2_nodes_name_role)
898     self.assertItems(["node-foo"])
899     self.assertQuery("force", ["1"])
900     self.assertEqual("\"master-candidate\"", self.rapi.GetLastRequestData())
901
902   def testGetNodeStorageUnits(self):
903     self.rapi.AddResponse("42")
904     self.assertEqual(42,
905         self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
906     self.assertHandler(rlib2.R_2_nodes_name_storage)
907     self.assertItems(["node-x"])
908     self.assertQuery("storage_type", ["lvm-pv"])
909     self.assertQuery("output_fields", ["fields"])
910
911   def testModifyNodeStorageUnits(self):
912     self.rapi.AddResponse("14")
913     self.assertEqual(14,
914         self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
915     self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
916     self.assertItems(["node-z"])
917     self.assertQuery("storage_type", ["lvm-pv"])
918     self.assertQuery("name", ["hda"])
919     self.assertQuery("allocatable", None)
920
921     for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
922       self.rapi.AddResponse("7205")
923       job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
924                                                   allocatable=allocatable)
925       self.assertEqual(7205, job_id)
926       self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
927       self.assertItems(["node-z"])
928       self.assertQuery("storage_type", ["lvm-pv"])
929       self.assertQuery("name", ["hda"])
930       self.assertQuery("allocatable", [query_allocatable])
931
932   def testRepairNodeStorageUnits(self):
933     self.rapi.AddResponse("99")
934     self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
935                                                             "hda"))
936     self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
937     self.assertItems(["node-z"])
938     self.assertQuery("storage_type", ["lvm-pv"])
939     self.assertQuery("name", ["hda"])
940
941   def testGetNodeTags(self):
942     self.rapi.AddResponse("[\"fry\", \"bender\"]")
943     self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
944     self.assertHandler(rlib2.R_2_nodes_name_tags)
945     self.assertItems(["node-k"])
946
947   def testAddNodeTags(self):
948     self.rapi.AddResponse("1234")
949     self.assertEqual(1234,
950         self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
951     self.assertHandler(rlib2.R_2_nodes_name_tags)
952     self.assertItems(["node-v"])
953     self.assertDryRun()
954     self.assertQuery("tag", ["awesome"])
955
956   def testDeleteNodeTags(self):
957     self.rapi.AddResponse("16861")
958     self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
959                                                        dry_run=True))
960     self.assertHandler(rlib2.R_2_nodes_name_tags)
961     self.assertItems(["node-w"])
962     self.assertDryRun()
963     self.assertQuery("tag", ["awesome"])
964
965   def testGetGroups(self):
966     groups = [{"name": "group1",
967                "uri": "/2/groups/group1",
968                },
969               {"name": "group2",
970                "uri": "/2/groups/group2",
971                },
972               ]
973     self.rapi.AddResponse(serializer.DumpJson(groups))
974     self.assertEqual(["group1", "group2"], self.client.GetGroups())
975     self.assertHandler(rlib2.R_2_groups)
976
977   def testGetGroupsBulk(self):
978     groups = [{"name": "group1",
979                "uri": "/2/groups/group1",
980                "node_cnt": 2,
981                "node_list": ["gnt1.test",
982                              "gnt2.test",
983                              ],
984                },
985               {"name": "group2",
986                "uri": "/2/groups/group2",
987                "node_cnt": 1,
988                "node_list": ["gnt3.test",
989                              ],
990                },
991               ]
992     self.rapi.AddResponse(serializer.DumpJson(groups))
993
994     self.assertEqual(groups, self.client.GetGroups(bulk=True))
995     self.assertHandler(rlib2.R_2_groups)
996     self.assertBulk()
997
998   def testGetGroup(self):
999     group = {"ctime": None,
1000              "name": "default",
1001              }
1002     self.rapi.AddResponse(serializer.DumpJson(group))
1003     self.assertEqual({"ctime": None, "name": "default"},
1004                      self.client.GetGroup("default"))
1005     self.assertHandler(rlib2.R_2_groups_name)
1006     self.assertItems(["default"])
1007
1008   def testCreateGroup(self):
1009     self.rapi.AddResponse("12345")
1010     job_id = self.client.CreateGroup("newgroup", dry_run=True)
1011     self.assertEqual(job_id, 12345)
1012     self.assertHandler(rlib2.R_2_groups)
1013     self.assertDryRun()
1014
1015   def testDeleteGroup(self):
1016     self.rapi.AddResponse("12346")
1017     job_id = self.client.DeleteGroup("newgroup", dry_run=True)
1018     self.assertEqual(job_id, 12346)
1019     self.assertHandler(rlib2.R_2_groups_name)
1020     self.assertDryRun()
1021
1022   def testRenameGroup(self):
1023     self.rapi.AddResponse("12347")
1024     job_id = self.client.RenameGroup("oldname", "newname")
1025     self.assertEqual(job_id, 12347)
1026     self.assertHandler(rlib2.R_2_groups_name_rename)
1027
1028   def testModifyGroup(self):
1029     self.rapi.AddResponse("12348")
1030     job_id = self.client.ModifyGroup("mygroup", alloc_policy="foo")
1031     self.assertEqual(job_id, 12348)
1032     self.assertHandler(rlib2.R_2_groups_name_modify)
1033
1034   def testAssignGroupNodes(self):
1035     self.rapi.AddResponse("12349")
1036     job_id = self.client.AssignGroupNodes("mygroup", ["node1", "node2"],
1037                                           force=True, dry_run=True)
1038     self.assertEqual(job_id, 12349)
1039     self.assertHandler(rlib2.R_2_groups_name_assign_nodes)
1040     self.assertDryRun()
1041     self.assertUseForce()
1042
1043   def testModifyInstance(self):
1044     self.rapi.AddResponse("23681")
1045     job_id = self.client.ModifyInstance("inst7210", os_name="linux")
1046     self.assertEqual(job_id, 23681)
1047     self.assertItems(["inst7210"])
1048     self.assertHandler(rlib2.R_2_instances_name_modify)
1049     self.assertEqual(serializer.LoadJson(self.rapi.GetLastRequestData()),
1050                      { "os_name": "linux", })
1051
1052   def testModifyCluster(self):
1053     for mnh in [None, False, True]:
1054       self.rapi.AddResponse("14470")
1055       self.assertEqual(14470,
1056         self.client.ModifyCluster(maintain_node_health=mnh))
1057       self.assertHandler(rlib2.R_2_cluster_modify)
1058       self.assertItems([])
1059       data = serializer.LoadJson(self.rapi.GetLastRequestData())
1060       self.assertEqual(len(data), 1)
1061       self.assertEqual(data["maintain_node_health"], mnh)
1062       self.assertEqual(self.rapi.CountPending(), 0)
1063
1064   def testRedistributeConfig(self):
1065     self.rapi.AddResponse("3364")
1066     job_id = self.client.RedistributeConfig()
1067     self.assertEqual(job_id, 3364)
1068     self.assertItems([])
1069     self.assertHandler(rlib2.R_2_redist_config)
1070
1071   def testActivateInstanceDisks(self):
1072     self.rapi.AddResponse("23547")
1073     job_id = self.client.ActivateInstanceDisks("inst28204")
1074     self.assertEqual(job_id, 23547)
1075     self.assertItems(["inst28204"])
1076     self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1077     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1078
1079   def testActivateInstanceDisksIgnoreSize(self):
1080     self.rapi.AddResponse("11044")
1081     job_id = self.client.ActivateInstanceDisks("inst28204", ignore_size=True)
1082     self.assertEqual(job_id, 11044)
1083     self.assertItems(["inst28204"])
1084     self.assertHandler(rlib2.R_2_instances_name_activate_disks)
1085     self.assertQuery("ignore_size", ["1"])
1086
1087   def testDeactivateInstanceDisks(self):
1088     self.rapi.AddResponse("14591")
1089     job_id = self.client.DeactivateInstanceDisks("inst28234")
1090     self.assertEqual(job_id, 14591)
1091     self.assertItems(["inst28234"])
1092     self.assertHandler(rlib2.R_2_instances_name_deactivate_disks)
1093     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1094
1095   def testGetInstanceConsole(self):
1096     self.rapi.AddResponse("26876")
1097     job_id = self.client.GetInstanceConsole("inst21491")
1098     self.assertEqual(job_id, 26876)
1099     self.assertItems(["inst21491"])
1100     self.assertHandler(rlib2.R_2_instances_name_console)
1101     self.assertFalse(self.rapi.GetLastHandler().queryargs)
1102     self.assertFalse(self.rapi.GetLastRequestData())
1103
1104   def testGrowInstanceDisk(self):
1105     for idx, wait_for_sync in enumerate([None, False, True]):
1106       amount = 128 + (512 * idx)
1107       self.assertEqual(self.rapi.CountPending(), 0)
1108       self.rapi.AddResponse("30783")
1109       self.assertEqual(30783,
1110         self.client.GrowInstanceDisk("eze8ch", idx, amount,
1111                                      wait_for_sync=wait_for_sync))
1112       self.assertHandler(rlib2.R_2_instances_name_disk_grow)
1113       self.assertItems(["eze8ch", str(idx)])
1114       data = serializer.LoadJson(self.rapi.GetLastRequestData())
1115       if wait_for_sync is None:
1116         self.assertEqual(len(data), 1)
1117         self.assert_("wait_for_sync" not in data)
1118       else:
1119         self.assertEqual(len(data), 2)
1120         self.assertEqual(data["wait_for_sync"], wait_for_sync)
1121       self.assertEqual(data["amount"], amount)
1122       self.assertEqual(self.rapi.CountPending(), 0)
1123
1124   def testGetGroupTags(self):
1125     self.rapi.AddResponse("[]")
1126     self.assertEqual([], self.client.GetGroupTags("fooGroup"))
1127     self.assertHandler(rlib2.R_2_groups_name_tags)
1128     self.assertItems(["fooGroup"])
1129
1130   def testAddGroupTags(self):
1131     self.rapi.AddResponse("1234")
1132     self.assertEqual(1234,
1133         self.client.AddGroupTags("fooGroup", ["awesome"], dry_run=True))
1134     self.assertHandler(rlib2.R_2_groups_name_tags)
1135     self.assertItems(["fooGroup"])
1136     self.assertDryRun()
1137     self.assertQuery("tag", ["awesome"])
1138
1139   def testDeleteGroupTags(self):
1140     self.rapi.AddResponse("25826")
1141     self.assertEqual(25826, self.client.DeleteGroupTags("foo", ["awesome"],
1142                                                         dry_run=True))
1143     self.assertHandler(rlib2.R_2_groups_name_tags)
1144     self.assertItems(["foo"])
1145     self.assertDryRun()
1146     self.assertQuery("tag", ["awesome"])
1147
1148   def testQuery(self):
1149     for idx, what in enumerate(constants.QR_VIA_RAPI):
1150       for idx2, filter_ in enumerate([None, ["?", "name"]]):
1151         job_id = 11010 + (idx << 4) + (idx2 << 16)
1152         fields = sorted(query.ALL_FIELDS[what].keys())[:10]
1153
1154         self.rapi.AddResponse(str(job_id))
1155         self.assertEqual(self.client.Query(what, fields, filter_=filter_),
1156                          job_id)
1157         self.assertItems([what])
1158         self.assertHandler(rlib2.R_2_query)
1159         self.assertFalse(self.rapi.GetLastHandler().queryargs)
1160         data = serializer.LoadJson(self.rapi.GetLastRequestData())
1161         self.assertEqual(data["fields"], fields)
1162         if filter_ is None:
1163           self.assertTrue("filter" not in data)
1164         else:
1165           self.assertEqual(data["filter"], filter_)
1166         self.assertEqual(self.rapi.CountPending(), 0)
1167
1168   def testQueryFields(self):
1169     exp_result = objects.QueryFieldsResponse(fields=[
1170       objects.QueryFieldDefinition(name="pnode", title="PNode",
1171                                    kind=constants.QFT_NUMBER),
1172       objects.QueryFieldDefinition(name="other", title="Other",
1173                                    kind=constants.QFT_BOOL),
1174       ])
1175
1176     for what in constants.QR_VIA_RAPI:
1177       for fields in [None, ["name", "_unknown_"], ["&", "?|"]]:
1178         self.rapi.AddResponse(serializer.DumpJson(exp_result.ToDict()))
1179         result = self.client.QueryFields(what, fields=fields)
1180         self.assertItems([what])
1181         self.assertHandler(rlib2.R_2_query_fields)
1182         self.assertFalse(self.rapi.GetLastRequestData())
1183
1184         queryargs = self.rapi.GetLastHandler().queryargs
1185         if fields is None:
1186           self.assertFalse(queryargs)
1187         else:
1188           self.assertEqual(queryargs, {
1189             "fields": [",".join(fields)],
1190             })
1191
1192         self.assertEqual(objects.QueryFieldsResponse.FromDict(result).ToDict(),
1193                          exp_result.ToDict())
1194
1195         self.assertEqual(self.rapi.CountPending(), 0)
1196
1197   def testWaitForJobCompletionNoChange(self):
1198     resp = serializer.DumpJson({
1199       "status": constants.JOB_STATUS_WAITLOCK,
1200       })
1201
1202     for retries in [1, 5, 25]:
1203       for _ in range(retries):
1204         self.rapi.AddResponse(resp)
1205
1206       self.assertFalse(self.client.WaitForJobCompletion(22789, period=None,
1207                                                         retries=retries))
1208       self.assertHandler(rlib2.R_2_jobs_id)
1209       self.assertItems(["22789"])
1210
1211       self.assertEqual(self.rapi.CountPending(), 0)
1212
1213   def testWaitForJobCompletionAlreadyFinished(self):
1214     self.rapi.AddResponse(serializer.DumpJson({
1215       "status": constants.JOB_STATUS_SUCCESS,
1216       }))
1217
1218     self.assertTrue(self.client.WaitForJobCompletion(22793, period=None,
1219                                                      retries=1))
1220     self.assertHandler(rlib2.R_2_jobs_id)
1221     self.assertItems(["22793"])
1222
1223     self.assertEqual(self.rapi.CountPending(), 0)
1224
1225   def testWaitForJobCompletionEmptyResponse(self):
1226     self.rapi.AddResponse("{}")
1227     self.assertFalse(self.client.WaitForJobCompletion(22793, period=None,
1228                                                      retries=10))
1229     self.assertHandler(rlib2.R_2_jobs_id)
1230     self.assertItems(["22793"])
1231
1232     self.assertEqual(self.rapi.CountPending(), 0)
1233
1234   def testWaitForJobCompletionOutOfRetries(self):
1235     for retries in [3, 10, 21]:
1236       for _ in range(retries):
1237         self.rapi.AddResponse(serializer.DumpJson({
1238           "status": constants.JOB_STATUS_RUNNING,
1239           }))
1240
1241       self.assertFalse(self.client.WaitForJobCompletion(30948, period=None,
1242                                                         retries=retries - 1))
1243       self.assertHandler(rlib2.R_2_jobs_id)
1244       self.assertItems(["30948"])
1245
1246       self.assertEqual(self.rapi.CountPending(), 1)
1247       self.rapi.ResetResponses()
1248
1249   def testWaitForJobCompletionSuccessAndFailure(self):
1250     for retries in [1, 4, 13]:
1251       for (success, end_status) in [(False, constants.JOB_STATUS_ERROR),
1252                                     (True, constants.JOB_STATUS_SUCCESS)]:
1253         for _ in range(retries):
1254           self.rapi.AddResponse(serializer.DumpJson({
1255             "status": constants.JOB_STATUS_RUNNING,
1256             }))
1257
1258         self.rapi.AddResponse(serializer.DumpJson({
1259           "status": end_status,
1260           }))
1261
1262         result = self.client.WaitForJobCompletion(3187, period=None,
1263                                                   retries=retries + 1)
1264         self.assertEqual(result, success)
1265         self.assertHandler(rlib2.R_2_jobs_id)
1266         self.assertItems(["3187"])
1267
1268         self.assertEqual(self.rapi.CountPending(), 0)
1269
1270
1271 class RapiTestRunner(unittest.TextTestRunner):
1272   def run(self, *args):
1273     global _used_handlers
1274     assert _used_handlers is None
1275
1276     _used_handlers = set()
1277     try:
1278       # Run actual tests
1279       result = unittest.TextTestRunner.run(self, *args)
1280
1281       diff = (set(connector.CONNECTOR.values()) - _used_handlers -
1282              _KNOWN_UNUSED)
1283       if diff:
1284         raise AssertionError("The following RAPI resources were not used by the"
1285                              " RAPI client: %r" % utils.CommaJoin(diff))
1286     finally:
1287       # Reset global variable
1288       _used_handlers = None
1289
1290     return result
1291
1292
1293 if __name__ == '__main__':
1294   client.UsesRapiClient(testutils.GanetiTestProgram)(testRunner=RapiTestRunner)