Add RST version of ganeti-rapi man page
[ganeti-local] / qa / qa_rapi.py
index 639f62a..a1b371e 100644 (file)
@@ -1,6 +1,6 @@
 #
 
-# Copyright (C) 2007, 2008 Google Inc.
+# Copyright (C) 2007, 2008, 2009, 2010 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -39,11 +39,13 @@ import qa_utils
 import qa_error
 
 from qa_utils import (AssertEqual, AssertNotEqual, AssertIn, AssertMatch,
-                      StartSSH)
+                      StartLocalCommand)
 
 
 _rapi_ca = None
 _rapi_client = None
+_rapi_username = None
+_rapi_password = None
 
 
 def Setup(username, password):
@@ -52,6 +54,11 @@ def Setup(username, password):
   """
   global _rapi_ca
   global _rapi_client
+  global _rapi_username
+  global _rapi_password
+
+  _rapi_username = username
+  _rapi_password = password
 
   master = qa_config.GetMasterNode()
 
@@ -65,13 +72,13 @@ def Setup(username, password):
   _rapi_ca.flush()
 
   port = qa_config.get("rapi-port", default=constants.DEFAULT_RAPI_PORT)
-  cfg_ssl = rapi.client.CertAuthorityVerify(cafile=_rapi_ca.name)
+  cfg_curl = rapi.client.GenericCurlConfig(cafile=_rapi_ca.name,
+                                           proxy="")
 
   _rapi_client = rapi.client.GanetiRapiClient(master["primary"], port=port,
                                               username=username,
                                               password=password,
-                                              config_ssl_verification=cfg_ssl,
-                                              ignore_proxy=True)
+                                              curl_config_fn=cfg_curl)
 
   print "RAPI protocol version: %s" % _rapi_client.GetVersion()
 
@@ -81,7 +88,7 @@ INSTANCE_FIELDS = ("name", "os", "pnode", "snodes",
                    "disk_template", "disk.sizes",
                    "nic.ips", "nic.macs", "nic.modes", "nic.links",
                    "beparams", "hvparams",
-                   "oper_state", "oper_ram", "status", "tags")
+                   "oper_state", "oper_ram", "oper_vcpus", "status", "tags")
 
 NODE_FIELDS = ("name", "dtotal", "dfree",
                "mtotal", "mnode", "mfree",
@@ -109,6 +116,7 @@ def _DoTests(uris):
   for uri, verify, method, body in uris:
     assert uri.startswith("/")
 
+    print "%s %s" % (method, uri)
     data = _rapi_client._SendRequest(method, uri, None, body)
 
     if verify is not None:
@@ -117,7 +125,7 @@ def _DoTests(uris):
       else:
         AssertEqual(data, verify)
 
-      results.append(data)
+    results.append(data)
 
   return results
 
@@ -170,6 +178,24 @@ def TestEmptyCluster():
     ("/2/os", None, 'GET', None),
     ])
 
+  # Test HTTP Not Found
+  for method in ["GET", "PUT", "POST", "DELETE"]:
+    try:
+      _DoTests([("/99/resource/not/here/99", None, method, None)])
+    except rapi.client.GanetiApiError, err:
+      AssertEqual(err.code, 404)
+    else:
+      raise qa_error.Error("Non-existent resource didn't return HTTP 404")
+
+  # Test HTTP Not Implemented
+  for method in ["PUT", "POST", "DELETE"]:
+    try:
+      _DoTests([("/version", None, method, None)])
+    except rapi.client.GanetiApiError, err:
+      AssertEqual(err.code, 501)
+    else:
+      raise qa_error.Error("Non-implemented method didn't fail")
+
 
 def TestInstance(instance):
   """Testing getting instance(s) info via remote API.
@@ -198,6 +224,19 @@ def TestInstance(instance):
      _VerifyReturnsJob, 'PUT', None),
     ])
 
+  # Test OpPrepareExport
+  (job_id, ) = _DoTests([
+    ("/2/instances/%s/prepare-export?mode=%s" %
+     (instance["name"], constants.EXPORT_MODE_REMOTE),
+     _VerifyReturnsJob, "PUT", None),
+    ])
+
+  result = _WaitForRapiJob(job_id)[0]
+  AssertEqual(len(result["handshake"]), 3)
+  AssertEqual(result["handshake"][0], constants.RIE_VERSION)
+  AssertEqual(len(result["x509_key_name"]), 3)
+  AssertIn("-----BEGIN CERTIFICATE-----", result["x509_ca"])
+
 
 def TestNode(node):
   """Testing getting node(s) info via remote API.
@@ -239,10 +278,25 @@ def TestTags(kind, name, tags):
   def _VerifyTags(data):
     AssertEqual(sorted(tags), sorted(data))
 
+  query = "&".join("tag=%s" % i for i in tags)
+
+  # Add tags
+  (job_id, ) = _DoTests([
+    ("%s?%s" % (uri, query), _VerifyReturnsJob, "PUT", None),
+    ])
+  _WaitForRapiJob(job_id)
+
+  # Retrieve tags
   _DoTests([
     (uri, _VerifyTags, 'GET', None),
     ])
 
+  # Remove tags
+  (job_id, ) = _DoTests([
+    ("%s?%s" % (uri, query), _VerifyReturnsJob, "DELETE", None),
+    ])
+  _WaitForRapiJob(job_id)
+
 
 def _WaitForRapiJob(job_id):
   """Waits for a job to finish.
@@ -259,25 +313,45 @@ def _WaitForRapiJob(job_id):
     ("/2/jobs/%s" % job_id, _VerifyJob, "GET", None),
     ])
 
-  rapi.client_utils.PollJob(_rapi_client, job_id, cli.StdioJobPollReportCb())
+  return rapi.client_utils.PollJob(_rapi_client, job_id,
+                                   cli.StdioJobPollReportCb())
 
 
-def TestRapiInstanceAdd(node):
+def TestRapiInstanceAdd(node, use_client):
   """Test adding a new instance via RAPI"""
   instance = qa_config.AcquireInstance()
   try:
-    body = {
-      "name": instance["name"],
-      "os": qa_config.get("os"),
-      "disk_template": constants.DT_PLAIN,
-      "pnode": node["primary"],
-      "memory": utils.ParseUnit(qa_config.get("mem")),
-      "disks": [utils.ParseUnit(size) for size in qa_config.get("disk")],
-      }
-
-    (job_id, ) = _DoTests([
-      ("/2/instances", _VerifyReturnsJob, "POST", body),
-      ])
+    memory = utils.ParseUnit(qa_config.get("mem"))
+    disk_sizes = [utils.ParseUnit(size) for size in qa_config.get("disk")]
+
+    if use_client:
+      disks = [{"size": size} for size in disk_sizes]
+      nics = [{}]
+
+      beparams = {
+        constants.BE_MEMORY: memory,
+        }
+
+      job_id = _rapi_client.CreateInstance(constants.INSTANCE_CREATE,
+                                           instance["name"],
+                                           constants.DT_PLAIN,
+                                           disks, nics,
+                                           os=qa_config.get("os"),
+                                           pnode=node["primary"],
+                                           beparams=beparams)
+    else:
+      body = {
+        "name": instance["name"],
+        "os": qa_config.get("os"),
+        "disk_template": constants.DT_PLAIN,
+        "pnode": node["primary"],
+        "memory": memory,
+        "disks": disk_sizes,
+        }
+
+      (job_id, ) = _DoTests([
+        ("/2/instances", _VerifyReturnsJob, "POST", body),
+        ])
 
     _WaitForRapiJob(job_id)
 
@@ -287,12 +361,95 @@ def TestRapiInstanceAdd(node):
     raise
 
 
-def TestRapiInstanceRemove(instance):
+def TestRapiInstanceRemove(instance, use_client):
   """Test removing instance via RAPI"""
-  (job_id, ) = _DoTests([
-    ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
-    ])
+  if use_client:
+    job_id = _rapi_client.DeleteInstance(instance["name"])
+  else:
+    (job_id, ) = _DoTests([
+      ("/2/instances/%s" % instance["name"], _VerifyReturnsJob, "DELETE", None),
+      ])
 
   _WaitForRapiJob(job_id)
 
   qa_config.ReleaseInstance(instance)
+
+
+def TestRapiInstanceMigrate(instance):
+  """Test migrating instance via RAPI"""
+  # Move to secondary node
+  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
+  # And back to previous primary
+  _WaitForRapiJob(_rapi_client.MigrateInstance(instance["name"]))
+
+
+def TestRapiInstanceRename(instance, rename_target):
+  """Test renaming instance via RAPI"""
+  rename_source = instance["name"]
+
+  for name1, name2 in [(rename_source, rename_target),
+                       (rename_target, rename_source)]:
+    _WaitForRapiJob(_rapi_client.RenameInstance(name1, name2))
+
+
+def TestRapiInstanceModify(instance):
+  """Test modifying instance via RAPI"""
+  def _ModifyInstance(**kwargs):
+    _WaitForRapiJob(_rapi_client.ModifyInstance(instance["name"], **kwargs))
+
+  _ModifyInstance(hvparams={
+    constants.HV_KERNEL_ARGS: "single",
+    })
+
+  _ModifyInstance(beparams={
+    constants.BE_VCPUS: 3,
+    })
+
+  _ModifyInstance(beparams={
+    constants.BE_VCPUS: constants.VALUE_DEFAULT,
+    })
+
+  _ModifyInstance(hvparams={
+    constants.HV_KERNEL_ARGS: constants.VALUE_DEFAULT,
+    })
+
+
+def TestInterClusterInstanceMove(src_instance, dest_instance,
+                                 pnode, snode, tnode):
+  """Test tools/move-instance"""
+  master = qa_config.GetMasterNode()
+
+  rapi_pw_file = tempfile.NamedTemporaryFile()
+  rapi_pw_file.write(_rapi_password)
+  rapi_pw_file.flush()
+
+  # TODO: Run some instance tests before moving back
+
+  if snode is None:
+    # instance is not redundant, but we still need to pass a node
+    # (which will be ignored)
+    fsec = tnode
+  else:
+    fsec = snode
+  # note: pnode:snode are the *current* nodes, so we move it first to
+  # tnode:pnode, then back to pnode:snode
+  for si, di, pn, sn in [(src_instance["name"], dest_instance["name"],
+                          tnode["primary"], pnode["primary"]),
+                         (dest_instance["name"], src_instance["name"],
+                          pnode["primary"], fsec["primary"])]:
+    cmd = [
+      "../tools/move-instance",
+      "--verbose",
+      "--src-ca-file=%s" % _rapi_ca.name,
+      "--src-username=%s" % _rapi_username,
+      "--src-password-file=%s" % rapi_pw_file.name,
+      "--dest-instance-name=%s" % di,
+      "--dest-primary-node=%s" % pn,
+      "--dest-secondary-node=%s" % sn,
+      "--net=0:mac=%s" % constants.VALUE_GENERATE,
+      master["primary"],
+      master["primary"],
+      si,
+      ]
+
+    AssertEqual(StartLocalCommand(cmd).wait(), 0)