Merge branch 'devel-2.1'
[ganeti-local] / test / ganeti.rapi.client_unittest.py
index 4742c5e..10c23d0 100755 (executable)
@@ -27,6 +27,7 @@ import unittest
 import warnings
 
 from ganeti import http
+from ganeti import serializer
 
 from ganeti.rapi import connector
 from ganeti.rapi import rlib2
@@ -80,51 +81,42 @@ class OpenerDirectorMock:
 
 
 class RapiMock(object):
-
   def __init__(self):
     self._mapper = connector.Mapper()
     self._responses = []
     self._last_handler = None
 
-  def AddResponse(self, response):
-    self._responses.insert(0, response)
-
-  def PopResponse(self):
-    if len(self._responses) > 0:
-      return self._responses.pop()
-    else:
-      return None
+  def AddResponse(self, response, code=200):
+    self._responses.insert(0, (code, response))
 
   def GetLastHandler(self):
     return self._last_handler
 
   def FetchResponse(self, path, method):
-    code = 200
-    response = None
-
     try:
       HandlerClass, items, args = self._mapper.getController(path)
       self._last_handler = HandlerClass(items, args, None)
       if not hasattr(self._last_handler, method.upper()):
-        code = 400
-        response = "Bad request"
+        raise http.HttpNotImplemented(message="Method not implemented")
+
     except http.HttpException, ex:
       code = ex.code
       response = ex.message
+    else:
+      if not self._responses:
+        raise Exception("No responses")
 
-    if not response:
-      response = self.PopResponse()
+      (code, response) = self._responses.pop()
 
     return code, response
 
 
 class RapiMockTest(unittest.TestCase):
-
   def test(self):
     rapi = RapiMock()
     path = "/version"
     self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET"))
-    self.assertEqual((400, "Bad request"),
+    self.assertEqual((501, "Method not implemented"),
                      rapi.FetchResponse("/version", "POST"))
     rapi.AddResponse("2")
     code, response = rapi.FetchResponse("/version", "GET")
@@ -133,12 +125,10 @@ class RapiMockTest(unittest.TestCase):
     self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
 
 
-class GanetiRapiClientTests(unittest.TestCase):
-  """Tests for remote API client.
-
-  """
-
+class GanetiRapiClientTests(testutils.GanetiTestCase):
   def setUp(self):
+    testutils.GanetiTestCase.setUp(self)
+
     self.rapi = RapiMock()
     self.http = OpenerDirectorMock(self.rapi)
     self.client = client.GanetiRapiClient('master.foo.com')
@@ -161,12 +151,51 @@ class GanetiRapiClientTests(unittest.TestCase):
   def assertDryRun(self):
     self.assertTrue(self.rapi.GetLastHandler().dryRun())
 
+  def testEncodeQuery(self):
+    query = [
+      ("a", None),
+      ("b", 1),
+      ("c", 2),
+      ("d", "Foo"),
+      ("e", True),
+      ]
+
+    expected = [
+      ("a", ""),
+      ("b", 1),
+      ("c", 2),
+      ("d", "Foo"),
+      ("e", 1),
+      ]
+
+    self.assertEqualValues(self.client._EncodeQuery(query),
+                           expected)
+
+    # invalid types
+    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
+      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
+
+  def testHttpError(self):
+    self.rapi.AddResponse(None, code=404)
+    try:
+      self.client.GetJobStatus(15140)
+    except client.GanetiApiError, err:
+      self.assertEqual(err.code, 404)
+    else:
+      self.fail("Didn't raise exception")
+
   def testGetVersion(self):
     self.client._version = None
     self.rapi.AddResponse("2")
     self.assertEqual(2, self.client.GetVersion())
     self.assertHandler(rlib2.R_version)
 
+  def testGetFeatures(self):
+    for features in [[], ["foo", "bar", "baz"]]:
+      self.rapi.AddResponse(serializer.DumpJson(features))
+      self.assertEqual(features, self.client.GetFeatures())
+      self.assertHandler(rlib2.R_2_features)
+
   def testGetOperatingSystems(self):
     self.rapi.AddResponse("[\"beos\"]")
     self.assertEqual(["beos"], self.client.GetOperatingSystems())
@@ -204,18 +233,75 @@ class GanetiRapiClientTests(unittest.TestCase):
     self.assertHandler(rlib2.R_2_instances)
     self.assertBulk()
 
-  def testGetInstanceInfo(self):
+  def testGetInstance(self):
     self.rapi.AddResponse("[]")
-    self.assertEqual([], self.client.GetInstanceInfo("instance"))
+    self.assertEqual([], self.client.GetInstance("instance"))
     self.assertHandler(rlib2.R_2_instances_name)
     self.assertItems(["instance"])
 
+  def testGetInstanceInfo(self):
+    self.rapi.AddResponse("21291")
+    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
+    self.assertHandler(rlib2.R_2_instances_name_info)
+    self.assertItems(["inst3"])
+    self.assertQuery("static", None)
+
+    self.rapi.AddResponse("3428")
+    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
+    self.assertHandler(rlib2.R_2_instances_name_info)
+    self.assertItems(["inst31"])
+    self.assertQuery("static", ["0"])
+
+    self.rapi.AddResponse("15665")
+    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
+    self.assertHandler(rlib2.R_2_instances_name_info)
+    self.assertItems(["inst32"])
+    self.assertQuery("static", ["1"])
+
+  def testCreateInstanceOldVersion(self):
+    self.rapi.AddResponse(serializer.DumpJson([]))
+    self.assertRaises(NotImplementedError, self.client.CreateInstance,
+                      "create", "inst1.example.com", "plain", [], [],
+                      dry_run=True)
+
   def testCreateInstance(self):
-    self.rapi.AddResponse("1234")
-    self.assertEqual(1234, self.client.CreateInstance(dry_run=True))
+    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
+    self.rapi.AddResponse("23030")
+    job_id = self.client.CreateInstance("create", "inst1.example.com",
+                                        "plain", [], [], dry_run=True)
+    self.assertEqual(job_id, 23030)
     self.assertHandler(rlib2.R_2_instances)
     self.assertDryRun()
 
+    data = serializer.LoadJson(self.http.last_request.data)
+
+    for field in ["dry_run", "beparams", "hvparams", "start"]:
+      self.assertFalse(field in data)
+
+    self.assertEqual(data["name"], "inst1.example.com")
+    self.assertEqual(data["disk_template"], "plain")
+
+  def testCreateInstance2(self):
+    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
+    self.rapi.AddResponse("24740")
+    job_id = self.client.CreateInstance("import", "inst2.example.com",
+                                        "drbd8", [{"size": 100,}],
+                                        [{}, {"bridge": "br1", }],
+                                        dry_run=False, start=True,
+                                        pnode="node1", snode="node9",
+                                        ip_check=False)
+    self.assertEqual(job_id, 24740)
+    self.assertHandler(rlib2.R_2_instances)
+
+    data = serializer.LoadJson(self.http.last_request.data)
+    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
+    self.assertEqual(data["name"], "inst2.example.com")
+    self.assertEqual(data["disk_template"], "drbd8")
+    self.assertEqual(data["start"], True)
+    self.assertEqual(data["ip_check"], False)
+    self.assertEqualValues(data["disks"], [{"size": 100,}])
+    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
+
   def testDeleteInstance(self):
     self.rapi.AddResponse("1234")
     self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
@@ -256,7 +342,7 @@ class GanetiRapiClientTests(unittest.TestCase):
     self.assertItems(["i-bar"])
     self.assertDryRun()
     self.assertQuery("type", ["hard"])
-    self.assertQuery("ignore_secondaries", ["True"])
+    self.assertQuery("ignore_secondaries", ["1"])
 
   def testShutdownInstance(self):
     self.rapi.AddResponse("1487")
@@ -286,32 +372,50 @@ class GanetiRapiClientTests(unittest.TestCase):
   def testReplaceInstanceDisks(self):
     self.rapi.AddResponse("999")
     job_id = self.client.ReplaceInstanceDisks("instance-name",
-        ["hda", "hdc"], dry_run=True)
+        disks=[0, 1], dry_run=True, iallocator="hail")
     self.assertEqual(999, job_id)
     self.assertHandler(rlib2.R_2_instances_name_replace_disks)
     self.assertItems(["instance-name"])
-    self.assertQuery("disks", ["hda,hdc"])
+    self.assertQuery("disks", ["0,1"])
     self.assertQuery("mode", ["replace_auto"])
     self.assertQuery("iallocator", ["hail"])
     self.assertDryRun()
 
-    self.assertRaises(client.InvalidReplacementMode,
-                      self.client.ReplaceInstanceDisks,
-                      "instance_a", ["hda"], mode="invalid_mode")
-    self.assertRaises(client.GanetiApiError,
-                      self.client.ReplaceInstanceDisks,
-                      "instance-foo", ["hda"], mode="replace_on_secondary")
-
     self.rapi.AddResponse("1000")
     job_id = self.client.ReplaceInstanceDisks("instance-bar",
-        ["hda"], mode="replace_on_secondary", remote_node="foo-node",
+        disks=[1], mode="replace_on_secondary", remote_node="foo-node",
         dry_run=True)
     self.assertEqual(1000, job_id)
     self.assertItems(["instance-bar"])
-    self.assertQuery("disks", ["hda"])
+    self.assertQuery("disks", ["1"])
     self.assertQuery("remote_node", ["foo-node"])
     self.assertDryRun()
 
+    self.rapi.AddResponse("5175")
+    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
+    self.assertItems(["instance-moo"])
+    self.assertQuery("disks", None)
+
+  def testPrepareExport(self):
+    self.rapi.AddResponse("8326")
+    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
+    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
+    self.assertItems(["inst1"])
+    self.assertQuery("mode", ["local"])
+
+  def testExportInstance(self):
+    self.rapi.AddResponse("19695")
+    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
+                                        shutdown=True)
+    self.assertEqual(job_id, 19695)
+    self.assertHandler(rlib2.R_2_instances_name_export)
+    self.assertItems(["inst2"])
+
+    data = serializer.LoadJson(self.http.last_request.data)
+    self.assertEqual(data["mode"], "local")
+    self.assertEqual(data["destination"], "nodeX")
+    self.assertEqual(data["shutdown"], True)
+
   def testGetJobs(self):
     self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
                           '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
@@ -324,10 +428,23 @@ class GanetiRapiClientTests(unittest.TestCase):
     self.assertHandler(rlib2.R_2_jobs_id)
     self.assertItems(["1234"])
 
-  def testDeleteJob(self):
+  def testWaitForJobChange(self):
+    fields = ["id", "summary"]
+    expected = {
+      "job_info": [123, "something"],
+      "log_entries": [],
+      }
+
+    self.rapi.AddResponse(serializer.DumpJson(expected))
+    result = self.client.WaitForJobChange(123, fields, [], -1)
+    self.assertEqualValues(expected, result)
+    self.assertHandler(rlib2.R_2_jobs_id_wait)
+    self.assertItems(["123"])
+
+  def testCancelJob(self):
     self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
     self.assertEqual([True, "Job 123 will be canceled"],
-                     self.client.DeleteJob(999, dry_run=True))
+                     self.client.CancelJob(999, dry_run=True))
     self.assertHandler(rlib2.R_2_jobs_id)
     self.assertItems(["999"])
     self.assertDryRun()
@@ -346,9 +463,9 @@ class GanetiRapiClientTests(unittest.TestCase):
     self.assertHandler(rlib2.R_2_nodes)
     self.assertBulk()
 
-  def testGetNodeInfo(self):
+  def testGetNode(self):
     self.rapi.AddResponse("{}")
-    self.assertEqual({}, self.client.GetNodeInfo("node-foo"))
+    self.assertEqual({}, self.client.GetNode("node-foo"))
     self.assertHandler(rlib2.R_2_nodes_name)
     self.assertItems(["node-foo"])
 
@@ -391,12 +508,9 @@ class GanetiRapiClientTests(unittest.TestCase):
         self.client.SetNodeRole("node-foo", "master-candidate", force=True))
     self.assertHandler(rlib2.R_2_nodes_name_role)
     self.assertItems(["node-foo"])
-    self.assertQuery("force", ["True"])
+    self.assertQuery("force", ["1"])
     self.assertEqual("\"master-candidate\"", self.http.last_request.data)
 
-    self.assertRaises(client.InvalidNodeRole,
-                      self.client.SetNodeRole, "node-bar", "fake-role")
-
   def testGetNodeStorageUnits(self):
     self.rapi.AddResponse("42")
     self.assertEqual(42,
@@ -406,10 +520,6 @@ class GanetiRapiClientTests(unittest.TestCase):
     self.assertQuery("storage_type", ["lvm-pv"])
     self.assertQuery("output_fields", ["fields"])
 
-    self.assertRaises(client.InvalidStorageType,
-                      self.client.GetNodeStorageUnits,
-                      "node-y", "floppy-disk", "fields")
-
   def testModifyNodeStorageUnits(self):
     self.rapi.AddResponse("14")
     self.assertEqual(14,
@@ -418,10 +528,27 @@ class GanetiRapiClientTests(unittest.TestCase):
     self.assertItems(["node-z"])
     self.assertQuery("storage_type", ["lvm-pv"])
     self.assertQuery("name", ["hda"])
-
-    self.assertRaises(client.InvalidStorageType,
-                      self.client.ModifyNodeStorageUnits,
-                      "node-n", "floppy-disk", "hdc")
+    self.assertQuery("allocatable", None)
+
+    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
+      self.rapi.AddResponse("7205")
+      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
+                                                  allocatable=allocatable)
+      self.assertEqual(7205, job_id)
+      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
+      self.assertItems(["node-z"])
+      self.assertQuery("storage_type", ["lvm-pv"])
+      self.assertQuery("name", ["hda"])
+      self.assertQuery("allocatable", [query_allocatable])
+
+  def testRepairNodeStorageUnits(self):
+    self.rapi.AddResponse("99")
+    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
+                                                            "hda"))
+    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
+    self.assertItems(["node-z"])
+    self.assertQuery("storage_type", ["lvm-pv"])
+    self.assertQuery("name", ["hda"])
 
   def testGetNodeTags(self):
     self.rapi.AddResponse("[\"fry\", \"bender\"]")