diff --git a/Makefile.am b/Makefile.am
index 42b41b447c9e1d190af6bb81882f8f050905d932..d441fae76145d92bc54a7a4aed3b2618b606a170 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,7 +11,6 @@ abs_top_srcdir = @abs_top_srcdir@
 
 ACLOCAL_AMFLAGS = -I autotools
 DOCBOOK_WRAPPER = $(top_srcdir)/autotools/docbook-wrapper
-BUILD_RAPI_RESOURCE_DOC = $(top_srcdir)/doc/build-rapi-resources-doc
 REPLACE_VARS_SED = autotools/replace_vars.sed
 
 hypervisordir = $(pkgpythondir)/hypervisor
@@ -45,7 +44,6 @@ MAINTAINERCLEANFILES = \
 CLEANFILES = \
 	autotools/replace_vars.sed \
 	devel/upload \
-	doc/rapi-resources.gen \
 	doc/examples/bash_completion \
 	doc/examples/ganeti.initd \
 	doc/examples/ganeti.cron \
@@ -114,6 +112,7 @@ docrst = \
 	doc/hooks.rst \
 	doc/iallocator.rst \
 	doc/install.rst \
+	doc/rapi.rst \
 	doc/security.rst
 
 dochtml = $(patsubst %.rst,%.html,$(docrst))
@@ -152,7 +151,6 @@ EXTRA_DIST = \
 	devel/upload.in \
 	$(docrst) \
 	$(docdot) \
-	doc/build-rapi-resources-doc \
 	doc/examples/bash_completion.in \
 	doc/examples/ganeti.initd.in \
 	doc/examples/ganeti.cron.in \
@@ -224,8 +222,6 @@ TESTS = $(dist_TESTS) $(nodist_TESTS)
 
 TESTS_ENVIRONMENT = PYTHONPATH=.:$(top_builddir)
 
-RAPI_RESOURCES = $(wildcard lib/rapi/*.py)
-
 all-local: stamp-directories lib/_autoconf.py devel/upload \
 	doc/examples/bash_completion \
 	doc/examples/ganeti.initd doc/examples/ganeti.cron
@@ -248,12 +244,6 @@ doc/%.png: doc/%.dot
 
 doc/design-2.0.html: doc/design-2.0.rst doc/arch-2.0.png
 
-doc/rapi.html: doc/rapi-resources.gen
-
-doc/rapi-resources.gen: $(BUILD_RAPI_RESOURCE_DOC) $(RAPI_RESOURCES)
-	PYTHONPATH=.:$(top_builddir) $(BUILD_RAPI_RESOURCE_DOC) > $@ || \
-	  rm -f $@
-
 man/%.7.in man/%.8.in: man/%.sgml man/footer.sgml $(DOCBOOK_WRAPPER)
 	@test -n "$(DOCBOOK2MAN)" || { echo 'docbook2html' not found during configure; exit 1; }
 	TMPDIR=`mktemp -d` && { \
@@ -281,7 +271,7 @@ man/%.html: man/%.html.in stamp-directories $(REPLACE_VARS_SED)
 
 man/footer.sgml $(TESTS): srclinks
 
-$(TESTS) $(BUILD_RAPI_RESOURCE_DOC): ganeti lib/_autoconf.py
+$(TESTS): ganeti lib/_autoconf.py
 
 lib/_autoconf.py: Makefile stamp-directories
 	set -e; \
diff --git a/doc/build-rapi-resources-doc b/doc/build-rapi-resources-doc
deleted file mode 100755
index 3bc1381579c2e35689813bc70912e5cab474d920..0000000000000000000000000000000000000000
--- a/doc/build-rapi-resources-doc
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/python
-#
-
-# Copyright (C) 2008 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
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
-
-"""Script to generate documentation for remote API resources.
-
-This is hard-coded to the section numbering we have in the master RST
-document.
-
-"""
-
-import re
-import cgi
-import inspect
-
-from ganeti.rapi import rlib2
-from ganeti.rapi import connector
-
-
-CHECKED_COMMANDS = ["GET", "POST", "PUT", "DELETE"]
-
-
-def beautify(text):
-  """A couple of small enhancements, epydoc-to-rst.
-
-  """
-  pairs = [
-    ("@return:", "Returns:"),
-    ]
-
-  for old, new in pairs:
-    text = text.replace(old, new)
-
-  return text
-
-
-def indent(text):
-  """Returns a text block with all lines indented.
-
-  """
-  lines = text.splitlines()
-  lines = ["  " + l for l in lines]
-  return "\n".join(lines)
-
-
-def main():
-  # Get list of all resources
-  all = list(connector.CONNECTOR.itervalues())
-
-  # Sort rlib by URI
-  all.sort(cmp=lambda a, b: cmp(a.DOC_URI, b.DOC_URI))
-
-  print ".. Automatically generated, do not edit\n"
-
-  for cls in all:
-    title = cls.DOC_URI
-    print "%s\n%s\n" % (title, "+" * len(title))
-
-    # Class docstring
-    description = inspect.getdoc(cls)
-    if description:
-      print "::\n"
-      print indent(description.strip())
-    print
-    supported = [cmd for cmd in CHECKED_COMMANDS if hasattr(cls, cmd)]
-    print "It supports the following commands: %s." % (", ".join(supported))
-    print
-
-    for cmd in CHECKED_COMMANDS:
-      if not hasattr(cls, cmd):
-        continue
-
-      print "%s\n%s\n" % (cmd, "~" * len(cmd))
-
-      # Get docstring
-      text = inspect.getdoc(getattr(cls, cmd))
-      if text:
-        text = beautify(text)
-        print "::\n"
-        print indent(text)
-        print
-
-
-if __name__ == "__main__":
-  main()
diff --git a/doc/rapi.rst b/doc/rapi.rst
index c2ed7c459789530186f84f5286daeab18f71626f..91bd1f7d6df59095ea48f292fcc94280a0c96a4f 100644
--- a/doc/rapi.rst
+++ b/doc/rapi.rst
@@ -82,4 +82,533 @@ JavaScript
 Resources
 ---------
 
-.. include:: rapi-resources.gen
+/
++
+
+::
+
+  / resource.
+
+It supports the following commands: GET.
+
+GET
+~~~
+
+::
+
+  Show the list of mapped resources.
+
+  Returns: a dictionary with 'name' and 'uri' keys for each of them.
+
+/2
+++
+
+::
+
+  /2 resource, the root of the version 2 API.
+
+It supports the following commands: GET.
+
+GET
+~~~
+
+::
+
+  Show the list of mapped resources.
+
+  Returns: a dictionary with 'name' and 'uri' keys for each of them.
+
+/2/info
++++++++
+
+::
+
+  Cluster info.
+
+It supports the following commands: GET.
+
+GET
+~~~
+
+::
+
+  Returns cluster information.
+
+  Example::
+
+  {
+    "config_version": 2000000,
+    "name": "cluster",
+    "software_version": "2.0.0~beta2",
+    "os_api_version": 10,
+    "export_version": 0,
+    "candidate_pool_size": 10,
+    "enabled_hypervisors": [
+      "fake"
+    ],
+    "hvparams": {
+      "fake": {}
+     },
+    "default_hypervisor": "fake",
+    "master": "node1.example.com",
+    "architecture": [
+      "64bit",
+      "x86_64"
+    ],
+    "protocol_version": 20,
+    "beparams": {
+      "default": {
+        "auto_balance": true,
+        "vcpus": 1,
+        "memory": 128
+       }
+      }
+    }
+
+/2/instances
+++++++++++++
+
+::
+
+  /2/instances resource.
+
+It supports the following commands: GET, POST.
+
+GET
+~~~
+
+::
+
+  Returns a list of all available instances.
+
+
+  Example::
+
+    [
+      {
+        "name": "web.example.com",
+        "uri": "\/instances\/web.example.com"
+      },
+      {
+        "name": "mail.example.com",
+        "uri": "\/instances\/mail.example.com"
+      }
+    ]
+
+  If the optional 'bulk' argument is provided and set to 'true'
+  value (i.e '?bulk=1'), the output contains detailed
+  information about instances as a list.
+
+  Example::
+
+    [
+      {
+         "status": "running",
+         "disk_usage": 20480,
+         "nic.bridges": [
+           "xen-br0"
+          ],
+         "name": "web.example.com",
+         "tags": ["tag1", "tag2"],
+         "beparams": {
+           "vcpus": 2,
+           "memory": 512
+         },
+         "disk.sizes": [
+             20480
+         ],
+         "pnode": "node1.example.com",
+         "nic.macs": ["01:23:45:67:89:01"],
+         "snodes": ["node2.example.com"],
+         "disk_template": "drbd",
+         "admin_state": true,
+         "os": "debian-etch",
+         "oper_state": true
+      },
+      ...
+    ]
+
+  Returns: a dictionary with 'name' and 'uri' keys for each of them.
+
+POST
+~~~~
+
+::
+
+  Create an instance.
+
+  Returns: a job id
+
+/2/instances/[instance_name]
+++++++++++++++++++++++++++++
+
+::
+
+  /2/instances/[instance_name] resources.
+
+It supports the following commands: GET, DELETE.
+
+GET
+~~~
+
+::
+
+  Send information about an instance.
+
+
+
+DELETE
+~~~~~~
+
+::
+
+  Delete an instance.
+
+
+
+/2/instances/[instance_name]/reboot
++++++++++++++++++++++++++++++++++++
+
+::
+
+  /2/instances/[instance_name]/reboot resource.
+
+  Implements an instance reboot.
+
+It supports the following commands: POST.
+
+POST
+~~~~
+
+::
+
+  Reboot an instance.
+
+  The URI takes type=[hard|soft|full] and
+  ignore_secondaries=[False|True] parameters.
+
+/2/instances/[instance_name]/shutdown
++++++++++++++++++++++++++++++++++++++
+
+::
+
+  /2/instances/[instance_name]/shutdown resource.
+
+  Implements an instance shutdown.
+
+It supports the following commands: PUT.
+
+PUT
+~~~
+
+::
+
+  Shutdown an instance.
+
+
+
+/2/instances/[instance_name]/startup
+++++++++++++++++++++++++++++++++++++
+
+::
+
+  /2/instances/[instance_name]/startup resource.
+
+  Implements an instance startup.
+
+It supports the following commands: PUT.
+
+PUT
+~~~
+
+::
+
+  Startup an instance.
+
+  The URI takes force=[False|True] parameter to start the instance
+  if even if secondary disks are failing.
+
+/2/instances/[instance_name]/tags
++++++++++++++++++++++++++++++++++
+
+::
+
+  /2/instances/[instance_name]/tags resource.
+
+  Manages per-instance tags.
+
+It supports the following commands: GET, PUT, DELETE.
+
+GET
+~~~
+
+::
+
+  Returns a list of tags.
+
+  Example: ["tag1", "tag2", "tag3"]
+
+PUT
+~~~
+
+::
+
+  Add a set of tags.
+
+  The request as a list of strings should be PUT to this URI. And
+  you'll have back a job id.
+
+DELETE
+~~~~~~
+
+::
+
+  Delete a tag.
+
+  In order to delete a set of tags, the DELETE
+  request should be addressed to URI like:
+  /tags?tag=[tag]&tag=[tag]
+
+/2/jobs
++++++++
+
+::
+
+  /2/jobs resource.
+
+It supports the following commands: GET.
+
+GET
+~~~
+
+::
+
+  Returns a dictionary of jobs.
+
+  Returns: a dictionary with jobs id and uri.
+
+/2/jobs/[job_id]
+++++++++++++++++
+
+::
+
+  /2/jobs/[job_id] resource.
+
+It supports the following commands: GET, DELETE.
+
+GET
+~~~
+
+::
+
+  Returns a job status.
+
+  Returns: a dictionary with job parameters.
+      The result includes:
+          - id: job ID as a number
+          - status: current job status as a string
+          - ops: involved OpCodes as a list of dictionaries for each
+            opcodes in the job
+          - opstatus: OpCodes status as a list
+          - opresult: OpCodes results as a list of lists
+
+DELETE
+~~~~~~
+
+::
+
+  Cancel not-yet-started job.
+
+
+
+/2/nodes
+++++++++
+
+::
+
+  /2/nodes resource.
+
+It supports the following commands: GET.
+
+GET
+~~~
+
+::
+
+  Returns a list of all nodes.
+
+  Example::
+
+    [
+      {
+        "id": "node1.example.com",
+        "uri": "\/instances\/node1.example.com"
+      },
+      {
+        "id": "node2.example.com",
+        "uri": "\/instances\/node2.example.com"
+      }
+    ]
+
+  If the optional 'bulk' argument is provided and set to 'true'
+  value (i.e '?bulk=1'), the output contains detailed
+  information about nodes as a list.
+
+  Example::
+
+    [
+      {
+        "pinst_cnt": 1,
+        "mfree": 31280,
+        "mtotal": 32763,
+        "name": "www.example.com",
+        "tags": [],
+        "mnode": 512,
+        "dtotal": 5246208,
+        "sinst_cnt": 2,
+        "dfree": 5171712,
+        "offline": false
+      },
+      ...
+    ]
+
+  Returns: a dictionary with 'name' and 'uri' keys for each of them
+
+/2/nodes/[node_name]/tags
++++++++++++++++++++++++++
+
+::
+
+  /2/nodes/[node_name]/tags resource.
+
+  Manages per-node tags.
+
+It supports the following commands: GET, PUT, DELETE.
+
+GET
+~~~
+
+::
+
+  Returns a list of tags.
+
+  Example: ["tag1", "tag2", "tag3"]
+
+PUT
+~~~
+
+::
+
+  Add a set of tags.
+
+  The request as a list of strings should be PUT to this URI. And
+  you'll have back a job id.
+
+DELETE
+~~~~~~
+
+::
+
+  Delete a tag.
+
+  In order to delete a set of tags, the DELETE
+  request should be addressed to URI like:
+  /tags?tag=[tag]&tag=[tag]
+
+/2/os
++++++
+
+::
+
+  /2/os resource.
+
+It supports the following commands: GET.
+
+GET
+~~~
+
+::
+
+  Return a list of all OSes.
+
+  Can return error 500 in case of a problem.
+
+  Example: ["debian-etch"]
+
+/2/tags
++++++++
+
+::
+
+  /2/instances/tags resource.
+
+  Manages cluster tags.
+
+It supports the following commands: GET, PUT, DELETE.
+
+GET
+~~~
+
+::
+
+  Returns a list of tags.
+
+  Example: ["tag1", "tag2", "tag3"]
+
+PUT
+~~~
+
+::
+
+  Add a set of tags.
+
+  The request as a list of strings should be PUT to this URI. And
+  you'll have back a job id.
+
+DELETE
+~~~~~~
+
+::
+
+  Delete a tag.
+
+  In order to delete a set of tags, the DELETE
+  request should be addressed to URI like:
+  /tags?tag=[tag]&tag=[tag]
+
+/nodes/[node_name]
+++++++++++++++++++
+
+::
+
+  /2/nodes/[node_name] resources.
+
+It supports the following commands: GET.
+
+GET
+~~~
+
+::
+
+  Send information about a node.
+
+
+
+/version
+++++++++
+
+::
+
+  /version resource.
+
+  This resource should be used to determine the remote API version and
+  to adapt clients accordingly.
+
+It supports the following commands: GET.
+
+GET
+~~~
+
+::
+
+  Returns the remote API version.