From bf968b7ffc2fd7aea5400814648a58275d7c5dec Mon Sep 17 00:00:00 2001 From: Michael Hanselmann <hansmi@google.com> Date: Thu, 3 Sep 2009 12:28:14 +0200 Subject: [PATCH] Add simple unittest for remote API docs Signed-off-by: Michael Hanselmann <hansmi@google.com> Reviewed-by: Luca Bigliardi <shammash@google.com> --- lib/rapi/connector.py | 77 +++++++++++++++++++++++++++---------------- test/docs_unittest.py | 49 +++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 28 deletions(-) diff --git a/lib/rapi/connector.py b/lib/rapi/connector.py index 168d96067..eeae01e58 100644 --- a/lib/rapi/connector.py +++ b/lib/rapi/connector.py @@ -31,6 +31,9 @@ from ganeti import http from ganeti.rapi import baserlib from ganeti.rapi import rlib2 + +_NAME_PATTERN = r"[\w\._-]+" + # the connection map is created at the end of this file CONNECTOR = {} @@ -144,44 +147,62 @@ class R_2(baserlib.R_Generic): return baserlib.BuildUriList(_getResources("2"), "/2/%s") -CONNECTOR.update({ - "/": R_root, +def GetHandlers(node_name_pattern, instance_name_pattern, job_id_pattern): + """Returns all supported resources and their handlers. + + """ + return { + "/": R_root, + + "/version": rlib2.R_version, - "/version": rlib2.R_version, + "/2": R_2, - "/2": R_2, - "/2/jobs": rlib2.R_2_jobs, - "/2/nodes": rlib2.R_2_nodes, - re.compile(r'^/2/nodes/([\w\._-]+)$'): rlib2.R_2_nodes_name, - re.compile(r'^/2/nodes/([\w\._-]+)/tags$'): rlib2.R_2_nodes_name_tags, - re.compile(r'^/2/nodes/([\w\._-]+)/role$'): rlib2.R_2_nodes_name_role, - re.compile(r'^/2/nodes/([\w\._-]+)/evacuate$'): + "/2/nodes": rlib2.R_2_nodes, + re.compile(r'^/2/nodes/(%s)$' % node_name_pattern): + rlib2.R_2_nodes_name, + re.compile(r'^/2/nodes/(%s)/tags$' % node_name_pattern): + rlib2.R_2_nodes_name_tags, + re.compile(r'^/2/nodes/(%s)/role$' % node_name_pattern): + rlib2.R_2_nodes_name_role, + re.compile(r'^/2/nodes/(%s)/evacuate$' % node_name_pattern): rlib2.R_2_nodes_name_evacuate, - re.compile(r'^/2/nodes/([\w\._-]+)/migrate$'): + re.compile(r'^/2/nodes/(%s)/migrate$' % node_name_pattern): rlib2.R_2_nodes_name_migrate, - re.compile(r'^/2/nodes/([\w\._-]+)/storage$'): + re.compile(r'^/2/nodes/(%s)/storage$' % node_name_pattern): rlib2.R_2_nodes_name_storage, - re.compile(r'^/2/nodes/([\w\._-]+)/storage/modify$'): + re.compile(r'^/2/nodes/(%s)/storage/modify$' % node_name_pattern): rlib2.R_2_nodes_name_storage_modify, - re.compile(r'^/2/nodes/([\w\._-]+)/storage/repair$'): + re.compile(r'^/2/nodes/(%s)/storage/repair$' % node_name_pattern): rlib2.R_2_nodes_name_storage_repair, - "/2/instances": rlib2.R_2_instances, - re.compile(r'^/2/instances/([\w\._-]+)$'): rlib2.R_2_instances_name, - re.compile(r'^/2/instances/([\w\._-]+)/info$'): + + "/2/instances": rlib2.R_2_instances, + re.compile(r'^/2/instances/(%s)$' % instance_name_pattern): + rlib2.R_2_instances_name, + re.compile(r'^/2/instances/(%s)/info$' % instance_name_pattern): rlib2.R_2_instances_name_info, - re.compile(r'^/2/instances/([\w\._-]+)/tags$'): rlib2.R_2_instances_name_tags, - re.compile(r'^/2/instances/([\w\._-]+)/reboot$'): + re.compile(r'^/2/instances/(%s)/tags$' % instance_name_pattern): + rlib2.R_2_instances_name_tags, + re.compile(r'^/2/instances/(%s)/reboot$' % instance_name_pattern): rlib2.R_2_instances_name_reboot, - re.compile(r'^/2/instances/([\w\._-]+)/reinstall$'): + re.compile(r'^/2/instances/(%s)/reinstall$' % instance_name_pattern): rlib2.R_2_instances_name_reinstall, - re.compile(r'^/2/instances/([\w\._-]+)/replace-disks$'): + re.compile(r'^/2/instances/(%s)/replace-disks$' % instance_name_pattern): rlib2.R_2_instances_name_replace_disks, - re.compile(r'^/2/instances/([\w\._-]+)/shutdown$'): + re.compile(r'^/2/instances/(%s)/shutdown$' % instance_name_pattern): rlib2.R_2_instances_name_shutdown, - re.compile(r'^/2/instances/([\w\._-]+)/startup$'): + re.compile(r'^/2/instances/(%s)/startup$' % instance_name_pattern): rlib2.R_2_instances_name_startup, - re.compile(r'/2/jobs/(%s)$' % constants.JOB_ID_TEMPLATE): rlib2.R_2_jobs_id, - "/2/tags": rlib2.R_2_tags, - "/2/info": rlib2.R_2_info, - "/2/os": rlib2.R_2_os, - }) + + "/2/jobs": rlib2.R_2_jobs, + re.compile(r'/2/jobs/(%s)$' % job_id_pattern): + rlib2.R_2_jobs_id, + + "/2/tags": rlib2.R_2_tags, + "/2/info": rlib2.R_2_info, + "/2/os": rlib2.R_2_os, + } + + +CONNECTOR.update(GetHandlers(_NAME_PATTERN, _NAME_PATTERN, + constants.JOB_ID_TEMPLATE)) diff --git a/test/docs_unittest.py b/test/docs_unittest.py index 1021c3543..0ede50b8c 100755 --- a/test/docs_unittest.py +++ b/test/docs_unittest.py @@ -26,6 +26,7 @@ import re from ganeti import utils from ganeti import cmdlib +from ganeti.rapi import connector import testutils @@ -66,5 +67,53 @@ class TestDocs(unittest.TestCase): (lucls.HTYPE, lucls.HPATH))) + def testRapiDocs(self): + """Check whether all RAPI resources are documented. + + """ + rapidoc = self._ReadDocFile("rapi.rst") + + node_name = "[node_name]" + instance_name = "[instance_name]" + job_id = "[job_id]" + + resources = connector.GetHandlers(re.escape(node_name), + re.escape(instance_name), + re.escape(job_id)) + + titles = [] + + prevline = None + for line in rapidoc.splitlines(): + if re.match(r"^\++$", line): + titles.append(prevline) + + prevline = line + + undocumented = [] + + for key, handler in resources.iteritems(): + # Regex objects + if hasattr(key, "match"): + found = False + for title in titles: + if (title.startswith("``") and + title.endswith("``") and + key.match(title[2:-2])): + found = True + break + + if not found: + # TODO: Find better way of identifying resource + undocumented.append(str(handler)) + + elif ("``%s``" % key) not in titles: + undocumented.append(key) + + self.failIf(undocumented, + msg=("Missing RAPI resource documentation for %s" % + utils.CommaJoin(undocumented))) + + if __name__ == "__main__": unittest.main() -- GitLab